diff --git a/Core/Core.vcxproj b/Core/Core.vcxproj index c13af5654..6432703cc 100644 --- a/Core/Core.vcxproj +++ b/Core/Core.vcxproj @@ -114,6 +114,15 @@ + + + + + + + + + @@ -794,6 +803,7 @@ + @@ -938,6 +948,9 @@ + + + @@ -956,6 +969,9 @@ + + + diff --git a/Core/Core.vcxproj.filters b/Core/Core.vcxproj.filters index 18df80e88..5e6db8e0b 100644 --- a/Core/Core.vcxproj.filters +++ b/Core/Core.vcxproj.filters @@ -2943,6 +2943,33 @@ GBA + + SNES\Coprocessors\ST018 + + + SNES\Coprocessors\ST018 + + + SNES\Coprocessors\ST018 + + + SNES\Coprocessors\ST018 + + + SNES\Debugger + + + SNES\Debugger\TraceLogger + + + SNES\Debugger + + + SNES\Debugger + + + Shared + @@ -3284,6 +3311,27 @@ NES\Mappers\Homebrew + + SNES\Coprocessors\ST018 + + + SNES\Coprocessors\ST018 + + + SNES\Debugger + + + SNES\Debugger\TraceLogger + + + SNES\Debugger + + + Debugger + + + SNES\Debugger + @@ -3406,5 +3454,8 @@ {e8ed2b40-f0e0-4dc5-89b7-9acc460359c8} + + {387c0b44-8063-45e8-a28a-b6d9e27b5a3f} + \ No newline at end of file diff --git a/Core/Debugger/DebugUtilities.h b/Core/Debugger/DebugUtilities.h index 3a76e3189..74abb206e 100644 --- a/Core/Debugger/DebugUtilities.h +++ b/Core/Debugger/DebugUtilities.h @@ -16,6 +16,7 @@ class DebugUtilities case CpuType::Sa1: return MemoryType::Sa1Memory; case CpuType::Gsu: return MemoryType::GsuMemory; case CpuType::Cx4: return MemoryType::Cx4Memory; + case CpuType::St018: return MemoryType::St018Memory; case CpuType::Gameboy: return MemoryType::GameboyMemory; case CpuType::Nes: return MemoryType::NesMemory; case CpuType::Pce: return MemoryType::PceMemory; @@ -36,6 +37,7 @@ class DebugUtilities case CpuType::Sa1: return 6; case CpuType::Gsu: return 6; case CpuType::Cx4: return 6; + case CpuType::St018: return 8; case CpuType::Gameboy: return 4; case CpuType::Nes: return 4; case CpuType::Pce: return 4; @@ -85,7 +87,13 @@ class DebugUtilities case MemoryType::Cx4DataRam: case MemoryType::Cx4Memory: return CpuType::Cx4; - + + case MemoryType::St018Memory: + case MemoryType::St018PrgRom: + case MemoryType::St018DataRom: + case MemoryType::St018WorkRam: + return CpuType::St018; + case MemoryType::GbPrgRom: case MemoryType::GbWorkRam: case MemoryType::GbCartRam: @@ -222,6 +230,8 @@ class DebugUtilities case MemoryType::PcePrgRom: case MemoryType::DspDataRom: case MemoryType::DspProgramRom: + case MemoryType::St018PrgRom: + case MemoryType::St018DataRom: case MemoryType::SpcRom: case MemoryType::SmsPrgRom: case MemoryType::SmsBootRom: diff --git a/Core/Debugger/Debugger.cpp b/Core/Debugger/Debugger.cpp index c42c26d51..91214a7f1 100644 --- a/Core/Debugger/Debugger.cpp +++ b/Core/Debugger/Debugger.cpp @@ -25,9 +25,11 @@ #include "SNES/Coprocessors/GSU/GsuTypes.h" #include "SNES/Coprocessors/CX4/Cx4Types.h" #include "SNES/Coprocessors/DSP/NecDspTypes.h" +#include "SNES/Coprocessors/ST018/ArmV3Types.h" #include "SNES/Debugger/SnesDebugger.h" #include "SNES/Debugger/SpcDebugger.h" #include "SNES/Debugger/GsuDebugger.h" +#include "SNES/Debugger/St018Debugger.h" #include "SNES/Debugger/NecDspDebugger.h" #include "SNES/Debugger/Cx4Debugger.h" #include "NES/Debugger/NesDebugger.h" @@ -91,6 +93,7 @@ Debugger::Debugger(Emulator* emu, IConsole* console) case CpuType::Sa1: debugger.reset(new SnesDebugger(this, CpuType::Sa1)); break; case CpuType::Gsu: debugger.reset(new GsuDebugger(this)); break; case CpuType::Cx4: debugger.reset(new Cx4Debugger(this)); break; + case CpuType::St018: debugger.reset(new St018Debugger(this)); break; case CpuType::Gameboy: debugger.reset(new GbDebugger(this)); break; case CpuType::Nes: debugger.reset(new NesDebugger(this)); break; case CpuType::Pce: debugger.reset(new PceDebugger(this)); break; @@ -178,6 +181,7 @@ uint64_t Debugger::GetCpuCycleCount() case CpuType::Sa1: return GetDebugger()->GetCpuCycleCount(); case CpuType::Gsu: return GetDebugger()->GetCpuCycleCount(); case CpuType::Cx4: return GetDebugger()->GetCpuCycleCount(); + case CpuType::St018: return GetDebugger()->GetCpuCycleCount(); case CpuType::Gameboy: return GetDebugger()->GetCpuCycleCount(); case CpuType::Nes: return GetDebugger()->GetCpuCycleCount(); case CpuType::Pce: return GetDebugger()->GetCpuCycleCount(); @@ -225,6 +229,7 @@ void Debugger::ProcessInstruction() case CpuType::Sa1: GetDebugger()->ProcessInstruction(); break; case CpuType::Gsu: GetDebugger()->ProcessInstruction(); break; case CpuType::Cx4: GetDebugger()->ProcessInstruction(); break; + case CpuType::St018: GetDebugger()->ProcessInstruction(); break; case CpuType::Gameboy: GetDebugger()->ProcessInstruction(); break; case CpuType::Nes: GetDebugger()->ProcessInstruction(); break; case CpuType::Pce: GetDebugger()->ProcessInstruction(); break; @@ -258,6 +263,7 @@ void Debugger::ProcessMemoryRead(uint32_t addr, T& value, MemoryOperationType op case CpuType::Sa1: GetDebugger()->ProcessRead(addr, value, opType); break; case CpuType::Gsu: GetDebugger()->ProcessRead(addr, value, opType); break; case CpuType::Cx4: GetDebugger()->ProcessRead(addr, value, opType); break; + case CpuType::St018: GetDebugger()->ProcessRead(addr, value, opType); break; case CpuType::Gameboy: GetDebugger()->ProcessRead(addr, value, opType); break; case CpuType::Nes: GetDebugger()->ProcessRead(addr, value, opType); break; case CpuType::Pce: GetDebugger()->ProcessRead(addr, value, opType); break; @@ -290,6 +296,7 @@ bool Debugger::ProcessMemoryWrite(uint32_t addr, T& value, MemoryOperationType o case CpuType::Sa1: GetDebugger()->ProcessWrite(addr, value, opType); break; case CpuType::Gsu: GetDebugger()->ProcessWrite(addr, value, opType); break; case CpuType::Cx4: GetDebugger()->ProcessWrite(addr, value, opType); break; + case CpuType::St018: GetDebugger()->ProcessWrite(addr, value, opType); break; case CpuType::Gameboy: GetDebugger()->ProcessWrite(addr, value, opType); break; case CpuType::Nes: GetDebugger()->ProcessWrite(addr, value, opType); break; case CpuType::Pce: GetDebugger()->ProcessWrite(addr, value, opType); break; @@ -768,6 +775,7 @@ bool Debugger::IsDebugWindowOpened(CpuType cpuType) case CpuType::Sa1: return _settings->CheckDebuggerFlag(DebuggerFlags::Sa1DebuggerEnabled); case CpuType::Gsu: return _settings->CheckDebuggerFlag(DebuggerFlags::GsuDebuggerEnabled); case CpuType::Cx4: return _settings->CheckDebuggerFlag(DebuggerFlags::Cx4DebuggerEnabled); + case CpuType::St018: return _settings->CheckDebuggerFlag(DebuggerFlags::St018DebuggerEnabled); case CpuType::Gameboy: return _settings->CheckDebuggerFlag(DebuggerFlags::GbDebuggerEnabled); case CpuType::Nes: return _settings->CheckDebuggerFlag(DebuggerFlags::NesDebuggerEnabled); case CpuType::Pce: return _settings->CheckDebuggerFlag(DebuggerFlags::PceDebuggerEnabled); @@ -819,6 +827,7 @@ void Debugger::GetCpuState(BaseState &dstState, CpuType cpuType) case CpuType::Sa1: memcpy(&dstState, &srcState, sizeof(SnesCpuState)); break; case CpuType::Gsu: memcpy(&dstState, &srcState, sizeof(GsuState)); break; case CpuType::Cx4: memcpy(&dstState, &srcState, sizeof(Cx4State)); break; + case CpuType::St018: memcpy(&dstState, &srcState, sizeof(ArmV3CpuState)); break; case CpuType::Gameboy: memcpy(&dstState, &srcState, sizeof(GbCpuState)); break; case CpuType::Nes: memcpy(&dstState, &srcState, sizeof(NesCpuState)); break; case CpuType::Pce: memcpy(&dstState, &srcState, sizeof(PceCpuState)); break; @@ -839,6 +848,7 @@ void Debugger::SetCpuState(BaseState& srcState, CpuType cpuType) case CpuType::Sa1: memcpy(&dstState, &srcState, sizeof(SnesCpuState)); break; case CpuType::Gsu: memcpy(&dstState, &srcState, sizeof(GsuState)); break; case CpuType::Cx4: memcpy(&dstState, &srcState, sizeof(Cx4State)); break; + case CpuType::St018: memcpy(&dstState, &srcState, sizeof(ArmV3CpuState)); break; case CpuType::Gameboy: memcpy(&dstState, &srcState, sizeof(GbCpuState)); break; case CpuType::Nes: memcpy(&dstState, &srcState, sizeof(NesCpuState)); break; case CpuType::Pce: memcpy(&dstState, &srcState, sizeof(PceCpuState)); break; @@ -861,7 +871,8 @@ void Debugger::GetPpuState(BaseState& state, CpuType cpuType) case CpuType::NecDsp: case CpuType::Sa1: case CpuType::Gsu: - case CpuType::Cx4: { + case CpuType::Cx4: + case CpuType::St018: { GetDebugger()->GetPpuState(state); break; } @@ -907,7 +918,8 @@ void Debugger::SetPpuState(BaseState& state, CpuType cpuType) case CpuType::NecDsp: case CpuType::Sa1: case CpuType::Gsu: - case CpuType::Cx4: { + case CpuType::Cx4: + case CpuType::St018: { GetDebugger()->SetPpuState(state); break; } @@ -1168,6 +1180,7 @@ template void Debugger::ProcessInstruction(); template void Debugger::ProcessInstruction(); template void Debugger::ProcessInstruction(); template void Debugger::ProcessInstruction(); +template void Debugger::ProcessInstruction(); template void Debugger::ProcessInstruction(); template void Debugger::ProcessInstruction(); template void Debugger::ProcessInstruction(); @@ -1183,6 +1196,8 @@ template void Debugger::ProcessMemoryRead(uint32_t addr, uint8_t& template void Debugger::ProcessMemoryRead(uint32_t addr, uint32_t& value, MemoryOperationType opType); template void Debugger::ProcessMemoryRead(uint32_t addr, uint16_t& value, MemoryOperationType opType); template void Debugger::ProcessMemoryRead(uint32_t addr, uint8_t& value, MemoryOperationType opType); +template void Debugger::ProcessMemoryRead(uint32_t addr, uint32_t& value, MemoryOperationType opType); +template void Debugger::ProcessMemoryRead(uint32_t addr, uint32_t& value, MemoryOperationType opType); template void Debugger::ProcessMemoryRead(uint32_t addr, uint8_t& value, MemoryOperationType opType); template void Debugger::ProcessMemoryRead(uint32_t addr, uint8_t& value, MemoryOperationType opType); template void Debugger::ProcessMemoryRead(uint32_t addr, uint8_t& value, MemoryOperationType opType); @@ -1200,6 +1215,8 @@ template bool Debugger::ProcessMemoryWrite(uint32_t addr, uint8_t& value, MemoryOperationType opType); template bool Debugger::ProcessMemoryWrite(uint32_t addr, uint16_t& value, MemoryOperationType opType); template bool Debugger::ProcessMemoryWrite(uint32_t addr, uint8_t& value, MemoryOperationType opType); +template bool Debugger::ProcessMemoryWrite(uint32_t addr, uint32_t& value, MemoryOperationType opType); +template bool Debugger::ProcessMemoryWrite(uint32_t addr, uint32_t& value, MemoryOperationType opType); template bool Debugger::ProcessMemoryWrite(uint32_t addr, uint8_t& value, MemoryOperationType opType); template bool Debugger::ProcessMemoryWrite(uint32_t addr, uint8_t& value, MemoryOperationType opType); template bool Debugger::ProcessMemoryWrite(uint32_t addr, uint8_t& value, MemoryOperationType opType); diff --git a/Core/Debugger/Disassembler.cpp b/Core/Debugger/Disassembler.cpp index f5eb33652..a65af64bb 100644 --- a/Core/Debugger/Disassembler.cpp +++ b/Core/Debugger/Disassembler.cpp @@ -13,6 +13,7 @@ #include "SNES/SpcTypes.h" #include "SNES/Coprocessors/GSU/GsuTypes.h" #include "SNES/Coprocessors/CX4/Cx4Types.h" +#include "SNES/Coprocessors/ST018/ArmV3Types.h" #include "Gameboy/GbTypes.h" #include "GBA/GbaTypes.h" #include "NES/NesTypes.h" @@ -312,6 +313,8 @@ void Disassembler::GetLineData(DisassemblyResult& row, CpuType type, MemoryType data.Flags = row.Flags; data.LineCpuType = type; + //TODO move color logic to UI and complete missing data? + switch(row.Address.Type) { default: break; case MemoryType::GbPrgRom: @@ -461,6 +464,27 @@ void Disassembler::GetLineData(DisassemblyResult& row, CpuType type, MemoryType break; } + case CpuType::St018: + { + ArmV3CpuState state = (ArmV3CpuState&)_debugger->GetCpuStateRef(lineCpuType); + if(!disInfo.IsInitialized()) { + disInfo = DisassemblyInfo(row.Address.Address, state.CPSR.ToInt32(), CpuType::St018, row.Address.Type, _memoryDumper); + } else { + data.Flags |= LineFlags::VerifiedCode; + } + + data.OpSize = disInfo.GetOpSize(); + + state.Pipeline.Execute.Address = (uint32_t)row.CpuAddress; + state.R[15] = state.Pipeline.Execute.Address + (data.OpSize * 2); + + data.EffectiveAddress = disInfo.GetEffectiveAddress(_debugger, &state, lineCpuType); + if(showMemoryValues && data.EffectiveAddress.ValueSize >= 0) { + data.Value = disInfo.GetMemoryValue(data.EffectiveAddress, _memoryDumper, memType); + } + break; + } + case CpuType::Gameboy: { GbCpuState state = (GbCpuState&)_debugger->GetCpuStateRef(lineCpuType); diff --git a/Core/Debugger/DisassemblyInfo.cpp b/Core/Debugger/DisassemblyInfo.cpp index 63730549c..0f61a64c9 100644 --- a/Core/Debugger/DisassemblyInfo.cpp +++ b/Core/Debugger/DisassemblyInfo.cpp @@ -13,6 +13,7 @@ #include "SNES/Debugger/GsuDisUtils.h" #include "SNES/Debugger/NecDspDisUtils.h" #include "SNES/Debugger/Cx4DisUtils.h" +#include "SNES/Debugger/St018DisUtils.h" #include "Gameboy/Debugger/GameboyDisUtils.h" #include "NES/Debugger/NesDisUtils.h" #include "PCE/Debugger/PceDisUtils.h" @@ -73,6 +74,7 @@ void DisassemblyInfo::GetDisassembly(string &out, uint32_t memoryAddr, LabelMana case CpuType::NecDsp: NecDspDisUtils::GetDisassembly(*this, out, memoryAddr, labelManager, settings); break; case CpuType::Gsu: GsuDisUtils::GetDisassembly(*this, out, memoryAddr, labelManager, settings); break; case CpuType::Cx4: Cx4DisUtils::GetDisassembly(*this, out, memoryAddr, labelManager, settings); break; + case CpuType::St018: GbaDisUtils::GetDisassembly(*this, out, memoryAddr, labelManager, settings); break; case CpuType::Gameboy: GameboyDisUtils::GetDisassembly(*this, out, memoryAddr, labelManager, settings); break; case CpuType::Nes: NesDisUtils::GetDisassembly(*this, out, memoryAddr, labelManager, settings); break; case CpuType::Pce: PceDisUtils::GetDisassembly(*this, out, memoryAddr, labelManager, settings); break; @@ -96,6 +98,7 @@ EffectiveAddressInfo DisassemblyInfo::GetEffectiveAddress(Debugger *debugger, vo case CpuType::Spc: return SpcDisUtils::GetEffectiveAddress(*this, (SnesConsole*)debugger->GetConsole(), *(SpcState*)cpuState); case CpuType::Gsu: return GsuDisUtils::GetEffectiveAddress(*this, (SnesConsole*)debugger->GetConsole(), *(GsuState*)cpuState); case CpuType::Cx4: return Cx4DisUtils::GetEffectiveAddress(*this, *(Cx4State*)cpuState, debugger->GetMemoryDumper()); + case CpuType::St018: return St018DisUtils::GetEffectiveAddress(*this, (SnesConsole*)debugger->GetConsole(), *(ArmV3CpuState*)cpuState); case CpuType::NecDsp: return {}; @@ -136,6 +139,7 @@ uint32_t DisassemblyInfo::GetFullOpCode() default: return _byteCode[0]; case CpuType::NecDsp: return _byteCode[0] | (_byteCode[1] << 8) | (_byteCode[2] << 16); case CpuType::Cx4: return _byteCode[1]; + case CpuType::St018: return _byteCode[0] | (_byteCode[1] << 8) | (_opSize == 4 ? ((_byteCode[2] << 16) | (_byteCode[3] << 24)) : 0); case CpuType::Gba: return _byteCode[0] | (_byteCode[1] << 8) | (_opSize == 4 ? ((_byteCode[2] << 16) | (_byteCode[3] << 24)) : 0); case CpuType::Ws: return WsDisUtils::GetFullOpCode(*this); } @@ -182,6 +186,7 @@ uint8_t DisassemblyInfo::GetOpSize(uint32_t opCode, uint8_t flags, CpuType type, case CpuType::Sa1:return SnesDisUtils::GetOpSize(opCode, flags); case CpuType::Gsu: return GsuDisUtils::GetOpSize(opCode); case CpuType::Cx4: return Cx4DisUtils::GetOpSize(); + case CpuType::St018: return GbaDisUtils::GetOpSize(opCode, flags); case CpuType::Gameboy: return GameboyDisUtils::GetOpSize(opCode); case CpuType::Nes: return NesDisUtils::GetOpSize(opCode); case CpuType::Pce: return PceDisUtils::GetOpSize(opCode); @@ -202,6 +207,7 @@ bool DisassemblyInfo::IsJumpToSub() case CpuType::Sa1: return SnesDisUtils::IsJumpToSub(GetOpCode()); case CpuType::Gsu: return false; //GSU has no JSR op codes case CpuType::Cx4: return Cx4DisUtils::IsJumpToSub(GetFullOpCode()); + case CpuType::St018: return GbaDisUtils::IsJumpToSub(GetFullOpCode(), _flags); case CpuType::Gameboy: return GameboyDisUtils::IsJumpToSub(GetOpCode()); case CpuType::Nes: return NesDisUtils::IsJumpToSub(GetOpCode()); case CpuType::Pce: return PceDisUtils::IsJumpToSub(GetOpCode()); @@ -222,6 +228,7 @@ bool DisassemblyInfo::IsReturnInstruction() case CpuType::Sa1: return SnesDisUtils::IsReturnInstruction(GetOpCode()); case CpuType::Gsu: return false; //GSU has no RTS/RTI op codes case CpuType::Cx4: return Cx4DisUtils::IsReturnInstruction(GetFullOpCode()); + case CpuType::St018: return GbaDisUtils::IsReturnInstruction(GetFullOpCode(), _flags); case CpuType::Gameboy: return GameboyDisUtils::IsReturnInstruction(GetOpCode()); case CpuType::Nes: return NesDisUtils::IsReturnInstruction(GetOpCode()); case CpuType::Pce: return PceDisUtils::IsReturnInstruction(GetOpCode()); @@ -257,6 +264,7 @@ bool DisassemblyInfo::IsUnconditionalJump() case CpuType::Sa1: return SnesDisUtils::IsUnconditionalJump(GetOpCode()); case CpuType::Gsu: return GsuDisUtils::IsUnconditionalJump(GetOpCode()); case CpuType::Cx4: return Cx4DisUtils::IsUnconditionalJump(GetFullOpCode()); + case CpuType::St018: return GbaDisUtils::IsUnconditionalJump(GetFullOpCode(), _flags); case CpuType::Gameboy: return GameboyDisUtils::IsUnconditionalJump(GetOpCode()); case CpuType::Nes: return NesDisUtils::IsUnconditionalJump(GetOpCode()); case CpuType::Pce: return PceDisUtils::IsUnconditionalJump(GetOpCode()); @@ -282,6 +290,7 @@ bool DisassemblyInfo::IsJump() case CpuType::Sa1: return SnesDisUtils::IsConditionalJump(GetOpCode()); case CpuType::Gsu: return GsuDisUtils::IsConditionalJump(GetOpCode()); case CpuType::Cx4: return Cx4DisUtils::IsConditionalJump(GetFullOpCode(), GetByteCode()[0]); + case CpuType::St018: return GbaDisUtils::IsConditionalJump(GetFullOpCode(), GetByteCode()[0]); case CpuType::Gameboy: return GameboyDisUtils::IsConditionalJump(GetOpCode()); case CpuType::Nes: return NesDisUtils::IsConditionalJump(GetOpCode()); case CpuType::Pce: return PceDisUtils::IsConditionalJump(GetOpCode()); diff --git a/Core/Debugger/ExpressionEvaluator.St018.cpp b/Core/Debugger/ExpressionEvaluator.St018.cpp new file mode 100644 index 000000000..5cfe0bd75 --- /dev/null +++ b/Core/Debugger/ExpressionEvaluator.St018.cpp @@ -0,0 +1,58 @@ +#include "pch.h" +#include "Debugger/ExpressionEvaluator.h" +#include "SNES/Debugger/St018Debugger.h" +#include "SNES/Coprocessors/ST018/ArmV3Types.h" + +unordered_map& ExpressionEvaluator::GetSt018Tokens() +{ + static unordered_map supportedTokens = { + { "r0", EvalValues::R0 }, + { "r1", EvalValues::R1 }, + { "r2", EvalValues::R2 }, + { "r3", EvalValues::R3 }, + { "r4", EvalValues::R4 }, + { "r5", EvalValues::R5 }, + { "r6", EvalValues::R6 }, + { "r7", EvalValues::R7 }, + { "r8", EvalValues::R8 }, + { "r9", EvalValues::R9 }, + { "r10", EvalValues::R10 }, + { "r11", EvalValues::R11 }, + { "r12", EvalValues::R12 }, + { "r13", EvalValues::R13 }, + { "r14", EvalValues::R14 }, + { "r15", EvalValues::R15 }, + { "sp", EvalValues::R13 }, + { "lr", EvalValues::R14 }, + { "pc", EvalValues::R15 }, + { "cpsr", EvalValues::CPSR } + }; + + return supportedTokens; +} + +int64_t ExpressionEvaluator::GetSt018TokenValue(int64_t token, EvalResultType& resultType) +{ + ArmV3CpuState& s = (ArmV3CpuState&)((St018Debugger*)_cpuDebugger)->GetState(); + switch(token) { + case EvalValues::R0: return s.R[0]; + case EvalValues::R1: return s.R[1]; + case EvalValues::R2: return s.R[2]; + case EvalValues::R3: return s.R[3]; + case EvalValues::R4: return s.R[4]; + case EvalValues::R5: return s.R[5]; + case EvalValues::R6: return s.R[6]; + case EvalValues::R7: return s.R[7]; + case EvalValues::R8: return s.R[8]; + case EvalValues::R9: return s.R[9]; + case EvalValues::R10: return s.R[10]; + case EvalValues::R11: return s.R[11]; + case EvalValues::R12: return s.R[12]; + case EvalValues::R13: return s.R[13]; + case EvalValues::R14: return s.R[14]; + case EvalValues::R15: return s.R[15]; + case EvalValues::CPSR: return s.CPSR.ToInt32(); + + default: return 0; + } +} \ No newline at end of file diff --git a/Core/Debugger/ExpressionEvaluator.cpp b/Core/Debugger/ExpressionEvaluator.cpp index baab0e3c4..8b398ac6b 100644 --- a/Core/Debugger/ExpressionEvaluator.cpp +++ b/Core/Debugger/ExpressionEvaluator.cpp @@ -65,6 +65,7 @@ unordered_map* ExpressionEvaluator::GetAvailableTokens() case CpuType::Sa1: return &GetSnesTokens(); case CpuType::Gsu: return &GetGsuTokens(); case CpuType::Cx4: return &GetCx4Tokens(); + case CpuType::St018: return &GetSt018Tokens(); case CpuType::Gameboy: return &GetGameboyTokens(); case CpuType::Nes: return &GetNesTokens(); case CpuType::Pce: return &GetPceTokens(); @@ -420,6 +421,7 @@ int64_t ExpressionEvaluator::Evaluate(ExpressionData &data, EvalResultType &resu case CpuType::Sa1: token = GetSnesTokenValue(token, resultType); break; case CpuType::Gsu: token = GetGsuTokenValue(token, resultType); break; case CpuType::Cx4: token = GetCx4TokenValue(token, resultType); break; + case CpuType::St018: token = GetSt018TokenValue(token, resultType); break; case CpuType::Gameboy: token = GetGameboyTokenValue(token, resultType); break; case CpuType::Nes: token = GetNesTokenValue(token, resultType); break; case CpuType::Pce: token = GetPceTokenValue(token, resultType); break; diff --git a/Core/Debugger/ExpressionEvaluator.h b/Core/Debugger/ExpressionEvaluator.h index 17489b1bf..4a1d2b835 100644 --- a/Core/Debugger/ExpressionEvaluator.h +++ b/Core/Debugger/ExpressionEvaluator.h @@ -268,6 +268,9 @@ class ExpressionEvaluator unordered_map& GetNecDspTokens(); int64_t GetNecDspTokenValue(int64_t token, EvalResultType& resultType); + unordered_map& GetSt018Tokens(); + int64_t GetSt018TokenValue(int64_t token, EvalResultType& resultType); + unordered_map& GetGameboyTokens(); int64_t GetGameboyTokenValue(int64_t token, EvalResultType& resultType); diff --git a/Core/Debugger/MemoryDumper.cpp b/Core/Debugger/MemoryDumper.cpp index 6fb3f39b7..fc720d045 100644 --- a/Core/Debugger/MemoryDumper.cpp +++ b/Core/Debugger/MemoryDumper.cpp @@ -7,6 +7,7 @@ #include "SNES/Coprocessors/SA1/Sa1.h" #include "SNES/Coprocessors/CX4/Cx4.h" #include "SNES/Coprocessors/GSU/Gsu.h" +#include "SNES/Coprocessors/ST018/St018.h" #include "Gameboy/Gameboy.h" #include "Gameboy/GbMemoryManager.h" #include "SNES/Coprocessors/BSX/BsxCart.h" @@ -95,6 +96,7 @@ uint32_t MemoryDumper::GetMemorySize(MemoryType type) case MemoryType::Sa1Memory: return 0x1000000; case MemoryType::GsuMemory: return 0x1000000; case MemoryType::Cx4Memory: return 0x1000000; + case MemoryType::St018Memory: return 0x20000; case MemoryType::GameboyMemory: return 0x10000; case MemoryType::NesMemory: return 0x10000; case MemoryType::NesPpuMemory: return 0x4000; @@ -156,6 +158,14 @@ void MemoryDumper::GetMemoryState(MemoryType type, uint8_t *buffer) } break; + case MemoryType::St018Memory: + if(_cartridge->GetSt018()) { + for(int i = 0; i < 0x20000; i += 0x1000) { + _cartridge->GetSt018()->PeekBlock(i, buffer + i); + } + } + break; + case MemoryType::GameboyMemory: { if(_gameboy) { GbMemoryManager* memManager = _gameboy->GetMemoryManager(); @@ -268,6 +278,7 @@ void MemoryDumper::InternalSetMemoryValues(MemoryType originalMemoryType, uint32 case MemoryType::NecDspMemory: SetMemoryValue(MemoryType::DspProgramRom, address, value, disableSideEffects); return; case MemoryType::GsuMemory: _cartridge->GetGsu()->GetMemoryMappings()->DebugWrite(address, value); break; case MemoryType::Cx4Memory: _cartridge->GetCx4()->GetMemoryMappings()->DebugWrite(address, value); break; + case MemoryType::St018Memory: _cartridge->GetSt018()->DebugWrite(address, value); break; case MemoryType::GameboyMemory: _gameboy->GetMemoryManager()->DebugWrite(address, value); break; case MemoryType::NesMemory: _nesConsole->DebugWrite(address, value, disableSideEffects); break; case MemoryType::NesPpuMemory: _nesConsole->DebugWriteVram(address, value); break; @@ -362,6 +373,7 @@ uint8_t MemoryDumper::InternalGetMemoryValue(MemoryType memoryType, uint32_t add case MemoryType::NecDspMemory: return GetMemoryValue(MemoryType::DspProgramRom, address); case MemoryType::GsuMemory: return _cartridge->GetGsu()->GetMemoryMappings()->Peek(address); case MemoryType::Cx4Memory: return _cartridge->GetCx4()->GetMemoryMappings()->Peek(address); + case MemoryType::St018Memory: return _cartridge->GetSt018()->DebugRead(address); case MemoryType::GameboyMemory: return _gameboy->GetMemoryManager()->DebugRead(address); case MemoryType::NesMemory: return _nesConsole->DebugRead(address); case MemoryType::NesPpuMemory: return _nesConsole->DebugReadVram(address); diff --git a/Core/GBA/Debugger/GbaDisUtils.cpp b/Core/GBA/Debugger/GbaDisUtils.cpp index bd7896259..f1b8eae0d 100644 --- a/Core/GBA/Debugger/GbaDisUtils.cpp +++ b/Core/GBA/Debugger/GbaDisUtils.cpp @@ -403,7 +403,7 @@ void GbaDisUtils::ArmDisassemble(DisassemblyInfo& info, string& out, uint32_t me FastString str(settings->GetDebugConfig().UseLowerCaseDisassembly); uint32_t opCode = info.GetByteCode()[0] | (info.GetByteCode()[1] << 8) | (info.GetByteCode()[2] << 16) | (info.GetByteCode()[3] << 24); - GbaArmOpCategory category = GbaCpu::GetArmOpCategory(opCode); + ArmOpCategory category = GbaCpu::GetArmOpCategory(opCode); auto writeBranchTarget = [&str, labelManager](uint32_t addr) { AddressInfo relAddr = { (int32_t)addr, MemoryType::GbaMemory }; @@ -416,7 +416,7 @@ void GbaDisUtils::ArmDisassemble(DisassemblyInfo& info, string& out, uint32_t me }; switch(category) { - case GbaArmOpCategory::Branch: + case ArmOpCategory::Branch: { str.Write('B'); if(opCode & (1 << 24)) { @@ -430,42 +430,42 @@ void GbaDisUtils::ArmDisassemble(DisassemblyInfo& info, string& out, uint32_t me break; } - case GbaArmOpCategory::DataProcessing: + case ArmOpCategory::DataProcessing: { bool immediate = (opCode & (1 << 25)) != 0; uint8_t rn = (opCode >> 16) & 0x0F; uint8_t rd = (opCode >> 12) & 0x0F; bool updateFlags = (opCode & (1 << 20)) != 0; - GbaAluOperation aluOp = (GbaAluOperation)((opCode >> 21) & 0x0F); + ArmAluOperation aluOp = (ArmAluOperation)((opCode >> 21) & 0x0F); switch(aluOp) { - case GbaAluOperation::And: str.Write("AND"); break; - case GbaAluOperation::Eor: str.Write("EOR"); break; - case GbaAluOperation::Sub: str.Write("SUB"); break; - case GbaAluOperation::Rsb: str.Write("RSB"); break; - case GbaAluOperation::Add: str.Write("ADD"); break; - case GbaAluOperation::Adc: str.Write("ADC"); break; - case GbaAluOperation::Sbc: str.Write("SBC"); break; - case GbaAluOperation::Rsc: str.Write("RSC"); break; - case GbaAluOperation::Tst: str.Write("TST"); break; - case GbaAluOperation::Teq: str.Write("TEQ"); break; - case GbaAluOperation::Cmp: str.Write("CMP"); break; - case GbaAluOperation::Cmn: str.Write("CMN"); break; - case GbaAluOperation::Orr: str.Write("ORR"); break; - case GbaAluOperation::Mov: str.Write("MOV"); break; - case GbaAluOperation::Bic: str.Write("BIC"); break; - case GbaAluOperation::Mvn: str.Write("MVN"); break; + case ArmAluOperation::And: str.Write("AND"); break; + case ArmAluOperation::Eor: str.Write("EOR"); break; + case ArmAluOperation::Sub: str.Write("SUB"); break; + case ArmAluOperation::Rsb: str.Write("RSB"); break; + case ArmAluOperation::Add: str.Write("ADD"); break; + case ArmAluOperation::Adc: str.Write("ADC"); break; + case ArmAluOperation::Sbc: str.Write("SBC"); break; + case ArmAluOperation::Rsc: str.Write("RSC"); break; + case ArmAluOperation::Tst: str.Write("TST"); break; + case ArmAluOperation::Teq: str.Write("TEQ"); break; + case ArmAluOperation::Cmp: str.Write("CMP"); break; + case ArmAluOperation::Cmn: str.Write("CMN"); break; + case ArmAluOperation::Orr: str.Write("ORR"); break; + case ArmAluOperation::Mov: str.Write("MOV"); break; + case ArmAluOperation::Bic: str.Write("BIC"); break; + case ArmAluOperation::Mvn: str.Write("MVN"); break; } WriteCond(str, opCode); - bool isCmp = aluOp >= GbaAluOperation::Tst && aluOp <= GbaAluOperation::Cmn; + bool isCmp = aluOp >= ArmAluOperation::Tst && aluOp <= ArmAluOperation::Cmn; if(updateFlags && !isCmp) { str.Write('S'); } str.Write(' '); - if(aluOp == GbaAluOperation::Mov || aluOp == GbaAluOperation::Mvn) { + if(aluOp == ArmAluOperation::Mov || aluOp == ArmAluOperation::Mvn) { WriteReg(str, rd); } else if(isCmp) { WriteReg(str, rn); @@ -521,7 +521,7 @@ void GbaDisUtils::ArmDisassemble(DisassemblyInfo& info, string& out, uint32_t me break; } - case GbaArmOpCategory::SingleDataTransfer: + case ArmOpCategory::SingleDataTransfer: { bool immediate = (opCode & (1 << 25)) == 0; bool pre = (opCode & (1 << 24)) != 0; @@ -593,7 +593,7 @@ void GbaDisUtils::ArmDisassemble(DisassemblyInfo& info, string& out, uint32_t me break; } - case GbaArmOpCategory::Mrs: + case ArmOpCategory::Mrs: { uint8_t rd = (opCode >> 12) & 0x0F; bool psrSrc = opCode & (1 << 22); @@ -606,7 +606,7 @@ void GbaDisUtils::ArmDisassemble(DisassemblyInfo& info, string& out, uint32_t me break; } - case GbaArmOpCategory::Msr: + case ArmOpCategory::Msr: { bool immediate = opCode & (1 << 25); bool psrDst = opCode & (1 << 22); @@ -646,7 +646,7 @@ void GbaDisUtils::ArmDisassemble(DisassemblyInfo& info, string& out, uint32_t me break; } - case GbaArmOpCategory::SingleDataSwap: + case ArmOpCategory::SingleDataSwap: { str.Write("SWP"); WriteCond(str, opCode); @@ -669,7 +669,7 @@ void GbaDisUtils::ArmDisassemble(DisassemblyInfo& info, string& out, uint32_t me break; } - case GbaArmOpCategory::BranchExchangeRegister: + case ArmOpCategory::BranchExchangeRegister: { str.Write("BX"); WriteCond(str, opCode); @@ -679,7 +679,7 @@ void GbaDisUtils::ArmDisassemble(DisassemblyInfo& info, string& out, uint32_t me break; } - case GbaArmOpCategory::BlockDataTransfer: + case ArmOpCategory::BlockDataTransfer: { bool pre = (opCode & (1 << 24)) != 0; bool up = (opCode & (1 << 23)) != 0; @@ -720,7 +720,7 @@ void GbaDisUtils::ArmDisassemble(DisassemblyInfo& info, string& out, uint32_t me break; } - case GbaArmOpCategory::SignedHalfDataTransfer: + case ArmOpCategory::SignedHalfDataTransfer: { bool pre = (opCode & (1 << 24)) != 0; bool up = (opCode & (1 << 23)) != 0; @@ -786,7 +786,7 @@ void GbaDisUtils::ArmDisassemble(DisassemblyInfo& info, string& out, uint32_t me break; } - case GbaArmOpCategory::Multiply: + case ArmOpCategory::Multiply: { uint8_t rd = (opCode >> 16) & 0x0F; uint8_t rn = (opCode >> 12) & 0x0F; @@ -815,7 +815,7 @@ void GbaDisUtils::ArmDisassemble(DisassemblyInfo& info, string& out, uint32_t me break; } - case GbaArmOpCategory::MultiplyLong: + case ArmOpCategory::MultiplyLong: { uint8_t rh = (opCode >> 16) & 0x0F; uint8_t rl = (opCode >> 12) & 0x0F; @@ -843,7 +843,7 @@ void GbaDisUtils::ArmDisassemble(DisassemblyInfo& info, string& out, uint32_t me break; } - case GbaArmOpCategory::SoftwareInterrupt: + case ArmOpCategory::SoftwareInterrupt: { uint32_t value = opCode & 0xFFFFFF; str.Write("SWI"); @@ -955,15 +955,15 @@ EffectiveAddressInfo GbaDisUtils::GetEffectiveAddress(DisassemblyInfo& info, Gba return {}; } } else { - GbaArmOpCategory category = GbaCpu::GetArmOpCategory(opCode); + ArmOpCategory category = GbaCpu::GetArmOpCategory(opCode); switch(category) { - case GbaArmOpCategory::BranchExchangeRegister: { + case ArmOpCategory::BranchExchangeRegister: { uint8_t rs = opCode & 0x0F; return { (int)state.R[rs], 0, true, MemoryType::GbaMemory }; } - case GbaArmOpCategory::InvalidOp: - case GbaArmOpCategory::BlockDataTransfer: + case ArmOpCategory::InvalidOp: + case ArmOpCategory::BlockDataTransfer: return {}; } } @@ -1011,8 +1011,8 @@ bool GbaDisUtils::IsJumpToSub(uint32_t opCode, uint8_t flags) } return false; } else { - GbaArmOpCategory category = GbaCpu::GetArmOpCategory(opCode); - if(category == GbaArmOpCategory::Branch) { + ArmOpCategory category = GbaCpu::GetArmOpCategory(opCode); + if(category == ArmOpCategory::Branch) { bool withLink = opCode & (1 << 24); return withLink; } @@ -1036,8 +1036,8 @@ bool GbaDisUtils::IsReturnInstruction(uint32_t opCode, uint8_t flags) } return false; } else { - GbaArmOpCategory category = GbaCpu::GetArmOpCategory(opCode); - if(category == GbaArmOpCategory::BranchExchangeRegister) { + ArmOpCategory category = GbaCpu::GetArmOpCategory(opCode); + if(category == ArmOpCategory::BranchExchangeRegister) { //BX 14 uint8_t rs = opCode & 0x0F; return rs == 14; @@ -1093,24 +1093,24 @@ bool GbaDisUtils::IsUnconditionalJump(uint32_t opCode, uint8_t flags) bool GbaDisUtils::IsArmBranch(uint32_t opCode) { - GbaArmOpCategory category = GbaCpu::GetArmOpCategory(opCode); + ArmOpCategory category = GbaCpu::GetArmOpCategory(opCode); switch(category) { - case GbaArmOpCategory::Branch: return true; - case GbaArmOpCategory::BranchExchangeRegister: return true; - case GbaArmOpCategory::SoftwareInterrupt: return true; + case ArmOpCategory::Branch: return true; + case ArmOpCategory::BranchExchangeRegister: return true; + case ArmOpCategory::SoftwareInterrupt: return true; - case GbaArmOpCategory::Msr: return false; + case ArmOpCategory::Msr: return false; - case GbaArmOpCategory::Mrs: - case GbaArmOpCategory::DataProcessing: - case GbaArmOpCategory::Multiply: - case GbaArmOpCategory::SingleDataSwap: { + case ArmOpCategory::Mrs: + case ArmOpCategory::DataProcessing: + case ArmOpCategory::Multiply: + case ArmOpCategory::SingleDataSwap: { uint8_t rd = (opCode >> 12) & 0x0F; return rd == 15; } - case GbaArmOpCategory::SingleDataTransfer: - case GbaArmOpCategory::SignedHalfDataTransfer: { + case ArmOpCategory::SingleDataTransfer: + case ArmOpCategory::SignedHalfDataTransfer: { uint8_t rd = (opCode >> 12) & 0x0F; if(rd == 15) { bool pre = (opCode & (1 << 24)); @@ -1124,13 +1124,13 @@ bool GbaDisUtils::IsArmBranch(uint32_t opCode) return false; } - case GbaArmOpCategory::MultiplyLong: { + case ArmOpCategory::MultiplyLong: { uint8_t rl = (opCode >> 12) & 0x0F; uint8_t rh = (opCode >> 16) & 0x0F; return rl == 15 || rh == 15; } - case GbaArmOpCategory::BlockDataTransfer: { + case ArmOpCategory::BlockDataTransfer: { uint8_t rn = (opCode >> 16) & 0x0F; bool load = (opCode & (1 << 20)); if(rn == 15) { diff --git a/Core/GBA/GbaCpu.Arm.cpp b/Core/GBA/GbaCpu.Arm.cpp index 82f5fa66d..d7fc8e878 100644 --- a/Core/GBA/GbaCpu.Arm.cpp +++ b/Core/GBA/GbaCpu.Arm.cpp @@ -4,10 +4,10 @@ #include "GBA/GbaCpuMultiply.h" #include "Shared/Emulator.h" -GbaArmOpCategory GbaCpu::_armCategory[0x1000]; +ArmOpCategory GbaCpu::_armCategory[0x1000]; GbaCpu::Func GbaCpu::_armTable[0x1000]; -GbaArmOpCategory GbaCpu::GetArmOpCategory(uint32_t opCode) +ArmOpCategory GbaCpu::GetArmOpCategory(uint32_t opCode) { uint16_t opType = ((opCode & 0x0FF00000) >> 16) | ((opCode & 0xF0) >> 4); return _armCategory[opType]; @@ -146,23 +146,23 @@ void GbaCpu::ArmDataProcessing() } } - switch((GbaAluOperation)((_opCode >> 21) & 0x0F)) { - case GbaAluOperation::And: SetR(dstReg, LogicalOp(op1 & op2, carry, updateFlags)); break; - case GbaAluOperation::Eor: SetR(dstReg, LogicalOp(op1 ^ op2, carry, updateFlags)); break; - case GbaAluOperation::Sub: SetR(dstReg, Sub(op1, op2, true, updateFlags)); break; - case GbaAluOperation::Rsb: SetR(dstReg, Sub(op2, op1, true, updateFlags)); break; - case GbaAluOperation::Add: SetR(dstReg, Add(op1, op2, false, updateFlags)); break; - case GbaAluOperation::Adc: SetR(dstReg, Add(op1, op2, _state.CPSR.Carry, updateFlags)); break; - case GbaAluOperation::Sbc: SetR(dstReg, Sub(op1, op2, _state.CPSR.Carry, updateFlags)); break; - case GbaAluOperation::Rsc: SetR(dstReg, Sub(op2, op1, _state.CPSR.Carry, updateFlags)); break; - case GbaAluOperation::Tst: LogicalOp(op1 & op2, carry, true); break; - case GbaAluOperation::Teq: LogicalOp(op1 ^ op2, carry, true); break; - case GbaAluOperation::Cmp: Sub(op1, op2, true, true); break; - case GbaAluOperation::Cmn: Add(op1, op2, false, true); break; - case GbaAluOperation::Orr: SetR(dstReg, LogicalOp(op1 | op2, carry, updateFlags)); break; - case GbaAluOperation::Mov: SetR(dstReg, LogicalOp(op2, carry, updateFlags)); break; - case GbaAluOperation::Bic: SetR(dstReg, LogicalOp(op1 & ~op2, carry, updateFlags)); break; - case GbaAluOperation::Mvn: SetR(dstReg, LogicalOp(~op2, carry, updateFlags)); break; + switch((ArmAluOperation)((_opCode >> 21) & 0x0F)) { + case ArmAluOperation::And: SetR(dstReg, LogicalOp(op1 & op2, carry, updateFlags)); break; + case ArmAluOperation::Eor: SetR(dstReg, LogicalOp(op1 ^ op2, carry, updateFlags)); break; + case ArmAluOperation::Sub: SetR(dstReg, Sub(op1, op2, true, updateFlags)); break; + case ArmAluOperation::Rsb: SetR(dstReg, Sub(op2, op1, true, updateFlags)); break; + case ArmAluOperation::Add: SetR(dstReg, Add(op1, op2, false, updateFlags)); break; + case ArmAluOperation::Adc: SetR(dstReg, Add(op1, op2, _state.CPSR.Carry, updateFlags)); break; + case ArmAluOperation::Sbc: SetR(dstReg, Sub(op1, op2, _state.CPSR.Carry, updateFlags)); break; + case ArmAluOperation::Rsc: SetR(dstReg, Sub(op2, op1, _state.CPSR.Carry, updateFlags)); break; + case ArmAluOperation::Tst: LogicalOp(op1 & op2, carry, true); break; + case ArmAluOperation::Teq: LogicalOp(op1 ^ op2, carry, true); break; + case ArmAluOperation::Cmp: Sub(op1, op2, true, true); break; + case ArmAluOperation::Cmn: Add(op1, op2, false, true); break; + case ArmAluOperation::Orr: SetR(dstReg, LogicalOp(op1 | op2, carry, updateFlags)); break; + case ArmAluOperation::Mov: SetR(dstReg, LogicalOp(op2, carry, updateFlags)); break; + case ArmAluOperation::Bic: SetR(dstReg, LogicalOp(op1 & ~op2, carry, updateFlags)); break; + case ArmAluOperation::Mvn: SetR(dstReg, LogicalOp(~op2, carry, updateFlags)); break; } if(dstReg == 15 && updateFlags) { @@ -532,77 +532,77 @@ bool GbaCpu::CheckConditions(uint32_t condCode) void GbaCpu::InitArmOpTable() { - auto addEntry = [=](int i, Func func, GbaArmOpCategory category) { + auto addEntry = [=](int i, Func func, ArmOpCategory category) { _armTable[i] = func; _armCategory[i] = category; }; for(int i = 0; i < 0x1000; i++) { - addEntry(i, &GbaCpu::ArmInvalidOp, GbaArmOpCategory::InvalidOp); + addEntry(i, &GbaCpu::ArmInvalidOp, ArmOpCategory::InvalidOp); } //Data Processing / PSR Transfer (MRS, MSR) //----_00??_????_----_----_----_????_---- for(int i = 0; i <= 0x3FF; i++) { - GbaAluOperation operation = (GbaAluOperation)((i >> 5) & 0x0F); + ArmAluOperation operation = (ArmAluOperation)((i >> 5) & 0x0F); bool setConditionCodes = (i & 0x10) != 0; - if(!setConditionCodes && operation >= GbaAluOperation::Tst && operation <= GbaAluOperation::Cmn) { + if(!setConditionCodes && operation >= ArmAluOperation::Tst && operation <= ArmAluOperation::Cmn) { if(i & 0x020) { - addEntry(0x000 | i, &GbaCpu::ArmMsr, GbaArmOpCategory::Msr); + addEntry(0x000 | i, &GbaCpu::ArmMsr, ArmOpCategory::Msr); } else { - addEntry(0x000 | i, &GbaCpu::ArmMrs, GbaArmOpCategory::Mrs); + addEntry(0x000 | i, &GbaCpu::ArmMrs, ArmOpCategory::Mrs); } } else { - addEntry(0x000 | i, &GbaCpu::ArmDataProcessing, GbaArmOpCategory::DataProcessing); + addEntry(0x000 | i, &GbaCpu::ArmDataProcessing, ArmOpCategory::DataProcessing); } } //Branch and Exchange (BX) //----_0001_0010_----_----_----_0001_---- - addEntry(0x121, &GbaCpu::ArmBranchExchangeRegister, GbaArmOpCategory::BranchExchangeRegister); + addEntry(0x121, &GbaCpu::ArmBranchExchangeRegister, ArmOpCategory::BranchExchangeRegister); //Branch and Branch with Link (B, BL) //----_101?_????_----_----_----_????_---- for(int i = 0; i <= 0x1FF; i++) { - addEntry(0xA00 | i, &GbaCpu::ArmBranch, GbaArmOpCategory::Branch); + addEntry(0xA00 | i, &GbaCpu::ArmBranch, ArmOpCategory::Branch); } //Single Data Transfer (LDR, STR) //----_01??_????_----_----_----_????_---- for(int i = 0; i <= 0x3FF; i++) { - addEntry(0x400 | i, &GbaCpu::ArmSingleDataTransfer, GbaArmOpCategory::SingleDataTransfer); + addEntry(0x400 | i, &GbaCpu::ArmSingleDataTransfer, ArmOpCategory::SingleDataTransfer); } //Halfword and Signed Data Transfer (LDRH / STRH / LDRSB / LDRSH) //----_000?_????_----_----_----_1??1_---- for(int i = 0; i <= 0x7F; i++) { - addEntry(((i & 0x7C) << 2) | ((i & 0x03) << 1) | 0x09, &GbaCpu::ArmSignedHalfDataTransfer, GbaArmOpCategory::SignedHalfDataTransfer); + addEntry(((i & 0x7C) << 2) | ((i & 0x03) << 1) | 0x09, &GbaCpu::ArmSignedHalfDataTransfer, ArmOpCategory::SignedHalfDataTransfer); } //Block Data Transfer (LDM, STM) //----_100?_????_----_----_----_????_---- for(int i = 0; i <= 0x1FF; i++) { - addEntry(0x800 | i, &GbaCpu::ArmBlockDataTransfer, GbaArmOpCategory::BlockDataTransfer); + addEntry(0x800 | i, &GbaCpu::ArmBlockDataTransfer, ArmOpCategory::BlockDataTransfer); } //Multiply and Multiply-Accumulate (MUL, MLA) //----_0000_00??_----_----_----_1001_---- for(int i = 0; i <= 0x03; i++) { - addEntry(0x009 | (i << 4), &GbaCpu::ArmMultiply, GbaArmOpCategory::Multiply); + addEntry(0x009 | (i << 4), &GbaCpu::ArmMultiply, ArmOpCategory::Multiply); } //Multiply Long and Multiply-Accumulate Long (MULL,MLAL) //----_0000_1???_----_----_----_1001_---- for(int i = 0; i <= 0x07; i++) { - addEntry(0x089 | (i << 4), &GbaCpu::ArmMultiplyLong, GbaArmOpCategory::MultiplyLong); + addEntry(0x089 | (i << 4), &GbaCpu::ArmMultiplyLong, ArmOpCategory::MultiplyLong); } //Single Data Swap (SWP) //----_0001_0000_----_----_----_1001_---- - addEntry(0x109, &GbaCpu::ArmSingleDataSwap, GbaArmOpCategory::SingleDataSwap); //word - addEntry(0x149, &GbaCpu::ArmSingleDataSwap, GbaArmOpCategory::SingleDataSwap); //byte + addEntry(0x109, &GbaCpu::ArmSingleDataSwap, ArmOpCategory::SingleDataSwap); //word + addEntry(0x149, &GbaCpu::ArmSingleDataSwap, ArmOpCategory::SingleDataSwap); //byte for(int i = 0; i <= 0xFF; i++) { - addEntry(0xF00 + i, &GbaCpu::ArmSoftwareInterrupt, GbaArmOpCategory::SoftwareInterrupt); + addEntry(0xF00 + i, &GbaCpu::ArmSoftwareInterrupt, ArmOpCategory::SoftwareInterrupt); } } diff --git a/Core/GBA/GbaCpu.h b/Core/GBA/GbaCpu.h index 407c68b70..843514b09 100644 --- a/Core/GBA/GbaCpu.h +++ b/Core/GBA/GbaCpu.h @@ -29,7 +29,7 @@ class GbaCpu : public ISerializable typedef void(GbaCpu::* Func)(); static Func _armTable[0x1000]; static Func _thumbTable[0x100]; - static GbaArmOpCategory _armCategory[0x1000]; + static ArmOpCategory _armCategory[0x1000]; static GbaThumbOpCategory _thumbCategory[0x100]; uint32_t Add(uint32_t op1, uint32_t op2, bool carry, bool updateFlags); @@ -129,7 +129,7 @@ class GbaCpu : public ISerializable void Init(Emulator* emu, GbaMemoryManager* memoryManager, GbaRomPrefetch* prefetch); - static GbaArmOpCategory GetArmOpCategory(uint32_t opCode); + static ArmOpCategory GetArmOpCategory(uint32_t opCode); static GbaThumbOpCategory GetThumbOpCategory(uint16_t opCode); GbaCpuState& GetState(); diff --git a/Core/GBA/GbaMemoryManager.cpp b/Core/GBA/GbaMemoryManager.cpp index a1abfbc06..a232af228 100644 --- a/Core/GBA/GbaMemoryManager.cpp +++ b/Core/GBA/GbaMemoryManager.cpp @@ -278,7 +278,7 @@ uint32_t GbaMemoryManager::Read(GbaAccessModeVal mode, uint32_t addr) value = InternalRead(mode, addr, addr); UpdateOpenBus<1>(addr, value); value = isSigned ? (uint32_t)(int8_t)value : (uint8_t)value; - _emu->ProcessMemoryRead(addr, value, mode & GbaAccessMode::Prefetch ? MemoryOperationType::ExecOpCode : MemoryOperationType::Read); + _emu->ProcessMemoryRead(addr, value, MemoryOperationType::Read); } else if(mode & GbaAccessMode::HalfWord) { uint8_t b0 = InternalRead(mode, addr & ~0x01, addr); uint8_t b1 = InternalRead(mode, addr | 1, addr); diff --git a/Core/GBA/GbaTypes.h b/Core/GBA/GbaTypes.h index 3656ce600..a7201e5b0 100644 --- a/Core/GBA/GbaTypes.h +++ b/Core/GBA/GbaTypes.h @@ -2,6 +2,7 @@ #include "pch.h" #include "Shared/MemoryType.h" #include "Shared/BaseState.h" +#include "Shared/ArmEnums.h" #include "Utilities/Serializer.h" enum class GbaCpuMode : uint8_t @@ -507,23 +508,6 @@ struct GbaState GbaControlManagerState ControlManager; }; -enum class GbaArmOpCategory -{ - BranchExchangeRegister, - Branch, - Msr, - Mrs, - DataProcessing, - Multiply, - MultiplyLong, - SingleDataTransfer, - SignedHalfDataTransfer, - BlockDataTransfer, - SingleDataSwap, - SoftwareInterrupt, - InvalidOp, -}; - enum class GbaThumbOpCategory { MoveShiftedRegister, @@ -549,29 +533,6 @@ enum class GbaThumbOpCategory InvalidOp, }; -enum class GbaAluOperation : uint8_t -{ - And, - Eor, - Sub, - Rsb, - - Add, - Adc, - Sbc, - Rsc, - - Tst, - Teq, - Cmp, - Cmn, - - Orr, - Mov, - Bic, - Mvn -}; - enum class GbaIrqSource { None = 0, diff --git a/Core/SNES/BaseCartridge.cpp b/Core/SNES/BaseCartridge.cpp index 70cf75d5b..73a489294 100644 --- a/Core/SNES/BaseCartridge.cpp +++ b/Core/SNES/BaseCartridge.cpp @@ -13,6 +13,7 @@ #include "SNES/Coprocessors/GSU/Gsu.h" #include "SNES/Coprocessors/SDD1/Sdd1.h" #include "SNES/Coprocessors/CX4/Cx4.h" +#include "SNES/Coprocessors/ST018/St018.h" #include "SNES/Coprocessors/OBC1/Obc1.h" #include "SNES/Coprocessors/SPC7110/Spc7110.h" #include "SNES/Coprocessors/BSX/BsxCart.h" @@ -537,6 +538,9 @@ void BaseCartridge::InitCoprocessor() _coprocessor.reset(new Cx4(_console)); _cx4 = dynamic_cast(_coprocessor.get()); _needCoprocSync = true; + } else if(_coprocessorType == CoprocessorType::ST018) { + _coprocessor.reset(new St018(_console)); + _st018 = dynamic_cast(_coprocessor.get()); } else if(_coprocessorType == CoprocessorType::OBC1 && _saveRamSize > 0) { _coprocessor.reset(new Obc1(_console, _saveRam, _saveRamSize)); } else if(_coprocessorType == CoprocessorType::SGB) { @@ -850,6 +854,11 @@ Cx4* BaseCartridge::GetCx4() return _cx4; } +St018* BaseCartridge::GetSt018() +{ + return _st018; +} + SuperGameboy* BaseCartridge::GetSuperGameboy() { return _sgb; diff --git a/Core/SNES/BaseCartridge.h b/Core/SNES/BaseCartridge.h index d5f6d366e..dd61dd844 100644 --- a/Core/SNES/BaseCartridge.h +++ b/Core/SNES/BaseCartridge.h @@ -12,6 +12,7 @@ class EmuSettings; class NecDsp; class Sa1; class Gsu; +class St018; class Cx4; class SuperGameboy; class BsxCart; @@ -40,7 +41,8 @@ class BaseCartridge : public ISerializable NecDsp *_necDsp = nullptr; Sa1 *_sa1 = nullptr; Gsu *_gsu = nullptr; - Cx4 *_cx4 = nullptr; + Cx4* _cx4 = nullptr; + St018 *_st018 = nullptr; SuperGameboy *_sgb = nullptr; BsxCart* _bsx = nullptr; unique_ptr _bsxMemPack; @@ -124,6 +126,7 @@ class BaseCartridge : public ISerializable Sa1* GetSa1(); Gsu* GetGsu(); Cx4* GetCx4(); + St018* GetSt018(); SuperGameboy* GetSuperGameboy(); BsxCart* GetBsx(); BsxMemoryPack* GetBsxMemoryPack(); diff --git a/Core/SNES/Coprocessors/SA1/Sa1.cpp b/Core/SNES/Coprocessors/SA1/Sa1.cpp index b33dd71a6..efad52b93 100644 --- a/Core/SNES/Coprocessors/SA1/Sa1.cpp +++ b/Core/SNES/Coprocessors/SA1/Sa1.cpp @@ -791,12 +791,9 @@ uint32_t Sa1::DebugGetInternalRamSize() return Sa1::InternalRamSize; } -DebugSa1State Sa1::GetState() +Sa1State& Sa1::GetState() { - return { - _cpu->GetState(), - _state - }; + return _state; } SnesCpuState& Sa1::GetCpuState() diff --git a/Core/SNES/Coprocessors/SA1/Sa1.h b/Core/SNES/Coprocessors/SA1/Sa1.h index 662df1269..9786f758d 100644 --- a/Core/SNES/Coprocessors/SA1/Sa1.h +++ b/Core/SNES/Coprocessors/SA1/Sa1.h @@ -92,7 +92,7 @@ class Sa1 : public BaseCoprocessor uint8_t* DebugGetInternalRam(); uint32_t DebugGetInternalRamSize(); - DebugSa1State GetState(); + Sa1State& GetState(); SnesCpuState& GetCpuState(); uint16_t ReadVector(uint16_t vector); diff --git a/Core/SNES/Coprocessors/SA1/Sa1Cpu.cpp b/Core/SNES/Coprocessors/SA1/Sa1Cpu.cpp index 1e5e1427e..39d9be8b1 100644 --- a/Core/SNES/Coprocessors/SA1/Sa1Cpu.cpp +++ b/Core/SNES/Coprocessors/SA1/Sa1Cpu.cpp @@ -3,7 +3,6 @@ #include "Shared/Emulator.h" #include "SNES/SnesCpuTypes.h" #include "SNES/Coprocessors/SA1/Sa1Cpu.h" -#include "SNES/SnesMemoryManager.h" #include "SNES/Coprocessors/SA1/Sa1.h" #include "SNES/MemoryMappings.h" #include "Shared/MemoryOperationType.h" diff --git a/Core/SNES/Coprocessors/SA1/Sa1Types.h b/Core/SNES/Coprocessors/SA1/Sa1Types.h index c52818845..00a057276 100644 --- a/Core/SNES/Coprocessors/SA1/Sa1Types.h +++ b/Core/SNES/Coprocessors/SA1/Sa1Types.h @@ -107,9 +107,3 @@ struct Sa1State uint8_t Banks[4]; }; - -struct DebugSa1State -{ - SnesCpuState Cpu; - Sa1State Sa1; -}; \ No newline at end of file diff --git a/Core/SNES/Coprocessors/ST018/ArmV3Cpu.cpp b/Core/SNES/Coprocessors/ST018/ArmV3Cpu.cpp new file mode 100644 index 000000000..3af89dbb2 --- /dev/null +++ b/Core/SNES/Coprocessors/ST018/ArmV3Cpu.cpp @@ -0,0 +1,904 @@ +#include "pch.h" +#include "SNES/Coprocessors/ST018/ArmV3Cpu.h" +#include "SNES/Coprocessors/ST018/St018.h" +#include "GBA/GbaCpuMultiply.h" +#include "Shared/ArmEnums.h" +#include "Shared/Emulator.h" +#include "Shared/EmuSettings.h" +#include "Utilities/Serializer.h" + +ArmOpCategory ArmV3Cpu::_armCategory[0x1000]; +ArmV3Cpu::Func ArmV3Cpu::_armTable[0x1000]; + +void ArmV3Cpu::Init(Emulator* emu, St018* st018) +{ + _emu = emu; + _st018 = st018; +} + +ArmV3Cpu::~ArmV3Cpu() +{ +} + +void ArmV3Cpu::StaticInit() +{ + InitArmOpTable(); +} + +void ArmV3Cpu::SwitchMode(ArmV3CpuMode mode) +{ + //High bit of mode is always set according to psr test + mode = (ArmV3CpuMode)((int)mode | 0x10); + + if(_state.CPSR.Mode == mode) { + return; + } + + ArmV3CpuMode orgMode = _state.CPSR.Mode; + switch(orgMode) { + default: + case ArmV3CpuMode::System: + case ArmV3CpuMode::User: + memcpy(_state.UserRegs, &_state.R[8], 7 * sizeof(uint32_t)); + break; + + case ArmV3CpuMode::Fiq: + memcpy(_state.FiqRegs, &_state.R[8], 7 * sizeof(uint32_t)); + break; + + case ArmV3CpuMode::Irq: + memcpy(_state.UserRegs, &_state.R[8], 5 * sizeof(uint32_t)); + memcpy(_state.IrqRegs, &_state.R[13], 2 * sizeof(uint32_t)); + break; + + case ArmV3CpuMode::Supervisor: + memcpy(_state.UserRegs, &_state.R[8], 5 * sizeof(uint32_t)); + memcpy(_state.SupervisorRegs, &_state.R[13], 2 * sizeof(uint32_t)); + break; + + case ArmV3CpuMode::Abort: + memcpy(_state.UserRegs, &_state.R[8], 5 * sizeof(uint32_t)); + memcpy(_state.AbortRegs, &_state.R[13], 2 * sizeof(uint32_t)); + break; + + case ArmV3CpuMode::Undefined: + memcpy(_state.UserRegs, &_state.R[8], 5 * sizeof(uint32_t)); + memcpy(_state.UndefinedRegs, &_state.R[13], 2 * sizeof(uint32_t)); + break; + } + + _state.CPSR.Mode = mode; + + if(mode != ArmV3CpuMode::Fiq) { + memcpy(&_state.R[8], _state.UserRegs, 7 * sizeof(uint32_t)); + switch(mode) { + case ArmV3CpuMode::Irq: memcpy(&_state.R[13], _state.IrqRegs, 2 * sizeof(uint32_t)); break; + case ArmV3CpuMode::Supervisor: memcpy(&_state.R[13], _state.SupervisorRegs, 2 * sizeof(uint32_t)); break; + case ArmV3CpuMode::Abort: memcpy(&_state.R[13], _state.AbortRegs, 2 * sizeof(uint32_t)); break; + case ArmV3CpuMode::Undefined: memcpy(&_state.R[13], _state.UndefinedRegs, 2 * sizeof(uint32_t)); break; + } + } else { + memcpy(&_state.R[8], _state.FiqRegs, 7 * sizeof(uint32_t)); + } +} + +void ArmV3Cpu::ReloadPipeline() +{ + ArmV3CpuPipeline& pipe = _state.Pipeline; + pipe.Mode = ArmV3AccessMode::Prefetch | ArmV3AccessMode::Word; + + pipe.ReloadRequested = false; + pipe.Fetch.Address = _state.R[15] = _state.R[15] & ~0x03; + + pipe.Fetch.OpCode = ReadCode(pipe.Mode, pipe.Fetch.Address); + pipe.Execute = pipe.Decode; + pipe.Decode = pipe.Fetch; + + pipe.Fetch.Address = _state.R[15] = (_state.R[15] + 4); + pipe.Fetch.OpCode = ReadCode(pipe.Mode, pipe.Fetch.Address); +} + +void ArmV3Cpu::ProcessException(ArmV3CpuMode mode, ArmV3CpuVector vector) +{ +#ifndef DUMMYCPU + ArmV3CpuFlags cpsr = _state.CPSR; + SwitchMode(mode); + GetSpsr() = cpsr; + _state.CPSR.IrqDisable = true; + _state.R[14] = _state.Pipeline.Decode.Address; + _state.R[15] = (uint32_t)vector; + _state.Pipeline.ReloadRequested = true; +#endif +} + +uint32_t ArmV3Cpu::ReadCode(ArmV3AccessModeVal mode, uint32_t addr) +{ +#ifndef DUMMYCPU + //Next access should be sequential + //This is done before the call to Read() because e.g if DMA pauses the CPU and + //runs, the next access will not be sequential (force-nseq-access test) + _state.Pipeline.Mode |= ArmV3AccessMode::Sequential; + return _st018->ReadCpu(mode, addr); +#else + uint32_t value = _st018->DebugCpuRead(mode, addr); + LogMemoryOperation(addr, value, mode, MemoryOperationType::ExecOpCode); + return value; +#endif +} + +uint32_t ArmV3Cpu::Read(ArmV3AccessModeVal mode, uint32_t addr) +{ +#ifndef DUMMYCPU + _state.Pipeline.Mode &= ~ArmV3AccessMode::Sequential; + return _st018->ReadCpu(mode, addr); +#else + uint32_t value = _st018->DebugCpuRead(mode, addr); + LogMemoryOperation(addr, value, mode, MemoryOperationType::Read); + return value; +#endif +} + +void ArmV3Cpu::Write(ArmV3AccessModeVal mode, uint32_t addr, uint32_t value) +{ +#ifndef DUMMYCPU + _state.Pipeline.Mode &= ~ArmV3AccessMode::Sequential; + _st018->WriteCpu(mode, addr, value); +#else + LogMemoryOperation(addr, value, mode, MemoryOperationType::Write); +#endif +} + +void ArmV3Cpu::Idle() +{ +#ifndef DUMMYCPU + _state.Pipeline.Mode &= ~ArmV3AccessMode::Sequential; + _st018->ProcessIdleCycle(); +#endif +} + +void ArmV3Cpu::Idle(uint8_t cycleCount) +{ + switch(cycleCount) { + case 4: Idle(); [[fallthrough]]; + case 3: Idle(); [[fallthrough]]; + case 2: Idle(); [[fallthrough]]; + case 1: Idle(); break; + } +} + +uint32_t ArmV3Cpu::R(uint8_t reg) +{ + return _state.R[reg]; +} + +ArmV3CpuFlags& ArmV3Cpu::GetSpsr() +{ + switch(_state.CPSR.Mode) { + default: + case ArmV3CpuMode::User: return _state.CPSR; + case ArmV3CpuMode::Fiq: return _state.FiqSpsr; + case ArmV3CpuMode::Irq: return _state.IrqSpsr; + case ArmV3CpuMode::Supervisor: return _state.SupervisorSpsr; + case ArmV3CpuMode::Abort: return _state.AbortSpsr; + case ArmV3CpuMode::Undefined: return _state.UndefinedSpsr; + case ArmV3CpuMode::System: return _state.CPSR; + } +} + +uint32_t ArmV3Cpu::Add(uint32_t op1, uint32_t op2, bool carry, bool updateFlags) +{ + uint32_t result = op1 + op2 + (uint32_t)carry; + if(updateFlags) { + uint32_t overflow = ~(op1 ^ op2) & (op1 ^ result) & (1 << 31); + _state.CPSR.Negative = result & (1 << 31); + _state.CPSR.Zero = (uint32_t)result == 0; + _state.CPSR.Overflow = overflow; + _state.CPSR.Carry = (op1 ^ op2 ^ overflow ^ result) & (1 << 31); + } + return (uint32_t)result; +} + +uint32_t ArmV3Cpu::Sub(uint32_t op1, uint32_t op2, bool carry, bool updateFlags) +{ + return Add(op1, ~op2, carry, updateFlags); +} + +uint32_t ArmV3Cpu::LogicalOp(uint32_t result, bool carry, bool updateFlags) +{ + //"If the S bit is set(and Rd is not R15, see below) the V flag in the CPSR will be unaffected, the C + //flag will be set to the carry out from the barrel shifter (or preserved when the shift + //operation is LSL #0), the Z flag will be set if and only if the result is all zeros, and the N + //flag will be set to the logical value of bit 31 of the result." + if(updateFlags) { + _state.CPSR.Carry = carry; + _state.CPSR.Zero = result == 0; + _state.CPSR.Negative = result & (1 << 31); + } + return result; +} + +uint32_t ArmV3Cpu::RotateRight(uint32_t value, uint32_t shift) +{ + return (value >> shift) | (value << (32 - shift)); +} + +uint32_t ArmV3Cpu::RotateRight(uint32_t value, uint32_t shift, bool& carry) +{ + carry = (value >> (shift - 1)) & 1; + return (value >> shift) | (value << (32 - shift)); +} + +uint32_t ArmV3Cpu::ShiftLsl(uint32_t value, uint8_t shift, bool& carry) +{ + if(shift) { + carry = shift < 33 ? (value & (1 << (32 - shift))) : 0; + value = shift < 32 ? (value << shift) : 0; + } + return value; +} + +uint32_t ArmV3Cpu::ShiftLsr(uint32_t value, uint8_t shift, bool& carry) +{ + if(shift) { + carry = shift < 33 ? (value & (1 << (shift - 1))) : 0; + value = shift < 32 ? (value >> shift) : 0; + } + return value; +} + +uint32_t ArmV3Cpu::ShiftAsr(uint32_t value, uint8_t shift, bool& carry) +{ + if(shift) { + carry = shift < 33 ? (value & (1 << (shift - 1))) : (value & (1 << 31)); + value = shift < 32 ? ((int32_t)value >> shift) : ((int32_t)value >> 31); + } + return value; +} + +uint32_t ArmV3Cpu::ShiftRor(uint32_t value, uint8_t shift, bool& carry) +{ + if(shift) { + shift &= 0x1F; + if(shift) { + value = (value << (32 - shift)) | (value >> shift); + } + carry = value & (1 << 31); + } + return value; +} + +uint32_t ArmV3Cpu::ShiftRrx(uint32_t value, bool& carry) +{ + bool orgCarry = carry; + carry = value & 0x01; + return (value >> 1) | (orgCarry << 31); +} + +void ArmV3Cpu::PowerOn(bool forReset) +{ + uint64_t cycleCount = _state.CycleCount; + _state = {}; + _state.Pipeline.ReloadRequested = true; + + _state.CPSR.Mode = ArmV3CpuMode::Supervisor; + _state.CPSR.IrqDisable = true; + _state.CPSR.FiqDisable = true; + + ProcessPipeline(); + + if(forReset) { + _state.CycleCount = cycleCount; + } +} + +void ArmV3Cpu::SetProgramCounter(uint32_t addr) +{ + //Used by debugger - set new PC and reload pipeline (using debugger reads) + _state.R[15] = addr; + + ArmV3CpuPipeline& pipe = _state.Pipeline; + pipe.Mode = ArmV3AccessMode::Prefetch | ArmV3AccessMode::Word | ArmV3AccessMode::Sequential; + pipe.ReloadRequested = false; + pipe.Execute.Address = _state.R[15] = _state.R[15] & ~0x03; + pipe.Execute.OpCode = _st018->DebugCpuRead(pipe.Mode, pipe.Execute.Address); + pipe.Decode.Address = _state.R[15] = _state.R[15] + 4; + pipe.Decode.OpCode = _st018->DebugCpuRead(pipe.Mode, pipe.Decode.Address); + pipe.Fetch.Address = _state.R[15] = _state.R[15] + 4; + pipe.Fetch.OpCode = _st018->DebugCpuRead(pipe.Mode, pipe.Fetch.Address); +} + +ArmV3CpuState& ArmV3Cpu::GetState() +{ + return _state; +} + +ArmOpCategory ArmV3Cpu::GetArmOpCategory(uint32_t opCode) +{ + uint16_t opType = ((opCode & 0x0FF00000) >> 16) | ((opCode & 0xF0) >> 4); + return _armCategory[opType]; +} + +void ArmV3Cpu::ArmBranch() +{ + bool withLink = (_opCode & (1 << 24)) != 0; + int32_t offset = (((int32_t)_opCode << 8) >> 6); //sign extend + shift right by 2 + if(withLink) { + _state.R[14] = _state.R[15] - 4; + } + + _state.R[15] += offset; + _state.Pipeline.ReloadRequested = true; +} + +void ArmV3Cpu::ArmMsr() +{ + //----_00i1_0d10_mmmm_1111_oooo_oooo_oooo + //i: immediate + //d: destination psr + //o: operand + //m: mask + bool immediate = _opCode & (1 << 25); + bool writeToSpsr = _opCode & (1 << 22); + uint8_t mask = (_opCode >> 16) & 0x0F; + + if(writeToSpsr && (_state.CPSR.Mode == ArmV3CpuMode::User || _state.CPSR.Mode == ArmV3CpuMode::System)) { + return; + } + + uint32_t value; + if(immediate) { + value = _opCode & 0xFF; + uint8_t shift = (_opCode >> 8) & 0x0F; + if(shift) { + value = RotateRight(value, shift * 2); + } + } else { + value = R(_opCode & 0x0F); + } + + ArmV3CpuFlags& flags = writeToSpsr ? GetSpsr() : _state.CPSR; + if(mask & 0x08) { + flags.Negative = value & (1 << 31); + flags.Zero = value & (1 << 30); + flags.Carry = value & (1 << 29); + flags.Overflow = value & (1 << 28); + } + + if(mask & 0x01) { + if(writeToSpsr || _state.CPSR.Mode != ArmV3CpuMode::User) { + if(!writeToSpsr) { + SwitchMode((ArmV3CpuMode)(value & 0x1F)); + } else { + flags.Mode = (ArmV3CpuMode)(value & 0x1F); + } + flags.FiqDisable = value & (1 << 6); + flags.IrqDisable = value & (1 << 7); + } + } +} + +void ArmV3Cpu::ArmMrs() +{ + //----_0001_0s00_1111_dddd_0000_0000_0000 + //s: source psr + //d: destination reg + bool useSpsr = _opCode & (1 << 22); + uint8_t rd = (_opCode >> 12) & 0x0F; + SetR(rd, useSpsr ? GetSpsr().ToInt32() : _state.CPSR.ToInt32()); +} + +void ArmV3Cpu::ArmDataProcessing() +{ + //Data Processing + //----_00io_ooos_nnnn_dddd_pppp_pppp_pppp + //i: immediate + //o: opcode + //s: set flags + //n: 1st operand reg + //d: destination reg + //p: 2nd operand + + bool immediate = (_opCode & (1 << 25)) != 0; + uint8_t rn = (_opCode >> 16) & 0x0F; + uint32_t op1 = R(rn); + uint8_t dstReg = (_opCode >> 12) & 0x0F; + bool updateFlags = (_opCode & (1 << 20)) != 0; + + uint32_t op2; + bool carry = _state.CPSR.Carry; + if(immediate) { + uint8_t shift = (_opCode >> 8) & 0x0F; + op2 = (_opCode & 0xFF); + if(shift) { + op2 = RotateRight(op2, shift * 2, carry); + } + } else { + uint8_t shiftType = (_opCode >> 5) & 0x03; + uint8_t shift; + uint8_t rm = _opCode & 0x0F; + op2 = R(rm); + + bool useRegValue = _opCode & (1 << 4); + if(useRegValue) { + //Shift amount in register + Idle(); + uint8_t rs = (_opCode >> 8) & 0x0F; + shift = R(rs) + (rs == 15 ? 4 : 0); + if(rm == 15) { + op2 += 4; + } + if(rn == 15) { + op1 += 4; + } + } else { + shift = (_opCode >> 7) & 0x1F; + } + + switch(shiftType) { + case 0: op2 = ShiftLsl(op2, shift, carry); break; + case 1: op2 = ShiftLsr(op2, (useRegValue || shift) ? shift : 32, carry); break; + case 2: op2 = ShiftAsr(op2, (useRegValue || shift) ? shift : 32, carry); break; + case 3: op2 = (!useRegValue && shift == 0) ? ShiftRrx(op2, carry) : ShiftRor(op2, shift, carry); break; + } + } + + switch((ArmAluOperation)((_opCode >> 21) & 0x0F)) { + case ArmAluOperation::And: SetR(dstReg, LogicalOp(op1 & op2, carry, updateFlags)); break; + case ArmAluOperation::Eor: SetR(dstReg, LogicalOp(op1 ^ op2, carry, updateFlags)); break; + case ArmAluOperation::Sub: SetR(dstReg, Sub(op1, op2, true, updateFlags)); break; + case ArmAluOperation::Rsb: SetR(dstReg, Sub(op2, op1, true, updateFlags)); break; + case ArmAluOperation::Add: SetR(dstReg, Add(op1, op2, false, updateFlags)); break; + case ArmAluOperation::Adc: SetR(dstReg, Add(op1, op2, _state.CPSR.Carry, updateFlags)); break; + case ArmAluOperation::Sbc: SetR(dstReg, Sub(op1, op2, _state.CPSR.Carry, updateFlags)); break; + case ArmAluOperation::Rsc: SetR(dstReg, Sub(op2, op1, _state.CPSR.Carry, updateFlags)); break; + case ArmAluOperation::Tst: LogicalOp(op1 & op2, carry, true); break; + case ArmAluOperation::Teq: LogicalOp(op1 ^ op2, carry, true); break; + case ArmAluOperation::Cmp: Sub(op1, op2, true, true); break; + case ArmAluOperation::Cmn: Add(op1, op2, false, true); break; + case ArmAluOperation::Orr: SetR(dstReg, LogicalOp(op1 | op2, carry, updateFlags)); break; + case ArmAluOperation::Mov: SetR(dstReg, LogicalOp(op2, carry, updateFlags)); break; + case ArmAluOperation::Bic: SetR(dstReg, LogicalOp(op1 & ~op2, carry, updateFlags)); break; + case ArmAluOperation::Mvn: SetR(dstReg, LogicalOp(~op2, carry, updateFlags)); break; + } + + if(dstReg == 15 && updateFlags) { + ArmV3CpuFlags spsr = GetSpsr(); + SwitchMode(spsr.Mode); + _state.CPSR = spsr; + } +} + +void ArmV3Cpu::ArmMultiply() +{ + //Multiply and Multiply-Accumulate (MUL, MLA) + //----_0000_00as_dddd_nnnn_ssss_1001_mmmm + uint8_t rd = (_opCode >> 16) & 0x0F; + uint8_t rn = (_opCode >> 12) & 0x0F; + uint8_t rs = (_opCode >> 8) & 0x0F; + uint8_t rm = _opCode & 0x0F; + bool updateFlags = (_opCode & (1 << 20)) != 0; + bool multAndAcc = (_opCode & (1 << 21)) != 0; + + MultiplicationOutput output; + if(multAndAcc) { + output = GbaCpuMultiply::mla(R(rm), R(rs), R(rn)); + } else { + output = GbaCpuMultiply::mul(R(rm), R(rs)); + } + + Idle(output.CycleCount); + if(multAndAcc) { + Idle(); + } + + uint32_t result = output.Output; + SetR(rd, result); + + if(updateFlags) { + _state.CPSR.Carry = output.Carry; + _state.CPSR.Zero = result == 0; + _state.CPSR.Negative = (result & (1 << 31)); + } +} + +void ArmV3Cpu::ArmMultiplyLong() +{ + //Multiply Long and Multiply-Accumulate Long (MULL,MLAL) + //----_0000_1uas_hhhh_llll_ssss_1001_mmmm + uint8_t rh = (_opCode >> 16) & 0x0F; + uint8_t rl = (_opCode >> 12) & 0x0F; + uint8_t rs = (_opCode >> 8) & 0x0F; + uint8_t rm = _opCode & 0x0F; + + bool updateFlags = (_opCode & (1 << 20)) != 0; + bool multAndAcc = (_opCode & (1 << 21)) != 0; + bool sign = (_opCode & (1 << 22)) != 0; + + Idle(); + + MultiplicationOutput output; + if(sign) { + if(multAndAcc) { + output = GbaCpuMultiply::smlal(R(rl), R(rh), R(rm), R(rs)); + } else { + output = GbaCpuMultiply::smull(R(rm), R(rs)); + } + } else { + if(multAndAcc) { + output = GbaCpuMultiply::umlal(R(rl), R(rh), R(rm), R(rs)); + } else { + output = GbaCpuMultiply::umull(R(rm), R(rs)); + } + } + + Idle(output.CycleCount); + if(multAndAcc) { + Idle(); + } + + uint64_t result = output.Output; + + SetR(rl, (uint32_t)result); + SetR(rh, (uint32_t)(result >> 32)); + + if(updateFlags) { + _state.CPSR.Carry = output.Carry; + _state.CPSR.Zero = result == 0; + _state.CPSR.Negative = (result & ((uint64_t)1 << 63)); + } +} + +void ArmV3Cpu::ArmSingleDataTransfer() +{ + //Single Data Transfer (LDR, STR) + //----_01ip_ubwl_nnnn_dddd_oooo_oooo_oooo + //i: immediate (when cleared) + //p: post/pre: add offset after/before transfer + //u: down/up: subtract/add offset from/to base + //b: byte transfer + //w: write-back: write address into base + //l: store/load + //n: base register + //d: src/dst register + //o: offset (immediate or register) + bool immediate = (_opCode & (1 << 25)) == 0; + bool pre = (_opCode & (1 << 24)) != 0; + bool up = (_opCode & (1 << 23)) != 0; + bool byte = (_opCode & (1 << 22)) != 0; + bool writeBack = (_opCode & (1 << 21)) != 0; + bool load = (_opCode & (1 << 20)) != 0; + uint8_t rn = (_opCode >> 16) & 0x0F; + uint8_t rd = (_opCode >> 12) & 0x0F; + + uint32_t addr = R(rn); + + int32_t offset; + if(immediate) { + offset = _opCode & 0xFFF; + } else { + uint8_t shiftType = (_opCode >> 5) & 0x03; + uint8_t shift; + uint8_t rm = _opCode & 0x0F; + shift = (_opCode >> 7) & 0x1F; + + offset = R(rm); + bool carry = _state.CPSR.Carry; + switch(shiftType) { + case 0: offset = ShiftLsl(offset, shift, carry); break; + case 1: offset = ShiftLsr(offset, shift ? shift : 32, carry); break; + case 2: offset = ShiftAsr(offset, shift ? shift : 32, carry); break; + case 3: offset = shift == 0 ? ShiftRrx(offset, carry) : ShiftRor(offset, shift, carry); break; + } + } + + if(pre) { + addr += up ? offset : -offset; + } + + ArmV3AccessModeVal mode = byte ? ArmV3AccessMode::Byte : ArmV3AccessMode::Word; + if(load) { + SetR(rd, Read(mode, addr)); + Idle(); + } else { + Write(mode, addr, R(rd) + (rd == 15 ? 4 : 0)); + } + + if(!pre) { + addr += up ? offset : -offset; + } + + if((rd != rn || !load) && (writeBack || !pre)) { + SetR(rn, addr); + } +} + +void ArmV3Cpu::ArmBlockDataTransfer() +{ + //Block Data Transfer (LDM, STM) + //----_100p_uswl_nnnn_rrrr_rrrr_rrrr_rrrr + //p: post/pre: add offset after/before transfer + //u: down/up: subtract/add offset from/to base + //s: psr & force user bit + //w: write-back: write address into base + //l: store/load + //n: base register + //r: register list + bool pre = (_opCode & (1 << 24)) != 0; + bool up = (_opCode & (1 << 23)) != 0; + bool psrForceUser = (_opCode & (1 << 22)) != 0; + bool writeBack = (_opCode & (1 << 21)) != 0; + bool load = (_opCode & (1 << 20)) != 0; + uint8_t rn = (_opCode >> 16) & 0x0F; + uint16_t regMask = (uint16_t)_opCode; + + uint32_t base = R(rn) + (rn == 15 ? 4 : 0); + uint32_t addr = base; + + uint8_t regCount = 0; + for(int i = 0; i < 16; i++) { + regCount += (regMask & (1 << i)) ? 1 : 0; + } + + if(!regMask) { + //Glitch when mask is empty - only R15 is stored/loaded, but address changes as if all 16 were written/loaded + regCount = 16; + regMask = 0x8000; + } + + if(!up) { + addr -= (regCount - (pre ? 0 : 1)) * 4; + } else if(pre) { + addr += 4; + } + + if(writeBack && load) { + SetR(rn, base + (regCount * (up ? 4 : -4))); + } + + ArmV3CpuMode orgMode = _state.CPSR.Mode; + if(psrForceUser && (!load || (load && !(regMask & 0x8000)))) { + SwitchMode(ArmV3CpuMode::User); + } + + bool firstReg = true; + ArmV3AccessModeVal mode = ArmV3AccessMode::Word; + for(int i = 0; i < 16; i++) { + if(regMask & (1 << i)) { + if(!load) { + Write(mode, addr, R(i) + (i == 15 ? 4 : 0)); + } + + if(firstReg && writeBack) { + //Write-back happens at this point, based on the gba-tests/arm test 522 + SetR(rn, base + (regCount * (up ? 4 : -4))); + firstReg = false; + } + + if(load) { + //LDM doesn't appear to be affected by the rotation that is usually applied to unaligned reads? based on gba-tests/arm test 508 + SetR(i, Read(mode | ArmV3AccessMode::NoRotate, addr)); + } + + mode |= ArmV3AccessMode::Sequential; + addr += 4; + } + } + + if(load) { + Idle(); + } + + SwitchMode(orgMode); + + if(psrForceUser && load && (regMask & 0x8000)) { + ArmV3CpuFlags spsr = GetSpsr(); + SwitchMode(spsr.Mode); + _state.CPSR = spsr; + } +} + +void ArmV3Cpu::ArmSingleDataSwap() +{ + //Single Data Swap (SWP) + //----_0001_0b00_nnnn_dddd_0000_1001_mmmm + bool byte = _opCode & (1 << 22); + uint8_t rn = (_opCode >> 16) & 0x0F; + uint8_t rd = (_opCode >> 12) & 0x0F; + uint8_t rm = _opCode & 0x0F; + + uint32_t mode = byte ? ArmV3AccessMode::Byte : ArmV3AccessMode::Word; + uint32_t val = Read(mode, R(rn)); + Idle(); + Write(mode, R(rn), R(rm)); + SetR(rd, val); +} + +void ArmV3Cpu::ArmSoftwareInterrupt() +{ + ProcessException(ArmV3CpuMode::Supervisor, ArmV3CpuVector::SoftwareIrq); +} + +void ArmV3Cpu::ArmInvalidOp() +{ +#ifndef DUMMYCPU + ProcessException(ArmV3CpuMode::Undefined, ArmV3CpuVector::Undefined); +#endif +} + +bool ArmV3Cpu::CheckConditions(uint32_t condCode) +{ + /*Code Suffix Flags Meaning + 0000 EQ Z set equal + 0001 NE Z clear not equal + 0010 CS C set unsigned higher or same + 0011 CC C clear unsigned lower + 0100 MI N set negative + 0101 PL N clear positive or zero + 0110 VS V set overflow + 0111 VC V clear no overflow + 1000 HI C set and Z clear unsigned higher + 1001 LS C clear or Z set unsigned lower or same + 1010 GE N equals V greater or equal + 1011 LT N not equal to V less than + 1100 GT Z clear AND(N equals V) greater than + 1101 LE Z set OR(N not equal to V) less than or equal + 1110 AL(ignored) always + */ + switch(condCode) { + case 0: return _state.CPSR.Zero; + case 1: return !_state.CPSR.Zero; + case 2: return _state.CPSR.Carry; + case 3: return !_state.CPSR.Carry; + case 4: return _state.CPSR.Negative; + case 5: return !_state.CPSR.Negative; + case 6: return _state.CPSR.Overflow; + case 7: return !_state.CPSR.Overflow; + case 8: return _state.CPSR.Carry && !_state.CPSR.Zero; + case 9: return !_state.CPSR.Carry || _state.CPSR.Zero; + case 10: return _state.CPSR.Negative == _state.CPSR.Overflow; + case 11: return _state.CPSR.Negative != _state.CPSR.Overflow; + case 12: return !_state.CPSR.Zero && (_state.CPSR.Negative == _state.CPSR.Overflow); + case 13: return _state.CPSR.Zero || (_state.CPSR.Negative != _state.CPSR.Overflow); + case 14: return true; + case 15: return false; + } + + return true; +} + +void ArmV3Cpu::InitArmOpTable() +{ + auto addEntry = [=](int i, Func func, ArmOpCategory category) { + _armTable[i] = func; + _armCategory[i] = category; + }; + + for(int i = 0; i < 0x1000; i++) { + addEntry(i, &ArmV3Cpu::ArmInvalidOp, ArmOpCategory::InvalidOp); + } + + //Data Processing / PSR Transfer (MRS, MSR) + //----_00??_????_----_----_----_????_---- + for(int i = 0; i <= 0x3FF; i++) { + ArmAluOperation operation = (ArmAluOperation)((i >> 5) & 0x0F); + bool setConditionCodes = (i & 0x10) != 0; + if(!setConditionCodes && operation >= ArmAluOperation::Tst && operation <= ArmAluOperation::Cmn) { + if(i & 0x020) { + addEntry(0x000 | i, &ArmV3Cpu::ArmMsr, ArmOpCategory::Msr); + } else { + addEntry(0x000 | i, &ArmV3Cpu::ArmMrs, ArmOpCategory::Mrs); + } + } else { + addEntry(0x000 | i, &ArmV3Cpu::ArmDataProcessing, ArmOpCategory::DataProcessing); + } + } + + //Branch and Branch with Link (B, BL) + //----_101?_????_----_----_----_????_---- + for(int i = 0; i <= 0x1FF; i++) { + addEntry(0xA00 | i, &ArmV3Cpu::ArmBranch, ArmOpCategory::Branch); + } + + //Single Data Transfer (LDR, STR) + //----_01??_????_----_----_----_????_---- + for(int i = 0; i <= 0x3FF; i++) { + addEntry(0x400 | i, &ArmV3Cpu::ArmSingleDataTransfer, ArmOpCategory::SingleDataTransfer); + } + + //Block Data Transfer (LDM, STM) + //----_100?_????_----_----_----_????_---- + for(int i = 0; i <= 0x1FF; i++) { + addEntry(0x800 | i, &ArmV3Cpu::ArmBlockDataTransfer, ArmOpCategory::BlockDataTransfer); + } + + //Multiply and Multiply-Accumulate (MUL, MLA) + //----_0000_00??_----_----_----_1001_---- + for(int i = 0; i <= 0x03; i++) { + addEntry(0x009 | (i << 4), &ArmV3Cpu::ArmMultiply, ArmOpCategory::Multiply); + } + + //Multiply Long and Multiply-Accumulate Long (MULL,MLAL) + //----_0000_1???_----_----_----_1001_---- + for(int i = 0; i <= 0x07; i++) { + addEntry(0x089 | (i << 4), &ArmV3Cpu::ArmMultiplyLong, ArmOpCategory::MultiplyLong); + } + + //Single Data Swap (SWP) + //----_0001_0000_----_----_----_1001_---- + addEntry(0x109, &ArmV3Cpu::ArmSingleDataSwap, ArmOpCategory::SingleDataSwap); //word + addEntry(0x149, &ArmV3Cpu::ArmSingleDataSwap, ArmOpCategory::SingleDataSwap); //byte + + for(int i = 0; i <= 0xFF; i++) { + addEntry(0xF00 + i, &ArmV3Cpu::ArmSoftwareInterrupt, ArmOpCategory::SoftwareInterrupt); + } +} + +void ArmV3Cpu::Serialize(Serializer& s) +{ + SV(_state.CycleCount); + + SV(_state.Pipeline.Fetch.Address); + SV(_state.Pipeline.Fetch.OpCode); + SV(_state.Pipeline.Decode.Address); + SV(_state.Pipeline.Decode.OpCode); + SV(_state.Pipeline.Execute.Address); + SV(_state.Pipeline.Execute.OpCode); + SV(_state.Pipeline.ReloadRequested); + SV(_state.Pipeline.Mode); + + SV(_state.CPSR.Mode); + SV(_state.CPSR.FiqDisable); + SV(_state.CPSR.IrqDisable); + SV(_state.CPSR.Overflow); + SV(_state.CPSR.Carry); + SV(_state.CPSR.Zero); + SV(_state.CPSR.Negative); + + SVArray(_state.R, 16); + SVArray(_state.UserRegs, 7); + SVArray(_state.FiqRegs, 7); + SVArray(_state.IrqRegs, 2); + SVArray(_state.SupervisorRegs, 2); + SVArray(_state.AbortRegs, 2); + SVArray(_state.UndefinedRegs, 2); + + SV(_state.FiqSpsr.Mode); + SV(_state.FiqSpsr.FiqDisable); + SV(_state.FiqSpsr.IrqDisable); + SV(_state.FiqSpsr.Overflow); + SV(_state.FiqSpsr.Carry); + SV(_state.FiqSpsr.Zero); + SV(_state.FiqSpsr.Negative); + + SV(_state.IrqSpsr.Mode); + SV(_state.IrqSpsr.FiqDisable); + SV(_state.IrqSpsr.IrqDisable); + SV(_state.IrqSpsr.Overflow); + SV(_state.IrqSpsr.Carry); + SV(_state.IrqSpsr.Zero); + SV(_state.IrqSpsr.Negative); + + SV(_state.SupervisorSpsr.Mode); + SV(_state.SupervisorSpsr.FiqDisable); + SV(_state.SupervisorSpsr.IrqDisable); + SV(_state.SupervisorSpsr.Overflow); + SV(_state.SupervisorSpsr.Carry); + SV(_state.SupervisorSpsr.Zero); + SV(_state.SupervisorSpsr.Negative); + + SV(_state.AbortSpsr.Mode); + SV(_state.AbortSpsr.FiqDisable); + SV(_state.AbortSpsr.IrqDisable); + SV(_state.AbortSpsr.Overflow); + SV(_state.AbortSpsr.Carry); + SV(_state.AbortSpsr.Zero); + SV(_state.AbortSpsr.Negative); + + SV(_state.UndefinedSpsr.Mode); + SV(_state.UndefinedSpsr.FiqDisable); + SV(_state.UndefinedSpsr.IrqDisable); + SV(_state.UndefinedSpsr.Overflow); + SV(_state.UndefinedSpsr.Carry); + SV(_state.UndefinedSpsr.Zero); + SV(_state.UndefinedSpsr.Negative); +} diff --git a/Core/SNES/Coprocessors/ST018/ArmV3Cpu.h b/Core/SNES/Coprocessors/ST018/ArmV3Cpu.h new file mode 100644 index 000000000..992f7ec55 --- /dev/null +++ b/Core/SNES/Coprocessors/ST018/ArmV3Cpu.h @@ -0,0 +1,149 @@ +#if (defined(DUMMYCPU) && !defined(__DUMMYARMV3CPU__H)) || (!defined(DUMMYCPU) && !defined(__ARMV3CPU__H)) +#ifdef DUMMYCPU +#define __DUMMYARMV3CPU__H +#else +#define __ARMV3CPU__H +#endif + +#include "pch.h" +#include "SNES/Coprocessors/ST018/ArmV3Types.h" +#include "Shared/Emulator.h" +#include "Shared/ArmEnums.h" +#include "Debugger/DebugTypes.h" +#include "Utilities/ISerializable.h" + +class Emulator; +class St018; + +class ArmV3Cpu : public ISerializable +{ +private: + uint32_t _opCode = 0; + ArmV3CpuState _state = {}; + + Emulator* _emu = nullptr; + St018* _st018 = nullptr; + + typedef void(ArmV3Cpu::* Func)(); + static Func _armTable[0x1000]; + static ArmOpCategory _armCategory[0x1000]; + + uint32_t Add(uint32_t op1, uint32_t op2, bool carry, bool updateFlags); + uint32_t Sub(uint32_t op1, uint32_t op2, bool carry, bool updateFlags); + uint32_t LogicalOp(uint32_t result, bool carry, bool updateFlags); + + uint32_t RotateRight(uint32_t value, uint32_t shift); + uint32_t RotateRight(uint32_t value, uint32_t shift, bool& carry); + + uint32_t ShiftLsl(uint32_t value, uint8_t shift, bool& carry); + uint32_t ShiftLsr(uint32_t value, uint8_t shift, bool& carry); + uint32_t ShiftAsr(uint32_t value, uint8_t shift, bool& carry); + uint32_t ShiftRor(uint32_t value, uint8_t shift, bool& carry); + uint32_t ShiftRrx(uint32_t value, bool& carry); + + uint32_t R(uint8_t reg); + void SetR(uint8_t reg, uint32_t value) + { + _state.R[reg] = value; + if(reg == 15) { + _state.Pipeline.ReloadRequested = true; + } + } + + ArmV3CpuFlags& GetSpsr(); + + static void InitArmOpTable(); + void ArmBranch(); + void ArmMsr(); + void ArmMrs(); + void ArmDataProcessing(); + void ArmMultiply(); + void ArmMultiplyLong(); + void ArmSingleDataTransfer(); + void ArmBlockDataTransfer(); + void ArmSingleDataSwap(); + void ArmSoftwareInterrupt(); + void ArmInvalidOp(); + + bool CheckConditions(uint32_t condCode); + + void SwitchMode(ArmV3CpuMode mode); + + void ReloadPipeline(); + + __forceinline void ProcessPipeline() + { + ArmV3CpuPipeline& pipe = _state.Pipeline; + + if(pipe.ReloadRequested) { + ReloadPipeline(); + } + + pipe.Execute = pipe.Decode; + pipe.Decode = pipe.Fetch; + + pipe.Fetch.Address = _state.R[15] = (_state.R[15] + 4); + pipe.Fetch.OpCode = ReadCode(pipe.Mode, pipe.Fetch.Address); + } + + uint32_t ReadCode(ArmV3AccessModeVal mode, uint32_t addr); + __forceinline uint32_t Read(ArmV3AccessModeVal mode, uint32_t addr); + __forceinline void Write(ArmV3AccessModeVal mode, uint32_t addr, uint32_t value); + void Idle(); + void Idle(uint8_t cycleCount); + + void ProcessException(ArmV3CpuMode mode, ArmV3CpuVector vector); + +public: + virtual ~ArmV3Cpu(); + + static void StaticInit(); + + void Init(Emulator* emu, St018* st018); + + static ArmOpCategory GetArmOpCategory(uint32_t opCode); + + ArmV3CpuState& GetState(); + uint32_t GetProgramCounter() { return _state.R[15]; } + void SetProgramCounter(uint32_t addr); + + __forceinline void Exec() + { +#ifndef DUMMYCPU + _emu->ProcessInstruction(); +#endif + + _opCode = _state.Pipeline.Execute.OpCode; +#ifndef DUMMYCPU + if(CheckConditions(_opCode >> 28)) { +#else + { +#endif + uint16_t opType = ((_opCode & 0x0FF00000) >> 16) | ((_opCode & 0xF0) >> 4); + (this->*_armTable[opType])(); + } + +#ifndef DUMMYCPU + ProcessPipeline(); +#endif + } + + void PowerOn(bool forReset); + + void Serialize(Serializer & s) override; + +#ifdef DUMMYCPU +private: + uint32_t _memOpCounter = 0; + MemoryOperationInfo _memOperations[32] = {}; + ArmV3AccessModeVal _memAccessMode[32] = {}; + +public: + void SetDummyState(ArmV3CpuState & state); + uint32_t GetOperationCount(); + void LogMemoryOperation(uint32_t addr, uint32_t value, ArmV3AccessModeVal mode, MemoryOperationType type); + MemoryOperationInfo GetOperationInfo(uint32_t index); + ArmV3AccessModeVal GetOperationMode(uint32_t index); +#endif + }; +#endif diff --git a/Core/SNES/Coprocessors/ST018/ArmV3Types.h b/Core/SNES/Coprocessors/ST018/ArmV3Types.h new file mode 100644 index 000000000..302a20517 --- /dev/null +++ b/Core/SNES/Coprocessors/ST018/ArmV3Types.h @@ -0,0 +1,104 @@ +#pragma once +#include "pch.h" +#include "Shared/MemoryType.h" +#include "Shared/BaseState.h" +#include "Utilities/Serializer.h" + +enum class ArmV3CpuMode : uint8_t +{ + User = 0b10000, + Fiq = 0b10001, + Irq = 0b10010, + Supervisor = 0b10011, + Abort = 0b10111, + Undefined = 0b11011, + System = 0b11111, +}; + +enum class ArmV3CpuVector : uint32_t +{ + Undefined = 0x04, + SoftwareIrq = 0x08, + AbortPrefetch = 0x0C, + AbortData = 0x10, + Irq = 0x18, + Fiq = 0x1C, +}; + +typedef uint8_t ArmV3AccessModeVal; + +namespace ArmV3AccessMode +{ + enum Mode + { + Sequential = (1 << 0), + Word = (1 << 1), + Byte = (1 << 2), + NoRotate = (1 << 3), + Prefetch = (1 << 4) + }; +} + +struct ArmV3CpuFlags +{ + ArmV3CpuMode Mode; + bool FiqDisable; + bool IrqDisable; + + bool Overflow; + bool Carry; + bool Zero; + bool Negative; + + uint32_t ToInt32() + { + return ( + (Negative << 31) | + (Zero << 30) | + (Carry << 29) | + (Overflow << 28) | + + (IrqDisable << 7) | + (FiqDisable << 6) | + (uint8_t)Mode + ); + } +}; + +struct ArmV3InstructionData +{ + uint32_t Address; + uint32_t OpCode; +}; + +struct ArmV3CpuPipeline +{ + ArmV3InstructionData Fetch; + ArmV3InstructionData Decode; + ArmV3InstructionData Execute; + bool ReloadRequested; + ArmV3AccessModeVal Mode; +}; + +struct ArmV3CpuState : BaseState +{ + ArmV3CpuPipeline Pipeline; + uint32_t R[16]; + ArmV3CpuFlags CPSR; + + uint32_t UserRegs[7]; + uint32_t FiqRegs[7]; + uint32_t IrqRegs[2]; + + uint32_t SupervisorRegs[2]; + uint32_t AbortRegs[2]; + uint32_t UndefinedRegs[2]; + + ArmV3CpuFlags FiqSpsr; + ArmV3CpuFlags IrqSpsr; + ArmV3CpuFlags SupervisorSpsr; + ArmV3CpuFlags AbortSpsr; + ArmV3CpuFlags UndefinedSpsr; + + uint64_t CycleCount; +}; diff --git a/Core/SNES/Coprocessors/ST018/St018.cpp b/Core/SNES/Coprocessors/ST018/St018.cpp new file mode 100644 index 000000000..cc448d772 --- /dev/null +++ b/Core/SNES/Coprocessors/ST018/St018.cpp @@ -0,0 +1,333 @@ +#include "pch.h" +#include "SNES/SnesConsole.h" +#include "SNES/SnesMemoryManager.h" +#include "SNES/MemoryMappings.h" +#include "SNES/Coprocessors/ST018/St018.h" +#include "SNES/Coprocessors/ST018/ArmV3Cpu.h" +#include "SNES/Coprocessors/ST018/ArmV3Types.h" +#include "SNES/Debugger/DummyArmV3Cpu.h" +#include "Shared/Emulator.h" +#include "Shared/FirmwareHelper.h" +#include "GBA/GbaCpu.h" + +St018::St018(SnesConsole* console) +{ + _emu = console->GetEmulator(); + _console = console; + _memoryManager = console->GetMemoryManager(); + _memoryManager->GetMemoryMappings()->RegisterHandler(0x00, 0x3F, 0x3000, 0x3FFF, this); + _memoryManager->GetMemoryMappings()->RegisterHandler(0x80, 0xBF, 0x3000, 0x3FFF, this); + + ArmV3Cpu::StaticInit(); + DummyArmV3Cpu::StaticInit(); + GbaCpu::StaticInit(); //todo remove + + _prgRom = new uint8_t[St018::PrgRomSize]; + _dataRom = new uint8_t[St018::DataRomSize]; + + _workRam = new uint8_t[St018::WorkRamSize]; + _console->InitializeRam(_workRam, St018::WorkRamSize); + + vector firmwareData; + FirmwareHelper::LoadSt018Firmware(_emu, firmwareData); + if(firmwareData.size() == 0x28000) { + memcpy(_prgRom, firmwareData.data(), St018::PrgRomSize); + memcpy(_dataRom, firmwareData.data() + St018::PrgRomSize, St018::DataRomSize); + } + + _emu->RegisterMemory(MemoryType::St018PrgRom, _prgRom, St018::PrgRomSize); + _emu->RegisterMemory(MemoryType::St018DataRom, _dataRom, St018::DataRomSize); + _emu->RegisterMemory(MemoryType::St018WorkRam, _workRam, St018::WorkRamSize); + + _cpu.reset(new ArmV3Cpu()); + _cpu->Init(_emu, this); + _cpu->PowerOn(false); +} + +St018::~St018() +{ + delete[] _prgRom; + delete[] _dataRom; + delete[] _workRam; +} + +void St018::Reset() +{ +} + +void St018::Run() +{ + ArmV3CpuState& state = _cpu->GetState(); + + uint64_t targetCycle = _memoryManager->GetMasterClock(); + + if(_state.ArmReset) { + state.CycleCount = targetCycle; + } + + while(state.CycleCount < targetCycle) { + _cpu->Exec(); + } +} + +void St018::ProcessEndOfFrame() +{ + Run(); +} + +uint8_t St018::Read(uint32_t addr) +{ + Run(); + + switch(addr & 0xFF06) { + case 0x3800: + _state.HasDataForSnes = false; + return _state.DataSnes; + + case 0x3802: + _state.Ack = false; + break; + + case 0x3804: + return GetStatus(); + } + + return _memoryManager->GetOpenBus(); +} + +uint8_t St018::Peek(uint32_t addr) +{ + switch(addr & 0xFF06) { + case 0x3800: return _state.DataSnes; + case 0x3802: break; + case 0x3804: return GetStatus(); + } + + return _memoryManager->GetOpenBus(); +} + +void St018::PeekBlock(uint32_t addr, uint8_t* output) +{ + for(int i = 0; i < 0x1000; i++) { + output[i] = Peek(i); + } +} + +void St018::Write(uint32_t addr, uint8_t value) +{ + Run(); + + switch(addr & 0xFF06) { + case 0x3802: + _state.DataArm = value; + _state.HasDataForArm = true; + break; + + case 0x3804: + if(_state.ArmReset && !value) { + _cpu->PowerOn(true); + } + _state.ArmReset = value != 0; + break; + } +} + +AddressInfo St018::GetAbsoluteAddress(uint32_t address) +{ + return { -1, MemoryType::None }; +} + +AddressInfo St018::GetArmAbsoluteAddress(uint32_t addr) +{ + switch(addr >> 28) { + case 0x00: return { (int)(addr & 0x1FFFF), MemoryType::St018PrgRom }; + case 0x0A: return { (int)(addr & 0x7FFF), MemoryType::St018DataRom}; + case 0x0E: return { (int)(addr & 0x3FFF), MemoryType::St018WorkRam }; + } + + return { -1, MemoryType::None }; +} + +int St018::GetArmRelativeAddress(AddressInfo& absoluteAddr) +{ + switch(absoluteAddr.Type) { + case MemoryType::St018PrgRom: return absoluteAddr.Address; + case MemoryType::St018DataRom: return 0xA0000000 | absoluteAddr.Address; + case MemoryType::St018WorkRam: return 0xE0000000 | absoluteAddr.Address; + } + + return -1; +} + +void St018::LoadBattery() +{ +} + +void St018::SaveBattery() +{ +} + +St018State& St018::GetState() +{ + return _state; +} + +uint32_t St018::ReadCpu(ArmV3AccessModeVal mode, uint32_t addr) +{ + _cpu->GetState().CycleCount++; + + uint32_t value; + if(mode & ArmV3AccessMode::Word) { + uint8_t b0 = ReadCpuByte(addr & ~0x03); + uint8_t b1 = ReadCpuByte((addr & ~0x03) | 1); + uint8_t b2 = ReadCpuByte((addr & ~0x03) | 2); + uint8_t b3 = ReadCpuByte(addr | 3); + value = b0 | (b1 << 8) | (b2 << 16) | (b3 << 24); + _emu->ProcessMemoryRead(addr & ~0x03, value, mode & ArmV3AccessMode::Prefetch ? MemoryOperationType::ExecOpCode : MemoryOperationType::Read); + } else { + value = ReadCpuByte(addr); + _emu->ProcessMemoryRead(addr, value, MemoryOperationType::Read); + } + return value; +} + +uint8_t St018::ReadCpuByte(uint32_t addr) +{ + switch(addr >> 28) { + case 0x00: return _prgRom[addr & 0x1FFFF]; + case 0x02: return 0; + case 0x04: + switch(addr & 0x3F) { + case 0x10: + _state.HasDataForArm = false; + return _state.DataArm; + + case 0x20: return GetStatus(); + + } + return 0; + + case 0x06: return 0; + case 0x08: return 0; + case 0x0A: return _dataRom[addr & 0x7FFF]; + case 0x0C: return 0; + case 0x0E: return _workRam[addr & 0x3FFF]; + } + + return 0; +} + +uint32_t St018::DebugCpuRead(ArmV3AccessModeVal mode, uint32_t addr) +{ + if(mode & ArmV3AccessMode::Byte) { + return DebugRead(addr); + } else { + uint8_t b0 = DebugRead(addr & ~0x03); + uint8_t b1 = DebugRead((addr & ~0x03) | 1); + uint8_t b2 = DebugRead((addr & ~0x03) | 2); + uint8_t b3 = DebugRead(addr | 3); + return b0 | (b1 << 8) | (b2 << 16) | (b3 << 24); + } +} + +uint8_t St018::DebugRead(uint32_t addr) +{ + switch(addr >> 28) { + case 0x00: return _prgRom[addr & 0x1FFFF]; + case 0x02: return 0; + case 0x04: return 0; + case 0x06: return 0; + case 0x08: return 0; + case 0x0A: return _dataRom[addr & 0x7FFF]; + case 0x0C: return 0; + case 0x0E: return _workRam[addr & 0x3FFF]; + } + + return 0; +} + +void St018::DebugWrite(uint32_t addr, uint8_t value) +{ + switch(addr >> 28) { + case 0x00: _prgRom[addr & 0x1FFFF] = value; break; + case 0x0A: _dataRom[addr & 0x7FFF] = value; break; + case 0x0E: _workRam[addr & 0x3FFF] = value; break; + } +} + +void St018::WriteCpu(ArmV3AccessModeVal mode, uint32_t addr, uint32_t value) +{ + _cpu->GetState().CycleCount++; + + if(mode & ArmV3AccessMode::Word) { + if(_emu->ProcessMemoryWrite(addr & ~0x03, value, MemoryOperationType::Write)) { + WriteCpuByte((addr & ~0x03), (uint8_t)value); + WriteCpuByte((addr & ~0x03) | 0x01, (uint8_t)(value >> 8)); + WriteCpuByte((addr & ~0x03) | 0x02, (uint8_t)(value >> 16)); + WriteCpuByte((addr & ~0x03) | 0x03, (uint8_t)(value >> 24)); + } + } else { + if(_emu->ProcessMemoryWrite(addr, value, MemoryOperationType::Write)) { + WriteCpuByte(addr, value); + } + } +} + +void St018::WriteCpuByte(uint32_t addr, uint8_t value) +{ + switch(addr >> 28) { + case 0x00: break; + case 0x02: break; + case 0x04: + switch(addr & 0x3F) { + case 0x00: + _state.HasDataForSnes = true; + _state.DataSnes = value; + break; + + case 0x10: + _state.Ack = true; + break; + + case 0x20: + case 0x24: + case 0x28: + case 0x2A: + //?? + break; + } + break; + + case 0x06: break; + case 0x08: break; + case 0x0A: break; + case 0x0C: break; + case 0x0E: _workRam[addr & 0x3FFF] = value; break; + } +} + +void St018::ProcessIdleCycle() +{ + _cpu->GetState().CycleCount++; +} + +uint8_t St018::GetStatus() +{ + return ( + (_state.HasDataForSnes ? 0x01 : 0) | + (_state.Ack ? 0x04 : 0) | + (_state.HasDataForArm ? 0x08 : 0) | + (!_state.ArmReset ? 0x80 : 0) + ); +} + +void St018::Serialize(Serializer& s) +{ + SV(_state.Ack); + SV(_state.ArmReset); + SV(_state.DataArm); + SV(_state.DataSnes); + SV(_state.HasDataForArm); + SV(_state.HasDataForSnes); + SV(_cpu); +} diff --git a/Core/SNES/Coprocessors/ST018/St018.h b/Core/SNES/Coprocessors/ST018/St018.h new file mode 100644 index 000000000..80fff6cc7 --- /dev/null +++ b/Core/SNES/Coprocessors/ST018/St018.h @@ -0,0 +1,71 @@ +#pragma once +#include "pch.h" +#include "SNES/Coprocessors/ST018/St018Types.h" +#include "SNES/Coprocessors/ST018/ArmV3Types.h" +#include "SNES/Coprocessors/ST018/ArmV3Cpu.h" +#include "SNES/Coprocessors/BaseCoprocessor.h" + +class SnesConsole; +class Emulator; +class ArmV3Cpu; +class SnesMemoryManager; + +class St018 final : public BaseCoprocessor +{ +private: + static constexpr int PrgRomSize = 0x20000; + static constexpr int DataRomSize = 0x8000; + static constexpr int WorkRamSize = 0x4000; + + SnesConsole* _console = nullptr; + Emulator* _emu = nullptr; + SnesMemoryManager* _memoryManager = nullptr; + St018State _state = {}; + unique_ptr _cpu; + + uint8_t* _prgRom = nullptr; + uint8_t* _dataRom = nullptr; + uint8_t* _workRam = nullptr; + + __forceinline uint8_t ReadCpuByte(uint32_t addr); + __forceinline void WriteCpuByte(uint32_t addr, uint8_t value); + + uint8_t GetStatus(); + +public: + St018(SnesConsole* console); + virtual ~St018(); + + void Reset() override; + void Run() override; + + void ProcessEndOfFrame() override; + + void LoadBattery() override; + void SaveBattery() override; + + ArmV3Cpu* GetCpu() { return _cpu.get(); } + + St018State& GetState(); + + uint32_t ReadCpu(ArmV3AccessModeVal mode, uint32_t addr); + uint32_t DebugCpuRead(ArmV3AccessModeVal mode, uint32_t addr); + void WriteCpu(ArmV3AccessModeVal mode, uint32_t addr, uint32_t value); + void ProcessIdleCycle(); + + uint8_t DebugRead(uint32_t addr); + void DebugWrite(uint32_t addr, uint8_t value); + + uint8_t Read(uint32_t addr) override; + void Write(uint32_t addr, uint8_t value) override; + + uint8_t Peek(uint32_t addr) override; + void PeekBlock(uint32_t addr, uint8_t* output) override; + + AddressInfo GetAbsoluteAddress(uint32_t address) override; + + AddressInfo GetArmAbsoluteAddress(uint32_t address); + int GetArmRelativeAddress(AddressInfo& absoluteAddr); + + void Serialize(Serializer& s) override; +}; \ No newline at end of file diff --git a/Core/SNES/Coprocessors/ST018/St018Types.h b/Core/SNES/Coprocessors/ST018/St018Types.h new file mode 100644 index 000000000..9155904fa --- /dev/null +++ b/Core/SNES/Coprocessors/ST018/St018Types.h @@ -0,0 +1,16 @@ +#pragma once +#include "pch.h" +#include "Shared/BaseState.h" +#include "SNES/Coprocessors/ST018/ArmV3Types.h" + +struct St018State : BaseState +{ + bool HasDataForSnes; + uint8_t DataSnes; + + bool HasDataForArm; + uint8_t DataArm; + + bool ArmReset; + bool Ack; +}; \ No newline at end of file diff --git a/Core/SNES/DSP/DspInterpolation.h b/Core/SNES/DSP/DspInterpolation.h index 47af54a23..743b5d013 100644 --- a/Core/SNES/DSP/DspInterpolation.h +++ b/Core/SNES/DSP/DspInterpolation.h @@ -102,7 +102,8 @@ class DspInterpolation import numpy as np fc = 0.5 # Cutoff frequency as a fraction of the sampling rate (in (0, 0.5)). - n = np.arange(10) + N = 10 + n = np.arange(N) for x in range(0, 256): # Compute sinc filter. diff --git a/Core/SNES/Debugger/Cx4Debugger.cpp b/Core/SNES/Debugger/Cx4Debugger.cpp index a4340aa6e..5f72d7ef7 100644 --- a/Core/SNES/Debugger/Cx4Debugger.cpp +++ b/Core/SNES/Debugger/Cx4Debugger.cpp @@ -199,7 +199,7 @@ uint64_t Cx4Debugger::GetCpuCycleCount(bool forProfiler) DebuggerFeatures Cx4Debugger::GetSupportedFeatures() { DebuggerFeatures features = {}; - features.ChangeProgramCounter = true; + features.ChangeProgramCounter = AllowChangeProgramCounter; features.CallStack = true; features.StepOut = true; features.StepOver = true; diff --git a/Core/SNES/Debugger/DummyArmV3Cpu.cpp b/Core/SNES/Debugger/DummyArmV3Cpu.cpp new file mode 100644 index 000000000..c4f296338 --- /dev/null +++ b/Core/SNES/Debugger/DummyArmV3Cpu.cpp @@ -0,0 +1,41 @@ +#include "pch.h" +#include "SNES/Debugger/DummyArmV3Cpu.h" + +#define DUMMYCPU +#define ArmV3Cpu DummyArmV3Cpu +#include "SNES/Coprocessors/ST018/ArmV3Cpu.cpp" +#undef ArmV3Cpu +#undef DUMMYCPU + +void DummyArmV3Cpu::SetDummyState(ArmV3CpuState& state) +{ + _memOpCounter = 0; + _state = state; +} + +uint32_t DummyArmV3Cpu::GetOperationCount() +{ + return _memOpCounter; +} + +void DummyArmV3Cpu::LogMemoryOperation(uint32_t addr, uint32_t value, ArmV3AccessModeVal mode, MemoryOperationType type) +{ + _memOperations[_memOpCounter] = { + addr, + (int32_t)value, + type, + MemoryType::St018Memory + }; + _memAccessMode[_memOpCounter] = mode; + _memOpCounter++; +} + +MemoryOperationInfo DummyArmV3Cpu::GetOperationInfo(uint32_t index) +{ + return _memOperations[index]; +} + +ArmV3AccessModeVal DummyArmV3Cpu::GetOperationMode(uint32_t index) +{ + return _memAccessMode[index]; +} \ No newline at end of file diff --git a/Core/SNES/Debugger/DummyArmV3Cpu.h b/Core/SNES/Debugger/DummyArmV3Cpu.h new file mode 100644 index 000000000..e66fd7bc5 --- /dev/null +++ b/Core/SNES/Debugger/DummyArmV3Cpu.h @@ -0,0 +1,10 @@ +#pragma once + +#include "pch.h" +#include "Debugger/DebugTypes.h" + +#define DUMMYCPU +#define ArmV3Cpu DummyArmV3Cpu +#include "SNES/Coprocessors/ST018/ArmV3Cpu.h" +#undef ArmV3Cpu +#undef DUMMYCPU diff --git a/Core/SNES/Debugger/GsuDebugger.cpp b/Core/SNES/Debugger/GsuDebugger.cpp index 9e5c84e39..a177dd536 100644 --- a/Core/SNES/Debugger/GsuDebugger.cpp +++ b/Core/SNES/Debugger/GsuDebugger.cpp @@ -160,7 +160,7 @@ uint64_t GsuDebugger::GetCpuCycleCount(bool forProfiler) DebuggerFeatures GsuDebugger::GetSupportedFeatures() { DebuggerFeatures features = {}; - features.ChangeProgramCounter = true; + features.ChangeProgramCounter = AllowChangeProgramCounter; return features; } diff --git a/Core/SNES/Debugger/St018Debugger.cpp b/Core/SNES/Debugger/St018Debugger.cpp new file mode 100644 index 000000000..91e2b7e6b --- /dev/null +++ b/Core/SNES/Debugger/St018Debugger.cpp @@ -0,0 +1,303 @@ +#include "pch.h" +#include "SNES/Debugger/St018Debugger.h" +#include "SNES/Debugger/DummyArmV3Cpu.h" +#include "SNES/Debugger/TraceLogger/St018TraceLogger.h" +#include "SNES/Coprocessors/ST018/ArmV3Cpu.h" +#include "SNES/Coprocessors/ST018/ArmV3Types.h" +#include "SNES/Coprocessors/ST018/St018.h" +#include "SNES/BaseCartridge.h" +#include "SNES/SnesConsole.h" +#include "GBA/Debugger/GbaDisUtils.h" +#include "Debugger/DisassemblyInfo.h" +#include "Debugger/Disassembler.h" +#include "Debugger/CallstackManager.h" +#include "Debugger/BreakpointManager.h" +#include "Debugger/Debugger.h" +#include "Debugger/MemoryAccessCounter.h" +#include "Debugger/ExpressionEvaluator.h" +#include "Debugger/MemoryDumper.h" +#include "Debugger/CodeDataLogger.h" +#include "Debugger/BaseEventManager.h" +#include "Utilities/HexUtilities.h" +#include "Shared/EmuSettings.h" +#include "Shared/Emulator.h" +#include "Shared/BaseControlManager.h" +#include "Shared/MemoryOperationType.h" + +St018Debugger::St018Debugger(Debugger* debugger) : IDebugger(debugger->GetEmulator()) +{ + SnesConsole* console = (SnesConsole*)debugger->GetConsole(); + + _debugger = debugger; + _emu = debugger->GetEmulator(); + + _disassembler = debugger->GetDisassembler(); + _memoryAccessCounter = debugger->GetMemoryAccessCounter(); + + _console = console; + + _st018 = _console->GetCartridge()->GetSt018(); + _cpu = _st018->GetCpu(); + + _settings = debugger->GetEmulator()->GetSettings(); + + _traceLogger.reset(new St018TraceLogger(debugger, this, console->GetPpu())); + + _callstackManager.reset(new CallstackManager(debugger, this)); + _breakpointManager.reset(new BreakpointManager(debugger, this, CpuType::St018, debugger->GetEventManager(CpuType::St018))); + _step.reset(new StepRequest()); + + _dummyCpu.reset(new DummyArmV3Cpu()); + _dummyCpu->Init(_emu, _st018); +} + +St018Debugger::~St018Debugger() +{ +} + +void St018Debugger::Reset() +{ + _callstackManager->Clear(); + ResetPrevOpCode(); +} + +void St018Debugger::ProcessInstruction() +{ + ArmV3CpuState& state = _cpu->GetState(); + uint32_t pc = state.Pipeline.Execute.Address; + uint32_t opCode = state.Pipeline.Execute.OpCode; + + uint8_t flags = 0; + + AddressInfo addressInfo = _st018->GetArmAbsoluteAddress(pc); + MemoryOperationInfo operation(pc, opCode, MemoryOperationType::ExecOpCode, MemoryType::St018Memory); + InstructionProgress.LastMemOperation = operation; + InstructionProgress.StartCycle = _cpu->GetState().CycleCount; + + if(addressInfo.Type != MemoryType::None) { + _disassembler->BuildCache(addressInfo, flags, CpuType::St018); + } + + ProcessCallStackUpdates(addressInfo, pc); + + _prevFlags = flags; + _prevOpCode = opCode; + _prevProgramCounter = pc; + + _step->ProcessCpuExec(); + + if(_step->StepCount != 0 && _breakpointManager->HasBreakpoints() && _settings->GetDebugConfig().UsePredictiveBreakpoints) { + _dummyCpu->SetDummyState(state); + _dummyCpu->Exec(); + for(uint32_t i = 1; i < _dummyCpu->GetOperationCount(); i++) { + MemoryOperationInfo memOp = _dummyCpu->GetOperationInfo(i); + if(_breakpointManager->HasBreakpointForType(memOp.Type)) { + AddressInfo absAddr = _st018->GetArmAbsoluteAddress(memOp.Address); + switch(_dummyCpu->GetOperationMode(i) & (ArmV3AccessMode::Byte | ArmV3AccessMode::Word)) { + case ArmV3AccessMode::Byte: _debugger->ProcessPredictiveBreakpoint<1>(CpuType::St018, _breakpointManager.get(), memOp, absAddr); break; + case ArmV3AccessMode::Word: _debugger->ProcessPredictiveBreakpoint<4>(CpuType::St018, _breakpointManager.get(), memOp, absAddr); break; + } + } + } + } + + _debugger->ProcessBreakConditions<4>(CpuType::St018, *_step.get(), _breakpointManager.get(), operation, addressInfo); + + if(_traceLogger->IsEnabled()) { + DisassemblyInfo disInfo = _disassembler->GetDisassemblyInfo(addressInfo, pc, _prevFlags, CpuType::St018); + _traceLogger->Log(state, disInfo, operation, addressInfo); + } +} + +template +void St018Debugger::ProcessRead(uint32_t addr, uint32_t value, MemoryOperationType type) +{ + AddressInfo addressInfo = _st018->GetArmAbsoluteAddress(addr); + MemoryOperationInfo operation(addr, value, type, MemoryType::St018Memory); + InstructionProgress.LastMemOperation = operation; + + if(type == MemoryOperationType::ExecOpCode) { + _memoryAccessCounter->ProcessMemoryExec(addressInfo, _console->GetMasterClock()); + } else { + if(addressInfo.Address >= 0) { + ReadResult result = _memoryAccessCounter->ProcessMemoryRead(addressInfo, _console->GetMasterClock()); + if(result != ReadResult::Normal) { + //Memory access was a read on an uninitialized memory address + if((int)result & (int)ReadResult::FirstUninitRead) { + //Only warn the first time + _debugger->Log("[ST018] Uninitialized memory read: $" + HexUtilities::ToHex(addr)); + } + if(_settings->CheckDebuggerFlag(DebuggerFlags::St018DebuggerEnabled) && _settings->GetDebugConfig().BreakOnUninitRead) { + _step->Break(BreakSource::BreakOnUninitMemoryRead); + } + } + } + + if(_traceLogger->IsEnabled()) { + _traceLogger->LogNonExec(operation, addressInfo); + } + + _debugger->ProcessBreakConditions(CpuType::St018, *_step.get(), _breakpointManager.get(), operation, addressInfo); + } +} + +template +void St018Debugger::ProcessWrite(uint32_t addr, uint32_t value, MemoryOperationType type) +{ + AddressInfo addressInfo = _st018->GetArmAbsoluteAddress(addr); + MemoryOperationInfo operation(addr, value, type, MemoryType::St018Memory); + InstructionProgress.LastMemOperation = operation; + _debugger->ProcessBreakConditions(CpuType::St018, *_step.get(), _breakpointManager.get(), operation, addressInfo); + + if(addressInfo.Type == MemoryType::St018WorkRam) { + _disassembler->InvalidateCache(addressInfo, CpuType::St018); + } + + if(_traceLogger->IsEnabled()) { + _traceLogger->LogNonExec(operation, addressInfo); + } + + _memoryAccessCounter->ProcessMemoryWrite(addressInfo, _console->GetMasterClock()); +} + +void St018Debugger::Run() +{ + _step.reset(new StepRequest()); +} + +void St018Debugger::Step(int32_t stepCount, StepType type) +{ + StepRequest step(type); + + switch(type) { + case StepType::Step: step.StepCount = stepCount; break; + case StepType::StepOut: step.BreakAddress = _callstackManager->GetReturnAddress(); break; + case StepType::StepOver: + if(GbaDisUtils::IsJumpToSub(_prevOpCode, _prevFlags)) { + step.BreakAddress = _prevProgramCounter + GbaDisUtils::GetOpSize(_prevOpCode, _prevFlags); + } else { + //For any other instruction, step over is the same as step into + step.StepCount = 1; + } + break; + } + + _step.reset(new StepRequest(step)); +} + +void St018Debugger::ProcessCallStackUpdates(AddressInfo& destAddr, uint32_t destPc) +{ + if(GbaDisUtils::IsJumpToSub(_prevOpCode, _prevFlags) && destPc != _prevProgramCounter + GbaDisUtils::GetOpSize(_prevOpCode, _prevFlags)) { + //New PC doesn't match the next instruction, so the call was (probably) done + uint32_t returnPc = _prevProgramCounter + GbaDisUtils::GetOpSize(_prevOpCode, _prevFlags); + AddressInfo src = _st018->GetArmAbsoluteAddress(_prevProgramCounter); + AddressInfo ret = _st018->GetArmAbsoluteAddress(returnPc); + _callstackManager->Push(src, _prevProgramCounter, destAddr, destPc, ret, returnPc, 0, StackFrameFlags::None); + } else if(GbaDisUtils::IsUnconditionalJump(_prevOpCode, _prevFlags) || GbaDisUtils::IsConditionalJump(_prevOpCode, _prevFlags)) { + if(destPc != _prevProgramCounter + GbaDisUtils::GetOpSize(_prevOpCode, _prevFlags)) { + //Return instruction used, and PC doesn't match the next instruction, so the ret was (probably) taken (can be conditional) + if(_callstackManager->IsReturnAddrMatch(destPc)) { + //Only pop top of callstack if the address matches the expected address + _callstackManager->Pop(destAddr, destPc, 0); + } + } + + if(_step->BreakAddress == destPc) { + //If we're on the expected return address, break immediately (for step over/step out) + _step->Break(BreakSource::CpuStep); + } + } +} + +void St018Debugger::ProcessInterrupt(uint32_t originalPc, uint32_t currentPc, bool forNmi) +{ + AddressInfo ret = _st018->GetArmAbsoluteAddress(originalPc); + AddressInfo dest = _st018->GetArmAbsoluteAddress(currentPc); + + //If a call/return occurred just before IRQ, it needs to be processed now + ProcessCallStackUpdates(ret, originalPc); + ResetPrevOpCode(); + + _debugger->InternalProcessInterrupt( + CpuType::St018, *this, *_step.get(), + ret, originalPc, dest, currentPc, ret, originalPc, 0, forNmi + ); +} + +DebuggerFeatures St018Debugger::GetSupportedFeatures() +{ + DebuggerFeatures features = {}; + features.ChangeProgramCounter = AllowChangeProgramCounter; + features.StepOver = true; + features.StepOut = true; + features.CallStack = true; + + features.CpuVectorCount = 3; + features.CpuVectors[0] = { "IRQ", (int)ArmV3CpuVector::Irq, VectorType::Direct }; + features.CpuVectors[1] = { "SWI", (int)ArmV3CpuVector::SoftwareIrq, VectorType::Direct }; + features.CpuVectors[2] = { "Undefined", (int)ArmV3CpuVector::Undefined, VectorType::Direct }; + + return features; +} + +void St018Debugger::SetProgramCounter(uint32_t addr, bool updateDebuggerOnly) +{ + if(!updateDebuggerOnly) { + _cpu->SetProgramCounter(addr); + } + _prevOpCode = _st018->DebugCpuRead(ArmV3AccessMode::Word, addr); + _prevProgramCounter = addr; +} + +uint32_t St018Debugger::GetProgramCounter(bool getInstPc) +{ + return getInstPc ? _prevProgramCounter : _cpu->GetState().R[15]; +} + +uint64_t St018Debugger::GetCpuCycleCount(bool forProfiler) +{ + return _cpu->GetState().CycleCount; +} + +void St018Debugger::ResetPrevOpCode() +{ + _prevOpCode = 0; +} + +CallstackManager* St018Debugger::GetCallstackManager() +{ + return _callstackManager.get(); +} + +BreakpointManager* St018Debugger::GetBreakpointManager() +{ + return _breakpointManager.get(); +} + +BaseState& St018Debugger::GetState() +{ + return _cpu->GetState(); +} + +ITraceLogger* St018Debugger::GetTraceLogger() +{ + return _traceLogger.get(); +} + +IAssembler* St018Debugger::GetAssembler() +{ + throw std::runtime_error("Assembler not supported for ST018"); +} + +BaseEventManager* St018Debugger::GetEventManager() +{ + return nullptr; +} + +template void St018Debugger::ProcessRead<1>(uint32_t addr, uint32_t value, MemoryOperationType type); +template void St018Debugger::ProcessRead<2>(uint32_t addr, uint32_t value, MemoryOperationType type); +template void St018Debugger::ProcessRead<4>(uint32_t addr, uint32_t value, MemoryOperationType type); + +template void St018Debugger::ProcessWrite<1>(uint32_t addr, uint32_t value, MemoryOperationType type); +template void St018Debugger::ProcessWrite<2>(uint32_t addr, uint32_t value, MemoryOperationType type); +template void St018Debugger::ProcessWrite<4>(uint32_t addr, uint32_t value, MemoryOperationType type); diff --git a/Core/SNES/Debugger/St018Debugger.h b/Core/SNES/Debugger/St018Debugger.h new file mode 100644 index 000000000..ba19ee75c --- /dev/null +++ b/Core/SNES/Debugger/St018Debugger.h @@ -0,0 +1,73 @@ +#pragma once +#include "pch.h" +#include "Debugger/DebugTypes.h" +#include "Debugger/IDebugger.h" + +class Disassembler; +class Debugger; +class St018; +class ArmV3Cpu; +class St018TraceLogger; +class SnesConsole; +class CallstackManager; +class BreakpointManager; +class MemoryAccessCounter; +class EmuSettings; +class Emulator; +class DummyArmV3Cpu; + +enum class MemoryOperationType; + +class St018Debugger final : public IDebugger +{ + Debugger* _debugger; + Emulator* _emu; + St018* _st018; + ArmV3Cpu* _cpu; + Disassembler* _disassembler; + MemoryAccessCounter* _memoryAccessCounter; + SnesConsole* _console; + EmuSettings* _settings; + + unique_ptr _callstackManager; + unique_ptr _breakpointManager; + unique_ptr _traceLogger; + unique_ptr _dummyCpu; + + uint32_t _prevOpCode = 0; + uint32_t _prevProgramCounter = 0; + uint8_t _prevFlags = 0; + + __forceinline void ProcessCallStackUpdates(AddressInfo& destAddr, uint32_t destPc); + template void ProcessInstruction(); + +public: + St018Debugger(Debugger* debugger); + ~St018Debugger(); + + void Reset() override; + + void ProcessInstruction(); + template void ProcessRead(uint32_t addr, uint32_t value, MemoryOperationType type); + template void ProcessWrite(uint32_t addr, uint32_t value, MemoryOperationType type); + + void ProcessInterrupt(uint32_t originalPc, uint32_t currentPc, bool forNmi) override; + + void Run() override; + void Step(int32_t stepCount, StepType type) override; + + void SetProgramCounter(uint32_t addr, bool updateDebuggerOnly = false) override; + uint32_t GetProgramCounter(bool getInstPc) override; + uint64_t GetCpuCycleCount(bool forProfiler = false) override; + void ResetPrevOpCode() override; + + DebuggerFeatures GetSupportedFeatures() override; + + CallstackManager* GetCallstackManager() override; + BreakpointManager* GetBreakpointManager() override; + ITraceLogger* GetTraceLogger() override; + IAssembler* GetAssembler() override; + BaseEventManager* GetEventManager() override; + + BaseState& GetState() override; +}; \ No newline at end of file diff --git a/Core/SNES/Debugger/St018DisUtils.cpp b/Core/SNES/Debugger/St018DisUtils.cpp new file mode 100644 index 000000000..8a8e36513 --- /dev/null +++ b/Core/SNES/Debugger/St018DisUtils.cpp @@ -0,0 +1,50 @@ +#include "pch.h" +#include "SNES/SnesConsole.h" +#include "SNES/BaseCartridge.h" +#include "SNES/Debugger/St018DisUtils.h" +#include "SNES/Coprocessors/ST018/ArmV3Types.h" +#include "SNES/Coprocessors/ST018/ArmV3Cpu.h" +#include "SNES/Coprocessors/ST018/St018.h" +#include "SNES/Debugger/DummyArmV3Cpu.h" +#include "Shared/ArmEnums.h" +#include "Debugger/DisassemblyInfo.h" + +EffectiveAddressInfo St018DisUtils::GetEffectiveAddress(DisassemblyInfo& info, SnesConsole* console, ArmV3CpuState& state) +{ + St018* st018 = console->GetCartridge()->GetSt018(); + uint32_t opCode = st018->DebugCpuRead(ArmV3AccessMode::Word, state.Pipeline.Execute.Address); + state.Pipeline.Execute.OpCode = opCode; + + ArmOpCategory category = ArmV3Cpu::GetArmOpCategory(opCode); + switch(category) { + case ArmOpCategory::InvalidOp: + case ArmOpCategory::BlockDataTransfer: + return {}; + } + + DummyArmV3Cpu dummyCpu; + dummyCpu.Init(console->GetEmulator(), st018); + dummyCpu.SetDummyState(state); + dummyCpu.Exec(); + + uint32_t count = dummyCpu.GetOperationCount(); + for(int i = count - 1; i >= 0; i--) { + MemoryOperationInfo opInfo = dummyCpu.GetOperationInfo(i); + if(opInfo.Type != MemoryOperationType::ExecOperand) { + EffectiveAddressInfo result; + result.Address = opInfo.Address; + result.Type = opInfo.MemType; + result.ValueSize = 1; + + switch(dummyCpu.GetOperationMode(i) & (ArmV3AccessMode::Byte | ArmV3AccessMode::Word)) { + case ArmV3AccessMode::Byte: result.ValueSize = 1; break; + case ArmV3AccessMode::Word: result.ValueSize = 4; break; + } + + result.ShowAddress = true; + return result; + } + } + + return {}; +} diff --git a/Core/SNES/Debugger/St018DisUtils.h b/Core/SNES/Debugger/St018DisUtils.h new file mode 100644 index 000000000..6bc18f124 --- /dev/null +++ b/Core/SNES/Debugger/St018DisUtils.h @@ -0,0 +1,12 @@ +#pragma once +#include "pch.h" +#include "Debugger/DebugTypes.h" + +class SnesConsole; +struct ArmV3CpuState; + +class St018DisUtils +{ +public: + static EffectiveAddressInfo GetEffectiveAddress(DisassemblyInfo& info, SnesConsole* console, ArmV3CpuState& state); +}; diff --git a/Core/SNES/Debugger/TraceLogger/Cx4TraceLogger.cpp b/Core/SNES/Debugger/TraceLogger/Cx4TraceLogger.cpp index 388056339..3c05fddbe 100644 --- a/Core/SNES/Debugger/TraceLogger/Cx4TraceLogger.cpp +++ b/Core/SNES/Debugger/TraceLogger/Cx4TraceLogger.cpp @@ -2,13 +2,12 @@ #include "SNES/Debugger/TraceLogger/Cx4TraceLogger.h" #include "SNES/SnesPpu.h" #include "SNES/SnesMemoryManager.h" -#include "SNES/Coprocessors/GSU/GsuTypes.h" #include "Debugger/DisassemblyInfo.h" #include "Debugger/Debugger.h" #include "Debugger/DebugTypes.h" #include "Utilities/HexUtilities.h" -Cx4TraceLogger::Cx4TraceLogger(Debugger* debugger, IDebugger* cpuDebugger, SnesPpu* ppu, SnesMemoryManager* memoryManager) : BaseTraceLogger(debugger, cpuDebugger, CpuType::Gsu) +Cx4TraceLogger::Cx4TraceLogger(Debugger* debugger, IDebugger* cpuDebugger, SnesPpu* ppu, SnesMemoryManager* memoryManager) : BaseTraceLogger(debugger, cpuDebugger, CpuType::Cx4) { _ppu = ppu; _memoryManager = memoryManager; diff --git a/Core/SNES/Debugger/TraceLogger/NecDspTraceLogger.cpp b/Core/SNES/Debugger/TraceLogger/NecDspTraceLogger.cpp index 353f86e13..2dc105abc 100644 --- a/Core/SNES/Debugger/TraceLogger/NecDspTraceLogger.cpp +++ b/Core/SNES/Debugger/TraceLogger/NecDspTraceLogger.cpp @@ -2,13 +2,12 @@ #include "SNES/Debugger/TraceLogger/NecDspTraceLogger.h" #include "SNES/SnesPpu.h" #include "SNES/SnesMemoryManager.h" -#include "SNES/Coprocessors/GSU/GsuTypes.h" #include "Debugger/DisassemblyInfo.h" #include "Debugger/Debugger.h" #include "Debugger/DebugTypes.h" #include "Utilities/HexUtilities.h" -NecDspTraceLogger::NecDspTraceLogger(Debugger* debugger, IDebugger* cpuDebugger, SnesPpu* ppu, SnesMemoryManager* memoryManager) : BaseTraceLogger(debugger, cpuDebugger, CpuType::Gsu) +NecDspTraceLogger::NecDspTraceLogger(Debugger* debugger, IDebugger* cpuDebugger, SnesPpu* ppu, SnesMemoryManager* memoryManager) : BaseTraceLogger(debugger, cpuDebugger, CpuType::NecDsp) { _ppu = ppu; _memoryManager = memoryManager; diff --git a/Core/SNES/Debugger/TraceLogger/St018TraceLogger.cpp b/Core/SNES/Debugger/TraceLogger/St018TraceLogger.cpp new file mode 100644 index 000000000..6aecfff8b --- /dev/null +++ b/Core/SNES/Debugger/TraceLogger/St018TraceLogger.cpp @@ -0,0 +1,126 @@ +#include "pch.h" +#include "SNES/Debugger/TraceLogger/St018TraceLogger.h" +#include "SNES/SnesPpu.h" +#include "SNES/Coprocessors/ST018/ArmV3Types.h" +#include "Debugger/DisassemblyInfo.h" +#include "Debugger/Debugger.h" +#include "Debugger/DebugTypes.h" +#include "Utilities/HexUtilities.h" + +St018TraceLogger::St018TraceLogger(Debugger* debugger, IDebugger* cpuDebugger, SnesPpu* ppu) : BaseTraceLogger(debugger, cpuDebugger, CpuType::St018) +{ + _ppu = ppu; +} + +RowDataType St018TraceLogger::GetFormatTagType(string& tag) +{ + if(tag == "R0") { + return RowDataType::R0; + } else if(tag == "R1") { + return RowDataType::R1; + } else if(tag == "R2") { + return RowDataType::R2; + } else if(tag == "R3") { + return RowDataType::R3; + } else if(tag == "R4") { + return RowDataType::R4; + } else if(tag == "R5") { + return RowDataType::R5; + } else if(tag == "R6") { + return RowDataType::R6; + } else if(tag == "R7") { + return RowDataType::R7; + } else if(tag == "R8") { + return RowDataType::R8; + } else if(tag == "R9") { + return RowDataType::R9; + } else if(tag == "R10") { + return RowDataType::R10; + } else if(tag == "R11") { + return RowDataType::R11; + } else if(tag == "R12") { + return RowDataType::R12; + } else if(tag == "R13") { + return RowDataType::R13; + } else if(tag == "R14") { + return RowDataType::R14; + } else if(tag == "R15") { + return RowDataType::R15; + } else if(tag == "CPSR") { + return RowDataType::CPSR; + } else if(tag == "Mode") { + return RowDataType::Mode; + } else { + return RowDataType::Text; + } +} + +void St018TraceLogger::GetTraceRow(string &output, ArmV3CpuState& cpuState, TraceLogPpuState &ppuState, DisassemblyInfo &disassemblyInfo) +{ + for(RowPart& rowPart : _rowParts) { + switch(rowPart.DataType) { + case RowDataType::R0: WriteIntValue(output, cpuState.R[0], rowPart); break; + case RowDataType::R1: WriteIntValue(output, cpuState.R[1], rowPart); break; + case RowDataType::R2: WriteIntValue(output, cpuState.R[2], rowPart); break; + case RowDataType::R3: WriteIntValue(output, cpuState.R[3], rowPart); break; + case RowDataType::R4: WriteIntValue(output, cpuState.R[4], rowPart); break; + case RowDataType::R5: WriteIntValue(output, cpuState.R[5], rowPart); break; + case RowDataType::R6: WriteIntValue(output, cpuState.R[6], rowPart); break; + case RowDataType::R7: WriteIntValue(output, cpuState.R[7], rowPart); break; + case RowDataType::R8: WriteIntValue(output, cpuState.R[8], rowPart); break; + case RowDataType::R9: WriteIntValue(output, cpuState.R[9], rowPart); break; + case RowDataType::R10: WriteIntValue(output, cpuState.R[10], rowPart); break; + case RowDataType::R11: WriteIntValue(output, cpuState.R[11], rowPart); break; + case RowDataType::R12: WriteIntValue(output, cpuState.R[12], rowPart); break; + case RowDataType::R13: WriteIntValue(output, cpuState.R[13], rowPart); break; + case RowDataType::R14: WriteIntValue(output, cpuState.R[14], rowPart); break; + case RowDataType::R15: WriteIntValue(output, cpuState.R[15], rowPart); break; + + case RowDataType::CPSR: { + if(rowPart.DisplayInHex) { + WriteIntValue(output, cpuState.CPSR.ToInt32(), rowPart); + } else { + FastString flags; + + bool showInactive = (rowPart.MinWidth > 0); + + flags.Write(cpuState.CPSR.Negative ? "N" : (showInactive ? "n" : "")); + flags.Write(cpuState.CPSR.Zero ? "Z" : (showInactive ? "z" : "")); + flags.Write(cpuState.CPSR.Carry ? "C" : (showInactive ? "c" : "")); + flags.Write(cpuState.CPSR.Overflow ? "V" : (showInactive ? "v" : "")); + + flags.Write(cpuState.CPSR.FiqDisable ? "F" : (showInactive ? "f" : "")); + flags.Write(cpuState.CPSR.IrqDisable ? "I" : (showInactive ? "i" : "")); + + WriteStringValue(output, flags.ToString(), rowPart); + } + break; + } + + case RowDataType::Mode: { + switch(cpuState.CPSR.Mode) { + case ArmV3CpuMode::User: WriteStringValue(output, "USR", rowPart); break; + case ArmV3CpuMode::Fiq: WriteStringValue(output, "FIQ", rowPart); break; + case ArmV3CpuMode::Irq: WriteStringValue(output, "IRQ", rowPart); break; + case ArmV3CpuMode::Supervisor: WriteStringValue(output, "SVC", rowPart); break; + case ArmV3CpuMode::Abort: WriteStringValue(output, "ABT", rowPart); break; + case ArmV3CpuMode::Undefined: WriteStringValue(output, "UND", rowPart); break; + case ArmV3CpuMode::System: WriteStringValue(output, "SYS", rowPart); break; + } + break; + } + + default: ProcessSharedTag(rowPart, output, cpuState, ppuState, disassemblyInfo); break; + } + } +} + +void St018TraceLogger::LogPpuState() +{ + _ppuState[_currentPos] = { + _ppu->GetCycle(), + _ppu->GetCycle(), + _ppu->GetScanline(), + _ppu->GetFrameCount() + }; +} \ No newline at end of file diff --git a/Core/SNES/Debugger/TraceLogger/St018TraceLogger.h b/Core/SNES/Debugger/TraceLogger/St018TraceLogger.h new file mode 100644 index 000000000..b95da8cd9 --- /dev/null +++ b/Core/SNES/Debugger/TraceLogger/St018TraceLogger.h @@ -0,0 +1,27 @@ +#pragma once +#include "pch.h" +#include "Debugger/BaseTraceLogger.h" +#include "SNES/Coprocessors/ST018/ArmV3Types.h" + +class DisassemblyInfo; +class Debugger; +class SnesPpu; + +class St018TraceLogger : public BaseTraceLogger +{ +private: + SnesPpu* _ppu = nullptr; + +protected: + RowDataType GetFormatTagType(string& tag) override; + +public: + St018TraceLogger(Debugger* debugger, IDebugger* cpuDebugger, SnesPpu* ppu); + + void GetTraceRow(string& output, ArmV3CpuState& cpuState, TraceLogPpuState& ppuState, DisassemblyInfo& disassemblyInfo); + void LogPpuState(); + + __forceinline uint32_t GetProgramCounter(ArmV3CpuState& state) { return state.Pipeline.Execute.Address; } + __forceinline uint64_t GetCycleCount(ArmV3CpuState& state) { return state.CycleCount; } + __forceinline uint8_t GetStackPointer(ArmV3CpuState& state) { return (uint8_t)state.R[13]; } +}; diff --git a/Core/SNES/SnesConsole.cpp b/Core/SNES/SnesConsole.cpp index 9eeb2fec3..1c683b935 100644 --- a/Core/SNES/SnesConsole.cpp +++ b/Core/SNES/SnesConsole.cpp @@ -23,6 +23,7 @@ #include "SNES/Coprocessors/SA1/Sa1.h" #include "SNES/Coprocessors/GSU/Gsu.h" #include "SNES/Coprocessors/CX4/Cx4.h" +#include "SNES/Coprocessors/ST018/St018.h" #include "Shared/Emulator.h" #include "Shared/TimingInfo.h" #include "Shared/EmuSettings.h" @@ -252,6 +253,8 @@ vector SnesConsole::GetCpuTypes() cpuTypes.push_back(CpuType::Gameboy); } else if(_cart->GetSa1()) { cpuTypes.push_back(CpuType::Sa1); + } else if(_cart->GetSt018()) { + cpuTypes.push_back(CpuType::St018); } return cpuTypes; } @@ -441,6 +444,7 @@ AddressInfo SnesConsole::GetAbsoluteAddress(AddressInfo& relAddress) case MemoryType::Sa1Memory: return _cart->GetSa1() ? _cart->GetSa1()->GetMemoryMappings()->GetAbsoluteAddress(relAddress.Address) : unmapped; case MemoryType::GsuMemory: return _cart->GetGsu() ? _cart->GetGsu()->GetMemoryMappings()->GetAbsoluteAddress(relAddress.Address) : unmapped; case MemoryType::Cx4Memory: return _cart->GetCx4() ? _cart->GetCx4()->GetMemoryMappings()->GetAbsoluteAddress(relAddress.Address) : unmapped; + case MemoryType::St018Memory: return _cart->GetSt018() ? _cart->GetSt018()->GetArmAbsoluteAddress(relAddress.Address) : unmapped; case MemoryType::NecDspMemory: return { relAddress.Address, MemoryType::DspProgramRom }; case MemoryType::GameboyMemory: return _cart->GetGameboy() ? _cart->GetGameboy()->GetAbsoluteAddress(relAddress.Address) : unmapped; default: return unmapped; @@ -459,6 +463,7 @@ AddressInfo SnesConsole::GetRelativeAddress(AddressInfo& absAddress, CpuType cpu case CpuType::Sa1: mappings = _cart->GetSa1() ? _cart->GetSa1()->GetMemoryMappings() : nullptr; break; case CpuType::Gsu: mappings = _cart->GetGsu() ? _cart->GetGsu()->GetMemoryMappings() : nullptr; break; case CpuType::Cx4: mappings = _cart->GetCx4() ? _cart->GetCx4()->GetMemoryMappings() : nullptr; break; + case CpuType::St018: break; case CpuType::Gameboy: break; default: return unmapped; } @@ -500,6 +505,11 @@ AddressInfo SnesConsole::GetRelativeAddress(AddressInfo& absAddress, CpuType cpu case MemoryType::GbBootRom: return _cart->GetGameboy() ? AddressInfo { _cart->GetGameboy()->GetRelativeAddress(absAddress), MemoryType::GameboyMemory } : unmapped; + case MemoryType::St018PrgRom: + case MemoryType::St018DataRom: + case MemoryType::St018WorkRam: + return _cart->GetSt018() ? AddressInfo { _cart->GetSt018()->GetArmRelativeAddress(absAddress), MemoryType::St018Memory } : unmapped; + case MemoryType::DspProgramRom: return { absAddress.Address, MemoryType::NecDspMemory }; @@ -542,6 +552,9 @@ void SnesConsole::GetConsoleState(BaseState& baseState, ConsoleType consoleType) if(_cart->GetCx4()) { state.Cx4 = _cart->GetCx4()->GetState(); } + if(_cart->GetSt018()) { + state.St018 = _cart->GetSt018()->GetState(); + } } void SnesConsole::InitializeRam(void* data, uint32_t length) diff --git a/Core/SNES/SnesState.h b/Core/SNES/SnesState.h index 72ecbbc68..c0cb5f838 100644 --- a/Core/SNES/SnesState.h +++ b/Core/SNES/SnesState.h @@ -8,6 +8,7 @@ #include "SNES/Coprocessors/SA1/Sa1Types.h" #include "SNES/Coprocessors/GSU/GsuTypes.h" #include "SNES/Coprocessors/CX4/Cx4Types.h" +#include "SNES/Coprocessors/ST018/St018Types.h" #include "SNES/DmaControllerTypes.h" #include "SNES/InternalRegisterTypes.h" #include "SNES/AluMulDiv.h" @@ -20,9 +21,10 @@ struct SnesState SpcState Spc; DspState Dsp; NecDspState NecDsp; - DebugSa1State Sa1; + Sa1State Sa1; GsuState Gsu; Cx4State Cx4; + St018State St018; SnesDmaControllerState Dma; InternalRegisterState InternalRegs; diff --git a/Core/Shared/ArmEnums.h b/Core/Shared/ArmEnums.h new file mode 100644 index 000000000..6fcf514a8 --- /dev/null +++ b/Core/Shared/ArmEnums.h @@ -0,0 +1,42 @@ +#pragma once +#include "pch.h" + +enum class ArmOpCategory +{ + BranchExchangeRegister, + Branch, + Msr, + Mrs, + DataProcessing, + Multiply, + MultiplyLong, + SingleDataTransfer, + SignedHalfDataTransfer, + BlockDataTransfer, + SingleDataSwap, + SoftwareInterrupt, + InvalidOp, +}; + +enum class ArmAluOperation : uint8_t +{ + And, + Eor, + Sub, + Rsb, + + Add, + Adc, + Sbc, + Rsc, + + Tst, + Teq, + Cmp, + Cmn, + + Orr, + Mov, + Bic, + Mvn +}; diff --git a/Core/Shared/CpuType.h b/Core/Shared/CpuType.h index b9d1af4d0..2ca3dd643 100644 --- a/Core/Shared/CpuType.h +++ b/Core/Shared/CpuType.h @@ -9,6 +9,7 @@ enum class CpuType : uint8_t Sa1, Gsu, Cx4, + St018, Gameboy, Nes, Pce, diff --git a/Core/Shared/FirmwareHelper.h b/Core/Shared/FirmwareHelper.h index 00c543fa6..5df7f24ec 100644 --- a/Core/Shared/FirmwareHelper.h +++ b/Core/Shared/FirmwareHelper.h @@ -149,6 +149,25 @@ class FirmwareHelper return false; } + static bool LoadSt018Firmware(Emulator* emu, vector& out) + { + string filename = "st018.rom"; + uint32_t size = 0x28000; + if(AttemptLoadFirmware(out, filename, size)) { + return true; + } + + MissingFirmwareMessage msg(filename.c_str(), FirmwareType::ST018, size); + emu->GetNotificationManager()->SendNotification(ConsoleNotificationType::MissingFirmware, &msg); + + if(AttemptLoadFirmware(out, filename, size)) { + return true; + } + + MessageManager::DisplayMessage("Error", "Could not find firmware file for ST018"); + return false; + } + static bool LoadBsxFirmware(Emulator* emu, uint8_t** prgRom, uint32_t& prgSize) { if(AttemptLoadBsxFirmware(prgRom, prgSize)) { diff --git a/Core/Shared/MemoryType.h b/Core/Shared/MemoryType.h index 9ee0eb0f6..56bdd1d53 100644 --- a/Core/Shared/MemoryType.h +++ b/Core/Shared/MemoryType.h @@ -8,6 +8,7 @@ enum class MemoryType NecDspMemory, GsuMemory, Cx4Memory, + St018Memory, GameboyMemory, NesMemory, NesPpuMemory, @@ -34,6 +35,9 @@ enum class MemoryType Cx4DataRam, BsxPsRam, BsxMemoryPack, + St018PrgRom, + St018DataRom, + St018WorkRam, GbPrgRom, GbWorkRam, diff --git a/Core/Shared/SettingTypes.h b/Core/Shared/SettingTypes.h index 9b4c97c75..f0da0dafd 100644 --- a/Core/Shared/SettingTypes.h +++ b/Core/Shared/SettingTypes.h @@ -1067,10 +1067,11 @@ enum class DebuggerFlags GsuDebuggerEnabled = (1 << 3), NecDspDebuggerEnabled = (1 << 4), Cx4DebuggerEnabled = (1 << 5), - GbDebuggerEnabled = (1 << 6), - NesDebuggerEnabled = (1 << 7), - PceDebuggerEnabled = (1 << 8), - SmsDebuggerEnabled = (1 << 9), - GbaDebuggerEnabled = (1 << 10), - WsDebuggerEnabled = (1 << 11), + St018DebuggerEnabled = (1 << 6), + GbDebuggerEnabled = (1 << 7), + NesDebuggerEnabled = (1 << 8), + PceDebuggerEnabled = (1 << 9), + SmsDebuggerEnabled = (1 << 10), + GbaDebuggerEnabled = (1 << 11), + WsDebuggerEnabled = (1 << 12), }; diff --git a/UI/Assets/St018Debugger.png b/UI/Assets/St018Debugger.png new file mode 100644 index 000000000..c1f33a7ac Binary files /dev/null and b/UI/Assets/St018Debugger.png differ diff --git a/UI/Config/Debugger/DebuggerShortcutsConfig.cs b/UI/Config/Debugger/DebuggerShortcutsConfig.cs index 692dd4b47..232c7492b 100644 --- a/UI/Config/Debugger/DebuggerShortcutsConfig.cs +++ b/UI/Config/Debugger/DebuggerShortcutsConfig.cs @@ -100,6 +100,7 @@ private void Init() Add(new() { Shortcut = DebuggerShortcut.OpenDebugger, KeyBinding = new(KeyModifiers.Control, Key.D) }); Add(new() { Shortcut = DebuggerShortcut.OpenSpcDebugger, KeyBinding = new(KeyModifiers.Control, Key.F) }); Add(new() { Shortcut = DebuggerShortcut.OpenSa1Debugger, KeyBinding = new() }); + Add(new() { Shortcut = DebuggerShortcut.OpenSt018Debugger, KeyBinding = new() }); Add(new() { Shortcut = DebuggerShortcut.OpenGsuDebugger, KeyBinding = new() }); Add(new() { Shortcut = DebuggerShortcut.OpenNecDspDebugger, KeyBinding = new() }); Add(new() { Shortcut = DebuggerShortcut.OpenCx4Debugger, KeyBinding = new() }); @@ -317,6 +318,7 @@ public enum DebuggerShortcut OpenDebugger, OpenSpcDebugger, OpenSa1Debugger, + OpenSt018Debugger, OpenGsuDebugger, OpenNecDspDebugger, OpenCx4Debugger, diff --git a/UI/Config/Debugger/TraceLoggerConfig.cs b/UI/Config/Debugger/TraceLoggerConfig.cs index 3bd1e14c3..39f7c4c76 100644 --- a/UI/Config/Debugger/TraceLoggerConfig.cs +++ b/UI/Config/Debugger/TraceLoggerConfig.cs @@ -27,6 +27,7 @@ public class TraceLoggerConfig : BaseWindowConfig [Reactive] public TraceLoggerCpuConfig Sa1Config { get; set; } = new(); [Reactive] public TraceLoggerCpuConfig GsuConfig { get; set; } = new(); [Reactive] public TraceLoggerCpuConfig Cx4Config { get; set; } = new(); + [Reactive] public TraceLoggerCpuConfig St018Config { get; set; } = new(); [Reactive] public TraceLoggerCpuConfig GbConfig { get; set; } = new(); [Reactive] public TraceLoggerCpuConfig NesConfig { get; set; } = new(); [Reactive] public TraceLoggerCpuConfig PceConfig { get; set; } = new(); @@ -47,6 +48,7 @@ public TraceLoggerCpuConfig GetCpuConfig(CpuType type) CpuType.Sa1 => Sa1Config, CpuType.Gsu => GsuConfig, CpuType.Cx4 => Cx4Config, + CpuType.St018 => St018Config, CpuType.Gameboy => GbConfig, CpuType.Nes => NesConfig, CpuType.Pce => PceConfig, diff --git a/UI/Debugger/RegisterViewer/SnesRegisterViewer.cs b/UI/Debugger/RegisterViewer/SnesRegisterViewer.cs index 6368bb69f..5564f8d21 100644 --- a/UI/Debugger/RegisterViewer/SnesRegisterViewer.cs +++ b/UI/Debugger/RegisterViewer/SnesRegisterViewer.cs @@ -28,6 +28,8 @@ public static List GetTabs(ref SnesState snesState, HashSet entries = new List() { + new RegEntry("", "SNES Registers"), + new RegEntry("$3800", "ARM -> SNES Data", state.DataSnes), + new RegEntry("$3804.0", "ARM -> SNES Data Ready", state.HasDataForSnes), + new RegEntry("$3804.2", "Ack", state.Ack), + new RegEntry("$3804.3", "SNES -> ARM Data Ready", state.HasDataForArm), + new RegEntry("$3804.7", "ARM CPU Reset", state.ArmReset), + + new RegEntry("", "ST018 Registers"), + new RegEntry("$40000010", "ARM -> SNES Data", state.DataArm), + new RegEntry("$40000020.0", "ARM -> SNES Data Ready", state.HasDataForSnes), + new RegEntry("$40000020.2", "Ack", state.Ack), + new RegEntry("$40000020.3", "SNES -> ARM Data Ready", state.HasDataForArm), + new RegEntry("$40000020.7", "ARM CPU Reset", state.ArmReset), + }; + + return new RegisterViewerTab("ST018", entries); + } + private static RegisterViewerTab GetSnesSa1Tab(ref SnesState state) { - Sa1State sa1 = state.Sa1.Sa1; + Sa1State sa1 = state.Sa1; List entries = new List() { new RegEntry("$2200", "SA-1 CPU Control"), diff --git a/UI/Debugger/StatusViews/St018StatusView.axaml b/UI/Debugger/StatusViews/St018StatusView.axaml new file mode 100644 index 000000000..319a83424 --- /dev/null +++ b/UI/Debugger/StatusViews/St018StatusView.axaml @@ -0,0 +1,152 @@ + + + + + + + + + + + + + + + + + + + + + R0: + + + + R1: + + + + R2: + + + + R3: + + + + R4: + + + + R5: + + + + R6: + + + + R7: + + + + + + + R8: + + + + R9: + + + + R10: + + + + R11: + + + + R12: + + + + SP (R13): + + + + LR (R14): + + + + PC (R15): + + + + + + + + + + + + + + + + Mode: + + + + + CPSR: + + + + + + + + + + + + + + + + + + + + + + diff --git a/UI/Debugger/StatusViews/St018StatusView.axaml.cs b/UI/Debugger/StatusViews/St018StatusView.axaml.cs new file mode 100644 index 000000000..033a93391 --- /dev/null +++ b/UI/Debugger/StatusViews/St018StatusView.axaml.cs @@ -0,0 +1,19 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Markup.Xaml; + +namespace Mesen.Debugger.StatusViews +{ + public class St018StatusView : UserControl + { + public St018StatusView() + { + InitializeComponent(); + } + + private void InitializeComponent() + { + AvaloniaXamlLoader.Load(this); + } + } +} diff --git a/UI/Debugger/StatusViews/St018StatusViewModel.cs b/UI/Debugger/StatusViews/St018StatusViewModel.cs new file mode 100644 index 000000000..3b3118441 --- /dev/null +++ b/UI/Debugger/StatusViews/St018StatusViewModel.cs @@ -0,0 +1,147 @@ +using Mesen.Interop; +using ReactiveUI; +using ReactiveUI.Fody.Helpers; +using System; +using System.Text; + +namespace Mesen.Debugger.StatusViews; + +public class St018StatusViewModel : BaseConsoleStatusViewModel +{ + [Reactive] public UInt32 Reg0 { get; set; } + [Reactive] public UInt32 Reg1 { get; set; } + [Reactive] public UInt32 Reg2 { get; set; } + [Reactive] public UInt32 Reg3 { get; set; } + [Reactive] public UInt32 Reg4 { get; set; } + [Reactive] public UInt32 Reg5 { get; set; } + [Reactive] public UInt32 Reg6 { get; set; } + [Reactive] public UInt32 Reg7 { get; set; } + [Reactive] public UInt32 Reg8 { get; set; } + [Reactive] public UInt32 Reg9 { get; set; } + [Reactive] public UInt32 Reg10 { get; set; } + [Reactive] public UInt32 Reg11 { get; set; } + [Reactive] public UInt32 Reg12 { get; set; } + [Reactive] public UInt32 Reg13 { get; set; } + [Reactive] public UInt32 Reg14 { get; set; } + [Reactive] public UInt32 Reg15 { get; set; } + + [Reactive] public UInt32 RegCpsr { get; set; } + + [Reactive] public ArmV3CpuMode Mode { get; set; } + [Reactive] public string ModeString { get; set; } = ArmV3CpuMode.User.ToString(); + + [Reactive] public bool FlagZero { get; set; } + [Reactive] public bool FlagCarry { get; set; } + [Reactive] public bool FlagNegative { get; set; } + [Reactive] public bool FlagOverflow { get; set; } + + [Reactive] public bool FlagThumb { get; set; } + [Reactive] public bool FlagIrqDisable { get; set; } + [Reactive] public bool FlagFiqDisable { get; set; } + + [Reactive] public string StackPreview { get; set; } = ""; + + public St018StatusViewModel() + { + this.WhenAnyValue(x => x.FlagZero, x => x.FlagCarry, x => x.FlagNegative, x => x.FlagOverflow).Subscribe(x => UpdateFlags()); + this.WhenAnyValue(x => x.FlagIrqDisable, x => x.FlagFiqDisable).Subscribe(x => UpdateFlags()); + } + + private void UpdateFlags() + { + RegCpsr = (UInt32)( + (FlagNegative ? (1 << 31) : 0) | + (FlagZero ? (1 << 30) : 0) | + (FlagCarry ? (1 << 29) : 0) | + (FlagOverflow ? (1 << 28) : 0) | + + (FlagIrqDisable ? (1 << 7) : 0) | + (FlagFiqDisable ? (1 << 6) : 0) | + (byte)Mode + ); + } + + protected override void InternalUpdateUiState() + { + ArmV3CpuState cpu = DebugApi.GetCpuState(CpuType.St018); + UpdateCycleCount(cpu.CycleCount); + + Reg0 = cpu.R[0]; + Reg1 = cpu.R[1]; + Reg2 = cpu.R[2]; + Reg3 = cpu.R[3]; + Reg4 = cpu.R[4]; + Reg5 = cpu.R[5]; + Reg6 = cpu.R[6]; + Reg7 = cpu.R[7]; + Reg8 = cpu.R[8]; + Reg9 = cpu.R[9]; + Reg10 = cpu.R[10]; + Reg11 = cpu.R[11]; + Reg12 = cpu.R[12]; + Reg13 = cpu.R[13]; + Reg14 = cpu.R[14]; + Reg15 = cpu.R[15]; + + FlagCarry = cpu.CPSR.Carry; + FlagZero = cpu.CPSR.Zero; + FlagNegative = cpu.CPSR.Negative; + FlagOverflow = cpu.CPSR.Overflow; + FlagIrqDisable = cpu.CPSR.IrqDisable; + FlagFiqDisable = cpu.CPSR.FiqDisable; + + Mode = cpu.CPSR.Mode; + if(cpu.CPSR.Mode == ArmV3CpuMode.Irq || cpu.CPSR.Mode == ArmV3CpuMode.Fiq) { + ModeString = cpu.CPSR.Mode.ToString().ToUpper(); + } else { + ModeString = cpu.CPSR.Mode.ToString(); + } + + StringBuilder sb = new StringBuilder(); + if(cpu.R[13] >= 0xE0000000) { + //Patch to show work ram data directly (since APIs don't work well with addresses over 2^31) + uint start = (cpu.R[13] - 0xE0000000) & 0x3FFF; + uint end = (cpu.R[13] - 0xE0000000 + 30 * 4 - 1) & 0x3FFF; + if(end < start) { + end = 0x3FFF; + } + byte[] stackValues = DebugApi.GetMemoryValues(MemoryType.St018WorkRam, start, end); + for(int i = 0; i < stackValues.Length; i += 4) { + UInt32 value = (uint)stackValues[i] | (uint)(stackValues[i + 1] << 8) | (uint)(stackValues[i + 2] << 16) | (uint)(stackValues[i + 3] << 24); + sb.Append($"${value:X8} "); + } + } + StackPreview = sb.ToString(); + } + + protected override void InternalUpdateConsoleState() + { + ArmV3CpuState cpu = DebugApi.GetCpuState(CpuType.St018); + + cpu.R[0] = Reg0; + cpu.R[1] = Reg1; + cpu.R[2] = Reg2; + cpu.R[3] = Reg3; + cpu.R[4] = Reg4; + cpu.R[5] = Reg5; + cpu.R[6] = Reg6; + cpu.R[7] = Reg7; + cpu.R[8] = Reg8; + cpu.R[9] = Reg9; + cpu.R[10] = Reg10; + cpu.R[11] = Reg11; + cpu.R[12] = Reg12; + cpu.R[13] = Reg13; + cpu.R[14] = Reg14; + cpu.R[15] = Reg15; + + cpu.CPSR.Carry = FlagCarry; + cpu.CPSR.Zero = FlagZero; + cpu.CPSR.Negative = FlagNegative; + cpu.CPSR.Overflow = FlagOverflow; + cpu.CPSR.IrqDisable = FlagIrqDisable; + cpu.CPSR.FiqDisable = FlagFiqDisable; + + DebugApi.SetCpuState(cpu, CpuType.St018); + } +} diff --git a/UI/Debugger/Utilities/ContextMenuAction.cs b/UI/Debugger/Utilities/ContextMenuAction.cs index ae8577046..a1d4d3252 100644 --- a/UI/Debugger/Utilities/ContextMenuAction.cs +++ b/UI/Debugger/Utilities/ContextMenuAction.cs @@ -573,6 +573,8 @@ public enum ActionType OpenGsuDebugger, [IconFile("Sa1Debugger")] OpenSa1Debugger, + [IconFile("St018Debugger")] + OpenSt018Debugger, [IconFile("GameboyDebugger")] OpenGameboyDebugger, diff --git a/UI/Debugger/Utilities/OpCodeHelper.cs b/UI/Debugger/Utilities/OpCodeHelper.cs index 002a2ac72..6771e0f6c 100644 --- a/UI/Debugger/Utilities/OpCodeHelper.cs +++ b/UI/Debugger/Utilities/OpCodeHelper.cs @@ -307,6 +307,9 @@ private static void InitGbaDocumentation() } return desc; }; + + //Use same tooltips for ST018 ARMv3 CPU + _data[CpuType.St018] = _data[CpuType.Gba]; } private static DocFileFormat? ReadDocumentationFile(string filename) diff --git a/UI/Debugger/ViewModels/DebuggerConfigWindowViewModel.cs b/UI/Debugger/ViewModels/DebuggerConfigWindowViewModel.cs index 0418cae15..9cf708553 100644 --- a/UI/Debugger/ViewModels/DebuggerConfigWindowViewModel.cs +++ b/UI/Debugger/ViewModels/DebuggerConfigWindowViewModel.cs @@ -162,6 +162,7 @@ private void InitShortcutLists() DebuggerShortcut.OpenSpcDebugger, DebuggerShortcut.OpenSa1Debugger, + DebuggerShortcut.OpenSt018Debugger, DebuggerShortcut.OpenGameboyDebugger, DebuggerShortcut.OpenGsuDebugger, DebuggerShortcut.OpenNecDspDebugger, diff --git a/UI/Debugger/ViewModels/DebuggerWindowViewModel.cs b/UI/Debugger/ViewModels/DebuggerWindowViewModel.cs index aab546571..6eea0edd8 100644 --- a/UI/Debugger/ViewModels/DebuggerWindowViewModel.cs +++ b/UI/Debugger/ViewModels/DebuggerWindowViewModel.cs @@ -124,6 +124,7 @@ public DebuggerWindowViewModel(CpuType? cpuType) CpuType.Sa1 => new SnesStatusViewModel(CpuType.Sa1), CpuType.Gsu => new GsuStatusViewModel(), CpuType.Cx4 => new Cx4StatusViewModel(), + CpuType.St018 => new St018StatusViewModel(), CpuType.Gameboy => new GbStatusViewModel(), CpuType.Nes => new NesStatusViewModel(), CpuType.Pce => new PceStatusViewModel(), diff --git a/UI/Debugger/ViewModels/TraceLoggerViewModel.cs b/UI/Debugger/ViewModels/TraceLoggerViewModel.cs index 56eae5fe8..d7876a9d7 100644 --- a/UI/Debugger/ViewModels/TraceLoggerViewModel.cs +++ b/UI/Debugger/ViewModels/TraceLoggerViewModel.cs @@ -595,6 +595,7 @@ void addTag(bool condition, string formatText, int align = 0) addTag(cfg.ShowRegisters, "HL:[H,2h][L,2h] IX:[IX,4h] IY:[IY,4h] S:[SP,4h] "); break; + case CpuType.St018: case CpuType.Gba: addTag(cfg.ShowRegisters, "R0:[R0,8h] R1:[R1,8h] R2:[R2,8h] R3:[R3,8h] R4:[R4,8h] R5:[R5,8h] R6:[R6,8h] R7:[R7,8h] R8:[R8,8h] R9:[R9,8h] R10:[R10,8h] R11:[R11,8h] R12:[R12,8h] R13:[R13,8h] R14:[R14,8h] R15:[R15,8h] "); addTag(cfg.ShowStatusFlags, cfg.StatusFormat switch { @@ -664,7 +665,7 @@ private Control GetFormatTooltip() CpuType.Nes => new string[] { "A", "X", "Y", "P", "SP" }, CpuType.Pce => new string[] { "A", "X", "Y", "P", "SP" }, CpuType.Sms => new string[] { "A", "B", "C", "D", "E", "F", "H", "L", "IX", "IY", "A'", "B'", "C'", "D'", "E'", "F'", "H'", "L'", "I", "R", "PS", "SP" }, - CpuType.Gba => new string[] { "R0", "R1", "R2", "R3", "R4", "R5", "R6", "R7", "R8", "R9", "R10", "R11", "R12", "R13", "R14", "R15", "CPSR" }, + CpuType.Gba or CpuType.St018 => new string[] { "R0", "R1", "R2", "R3", "R4", "R5", "R6", "R7", "R8", "R9", "R10", "R11", "R12", "R13", "R14", "R15", "CPSR" }, CpuType.Ws => new string[] { "AX", "BX", "CX", "DX", "CS", "IP", "SS", "SP", "BP", "DS", "ES", "SI", "DI", "F" }, _ => throw new Exception("unsupported cpu type") }; diff --git a/UI/Debugger/Windows/DebuggerWindow.axaml b/UI/Debugger/Windows/DebuggerWindow.axaml index ed23e47e0..6757855ad 100644 --- a/UI/Debugger/Windows/DebuggerWindow.axaml +++ b/UI/Debugger/Windows/DebuggerWindow.axaml @@ -76,6 +76,9 @@ + + + diff --git a/UI/Interop/ConfigApi.cs b/UI/Interop/ConfigApi.cs index 65ac6c940..f15c8ee54 100644 --- a/UI/Interop/ConfigApi.cs +++ b/UI/Interop/ConfigApi.cs @@ -67,12 +67,13 @@ public enum DebuggerFlags : UInt32 GsuDebuggerEnabled = (1 << 3), NecDspDebuggerEnabled = (1 << 4), Cx4DebuggerEnabled = (1 << 5), - GbDebuggerEnabled = (1 << 6), - NesDebuggerEnabled = (1 << 7), - PceDebuggerEnabled = (1 << 8), - SmsDebuggerEnabled = (1 << 9), - GbaDebuggerEnabled = (1 << 10), - WsDebuggerEnabled = (1 << 11), + St018DebuggerEnabled = (1 << 6), + GbDebuggerEnabled = (1 << 7), + NesDebuggerEnabled = (1 << 8), + PceDebuggerEnabled = (1 << 9), + SmsDebuggerEnabled = (1 << 10), + GbaDebuggerEnabled = (1 << 11), + WsDebuggerEnabled = (1 << 12), } public struct InteropShortcutKeyInfo diff --git a/UI/Interop/ConsoleState/SnesState.cs b/UI/Interop/ConsoleState/SnesState.cs index c4384f0e5..52a229a0d 100644 --- a/UI/Interop/ConsoleState/SnesState.cs +++ b/UI/Interop/ConsoleState/SnesState.cs @@ -344,6 +344,88 @@ public struct NecDspState : BaseState public byte SP; } + +public enum ArmV3CpuMode : byte +{ + User = 0b10000, + Fiq = 0b10001, + Irq = 0b10010, + Supervisor = 0b10011, + Abort = 0b10111, + Undefined = 0b11011, + System = 0b11111, +} + +public struct ArmV3CpuFlags +{ + public ArmV3CpuMode Mode; + [MarshalAs(UnmanagedType.I1)] public bool FiqDisable; + [MarshalAs(UnmanagedType.I1)] public bool IrqDisable; + + [MarshalAs(UnmanagedType.I1)] public bool Overflow; + [MarshalAs(UnmanagedType.I1)] public bool Carry; + [MarshalAs(UnmanagedType.I1)] public bool Zero; + [MarshalAs(UnmanagedType.I1)] public bool Negative; +} + +public struct ArmV3InstructionData +{ + public UInt32 Address; + public UInt32 OpCode; +} + +public struct ArmV3CpuPipeline +{ + public ArmV3InstructionData Fetch; + public ArmV3InstructionData Decode; + public ArmV3InstructionData Execute; + [MarshalAs(UnmanagedType.I1)] public bool ReloadRequested; + public byte Mode; +} + +public struct ArmV3CpuState : BaseState +{ + public ArmV3CpuPipeline Pipeline; + + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] + public UInt32[] R; + public ArmV3CpuFlags CPSR; + + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 7)] + public UInt32[] UserRegs; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 7)] + public UInt32[] FiqRegs; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] + public UInt32[] IrqRegs; + + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] + public UInt32[] SupervisorRegs; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] + public UInt32[] AbortRegs; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] + public UInt32[] UndefinedRegs; + + public ArmV3CpuFlags FiqSpsr; + public ArmV3CpuFlags IrqSpsr; + public ArmV3CpuFlags SupervisorSpsr; + public ArmV3CpuFlags AbortSpsr; + public ArmV3CpuFlags UndefinedSpsr; + + public UInt64 CycleCount; +} + +public struct St018State : BaseState +{ + [MarshalAs(UnmanagedType.I1)] public bool HasDataForSnes; + public byte DataSnes; + + [MarshalAs(UnmanagedType.I1)] public bool HasDataForArm; + public byte DataArm; + + [MarshalAs(UnmanagedType.I1)] public bool ArmReset; + [MarshalAs(UnmanagedType.I1)] public bool Ack; +} + public struct GsuFlags { [MarshalAs(UnmanagedType.I1)] public bool Zero; @@ -603,12 +685,6 @@ public struct Sa1State public byte[] Banks; } -public struct DebugSa1State -{ - public SnesCpuState Cpu; - public Sa1State Sa1; -} - public enum Sa1MathOp { Mul = 0, @@ -674,9 +750,10 @@ public struct SnesState : BaseState public SpcState Spc; public DspState Dsp; public NecDspState NecDsp; - public DebugSa1State Sa1; + public Sa1State Sa1; public GsuState Gsu; public Cx4State Cx4; + public St018State St018; public SnesDmaControllerState Dma; public InternalRegisterState InternalRegs; diff --git a/UI/Interop/CpuTypeExtensions.cs b/UI/Interop/CpuTypeExtensions.cs index 9e84510ed..a31121105 100644 --- a/UI/Interop/CpuTypeExtensions.cs +++ b/UI/Interop/CpuTypeExtensions.cs @@ -13,6 +13,7 @@ public static MemoryType ToMemoryType(this CpuType cpuType) CpuType.Sa1 => MemoryType.Sa1Memory, CpuType.Gsu => MemoryType.GsuMemory, CpuType.Cx4 => MemoryType.Cx4Memory, + CpuType.St018 => MemoryType.St018Memory, CpuType.Gameboy => MemoryType.GameboyMemory, CpuType.Nes => MemoryType.NesMemory, CpuType.Pce => MemoryType.PceMemory, @@ -59,6 +60,7 @@ public static MemoryType GetPrgRomMemoryType(this CpuType cpuType) CpuType.Sa1 => MemoryType.SnesPrgRom, CpuType.Gsu => MemoryType.SnesPrgRom, CpuType.Cx4 => MemoryType.SnesPrgRom, + CpuType.St018 => MemoryType.St018PrgRom, CpuType.Gameboy => MemoryType.GbPrgRom, CpuType.Nes => MemoryType.NesPrgRom, @@ -78,6 +80,7 @@ public static MemoryType GetSystemRamType(this CpuType cpuType) CpuType.Sa1 => MemoryType.Sa1InternalRam, CpuType.Gsu => MemoryType.GsuWorkRam, CpuType.Cx4 => MemoryType.Cx4DataRam, + CpuType.St018 => MemoryType.St018WorkRam, CpuType.Gameboy => MemoryType.GbWorkRam, CpuType.Nes => MemoryType.NesInternalRam, @@ -98,6 +101,7 @@ public static int GetAddressSize(this CpuType cpuType) CpuType.Sa1 => 6, CpuType.Gsu => 6, CpuType.Cx4 => 6, + CpuType.St018 => 8, CpuType.Gameboy => 4, CpuType.Nes => 4, CpuType.Pce => 4, @@ -117,6 +121,7 @@ public static int GetByteCodeSize(this CpuType cpuType) CpuType.Sa1 => 4, CpuType.Gsu => 3, CpuType.Cx4 => 4, + CpuType.St018 => 4, CpuType.Gameboy => 3, CpuType.Nes => 3, CpuType.Pce => 4, @@ -136,6 +141,7 @@ public static DebuggerFlags GetDebuggerFlag(this CpuType cpuType) CpuType.Sa1 => DebuggerFlags.Sa1DebuggerEnabled, CpuType.Gsu => DebuggerFlags.GsuDebuggerEnabled, CpuType.Cx4 => DebuggerFlags.Cx4DebuggerEnabled, + CpuType.St018 => DebuggerFlags.St018DebuggerEnabled, CpuType.Gameboy => DebuggerFlags.GbDebuggerEnabled, CpuType.Nes => DebuggerFlags.NesDebuggerEnabled, CpuType.Pce => DebuggerFlags.PceDebuggerEnabled, @@ -155,6 +161,7 @@ public static ConsoleType GetConsoleType(this CpuType cpuType) CpuType.Sa1 => ConsoleType.Snes, CpuType.Gsu => ConsoleType.Snes, CpuType.Cx4 => ConsoleType.Snes, + CpuType.St018 => ConsoleType.Snes, CpuType.Gameboy => ConsoleType.Gameboy, CpuType.Nes => ConsoleType.Nes, CpuType.Pce => ConsoleType.PcEngine, diff --git a/UI/Interop/DebugApi.cs b/UI/Interop/DebugApi.cs index d5ab22983..2fea84636 100644 --- a/UI/Interop/DebugApi.cs +++ b/UI/Interop/DebugApi.cs @@ -549,6 +549,7 @@ private static bool IsValidCpuState(ref T state, CpuType cpuType) where T : B CpuType.Sa1 => state is SnesCpuState, CpuType.Gsu => state is GsuState, CpuType.Cx4 => state is Cx4State, + CpuType.St018 => state is ArmV3CpuState, CpuType.Gameboy => state is GbCpuState, CpuType.Nes => state is NesCpuState, CpuType.Pce => state is PceCpuState, @@ -590,6 +591,7 @@ public enum MemoryType NecDspMemory, GsuMemory, Cx4Memory, + St018Memory, GameboyMemory, NesMemory, NesPpuMemory, @@ -616,6 +618,9 @@ public enum MemoryType Cx4DataRam, BsxPsRam, BsxMemoryPack, + St018PrgRom, + St018DataRom, + St018WorkRam, GbPrgRom, GbWorkRam, @@ -1500,6 +1505,7 @@ public enum CpuType : byte Sa1, Gsu, Cx4, + St018, Gameboy, Nes, Pce, diff --git a/UI/Interop/FirmwareTypeExtensions.cs b/UI/Interop/FirmwareTypeExtensions.cs index 3b95fa992..87a5b7072 100644 --- a/UI/Interop/FirmwareTypeExtensions.cs +++ b/UI/Interop/FirmwareTypeExtensions.cs @@ -16,7 +16,7 @@ public static FirmwareFiles GetFirmwareInfo(this FirmwareType type) case FirmwareType.ST010: return new("st010.rom") { new(0xD000, "FA9BCED838FEDEA11C6F6ACE33D1878024BDD0D02CC9485899D0BDD4015EC24C") }; case FirmwareType.ST011: return new("st011.rom") { new(0xD000, "8B2B3F3F3E6E29F4D21D8BC736B400BC988B7D2214EBEE15643F01C1FEE2F364") }; - case FirmwareType.ST018: return new("st018.rom") { new(0xD000, "6DF209AB5D2524D1839C038BE400AE5EB20DAFC14A3771A3239CD9E8ACD53806") }; + case FirmwareType.ST018: return new("st018.rom") { new(0x28000, "6DF209AB5D2524D1839C038BE400AE5EB20DAFC14A3771A3239CD9E8ACD53806") }; case FirmwareType.Satellaview: return new("BS-X.bin") { new(1024 * 1024, diff --git a/UI/Interop/MemoryTypeExtensions.cs b/UI/Interop/MemoryTypeExtensions.cs index dd8f8019f..77a7aa292 100644 --- a/UI/Interop/MemoryTypeExtensions.cs +++ b/UI/Interop/MemoryTypeExtensions.cs @@ -25,6 +25,12 @@ public static CpuType ToCpuType(this MemoryType memType) case MemoryType.Cx4Memory: return CpuType.Cx4; + case MemoryType.St018Memory: + case MemoryType.St018PrgRom: + case MemoryType.St018DataRom: + case MemoryType.St018WorkRam: + return CpuType.St018; + case MemoryType.DspDataRam: case MemoryType.DspDataRom: case MemoryType.DspProgramRom: @@ -214,6 +220,7 @@ public static bool IsRelativeMemory(this MemoryType memType) case MemoryType.GsuMemory: case MemoryType.NecDspMemory: case MemoryType.Cx4Memory: + case MemoryType.St018Memory: case MemoryType.GameboyMemory: case MemoryType.NesMemory: case MemoryType.NesPpuMemory: @@ -237,6 +244,8 @@ public static bool IsRomMemory(this MemoryType memType) case MemoryType.PcePrgRom: case MemoryType.DspDataRom: case MemoryType.DspProgramRom: + case MemoryType.St018PrgRom: + case MemoryType.St018DataRom: case MemoryType.SpcRom: case MemoryType.SmsPrgRom: case MemoryType.SmsBootRom: @@ -259,7 +268,11 @@ public static bool SupportsLabels(this MemoryType memType) case MemoryType.SpcRam: case MemoryType.SpcRom: case MemoryType.Sa1InternalRam: - + case MemoryType.St018Memory: + case MemoryType.St018PrgRom: + case MemoryType.St018DataRom: + case MemoryType.St018WorkRam: + //Gameboy case MemoryType.GbPrgRom: case MemoryType.GbWorkRam: @@ -326,6 +339,7 @@ public static bool SupportsWatch(this MemoryType memType) case MemoryType.GsuMemory: case MemoryType.NecDspMemory: case MemoryType.Cx4Memory: + case MemoryType.St018Memory: case MemoryType.GameboyMemory: case MemoryType.NesMemory: case MemoryType.PceMemory: @@ -419,6 +433,7 @@ public static string GetShortName(this MemoryType memType) MemoryType.GsuMemory => "GSU", MemoryType.NecDspMemory => "DSP", MemoryType.Cx4Memory => "CX4", + MemoryType.St018Memory => "ARM", MemoryType.SnesPrgRom => "PRG", MemoryType.SnesWorkRam => "WRAM", @@ -436,6 +451,10 @@ public static string GetShortName(this MemoryType memType) MemoryType.DspDataRam => "RAM", MemoryType.DspDataRom => "ROM", + MemoryType.St018PrgRom => "PRG", + MemoryType.St018DataRom => "ROM", + MemoryType.St018WorkRam => "RAM", + MemoryType.Sa1InternalRam => "IRAM", MemoryType.Cx4DataRam => "DATA", MemoryType.GsuWorkRam => "GWRAM", diff --git a/UI/Localization/resources.en.xml b/UI/Localization/resources.en.xml index ecb290a76..5633927dc 100644 --- a/UI/Localization/resources.en.xml +++ b/UI/Localization/resources.en.xml @@ -2711,6 +2711,7 @@ E SA-1 GSU CX4 + ST018 Game Boy NES PC Engine @@ -2726,6 +2727,7 @@ E GSU Memory DSP-n Memory CX4 Memory + ST018 Memory PRG ROM Work RAM Save RAM @@ -2742,9 +2744,12 @@ E SA-1 IRAM GSU Work RAM CX4 Data RAM - BS-X - PSRAM - BS-X - Memory Pack - + BS-X PSRAM + BS-X Memory Pack + ST018 PRG ROM + ST018 Data ROM + ST018 Work RAM + GB - CPU Memory GB - PRG ROM GB - Work RAM @@ -3150,6 +3155,7 @@ E Open Debugger Open SPC Debugger Open SA-1 Debugger + Open ST018 Debugger Open GSU Debugger Open DSP Debugger Open CX4 Debugger @@ -3432,6 +3438,7 @@ E Debugger SPC Debugger SA-1 Debugger + ST018 Debugger GSU Debugger DSP Debugger CX4 Debugger diff --git a/UI/UI.csproj b/UI/UI.csproj index 8732c3b27..772188458 100644 --- a/UI/UI.csproj +++ b/UI/UI.csproj @@ -165,6 +165,9 @@ OptionSection.axaml + + St018StatusView.axaml + GbaStatusView.axaml diff --git a/UI/ViewModels/MainMenuViewModel.cs b/UI/ViewModels/MainMenuViewModel.cs index b43163def..0cf3b4de3 100644 --- a/UI/ViewModels/MainMenuViewModel.cs +++ b/UI/ViewModels/MainMenuViewModel.cs @@ -946,6 +946,12 @@ private void InitDebugMenu(Window wnd) IsVisible = () => MainWindow.RomInfo.CpuTypes.Contains(CpuType.Sa1), OnClick = () => DebuggerWindow.GetOrOpenWindow(CpuType.Sa1) }, + new ContextMenuAction() { + ActionType = ActionType.OpenSt018Debugger, + Shortcut = () => ConfigManager.Config.Debug.Shortcuts.Get(DebuggerShortcut.OpenSt018Debugger), + IsVisible = () => MainWindow.RomInfo.CpuTypes.Contains(CpuType.St018), + OnClick = () => DebuggerWindow.GetOrOpenWindow(CpuType.St018) + }, new ContextMenuAction() { ActionType = ActionType.OpenGameboyDebugger, Shortcut = () => ConfigManager.Config.Debug.Shortcuts.Get(DebuggerShortcut.OpenGameboyDebugger), diff --git a/UI/Views/EmulationConfigView.axaml b/UI/Views/EmulationConfigView.axaml index 19fb21c63..8ebb0c21e 100644 --- a/UI/Views/EmulationConfigView.axaml +++ b/UI/Views/EmulationConfigView.axaml @@ -123,8 +123,8 @@ - - + +