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 @@
-
-
+
+