CX4 support (+ trace logger)

This commit is contained in:
Sour 2019-08-03 23:43:51 -04:00
parent b350469e4b
commit 0cd378c92d
29 changed files with 1852 additions and 35 deletions

View file

@ -12,6 +12,7 @@
#include "Sa1.h"
#include "Gsu.h"
#include "Sdd1.h"
#include "Cx4.h"
#include "../Utilities/HexUtilities.h"
#include "../Utilities/VirtualFile.h"
#include "../Utilities/FolderUtilities.h"
@ -193,7 +194,7 @@ CoprocessorType BaseCartridge::GetCoprocessorType()
case 0x05: return CoprocessorType::RTC; break;
case 0x0E: return CoprocessorType::Satellaview; break;
case 0x0F:
switch(_cartInfo.CartridgeType & 0x0F) {
switch(_cartInfo.CartridgeType) {
case 0x00: return CoprocessorType::SPC7110; _hasBattery = true; break;
case 0x01: return GetSt01xVersion(); _hasBattery = true; break;
case 0x02: return CoprocessorType::ST018; _hasBattery = true; break;
@ -314,7 +315,7 @@ void BaseCartridge::Init(MemoryMappings &mm)
void BaseCartridge::RegisterHandlers(MemoryMappings &mm)
{
if(MapSpecificCarts(mm) || _coprocessorType == CoprocessorType::GSU || _coprocessorType == CoprocessorType::SDD1) {
if(MapSpecificCarts(mm) || _coprocessorType == CoprocessorType::GSU || _coprocessorType == CoprocessorType::SDD1 || _coprocessorType == CoprocessorType::CX4) {
return;
}
@ -370,6 +371,9 @@ void BaseCartridge::InitCoprocessor()
_gsu = dynamic_cast<Gsu*>(_coprocessor.get());
} else if(_coprocessorType == CoprocessorType::SDD1) {
_coprocessor.reset(new Sdd1(_console));
} else if(_coprocessorType == CoprocessorType::CX4) {
_coprocessor.reset(new Cx4(_console));
_cx4 = dynamic_cast<Cx4*>(_coprocessor.get());
}
}
@ -524,6 +528,11 @@ Sa1* BaseCartridge::GetSa1()
return _sa1;
}
Cx4* BaseCartridge::GetCx4()
{
return _cx4;
}
Gsu* BaseCartridge::GetGsu()
{
return _gsu;

View file

@ -11,6 +11,7 @@ class EmuSettings;
class NecDsp;
class Sa1;
class Gsu;
class Cx4;
class Console;
class BaseCartridge : public ISerializable
@ -26,6 +27,7 @@ private:
NecDsp *_necDsp = nullptr;
Sa1 *_sa1 = nullptr;
Gsu *_gsu = nullptr;
Cx4 *_cx4 = nullptr;
CartFlags::CartFlags _flags = CartFlags::CartFlags::None;
CoprocessorType _coprocessorType = CoprocessorType::None;
@ -81,6 +83,7 @@ public:
NecDsp* GetDsp();
Sa1* GetSa1();
Gsu* GetGsu();
Cx4* GetCx4();
void RunCoprocessors();

View file

@ -675,6 +675,13 @@ void Console::ProcessNecDspExec(uint32_t addr, uint32_t value)
}
}
void Console::ProcessCx4Exec()
{
if(_debugger) {
_debugger->ProcessCx4Exec();
}
}
void Console::ProcessPpuCycle()
{
if(_debugger) {

View file

@ -135,6 +135,7 @@ public:
void ProcessWorkRamRead(uint32_t addr, uint8_t value);
void ProcessWorkRamWrite(uint32_t addr, uint8_t value);
void ProcessNecDspExec(uint32_t addr, uint32_t value);
void ProcessCx4Exec();
void ProcessPpuCycle();
template<CpuType type> void ProcessInterrupt(uint32_t originalPc, uint32_t currentPc, bool forNmi);
void ProcessEvent(EventType type);

View file

@ -50,6 +50,9 @@
<ClInclude Include="BaseCoprocessor.h" />
<ClInclude Include="Cpu.Shared.h" />
<ClInclude Include="CpuDebugger.h" />
<ClInclude Include="Cx4.h" />
<ClInclude Include="Cx4DisUtils.h" />
<ClInclude Include="Cx4Types.h" />
<ClInclude Include="DebugUtilities.h" />
<ClInclude Include="Gsu.h" />
<ClInclude Include="GsuDebugger.h" />
@ -184,6 +187,9 @@
<ClCompile Include="Cpu.cpp" />
<ClCompile Include="CpuDebugger.cpp" />
<ClCompile Include="CpuDisUtils.cpp" />
<ClCompile Include="Cx4.cpp" />
<ClCompile Include="Cx4.Instructions.cpp" />
<ClCompile Include="Cx4DisUtils.cpp" />
<ClCompile Include="Debugger.cpp" />
<ClCompile Include="DebugHud.cpp" />
<ClCompile Include="DebugStats.cpp" />

View file

@ -365,6 +365,15 @@
<ClInclude Include="Sdd1Decomp.h">
<Filter>SNES\Coprocessors\SDD1</Filter>
</ClInclude>
<ClInclude Include="Cx4.h">
<Filter>SNES\Coprocessors\CX4</Filter>
</ClInclude>
<ClInclude Include="Cx4Types.h">
<Filter>SNES\Coprocessors\CX4</Filter>
</ClInclude>
<ClInclude Include="Cx4DisUtils.h">
<Filter>Debugger\Disassembler</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="stdafx.cpp" />
@ -590,6 +599,15 @@
<ClCompile Include="Sdd1Decomp.cpp">
<Filter>SNES\Coprocessors\SDD1</Filter>
</ClCompile>
<ClCompile Include="Cx4.cpp">
<Filter>SNES\Coprocessors\CX4</Filter>
</ClCompile>
<ClCompile Include="Cx4.Instructions.cpp">
<Filter>SNES\Coprocessors\CX4</Filter>
</ClCompile>
<ClCompile Include="Cx4DisUtils.cpp">
<Filter>Debugger\Disassembler</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<Filter Include="SNES">
@ -640,5 +658,8 @@
<Filter Include="SNES\Coprocessors\SDD1">
<UniqueIdentifier>{991e1865-b4f2-4066-8b10-d7458e636e6e}</UniqueIdentifier>
</Filter>
<Filter Include="SNES\Coprocessors\CX4">
<UniqueIdentifier>{4bf65a17-58bf-4833-a4af-38ddc285f99d}</UniqueIdentifier>
</Filter>
</ItemGroup>
</Project>

711
Core/Cx4.Instructions.cpp Normal file
View file

@ -0,0 +1,711 @@
#include "stdafx.h"
#include "Cx4.h"
#include "Cpu.h"
#include "Cx4DisUtils.h"
#include "../Utilities/HexUtilities.h"
static constexpr int shiftLut[4] = { 0 , 1, 8, 16 };
extern const uint32_t _dataRom[1024];
void Cx4::Exec(uint16_t opCode)
{
uint8_t op = (opCode >> 8) & 0xFC;
uint8_t param1 = (opCode >> 8) & 0x03;
uint8_t param2 = opCode & 0xFF;
switch(op) {
case 0x00: NOP(); break;
case 0x04: NOP(); break; //???
case 0x08: Branch(true, param1, param2); break;
case 0x0C: Branch(_state.Zero, param1, param2); break;
case 0x10: Branch(_state.Carry, param1, param2); break;
case 0x14: Branch(_state.Negative, param1, param2); break;
case 0x18: Branch(_state.Overflow, param1, param2); break;
case 0x1C: WAIT(); break;
case 0x20: NOP(); break; //???
case 0x24: Skip(param1, param2); break;
case 0x28: JSR(true, param1, param2); break;
case 0x2C: JSR(_state.Zero, param1, param2); break;
case 0x30: JSR(_state.Carry, param1, param2); break;
case 0x34: JSR(_state.Negative, param1, param2); break;
case 0x38: JSR(_state.Overflow, param1, param2); break;
case 0x3C: RTS(); break;
case 0x40: IncMar(); break;
case 0x44: NOP(); break; //???
case 0x48: CMPR(param1, param2); break;
case 0x4C: CMPR_Imm(param1, param2); break;
case 0x50: CMP(param1, param2); break;
case 0x54: CMP_Imm(param1, param2); break;
case 0x58: SignExtend(param1); break;
case 0x5C: NOP(); break; //???
case 0x60: Load(param1, param2); break;
case 0x64: Load_Imm(param1, param2); break;
case 0x68: ReadRam(param1); break;
case 0x6C: ReadRam_Imm(param1, param2); break;
case 0x70: ReadRom(); break;
case 0x74: ReadRom_Imm((param1 << 8) | param2); break;
case 0x78: NOP(); break;
case 0x7C: LoadP(param1, param2); break;
case 0x80: ADD(param1, param2); break;
case 0x84: ADD_Imm(param1, param2); break;
case 0x88: SUBR(param1, param2); break;
case 0x8C: SUBR_Imm(param1, param2); break;
case 0x90: SUB(param1, param2); break;
case 0x94: SUB_Imm(param1, param2); break;
case 0x98: SMUL(param2); break;
case 0x9C: SMUL_Imm(param2); break;
case 0xA0: XNOR(param1, param2); break;
case 0xA4: XNOR_Imm(param1, param2); break;
case 0xA8: XOR(param1, param2); break;
case 0xAC: XOR_Imm(param1, param2); break;
case 0xB0: AND(param1, param2); break;
case 0xB4: AND_Imm(param1, param2); break;
case 0xB8: OR(param1, param2); break;
case 0xBC: OR_Imm(param1, param2); break;
case 0xC0: SHR(param2); break;
case 0xC4: SHR_Imm(param2); break;
case 0xC8: ASR(param2); break;
case 0xCC: ASR_Imm(param2); break;
case 0xD0: ROR(param2); break;
case 0xD4: ROR_Imm(param2); break;
case 0xD8: SHL(param2); break;
case 0xDC: SHL_Imm(param2); break;
case 0xE0: Store(param1, param2); break;
case 0xE4: NOP(); break; //???
case 0xE8: WriteRam(param1); break;
case 0xEC: WriteRam_Imm(param1, param2); break;
case 0xF0: Swap(param2 & 0x0F); break;
case 0xF4: NOP(); break; //???
case 0xF8: NOP(); break; //???
case 0xFC: Stop(); break;
}
Step(1);
}
uint32_t Cx4::GetSourceValue(uint8_t src)
{
switch(src & 0x7F) {
case 0x00: return _state.A;
case 0x01: return (_state.Mult >> 24) & 0xFFFFFF;
case 0x02: return _state.Mult & 0xFFFFFF;
case 0x03: return _state.MemoryDataReg;
case 0x08: return _state.RomBuffer;
case 0x0C: return (_state.RamBuffer[2] << 16) | (_state.RamBuffer[1] << 8) | _state.RamBuffer[0];
case 0x13: return _state.MemoryAddressReg;
case 0x1C: return _state.DataPointerReg;
case 0x20: return _state.PC;
case 0x28: return _state.P;
case 0x2E:
_state.Bus.Enabled = true;
_state.Bus.Reading = true;
_state.Bus.DelayCycles = 1 + _state.RomAccessDelay;
_state.Bus.Address = _state.MemoryAddressReg;
return 0;
case 0x2F:
_state.Bus.Enabled = true;
_state.Bus.Reading = true;
_state.Bus.DelayCycles = 1 + _state.RamAccessDelay;
_state.Bus.Address = _state.MemoryAddressReg;
return 0;
case 0x50: return 0x000000;
case 0x51: return 0xFFFFFF;
case 0x52: return 0x00FF00;
case 0x53: return 0xFF0000;
case 0x54: return 0x00FFFF;
case 0x55: return 0xFFFF00;
case 0x56: return 0x800000;
case 0x57: return 0x7FFFFF;
case 0x58: return 0x008000;
case 0x59: return 0x007FFF;
case 0x5A: return 0xFF7FFF;
case 0x5B: return 0xFFFF7F;
case 0x5C: return 0x010000;
case 0x5D: return 0xFEFFFF;
case 0x5E: return 0x000100;
case 0x5F: return 0x00FEFF;
case 0x60: case 0x70: return _state.Regs[0];
case 0x61: case 0x71: return _state.Regs[1];
case 0x62: case 0x72: return _state.Regs[2];
case 0x63: case 0x73: return _state.Regs[3];
case 0x64: case 0x74: return _state.Regs[4];
case 0x65: case 0x75: return _state.Regs[5];
case 0x66: case 0x76: return _state.Regs[6];
case 0x67: case 0x77: return _state.Regs[7];
case 0x68: case 0x78: return _state.Regs[8];
case 0x69: case 0x79: return _state.Regs[9];
case 0x6A: case 0x7A: return _state.Regs[10];
case 0x6B: case 0x7B: return _state.Regs[11];
case 0x6C: case 0x7C: return _state.Regs[12];
case 0x6D: case 0x7D: return _state.Regs[13];
case 0x6E: case 0x7E: return _state.Regs[14];
case 0x6F: case 0x7F: return _state.Regs[15];
}
return 0;
}
void Cx4::WriteRegister(uint8_t reg, uint32_t value)
{
value &= 0xFFFFFF;
switch(reg & 0x7F) {
case 0x01: _state.Mult = (_state.Mult & 0xFFFFFF) | (value << 24); break;
case 0x02: _state.Mult = (_state.Mult & 0xFFFFFF000000) | value; break;
case 0x03: _state.MemoryDataReg = value; break;
case 0x08: _state.RomBuffer = value; break;
case 0x0C:
_state.RamBuffer[0] = value;
_state.RamBuffer[1] = value >> 8;
_state.RamBuffer[2] = value >> 16;
break;
case 0x13: _state.MemoryAddressReg = value; break;
case 0x1C: _state.DataPointerReg = value; break;
case 0x20: _state.PC = value; break;
case 0x28: _state.P = (value & 0x7FFF); break;
case 0x2E:
_state.Bus.Enabled = true;
_state.Bus.Writing = true;
_state.Bus.DelayCycles = 1 + _state.RomAccessDelay;
_state.Bus.Address = _state.MemoryAddressReg;
break;
case 0x2F:
_state.Bus.Enabled = true;
_state.Bus.Writing = true;
_state.Bus.DelayCycles = 1 + _state.RamAccessDelay;
_state.Bus.Address = _state.MemoryAddressReg;
break;
case 0x60: case 0x70: _state.Regs[0] = value; break;
case 0x61: case 0x71: _state.Regs[1] = value; break;
case 0x62: case 0x72: _state.Regs[2] = value; break;
case 0x63: case 0x73: _state.Regs[3] = value; break;
case 0x64: case 0x74: _state.Regs[4] = value; break;
case 0x65: case 0x75: _state.Regs[5] = value; break;
case 0x66: case 0x76: _state.Regs[6] = value; break;
case 0x67: case 0x77: _state.Regs[7] = value; break;
case 0x68: case 0x78: _state.Regs[8] = value; break;
case 0x69: case 0x79: _state.Regs[9] = value; break;
case 0x6A: case 0x7a: _state.Regs[10] = value; break;
case 0x6B: case 0x7b: _state.Regs[11] = value; break;
case 0x6C: case 0x7c: _state.Regs[12] = value; break;
case 0x6D: case 0x7d: _state.Regs[13] = value; break;
case 0x6E: case 0x7e: _state.Regs[14] = value; break;
case 0x6F: case 0x7f: _state.Regs[15] = value; break;
}
}
void Cx4::SetA(uint32_t value)
{
_state.A = value & 0xFFFFFF;
}
void Cx4::NOP()
{
}
void Cx4::WAIT()
{
if(_state.Bus.Enabled) {
Step(_state.Bus.DelayCycles);
}
}
void Cx4::Skip(uint8_t flagToCheck, uint8_t skipIfSet)
{
bool skip;
switch(flagToCheck) {
case 0: skip = _state.Overflow == (skipIfSet & 0x01); break;
case 1: skip = _state.Carry == (skipIfSet & 0x01); break;
case 2: skip = _state.Zero == (skipIfSet & 0x01); break;
case 3: skip = _state.Negative == (skipIfSet & 0x01); break;
}
if(skip) {
_state.PC++;
if(_state.PC == 0) {
SwitchCachePage();
}
Step(1);
}
}
void Cx4::Branch(bool branch, uint8_t far, uint8_t dest)
{
if(branch) {
if(far) {
_state.PB = _state.P;
}
_state.PC = dest;
Step(2);
}
}
void Cx4::JSR(bool branch, uint8_t far, uint8_t dest)
{
if(branch) {
PushPC();
if(far) {
_state.PB = _state.P;
}
_state.PC = dest;
Step(2);
}
}
void Cx4::RTS()
{
PullPC();
Step(2);
}
void Cx4::PushPC()
{
_state.Stack[_state.SP] = (_state.PB << 8) | _state.PC;
_state.SP = (_state.SP + 1) & 0x07;
}
void Cx4::PullPC()
{
_state.SP = (_state.SP - 1) & 0x07;
uint16_t value = _state.Stack[_state.SP];
_state.PB = value >> 8;
_state.PC = value & 0xFF;
}
void Cx4::SetZeroNegativeFlags()
{
_state.Zero = _state.A == 0;
_state.Negative = _state.A & 0x800000;
}
uint32_t Cx4::AddValues(uint32_t a, uint32_t b)
{
uint32_t result = a + b;
_state.Carry = result > 0xFFFFFF;
_state.Negative = result & 0x800000;
_state.Overflow = ~(a ^ b) & (a ^ result) & 0x800000;
_state.Zero = (result & 0xFFFFFF) == 0;
return result & 0xFFFFFF;
}
uint32_t Cx4::Substract(uint32_t a, uint32_t b)
{
int32_t result = a - b;
_state.Carry = result >= 0;
_state.Negative = result & 0x800000;
_state.Overflow = ~(a ^ b) & (a ^ result) & 0x800000;
_state.Zero = result == 0;
return result & 0xFFFFFF;
}
void Cx4::CMPR(uint8_t shift, uint8_t src)
{
Substract(GetSourceValue(src), _state.A << shiftLut[shift]);
}
void Cx4::CMPR_Imm(uint8_t shift, uint8_t imm)
{
Substract(imm, _state.A << shiftLut[shift]);
}
void Cx4::CMP(uint8_t shift, uint8_t src)
{
Substract(_state.A << shiftLut[shift], GetSourceValue(src));
}
void Cx4::CMP_Imm(uint8_t shift, uint8_t imm)
{
Substract(_state.A << shiftLut[shift], imm);
}
void Cx4::SignExtend(uint8_t mode)
{
if(mode == 1) {
_state.A = ((uint32_t)(int8_t)_state.A) & 0xFFFFFF;
_state.Negative = _state.A & 0x800000;
_state.Zero = _state.A == 0;
} else if(mode == 2) {
_state.A = ((uint32_t)(int16_t)_state.A) & 0xFFFFFF;
_state.Negative = _state.A & 0x800000;
_state.Zero = _state.A == 0;
}
}
void Cx4::Load(uint8_t dest, uint8_t src)
{
switch(dest) {
case 0: _state.A = GetSourceValue(src); break;
case 1: _state.MemoryDataReg = GetSourceValue(src); break;
case 2: _state.MemoryAddressReg = GetSourceValue(src); break;
case 3: _state.P = GetSourceValue(src); break;
}
}
void Cx4::Load_Imm(uint8_t dest, uint8_t imm)
{
switch(dest) {
case 0: _state.A = imm; break;
case 1: _state.MemoryDataReg = imm; break;
case 2: _state.MemoryAddressReg = imm; break;
case 3: _state.P = imm; break;
}
}
void Cx4::ADD(uint8_t shift, uint8_t src)
{
_state.A = AddValues(_state.A << shiftLut[shift], GetSourceValue(src));
}
void Cx4::ADD_Imm(uint8_t shift, uint8_t imm)
{
_state.A = AddValues(_state.A << shiftLut[shift], imm);
}
void Cx4::SUB(uint8_t shift, uint8_t src)
{
_state.A = Substract(_state.A << shiftLut[shift], GetSourceValue(src));
}
void Cx4::SUB_Imm(uint8_t shift, uint8_t imm)
{
_state.A = Substract(_state.A << shiftLut[shift], imm);
}
void Cx4::SUBR(uint8_t shift, uint8_t src)
{
_state.A = Substract(GetSourceValue(src), _state.A << shiftLut[shift]);
}
void Cx4::SUBR_Imm(uint8_t shift, uint8_t imm)
{
_state.A = Substract(imm, _state.A << shiftLut[shift]);
}
void Cx4::SMUL(uint8_t src)
{
int64_t srcValue = ((int32_t)GetSourceValue(src) << 8) >> 8;
_state.Mult = (srcValue * (((int32_t)_state.A << 8) >> 8)) & 0xFFFFFFFFFFFF;
}
void Cx4::SMUL_Imm(uint8_t imm)
{
_state.Mult = ((int64_t)imm * (((int32_t)_state.A << 8) >> 8)) & 0xFFFFFFFFFFFF;
}
void Cx4::AND(uint8_t shift, uint8_t src)
{
SetA((_state.A << shiftLut[shift]) & GetSourceValue(src));
SetZeroNegativeFlags();
}
void Cx4::AND_Imm(uint8_t shift, uint8_t imm)
{
SetA((_state.A << shiftLut[shift]) & imm);
SetZeroNegativeFlags();
}
void Cx4::OR(uint8_t shift, uint8_t src)
{
SetA((_state.A << shiftLut[shift]) | GetSourceValue(src));
SetZeroNegativeFlags();
}
void Cx4::OR_Imm(uint8_t shift, uint8_t imm)
{
SetA((_state.A << shiftLut[shift]) | imm);
SetZeroNegativeFlags();
}
void Cx4::XOR(uint8_t shift, uint8_t src)
{
SetA((_state.A << shiftLut[shift]) ^ GetSourceValue(src));
SetZeroNegativeFlags();
}
void Cx4::XOR_Imm(uint8_t shift, uint8_t imm)
{
SetA((_state.A << shiftLut[shift]) ^ imm);
SetZeroNegativeFlags();
}
void Cx4::XNOR(uint8_t shift, uint8_t src)
{
SetA(~(_state.A << shiftLut[shift]) ^ GetSourceValue(src));
SetZeroNegativeFlags();
}
void Cx4::XNOR_Imm(uint8_t shift, uint8_t imm)
{
SetA(~(_state.A << shiftLut[shift]) ^ imm);
SetZeroNegativeFlags();
}
void Cx4::SHR(uint8_t src)
{
uint8_t shift = GetSourceValue(src) & 0x1F;
if(shift < 24) {
SetA(_state.A >> shift);
}
SetZeroNegativeFlags();
}
void Cx4::SHR_Imm(uint8_t imm)
{
uint8_t shift = imm & 0x1F;
if(shift < 24) {
SetA(_state.A >> shift);
}
SetZeroNegativeFlags();
}
void Cx4::ASR(uint8_t src)
{
uint8_t shift = GetSourceValue(src) & 0x1F;
if(shift < 24) {
SetA((((int32_t)_state.A << 8) >> 8) >> shift);
}
SetZeroNegativeFlags();
}
void Cx4::ASR_Imm(uint8_t imm)
{
uint8_t shift = imm & 0x1F;
if(shift < 24) {
SetA((((int32_t)_state.A << 8) >> 8) >> shift);
}
SetZeroNegativeFlags();
}
void Cx4::SHL(uint8_t src)
{
uint8_t shift = GetSourceValue(src) & 0x1F;
if(shift < 24) {
SetA(_state.A << shift);
}
SetZeroNegativeFlags();
}
void Cx4::SHL_Imm(uint8_t imm)
{
uint8_t shift = imm & 0x1F;
if(shift < 24) {
SetA(_state.A << shift);
}
SetZeroNegativeFlags();
}
void Cx4::ROR(uint8_t src)
{
uint8_t shift = GetSourceValue(src) & 0x1F;
if(shift < 24) {
SetA((_state.A >> shift) | (_state.A << (24 - shift)));
}
SetZeroNegativeFlags();
}
void Cx4::ROR_Imm(uint8_t imm)
{
uint8_t shift = imm & 0x1F;
if(shift < 24) {
SetA((_state.A >> shift) | (_state.A << (24 - shift)));
}
SetZeroNegativeFlags();
}
void Cx4::ReadRom()
{
_state.RomBuffer = _dataRom[_state.A & 0x3FF];
}
void Cx4::ReadRom_Imm(uint16_t imm)
{
_state.RomBuffer = _dataRom[imm & 0x3FF];
}
void Cx4::ReadRam(uint8_t byteIndex)
{
if(byteIndex >= 3) {
return;
}
uint16_t addr = _state.A & 0xFFF;
if(addr >= 0xC00) {
addr -= 0x400;
}
_state.RamBuffer[byteIndex] = _dataRam[addr];
}
void Cx4::ReadRam_Imm(uint8_t byteIndex, uint8_t imm)
{
if(byteIndex >= 3) {
return;
}
uint16_t addr = (_state.DataPointerReg + imm) & 0xFFF;
if(addr >= 0xC00) {
addr -= 0x400;
}
_state.RamBuffer[byteIndex] = _dataRam[addr];
}
void Cx4::WriteRam(uint8_t byteIndex)
{
if(byteIndex >= 3) {
return;
}
uint16_t addr = _state.A & 0xFFF;
if(addr >= 0xC00) {
addr -= 0x400;
}
_dataRam[addr] = _state.RamBuffer[byteIndex];
}
void Cx4::WriteRam_Imm(uint8_t byteIndex, uint8_t imm)
{
if(byteIndex >= 3) {
return;
}
uint16_t addr = (_state.DataPointerReg + imm) & 0xFFF;
if(addr >= 0xC00) {
addr -= 0x400;
}
_dataRam[addr] = _state.RamBuffer[byteIndex];
}
void Cx4::LoadP(uint8_t byteIndex, uint8_t imm)
{
switch(byteIndex) {
case 0: _state.P = (_state.P & 0x7F00) | imm; break;
case 1: _state.P = (_state.P & 0xFF) | ((imm & 0x7F) << 8); break;
default: break; //nop
}
}
void Cx4::Swap(uint8_t reg)
{
uint32_t tmp = _state.A;
_state.A = _state.Regs[reg];
_state.Regs[reg] = tmp;
}
void Cx4::Store(uint8_t src, uint8_t dst)
{
switch(src) {
case 0: WriteRegister(dst, _state.A); break;
case 1: WriteRegister(dst, _state.MemoryDataReg); break;
default: break; //nop
}
}
void Cx4::Stop()
{
_state.Stopped = true;
if(!_state.IrqDisabled) {
_state.IrqFlag = true;
_cpu->SetIrqSource(IrqSource::Coprocessor);
}
}
void Cx4::IncMar()
{
_state.MemoryAddressReg = (_state.MemoryAddressReg + 1) & 0xFFFFFF;
}
//Data ROM that contains precalculated math lookup tables for sines, cosines, division, etc.
const uint32_t _dataRom[1024] = {
0xFFFFFF, 0x800000, 0x400000, 0x2AAAAA, 0x200000, 0x199999, 0x155555, 0x124924, 0x100000, 0x0E38E3, 0x0CCCCC, 0x0BA2E8, 0x0AAAAA, 0x09D89D, 0x092492, 0x088888,
0x080000, 0x078787, 0x071C71, 0x06BCA1, 0x066666, 0x061861, 0x05D174, 0x0590B2, 0x055555, 0x051EB8, 0x04EC4E, 0x04BDA1, 0x049249, 0x0469EE, 0x044444, 0x042108,
0x040000, 0x03E0F8, 0x03C3C3, 0x03A83A, 0x038E38, 0x03759F, 0x035E50, 0x034834, 0x033333, 0x031F38, 0x030C30, 0x02FA0B, 0x02E8BA, 0x02D82D, 0x02C859, 0x02B931,
0x02AAAA, 0x029CBC, 0x028F5C, 0x028282, 0x027627, 0x026A43, 0x025ED0, 0x0253C8, 0x024924, 0x023EE0, 0x0234F7, 0x022B63, 0x022222, 0x02192E, 0x021084, 0x020820,
0x020000, 0x01F81F, 0x01F07C, 0x01E913, 0x01E1E1, 0x01DAE6, 0x01D41D, 0x01CD85, 0x01C71C, 0x01C0E0, 0x01BACF, 0x01B4E8, 0x01AF28, 0x01A98E, 0x01A41A, 0x019EC8,
0x019999, 0x01948B, 0x018F9C, 0x018ACB, 0x018618, 0x018181, 0x017D05, 0x0178A4, 0x01745D, 0x01702E, 0x016C16, 0x016816, 0x01642C, 0x016058, 0x015C98, 0x0158ED,
0x015555, 0x0151D0, 0x014E5E, 0x014AFD, 0x0147AE, 0x01446F, 0x014141, 0x013E22, 0x013B13, 0x013813, 0x013521, 0x01323E, 0x012F68, 0x012C9F, 0x0129E4, 0x012735,
0x012492, 0x0121FB, 0x011F70, 0x011CF0, 0x011A7B, 0x011811, 0x0115B1, 0x01135C, 0x011111, 0x010ECF, 0x010C97, 0x010A68, 0x010842, 0x010624, 0x010410, 0x010204,
0x010000, 0x00FE03, 0x00FC0F, 0x00FA23, 0x00F83E, 0x00F660, 0x00F489, 0x00F2B9, 0x00F0F0, 0x00EF2E, 0x00ED73, 0x00EBBD, 0x00EA0E, 0x00E865, 0x00E6C2, 0x00E525,
0x00E38E, 0x00E1FC, 0x00E070, 0x00DEE9, 0x00DD67, 0x00DBEB, 0x00DA74, 0x00D901, 0x00D794, 0x00D62B, 0x00D4C7, 0x00D368, 0x00D20D, 0x00D0B6, 0x00CF64, 0x00CE16,
0x00CCCC, 0x00CB87, 0x00CA45, 0x00C907, 0x00C7CE, 0x00C698, 0x00C565, 0x00C437, 0x00C30C, 0x00C1E4, 0x00C0C0, 0x00BFA0, 0x00BE82, 0x00BD69, 0x00BC52, 0x00BB3E,
0x00BA2E, 0x00B921, 0x00B817, 0x00B70F, 0x00B60B, 0x00B509, 0x00B40B, 0x00B30F, 0x00B216, 0x00B11F, 0x00B02C, 0x00AF3A, 0x00AE4C, 0x00AD60, 0x00AC76, 0x00AB8F,
0x00AAAA, 0x00A9C8, 0x00A8E8, 0x00A80A, 0x00A72F, 0x00A655, 0x00A57E, 0x00A4A9, 0x00A3D7, 0x00A306, 0x00A237, 0x00A16B, 0x00A0A0, 0x009FD8, 0x009F11, 0x009E4C,
0x009D89, 0x009CC8, 0x009C09, 0x009B4C, 0x009A90, 0x0099D7, 0x00991F, 0x009868, 0x0097B4, 0x009701, 0x00964F, 0x0095A0, 0x0094F2, 0x009445, 0x00939A, 0x0092F1,
0x009249, 0x0091A2, 0x0090FD, 0x00905A, 0x008FB8, 0x008F17, 0x008E78, 0x008DDA, 0x008D3D, 0x008CA2, 0x008C08, 0x008B70, 0x008AD8, 0x008A42, 0x0089AE, 0x00891A,
0x008888, 0x0087F7, 0x008767, 0x0086D9, 0x00864B, 0x0085BF, 0x008534, 0x0084A9, 0x008421, 0x008399, 0x008312, 0x00828C, 0x008208, 0x008184, 0x008102, 0x008080,
0x000000, 0x100000, 0x16A09E, 0x1BB67A, 0x200000, 0x23C6EF, 0x27311C, 0x2A54FF, 0x2D413C, 0x300000, 0x3298B0, 0x3510E5, 0x376CF5, 0x39B056, 0x3BDDD4, 0x3DF7BD,
0x400000, 0x41F83D, 0x43E1DB, 0x45BE0C, 0x478DDE, 0x49523A, 0x4B0BF1, 0x4CBBB9, 0x4E6238, 0x500000, 0x519595, 0x532370, 0x54A9FE, 0x5629A2, 0x57A2B7, 0x591590,
0x5A8279, 0x5BE9BA, 0x5D4B94, 0x5EA843, 0x600000, 0x6152FE, 0x62A170, 0x63EB83, 0x653160, 0x667332, 0x67B11D, 0x68EB44, 0x6A21CA, 0x6B54CD, 0x6C846C, 0x6DB0C2,
0x6ED9EB, 0x700000, 0x712318, 0x72434A, 0x7360AD, 0x747B54, 0x759354, 0x76A8BF, 0x77BBA8, 0x78CC1F, 0x79DA34, 0x7AE5F9, 0x7BEF7A, 0x7CF6C8, 0x7DFBEF, 0x7EFEFD,
0x800000, 0x80FF01, 0x81FC0F, 0x82F734, 0x83F07B, 0x84E7EE, 0x85DD98, 0x86D182, 0x87C3B6, 0x88B43D, 0x89A31F, 0x8A9066, 0x8B7C19, 0x8C6641, 0x8D4EE4, 0x8E360B,
0x8F1BBC, 0x900000, 0x90E2DB, 0x91C456, 0x92A475, 0x938341, 0x9460BD, 0x953CF1, 0x9617E2, 0x96F196, 0x97CA11, 0x98A159, 0x997773, 0x9A4C64, 0x9B2031, 0x9BF2DE,
0x9CC470, 0x9D94EB, 0x9E6454, 0x9F32AF, 0xA00000, 0xA0CC4A, 0xA19792, 0xA261DC, 0xA32B2A, 0xA3F382, 0xA4BAE6, 0xA5815A, 0xA646E1, 0xA70B7E, 0xA7CF35, 0xA89209,
0xA953FD, 0xAA1513, 0xAAD550, 0xAB94B4, 0xAC5345, 0xAD1103, 0xADCDF2, 0xAE8A15, 0xAF456E, 0xB00000, 0xB0B9CC, 0xB172D6, 0xB22B20, 0xB2E2AC, 0xB3997C, 0xB44F93,
0xB504F3, 0xB5B99D, 0xB66D95, 0xB720DC, 0xB7D375, 0xB88560, 0xB936A0, 0xB9E738, 0xBA9728, 0xBB4673, 0xBBF51A, 0xBCA320, 0xBD5086, 0xBDFD4E, 0xBEA979, 0xBF5509,
0xC00000, 0xC0AA5F, 0xC15428, 0xC1FD5C, 0xC2A5FD, 0xC34E0D, 0xC3F58C, 0xC49C7D, 0xC542E1, 0xC5E8B8, 0xC68E05, 0xC732C9, 0xC7D706, 0xC87ABB, 0xC91DEB, 0xC9C098,
0xCA62C1, 0xCB0469, 0xCBA591, 0xCC463A, 0xCCE664, 0xCD8612, 0xCE2544, 0xCEC3FC, 0xCF623A, 0xD00000, 0xD09D4E, 0xD13A26, 0xD1D689, 0xD27277, 0xD30DF3, 0xD3A8FC,
0xD44394, 0xD4DDBC, 0xD57774, 0xD610BE, 0xD6A99B, 0xD7420B, 0xD7DA0F, 0xD871A9, 0xD908D8, 0xD99F9F, 0xDA35FE, 0xDACBF5, 0xDB6185, 0xDBF6B0, 0xDC8B76, 0xDD1FD8,
0xDDB3D7, 0xDE4773, 0xDEDAAD, 0xDF6D86, 0xE00000, 0xE09219, 0xE123D4, 0xE1B530, 0xE24630, 0xE2D6D2, 0xE36719, 0xE3F704, 0xE48694, 0xE515CB, 0xE5A4A8, 0xE6332D,
0xE6C15A, 0xE74F2F, 0xE7DCAD, 0xE869D6, 0xE8F6A9, 0xE98326, 0xEA0F50, 0xEA9B26, 0xEB26A8, 0xEBB1D9, 0xEC3CB7, 0xECC743, 0xED517F, 0xEDDB6A, 0xEE6506, 0xEEEE52,
0xEF7750, 0xF00000, 0xF08861, 0xF11076, 0xF1983E, 0xF21FBA, 0xF2A6EA, 0xF32DCF, 0xF3B469, 0xF43AB9, 0xF4C0C0, 0xF5467D, 0xF5CBF2, 0xF6511E, 0xF6D602, 0xF75A9F,
0xF7DEF5, 0xF86305, 0xF8E6CE, 0xF96A52, 0xF9ED90, 0xFA708A, 0xFAF33F, 0xFB75B1, 0xFBF7DF, 0xFC79CA, 0xFCFB72, 0xFD7CD8, 0xFDFDFB, 0xFE7EDE, 0xFEFF7F, 0xFF7FDF,
0x000000, 0x03243A, 0x064855, 0x096C32, 0x0C8FB2, 0x0FB2B7, 0x12D520, 0x15F6D0, 0x1917A6, 0x1C3785, 0x1F564E, 0x2273E1, 0x259020, 0x28AAED, 0x2BC428, 0x2EDBB3,
0x31F170, 0x350540, 0x381704, 0x3B269F, 0x3E33F2, 0x413EE0, 0x444749, 0x474D10, 0x4A5018, 0x4D5043, 0x504D72, 0x534789, 0x563E69, 0x5931F7, 0x5C2214, 0x5F0EA4,
0x61F78A, 0x64DCA9, 0x67BDE5, 0x6A9B20, 0x6D7440, 0x704927, 0x7319BA, 0x75E5DD, 0x78AD74, 0x7B7065, 0x7E2E93, 0x80E7E4, 0x839C3C, 0x864B82, 0x88F59A, 0x8B9A6B,
0x8E39D9, 0x90D3CC, 0x93682A, 0x95F6D9, 0x987FBF, 0x9B02C5, 0x9D7FD1, 0x9FF6CA, 0xA26799, 0xA4D224, 0xA73655, 0xA99414, 0xABEB49, 0xAE3BDD, 0xB085BA, 0xB2C8C9,
0xB504F3, 0xB73A22, 0xB96841, 0xBB8F3A, 0xBDAEF9, 0xBFC767, 0xC1D870, 0xC3E200, 0xC5E403, 0xC7DE65, 0xC9D112, 0xCBBBF7, 0xCD9F02, 0xCF7A1F, 0xD14D3D, 0xD31848,
0xD4DB31, 0xD695E4, 0xD84852, 0xD9F269, 0xDB941A, 0xDD2D53, 0xDEBE05, 0xE04621, 0xE1C597, 0xE33C59, 0xE4AA59, 0xE60F87, 0xE76BD7, 0xE8BF3B, 0xEA09A6, 0xEB4B0B,
0xEC835E, 0xEDB293, 0xEED89D, 0xEFF573, 0xF10908, 0xF21352, 0xF31447, 0xF40BDD, 0xF4FA0A, 0xF5DEC6, 0xF6BA07, 0xF78BC5, 0xF853F7, 0xF91297, 0xF9C79D, 0xFA7301,
0xFB14BE, 0xFBACCD, 0xFC3B27, 0xFCBFC9, 0xFD3AAB, 0xFDABCB, 0xFE1323, 0xFE70AF, 0xFEC46D, 0xFF0E57, 0xFF4E6D, 0xFF84AB, 0xFFB10F, 0xFFD397, 0xFFEC43, 0xFFFB10,
0x000000, 0x00A2F9, 0x0145F6, 0x01E8F8, 0x028C01, 0x032F14, 0x03D234, 0x047564, 0x0518A5, 0x05BBFB, 0x065F68, 0x0702EF, 0x07A692, 0x084A54, 0x08EE38, 0x099240,
0x0A366E, 0x0ADAC7, 0x0B7F4C, 0x0C2401, 0x0CC8E7, 0x0D6E02, 0x0E1355, 0x0EB8E3, 0x0F5EAE, 0x1004B9, 0x10AB08, 0x11519E, 0x11F87D, 0x129FA9, 0x134725, 0x13EEF4,
0x149719, 0x153F99, 0x15E875, 0x1691B2, 0x173B53, 0x17E55C, 0x188FD1, 0x193AB4, 0x19E60A, 0x1A91D8, 0x1B3E20, 0x1BEAE7, 0x1C9831, 0x1D4602, 0x1DF45F, 0x1EA34C,
0x1F52CE, 0x2002EA, 0x20B3A3, 0x216500, 0x221705, 0x22C9B8, 0x237D1E, 0x24313C, 0x24E618, 0x259BB9, 0x265224, 0x27095F, 0x27C171, 0x287A61, 0x293436, 0x29EEF6,
0x2AAAAA, 0x2B6759, 0x2C250A, 0x2CE3C7, 0x2DA398, 0x2E6485, 0x2F2699, 0x2FE9DC, 0x30AE59, 0x31741B, 0x323B2C, 0x330398, 0x33CD6B, 0x3498B1, 0x356578, 0x3633CE,
0x3703C1, 0x37D560, 0x38A8BB, 0x397DE4, 0x3A54EC, 0x3B2DE6, 0x3C08E6, 0x3CE601, 0x3DC54D, 0x3EA6E3, 0x3F8ADC, 0x407152, 0x415A62, 0x42462C, 0x4334D0, 0x442671,
0x451B37, 0x46134A, 0x470ED6, 0x480E0C, 0x491120, 0x4A184C, 0x4B23CD, 0x4C33EA, 0x4D48EC, 0x4E6327, 0x4F82F9, 0x50A8C9, 0x51D50A, 0x53083F, 0x5442FC, 0x5585EA,
0x56D1CC, 0x582782, 0x598815, 0x5AF4BC, 0x5C6EED, 0x5DF86C, 0x5F9369, 0x6142A3, 0x6309A5, 0x64ED1E, 0x66F381, 0x692617, 0x6B9322, 0x6E52A5, 0x71937C, 0x75CEB4,
0x000000, 0x000324, 0x000648, 0x00096D, 0x000C93, 0x000FBA, 0x0012E2, 0x00160B, 0x001936, 0x001C63, 0x001F93, 0x0022C4, 0x0025F9, 0x002930, 0x002C6B, 0x002FA9,
0x0032EB, 0x003632, 0x00397C, 0x003CCB, 0x00401F, 0x004379, 0x0046D8, 0x004A3D, 0x004DA8, 0x005119, 0x005492, 0x005811, 0x005B99, 0x005F28, 0x0062C0, 0x006660,
0x006A09, 0x006DBC, 0x00717A, 0x007541, 0x007914, 0x007CF2, 0x0080DC, 0x0084D2, 0x0088D5, 0x008CE6, 0x009105, 0x009533, 0x009970, 0x009DBE, 0x00A21C, 0x00A68B,
0x00AB0D, 0x00AFA2, 0x00B44B, 0x00B909, 0x00BDDC, 0x00C2C6, 0x00C7C8, 0x00CCE3, 0x00D218, 0x00D767, 0x00DCD3, 0x00E25D, 0x00E806, 0x00EDCF, 0x00F3BB, 0x00F9CA,
0x010000, 0x01065C, 0x010CE2, 0x011394, 0x011A73, 0x012183, 0x0128C6, 0x01303E, 0x0137EF, 0x013FDC, 0x014808, 0x015077, 0x01592D, 0x01622D, 0x016B7D, 0x017522,
0x017F21, 0x018980, 0x019444, 0x019F76, 0x01AB1C, 0x01B73E, 0x01C3E7, 0x01D11F, 0x01DEF1, 0x01ED69, 0x01FC95, 0x020C83, 0x021D44, 0x022EE9, 0x024186, 0x025533,
0x026A09, 0x028025, 0x0297A7, 0x02B0B5, 0x02CB78, 0x02E823, 0x0306EC, 0x032815, 0x034BEB, 0x0372C6, 0x039D10, 0x03CB47, 0x03FE02, 0x0435F7, 0x047405, 0x04B93F,
0x0506FF, 0x055EF9, 0x05C35D, 0x063709, 0x06BDCF, 0x075CE6, 0x081B97, 0x09046D, 0x0A2736, 0x0B9CC6, 0x0D8E81, 0x1046E9, 0x145AFF, 0x1B2671, 0x28BC48, 0x517BB5,
0xFFFFFF, 0xFFFB10, 0xFFEC43, 0xFFD397, 0xFFB10F, 0xFF84AB, 0xFF4E6D, 0xFF0E57, 0xFEC46D, 0xFE70AF, 0xFE1323, 0xFDABCB, 0xFD3AAB, 0xFCBFC9, 0xFC3B27, 0xFBACCD,
0xFB14BE, 0xFA7301, 0xF9C79D, 0xF91297, 0xF853F7, 0xF78BC5, 0xF6BA07, 0xF5DEC6, 0xF4FA0A, 0xF40BDD, 0xF31447, 0xF21352, 0xF10908, 0xEFF573, 0xEED89D, 0xEDB293,
0xEC835E, 0xEB4B0B, 0xEA09A6, 0xE8BF3B, 0xE76BD7, 0xE60F87, 0xE4AA59, 0xE33C59, 0xE1C597, 0xE04621, 0xDEBE05, 0xDD2D53, 0xDB941A, 0xD9F269, 0xD84852, 0xD695E4,
0xD4DB31, 0xD31848, 0xD14D3D, 0xCF7A1F, 0xCD9F02, 0xCBBBF7, 0xC9D112, 0xC7DE65, 0xC5E403, 0xC3E200, 0xC1D870, 0xBFC767, 0xBDAEF9, 0xBB8F3A, 0xB96841, 0xB73A22,
0xB504F3, 0xB2C8C9, 0xB085BA, 0xAE3BDD, 0xABEB49, 0xA99414, 0xA73655, 0xA4D224, 0xA26799, 0x9FF6CA, 0x9D7FD1, 0x9B02C5, 0x987FBF, 0x95F6D9, 0x93682A, 0x90D3CC,
0x8E39D9, 0x8B9A6B, 0x88F59A, 0x864B82, 0x839C3C, 0x80E7E4, 0x7E2E93, 0x7B7065, 0x78AD74, 0x75E5DD, 0x7319BA, 0x704927, 0x6D7440, 0x6A9B20, 0x67BDE5, 0x64DCA9,
0x61F78A, 0x5F0EA4, 0x5C2214, 0x5931F7, 0x563E69, 0x534789, 0x504D72, 0x4D5043, 0x4A5018, 0x474D10, 0x444749, 0x413EE0, 0x3E33F2, 0x3B269F, 0x381704, 0x350540,
0x31F170, 0x2EDBB3, 0x2BC428, 0x28AAED, 0x259020, 0x2273E1, 0x1F564E, 0x1C3785, 0x1917A6, 0x15F6D0, 0x12D520, 0x0FB2B7, 0x0C8FB2, 0x096C32, 0x064855, 0x03243A
};

468
Core/Cx4.cpp Normal file
View file

@ -0,0 +1,468 @@
#include "stdafx.h"
#include "Cx4.h"
#include "Console.h"
#include "Cpu.h"
#include "MemoryManager.h"
#include "MemoryMappings.h"
#include "BaseCartridge.h"
#include "EmuSettings.h"
#include "RamHandler.h"
#include "../Utilities/HexUtilities.h"
//TODO: Proper open bus behavior (and return 0s for missing save ram, too)
//TODO: CPU shouldn't have access to PRG ROM while the CX4 is loading from PRG ROM
//TODO: Timings are apparently not perfect (desync in MMX2 intro)
Cx4::Cx4(Console* console)
{
_console = console;
_memoryType = SnesMemoryType::Register;
_memoryManager = console->GetMemoryManager().get();
_cpu = console->GetCpu().get();
console->GetSettings()->InitializeRam(_dataRam, Cx4::DataRamSize);
auto &prgRomHandlers = console->GetCartridge()->GetPrgRomHandlers();
auto &saveRamHandlers = console->GetCartridge()->GetSaveRamHandlers();
MemoryMappings* cpuMappings = _memoryManager->GetMemoryMappings();
//PRG ROM
cpuMappings->RegisterHandler(0x00, 0x3F, 0x8000, 0xFFFF, prgRomHandlers);
cpuMappings->RegisterHandler(0x80, 0xBF, 0x8000, 0xFFFF, prgRomHandlers);
_mappings.RegisterHandler(0x00, 0x3F, 0x8000, 0xFFFF, prgRomHandlers);
_mappings.RegisterHandler(0x80, 0xBF, 0x8000, 0xFFFF, prgRomHandlers);
//Save RAM
cpuMappings->RegisterHandler(0x70, 0x7D, 0x0000, 0x7FFF, saveRamHandlers);
cpuMappings->RegisterHandler(0xF0, 0xFF, 0x0000, 0x7FFF, saveRamHandlers);
_mappings.RegisterHandler(0x70, 0x7D, 0x0000, 0x7FFF, saveRamHandlers);
_mappings.RegisterHandler(0xF0, 0xFF, 0x0000, 0x7FFF, saveRamHandlers);
//Registers
cpuMappings->RegisterHandler(0x00, 0x3F, 0x6000, 0x7FFF, this);
cpuMappings->RegisterHandler(0x80, 0xBF, 0x6000, 0x7FFF, this);
_mappings.RegisterHandler(0x00, 0x3F, 0x6000, 0x7FFF, this);
_mappings.RegisterHandler(0x80, 0xBF, 0x6000, 0x7FFF, this);
_clockRatio = (double)20000000 / console->GetMasterClockRate();
Reset();
}
void Cx4::Reset()
{
_state = {};
_state.Stopped = true;
_state.SingleRom = true;
_state.RomAccessDelay = 3;
_state.RamAccessDelay = 3;
}
void Cx4::Run()
{
uint64_t targetCycle = (uint64_t)(_memoryManager->GetMasterClock() * _clockRatio);
while(_state.CycleCount < targetCycle) {
if(_state.Locked) {
Step(1);
} else if(_state.Suspend.Enabled) {
if(_state.Suspend.Duration == 0) {
Step(1);
} else {
Step(1);
_state.Suspend.Duration--;
if(_state.Suspend.Duration == 0) {
_state.Suspend.Enabled = false;
}
}
} else if(_state.Cache.Enabled) {
ProcessCache(targetCycle);
} else if(_state.Dma.Enabled) {
ProcessDma(targetCycle);
} else if(_state.Stopped) {
Step(targetCycle - _state.CycleCount);
} else if(!ProcessCache(targetCycle)) {
if(!_state.Cache.Enabled) {
//Cache operation required, but both caches are locked, stop
Stop();
}
} else {
_console->ProcessCx4Exec();
uint16_t opCode = _prgRam[_state.Cache.Page][_state.PC++];
Exec(opCode);
if(_state.PC == 0) {
SwitchCachePage();
}
}
}
}
void Cx4::Step(uint64_t cycles)
{
if(_state.Bus.Enabled) {
if(_state.Bus.DelayCycles > cycles) {
_state.Bus.DelayCycles -= (uint8_t)cycles;
} else {
_state.Bus.Enabled = false;
_state.Bus.DelayCycles = 0;
if(_state.Bus.Reading) {
_state.MemoryDataReg = ReadCx4(_state.Bus.Address);
_state.Bus.Reading = false;
}
if(_state.Bus.Writing) {
WriteCx4(_state.Bus.Address, _state.MemoryDataReg);
_state.Bus.Writing = false;
}
}
}
_state.CycleCount += cycles;
}
void Cx4::SwitchCachePage()
{
if(_state.Cache.Page == 1) {
Stop();
return;
}
_state.Cache.Page = 1;
if(_state.Cache.Lock[1]) {
Stop();
return;
}
_state.PB = _state.P;
uint64_t targetCycle = (uint64_t)(_memoryManager->GetMasterClock() * _clockRatio);
if(!ProcessCache(targetCycle) && !_state.Cache.Enabled) {
Stop();
}
}
bool Cx4::ProcessCache(uint64_t targetCycle)
{
uint32_t address = (_state.Cache.Base + (_state.PB << 9)) & 0xFFFFFF;
if(_state.Cache.Pos == 0) {
if(_state.Cache.Address[_state.Cache.Page] == address) {
//Current cache page matches the needed address, keep using it
_state.Cache.Enabled = false;
return true;
}
//Check if the other page matches
_state.Cache.Page ^= 1;
if(_state.Cache.Address[_state.Cache.Page] == address) {
//The other cache page matches, use it
_state.Cache.Enabled = false;
return true;
}
if(_state.Cache.Lock[_state.Cache.Page]) {
//If it's locked, use the other page
_state.Cache.Page ^= 1;
}
if(_state.Cache.Lock[_state.Cache.Page]) {
//The both pages are locked, and the cache is invalid, give up.
_state.Cache.Enabled = false;
return false;
}
_state.Cache.Enabled = true;
}
//Populate the cache
while(_state.Cache.Pos < 256) {
uint8_t lsb = ReadCx4(address + (_state.Cache.Pos * 2));
Step(GetAccessDelay(address + (_state.Cache.Pos * 2)));
uint8_t msb = ReadCx4(address + (_state.Cache.Pos * 2) + 1);
Step(GetAccessDelay(address + (_state.Cache.Pos * 2) + 1));
_prgRam[_state.Cache.Page][_state.Cache.Pos] = (msb << 8) | lsb;
_state.Cache.Pos++;
if(_state.CycleCount > targetCycle) {
break;
}
}
if(_state.Cache.Pos >= 256) {
_state.Cache.Address[_state.Cache.Page] = address;
_state.Cache.Pos = 0;
_state.Cache.Enabled = false;
return true;
}
//Cache loading is not finished
return false;
}
void Cx4::ProcessDma(uint64_t targetCycle)
{
while(_state.Dma.Pos < _state.Dma.Length) {
uint32_t src = (_state.Dma.Source + _state.Dma.Pos) & 0xFFFFFF;
uint32_t dest = (_state.Dma.Dest + _state.Dma.Pos) & 0xFFFFFF;
IMemoryHandler *srcHandler = _mappings.GetHandler(src);
IMemoryHandler *destHandler = _mappings.GetHandler(dest);
if(!srcHandler || !destHandler || srcHandler->GetMemoryType() == destHandler->GetMemoryType() || destHandler->GetMemoryType() == SnesMemoryType::PrgRom) {
//Invalid DMA, the chip is locked until it gets restarted by a write to $7F53
_state.Locked = true;
_state.Dma.Pos = 0;
_state.Dma.Enabled = false;
return;
}
Step(GetAccessDelay(src));
uint8_t value = ReadCx4(src);
Step(GetAccessDelay(dest));
WriteCx4(dest, value);
_state.Dma.Pos++;
if(_state.CycleCount > targetCycle) {
break;
}
}
if(_state.Dma.Pos >= _state.Dma.Length) {
_state.Dma.Pos = 0;
_state.Dma.Enabled = false;
}
}
uint8_t Cx4::GetAccessDelay(uint32_t addr)
{
IMemoryHandler* handler = _mappings.GetHandler(addr);
if(handler->GetMemoryType() == SnesMemoryType::PrgRom) {
return 1 + _state.RomAccessDelay;
} else if(handler->GetMemoryType() == SnesMemoryType::SaveRam) {
return 1 + _state.RamAccessDelay;
}
return 1;
}
uint8_t Cx4::ReadCx4(uint32_t addr)
{
IMemoryHandler* handler = _mappings.GetHandler(addr);
if(handler) {
return handler->Read(addr);
}
return 0;
}
void Cx4::WriteCx4(uint32_t addr, uint8_t value)
{
IMemoryHandler* handler = _mappings.GetHandler(addr);
if(handler) {
handler->Write(addr, value);
}
}
uint8_t Cx4::Read(uint32_t addr)
{
addr = 0x7000 | (addr & 0xFFF);
if(addr <= 0x7BFF) {
return _dataRam[addr & 0xFFF];
} else if(addr >= 0x7F60 && addr <= 0x7F7F) {
return _state.Vectors[addr & 0x1F];
} else if((addr >= 0x7F80 && addr <= 0x7FAF) || (addr >= 0x7FC0 && addr <= 0x7FEF)) {
addr &= 0x3F;
uint32_t &reg = _state.Regs[addr / 3];
switch(addr % 3) {
case 0: return reg;
case 1: return reg >> 8;
case 2: return reg >> 16;
}
} else if(addr >= 0x7F53 && addr <= 0x7F5F) {
return (uint8_t)_state.Suspend.Enabled | ((uint8_t)_state.IrqFlag << 1) | ((uint8_t)IsRunning() << 6) | ((uint8_t)IsBusy() << 7);
}
switch(addr) {
case 0x7F40: return _state.Dma.Source;
case 0x7F41: return _state.Dma.Source >> 8;
case 0x7F42: return _state.Dma.Source >> 16;
case 0x7F43: return (uint8_t)_state.Dma.Length;
case 0x7F44: return _state.Dma.Length >> 8;
case 0x7F45: return _state.Dma.Dest;
case 0x7F46: return _state.Dma.Dest >> 8;
case 0x7F47: return _state.Dma.Dest >> 16;
case 0x7F48: return _state.Cache.Page;
case 0x7F49: return _state.Cache.Base;
case 0x7F4A: return _state.Cache.Base >> 8;
case 0x7F4B: return _state.Cache.Base >> 16;
case 0x7F4C: return (uint8_t)_state.Cache.Lock[0] | ((uint8_t)_state.Cache.Lock[1] << 1);
case 0x7F4D: return (uint8_t)_state.Cache.ProgramBank;
case 0x7F4E: return _state.Cache.ProgramBank >> 8;
case 0x7F4F: return _state.Cache.ProgramCounter;
case 0x7F50: return _state.RamAccessDelay | (_state.RomAccessDelay << 4);
case 0x7F51: return _state.IrqDisabled;
case 0x7F52: return _state.SingleRom;
}
return 0;
}
void Cx4::Write(uint32_t addr, uint8_t value)
{
addr = 0x7000 | (addr & 0xFFF);
if(addr <= 0x7BFF) {
_dataRam[addr & 0xFFF] = value;
return;
}
if(addr >= 0x7F60 && addr <= 0x7F7F) {
_state.Vectors[addr & 0x1F] = value;
} else if((addr >= 0x7F80 && addr <= 0x7FAF) || (addr >= 0x7FC0 && addr <= 0x7FEF)) {
addr &= 0x3F;
uint32_t &reg = _state.Regs[addr / 3];
switch(addr % 3) {
case 0: reg = (reg & 0xFFFF00) | value; break;
case 1: reg = (reg & 0xFF00FF) | (value << 8); break;
case 2: reg = (reg & 0x00FFFF) | (value << 16); break;
}
} else if(addr >= 0x7F55 && addr <= 0x7F5C) {
_state.Suspend.Enabled = true;
_state.Suspend.Duration = (addr - 0x7F55) * 32;
} else {
switch(addr) {
case 0x7F40: _state.Dma.Source = (_state.Dma.Source & 0xFFFF00) | value; break;
case 0x7F41: _state.Dma.Source = (_state.Dma.Source & 0xFF00FF) | (value << 8); break;
case 0x7F42: _state.Dma.Source = (_state.Dma.Source & 0x00FFFF) | (value << 16); break;
case 0x7F43: _state.Dma.Length = (_state.Dma.Length & 0xFF00) | value; break;
case 0x7F44: _state.Dma.Length = (_state.Dma.Length & 0x00FF) | (value << 8); break;
case 0x7F45: _state.Dma.Dest = (_state.Dma.Dest & 0xFFFF00) | value; break;
case 0x7F46: _state.Dma.Dest = (_state.Dma.Dest & 0xFF00FF) | (value << 8); break;
case 0x7F47:
_state.Dma.Dest = (_state.Dma.Dest & 0x00FFFF) | (value << 16);
if(_state.Stopped) {
_state.Dma.Enabled = true;
}
break;
case 0x7F48:
_state.Cache.Page = value & 0x01;
if(_state.Stopped) {
_state.Cache.Enabled = true;
}
break;
case 0x7F49: _state.Cache.Base = (_state.Cache.Base & 0xFFFF00) | value; break;
case 0x7F4A: _state.Cache.Base = (_state.Cache.Base & 0xFF00FF) | (value << 8); break;
case 0x7F4B: _state.Cache.Base = (_state.Cache.Base & 0x00FFFF) | (value << 16); break;
case 0x7F4C:
_state.Cache.Lock[0] = (value & 0x01) != 0;
_state.Cache.Lock[1] = (value & 0x02) != 0;
break;
case 0x7F4D: _state.Cache.ProgramBank = (_state.Cache.ProgramBank & 0xFF00) | value; break;
case 0x7F4E: _state.Cache.ProgramBank = (_state.Cache.ProgramBank & 0x00FF) | ((value & 0x7F) << 8); break;
case 0x7F4F:
_state.Cache.ProgramCounter = value;
if(_state.Stopped) {
_state.Stopped = false;
_state.PB = _state.Cache.ProgramBank;
_state.PC = _state.Cache.ProgramCounter;
}
break;
case 0x7F50:
_state.RamAccessDelay = value & 0x07;
_state.RomAccessDelay = (value >> 4) & 0x07;
break;
case 0x7F51:
_state.IrqDisabled = value & 0x01;
if(_state.IrqDisabled) {
_state.IrqFlag = true;
_cpu->ClearIrqSource(IrqSource::Coprocessor);
}
break;
case 0x7F52: _state.SingleRom = (value & 0x01) != 0; break;
case 0x7F53:
_state.Locked = false;
_state.Stopped = true;
break;
case 0x7F5D: _state.Suspend.Enabled = false; break;
case 0x7F5E:
//Clear IRQ flag in CX4, but keeps IRQ signal high
_state.IrqFlag = false;
break;
}
}
}
bool Cx4::IsRunning()
{
return IsBusy() || !_state.Stopped;
}
bool Cx4::IsBusy()
{
return _state.Cache.Enabled || _state.Dma.Enabled || _state.Bus.DelayCycles > 0;
}
void Cx4::Serialize(Serializer &s)
{
s.Stream(
_state.CycleCount, _state.PB, _state.PC, _state.A, _state.P, _state.SP, _state.Mult, _state.RomBuffer,
_state.RamBuffer[0], _state.RamBuffer[1], _state.RamBuffer[2], _state.MemoryDataReg, _state.MemoryAddressReg,
_state.DataPointerReg, _state.Negative, _state.Zero, _state.Carry, _state.Overflow, _state.IrqFlag, _state.Stopped,
_state.Locked, _state.IrqDisabled, _state.SingleRom, _state.RamAccessDelay, _state.RomAccessDelay, _state.Bus.Address,
_state.Bus.DelayCycles, _state.Bus.Enabled, _state.Bus.Reading, _state.Bus.Writing, _state.Dma.Dest, _state.Dma.Enabled,
_state.Dma.Length, _state.Dma.Source, _state.Dma.Pos, _state.Suspend.Duration, _state.Suspend.Enabled, _state.Cache.Enabled,
_state.Cache.Lock[0], _state.Cache.Lock[1], _state.Cache.Address[0], _state.Cache.Address[1], _state.Cache.Base,
_state.Cache.Page, _state.Cache.ProgramBank, _state.Cache.ProgramCounter, _state.Cache.Pos
);
s.StreamArray(_state.Stack, 8);
s.StreamArray(_state.Regs, 16);
s.StreamArray(_state.Vectors, 0x20);
s.StreamArray(_prgRam[0], 256);
s.StreamArray(_prgRam[1], 256);
s.StreamArray(_dataRam, Cx4::DataRamSize);
}
uint8_t Cx4::Peek(uint32_t addr)
{
return 0;
}
void Cx4::PeekBlock(uint8_t* output)
{
memset(output, 0, 0x1000);
}
AddressInfo Cx4::GetAbsoluteAddress(uint32_t address)
{
return { -1, SnesMemoryType::Register };
}
uint8_t* Cx4::DebugGetDataRam()
{
return _dataRam;
}
uint32_t Cx4::DebugGetDataRamSize()
{
return Cx4::DataRamSize;
}
Cx4State Cx4::GetState()
{
return _state;
}

131
Core/Cx4.h Normal file
View file

@ -0,0 +1,131 @@
#pragma once
#include "stdafx.h"
#include "BaseCoprocessor.h"
#include "Cx4Types.h"
#include "MemoryMappings.h"
class Console;
class MemoryManager;
class Cpu;
class Cx4 : public BaseCoprocessor
{
private:
static constexpr int DataRamSize = 0xC00;
Console *_console;
MemoryManager *_memoryManager;
Cpu *_cpu;
MemoryMappings _mappings;
double _clockRatio;
Cx4State _state;
uint16_t _prgRam[2][256];
uint8_t _dataRam[Cx4::DataRamSize];
void Exec(uint16_t opCode);
void SwitchCachePage();
bool ProcessCache(uint64_t targetCycle);
void ProcessDma(uint64_t targetCycle);
void Step(uint64_t cycles);
bool IsRunning();
bool IsBusy();
uint8_t GetAccessDelay(uint32_t addr);
uint8_t ReadCx4(uint32_t addr);
void WriteCx4(uint32_t addr, uint8_t value);
uint32_t GetSourceValue(uint8_t src);
void WriteRegister(uint8_t reg, uint32_t value);
void SetA(uint32_t value);
void NOP();
void WAIT();
void Branch(bool branch, uint8_t, uint8_t dest);
void Skip(uint8_t flagToCheck, uint8_t skipIfSet);
void JSR(bool branch, uint8_t, uint8_t dest);
void RTS();
void PushPC();
void PullPC();
void SetZeroNegativeFlags();
uint32_t AddValues(uint32_t a, uint32_t b);
uint32_t Substract(uint32_t a, uint32_t b);
void CMPR(uint8_t shift, uint8_t src);
void CMPR_Imm(uint8_t shift, uint8_t imm);
void CMP(uint8_t shift, uint8_t src);
void CMP_Imm(uint8_t shift, uint8_t imm);
void SignExtend(uint8_t mode);
void Load(uint8_t dest, uint8_t src);
void Load_Imm(uint8_t dest, uint8_t imm);
void ADD(uint8_t shift, uint8_t src);
void ADD_Imm(uint8_t shift, uint8_t imm);
void SUB(uint8_t shift, uint8_t src);
void SUB_Imm(uint8_t shift, uint8_t imm);
void SUBR(uint8_t shift, uint8_t src);
void SUBR_Imm(uint8_t shift, uint8_t imm);
void SMUL(uint8_t src);
void SMUL_Imm(uint8_t imm);
void AND(uint8_t shift, uint8_t src);
void AND_Imm(uint8_t shift, uint8_t imm);
void OR(uint8_t shift, uint8_t src);
void OR_Imm(uint8_t shift, uint8_t imm);
void XOR(uint8_t shift, uint8_t src);
void XOR_Imm(uint8_t shift, uint8_t imm);
void XNOR(uint8_t shift, uint8_t src);
void XNOR_Imm(uint8_t shift, uint8_t imm);
void SHR(uint8_t src);
void SHR_Imm(uint8_t imm);
void ASR(uint8_t src);
void ASR_Imm(uint8_t imm);
void SHL(uint8_t src);
void SHL_Imm(uint8_t imm);
void ROR(uint8_t src);
void ROR_Imm(uint8_t imm);
void ReadRom();
void ReadRom_Imm(uint16_t imm);
void ReadRam(uint8_t byteIndex);
void ReadRam_Imm(uint8_t byteIndex, uint8_t imm);
void WriteRam(uint8_t byteIndex);
void WriteRam_Imm(uint8_t byteIndex, uint8_t imm);
void LoadP(uint8_t byteIndex, uint8_t imm);
void Swap(uint8_t reg);
void IncMar();
void Store(uint8_t src, uint8_t dst);
void Stop();
public:
Cx4(Console* console);
void Reset() override;
void Run();
uint8_t Read(uint32_t addr) override;
void Write(uint32_t addr, uint8_t value) override;
void Serialize(Serializer &s) override;
uint8_t Peek(uint32_t addr) override;
void PeekBlock(uint8_t* output) override;
AddressInfo GetAbsoluteAddress(uint32_t address) override;
uint8_t* DebugGetDataRam();
uint32_t DebugGetDataRamSize();
Cx4State GetState();
};

215
Core/Cx4DisUtils.cpp Normal file
View file

@ -0,0 +1,215 @@
#include "stdafx.h"
#include "Cx4DisUtils.h"
#include "DisassemblyInfo.h"
#include "../Utilities/HexUtilities.h"
#include "../Utilities/FastString.h"
void Cx4DisUtils::GetDisassembly(DisassemblyInfo &info, string &out, uint32_t memoryAddr, LabelManager *labelManager)
{
FastString str;
uint8_t op = info.GetByteCode()[1] & 0xFC;
uint8_t param1 = info.GetByteCode()[1] & 0x03;
uint8_t param2 = info.GetByteCode()[0] & 0xFF;
auto writeSrc = [&str, param2]() -> void {
switch(param2 & 0x7F) {
case 0x00: str.Write("A"); break;
case 0x01: str.Write("MULTL"); break;
case 0x02: str.Write("MULTH"); break;
case 0x03: str.Write("MDR"); break;
case 0x08: str.Write("ROM"); break;
case 0x0C: str.Write("RAM"); break;
case 0x13: str.Write("MAR"); break;
case 0x1C: str.Write("DPR"); break;
case 0x20: str.Write("PC"); break;
case 0x28: str.Write("P"); break;
case 0x2E: str.Write("RDROM"); break;
case 0x2F: str.Write("RDRAM"); break;
case 0x50: str.Write("#$000000"); break;
case 0x51: str.Write("#$FFFFFF"); break;
case 0x52: str.Write("#$00FF00"); break;
case 0x53: str.Write("#$FF0000"); break;
case 0x54: str.Write("#$00FFFF"); break;
case 0x55: str.Write("#$FFFF00"); break;
case 0x56: str.Write("#$800000"); break;
case 0x57: str.Write("#$7FFFFF"); break;
case 0x58: str.Write("#$008000"); break;
case 0x59: str.Write("#$007FFF"); break;
case 0x5A: str.Write("#$FF7FFF"); break;
case 0x5B: str.Write("#$FFFF7F"); break;
case 0x5C: str.Write("#$010000"); break;
case 0x5D: str.Write("#$FEFFFF"); break;
case 0x5E: str.Write("#$000100"); break;
case 0x5F: str.Write("#$00FEFF"); break;
case 0x60: case 0x70: str.Write("R0"); break;
case 0x61: case 0x71: str.Write("R1"); break;
case 0x62: case 0x72: str.Write("R2"); break;
case 0x63: case 0x73: str.Write("R3"); break;
case 0x64: case 0x74: str.Write("R4"); break;
case 0x65: case 0x75: str.Write("R5"); break;
case 0x66: case 0x76: str.Write("R6"); break;
case 0x67: case 0x77: str.Write("R7"); break;
case 0x68: case 0x78: str.Write("R8"); break;
case 0x69: case 0x79: str.Write("R9"); break;
case 0x6A: case 0x7A: str.Write("R10"); break;
case 0x6B: case 0x7B: str.Write("R11"); break;
case 0x6C: case 0x7C: str.Write("R12"); break;
case 0x6D: case 0x7D: str.Write("R13"); break;
case 0x6E: case 0x7E: str.Write("R14"); break;
case 0x6F: case 0x7F: str.Write("R15"); break;
}
};
auto writeDest = [&str, param1]() -> void {
switch(param1) {
case 0: str.Write("A"); break;
case 1: str.Write("MDR"); break;
case 2: str.Write("MAR"); break;
case 3: str.Write("P"); break;
}
};
auto writeShiftedA = [&str, param1]() -> void {
switch(param1) {
case 0: str.Write("A"); break;
case 1: str.Write("(A << 1)"); break;
case 2: str.Write("(A << 8)"); break;
case 3: str.Write("(A << 16)"); break;
}
};
auto writeBranchTarget = [&str, param1, param2]() -> void {
if(param1) {
//Far jump
str.Write("P:");
}
str.WriteAll('$', HexUtilities::ToHex(param2));
};
auto writeSrcShiftedA = [&str, &writeSrc, &writeShiftedA]() -> void {
writeShiftedA();
str.Write(", ");
writeSrc();
};
switch(op) {
case 0x00: str.Write("NOP"); break;
case 0x04: str.Write("???"); break;
case 0x08: str.Write("BRA "); writeBranchTarget(); break;
case 0x0C: str.Write("BEQ "); writeBranchTarget(); break;
case 0x10: str.Write("BCS "); writeBranchTarget(); break;
case 0x14: str.Write("BMI "); writeBranchTarget(); break;
case 0x18: str.Write("BVS "); writeBranchTarget(); break;
case 0x1C: str.Write("WAIT"); break;
case 0x20: str.Write("???"); break;
case 0x24:
str.Write("SKIP");
switch(param1) {
case 0: str.Write('V'); break;
case 1: str.Write('C'); break;
case 2: str.Write('Z'); break;
case 3: str.Write('N'); break;
}
str.Write((param2 & 0x01) ? 'S' : 'C');
break;
case 0x28: str.Write("JSR "); writeBranchTarget(); break;
case 0x2C: str.Write("JEQ "); writeBranchTarget(); break;
case 0x30: str.Write("JCS "); writeBranchTarget(); break;
case 0x34: str.Write("JMI "); writeBranchTarget(); break;
case 0x38: str.Write("JVS "); writeBranchTarget(); break;
case 0x3C: str.Write("RTS"); break;
case 0x40: str.Write("INC MAR"); break;
case 0x44: str.Write("???"); break;
case 0x48: str.Write("CMPR "); writeSrc(); str.Write(", "); writeShiftedA(); break;
case 0x4C: str.WriteAll("CMPR #$", HexUtilities::ToHex(param2)); str.Write(", "); writeShiftedA(); break;
case 0x50: str.Write("CMP "); writeShiftedA(); str.Write(", "); writeSrc(); break;
case 0x54: str.WriteAll("CMP "); writeShiftedA(); str.WriteAll(", #$", HexUtilities::ToHex(param2)); break;
case 0x58:
if(param1 == 1) {
str.Write("SXB");
} else if(param1 == 2) {
str.Write("SXW");
} else {
str.Write("???");
}
break;
case 0x5C: str.Write("???"); break;
case 0x60: str.Write("LD "); writeDest(); str.Write(", "); writeSrc(); break;
case 0x64: str.Write("LD "); writeDest(); str.WriteAll(", #$", HexUtilities::ToHex(param2)); break;
case 0x68: str.WriteAll("RDRAM RAM:", '0' + param1, ", A"); break;
case 0x6C: str.WriteAll("RDRAM RAM:", '0' + param1, ", DPR+#$", HexUtilities::ToHex(param2)); break;
case 0x70: str.Write("RDROM (a)"); break;
case 0x74: str.WriteAll("RDROM (#$", HexUtilities::ToHex((param1 << 8) | param2), ")"); break;
case 0x78: str.Write("???"); break;
case 0x7C:
if(param1 <= 1) {
str.WriteAll("LD P", param1 ? "H" : "L", ", #$", HexUtilities::ToHex(param2));
} else {
str.Write("???");
}
break;
case 0x80: str.Write("ADD "); writeShiftedA(); str.Write(", "); writeSrc(); break;
case 0x84: str.WriteAll("ADD "); writeShiftedA(); str.WriteAll(", #$", HexUtilities::ToHex(param2)); break;
case 0x88: str.Write("SUBR "); writeSrc(); str.Write(", "); writeShiftedA(); break;
case 0x8C: str.WriteAll("SUBR #$", HexUtilities::ToHex(param2)); str.Write(", "); writeShiftedA(); break;
case 0x90: str.Write("SUB "); writeShiftedA(); str.Write(", "); writeSrc(); break;
case 0x94: str.WriteAll("SUB "); writeShiftedA(); str.WriteAll(", #$", HexUtilities::ToHex(param2)); break;
case 0x98: str.Write("SMUL A, "); writeSrc(); break;
case 0x9C: str.WriteAll("SMUL A, #$", HexUtilities::ToHex(param2)); break;
case 0xA0: str.Write("XNOR "); writeShiftedA(); str.Write(", "); writeSrc(); break;
case 0xA4: str.WriteAll("XNOR "); writeShiftedA(); str.WriteAll(", #$", HexUtilities::ToHex(param2)); break;
case 0xA8: str.Write("XOR "); writeShiftedA(); str.Write(", "); writeSrc(); break;
case 0xAC: str.WriteAll("XOR "); writeShiftedA(); str.WriteAll(", #$", HexUtilities::ToHex(param2)); break;
case 0xB0: str.Write("AND "); writeShiftedA(); str.Write(", "); writeSrc(); break;
case 0xB4: str.WriteAll("AND "); writeShiftedA(); str.WriteAll(", #$", HexUtilities::ToHex(param2)); break;
case 0xB8: str.Write("OR "); writeShiftedA(); str.Write(", "); writeSrc(); break;
case 0xBC: str.WriteAll("OR "); writeShiftedA(); str.WriteAll(", #$", HexUtilities::ToHex(param2)); break;
case 0xC0: str.Write("SHR A, "); writeSrc(); break;
case 0xC4: str.WriteAll("SHR A, #$", HexUtilities::ToHex(param2 & 0x1F)); break;
case 0xC8: str.Write("ASR A, "); writeSrc(); break;
case 0xCC: str.WriteAll("ASR A, #$", HexUtilities::ToHex(param2 & 0x1F)); break;
case 0xD0: str.Write("ROR A, "); writeSrc(); break;
case 0xD4: str.WriteAll("ROR A, #$", HexUtilities::ToHex(param2 & 0x1F)); break;
case 0xD8: str.Write("SHL A, "); writeSrc(); break;
case 0xDC: str.WriteAll("SHL A, #$", HexUtilities::ToHex(param2 & 0x1F)); break;
case 0xE0:
if(param1 <= 1) {
str.Write("ST "); writeSrc(); str.WriteAll(", ", param1 ? "MDR" : "A");
} else {
str.Write("???");
}
break;
case 0xE4: str.Write("???"); break;
case 0xE8: str.WriteAll("WRRAM A, RAM:", '0' + param1); break;
case 0xEC: str.WriteAll("WRRAM DPR+#$", HexUtilities::ToHex(param2), ", RAM:", '0' + param1); break;
case 0xF0: str.WriteAll("SWAP A, R", std::to_string(param2)); break;
case 0xF4: str.Write("???"); break;
case 0xF8: str.Write("???"); break;
case 0xFC: str.Write("STOP"); break;
}
out += str.ToString();
}

11
Core/Cx4DisUtils.h Normal file
View file

@ -0,0 +1,11 @@
#pragma once
#include "stdafx.h"
class DisassemblyInfo;
class LabelManager;
class Cx4DisUtils
{
public:
static void GetDisassembly(DisassemblyInfo &info, string &out, uint32_t memoryAddr, LabelManager* labelManager);
};

90
Core/Cx4Types.h Normal file
View file

@ -0,0 +1,90 @@
#pragma once
#include "stdafx.h"
struct Cx4Dma
{
uint32_t Source;
uint32_t Dest;
uint16_t Length;
uint32_t Pos;
bool Enabled;
};
struct Cx4Suspend
{
uint32_t Duration;
bool Enabled;
};
struct Cx4Cache
{
bool Enabled;
uint8_t Page;
bool Lock[2];
uint32_t Address[2];
uint32_t Base;
uint16_t ProgramBank;
uint8_t ProgramCounter;
uint16_t Pos;
};
struct Cx4Bus
{
bool Enabled;
bool Reading;
bool Writing;
uint8_t DelayCycles;
uint32_t Address;
};
struct Cx4State
{
uint64_t CycleCount;
//Program bank
uint16_t PB;
//Program counter
uint8_t PC;
//Accumulator
uint32_t A;
//Page register
uint16_t P;
uint8_t SP;
uint32_t Stack[8];
//Multiplier
uint64_t Mult;
uint32_t RomBuffer;
uint8_t RamBuffer[3];
uint32_t MemoryDataReg;
uint32_t MemoryAddressReg;
uint32_t DataPointerReg;
uint32_t Regs[16];
bool Negative;
bool Zero;
bool Carry;
bool Overflow;
bool IrqFlag;
bool Stopped;
bool Locked;
bool IrqDisabled;
bool SingleRom;
uint8_t RomAccessDelay;
uint8_t RamAccessDelay;
Cx4Bus Bus;
Cx4Dma Dma;
Cx4Cache Cache;
Cx4Suspend Suspend;
uint8_t Vectors[0x20];
};

View file

@ -5,6 +5,7 @@
#include "SpcTypes.h"
#include "NecDspTypes.h"
#include "GsuTypes.h"
#include "Cx4Types.h"
struct DebugState
{
@ -15,6 +16,7 @@ struct DebugState
NecDspState Dsp;
CpuState Sa1;
GsuState Gsu;
Cx4State Cx4;
};
enum class SnesMemoryType
@ -36,6 +38,7 @@ enum class SnesMemoryType
DspDataRam,
Sa1InternalRam,
GsuWorkRam,
Cx4DataRam,
Register,
};
@ -258,4 +261,5 @@ enum class CpuType : uint8_t
NecDsp,
Sa1,
Gsu,
Cx4
};

View file

@ -12,6 +12,8 @@ public:
case CpuType::Spc: return SnesMemoryType::SpcMemory;
case CpuType::Sa1: return SnesMemoryType::Sa1Memory;
case CpuType::Gsu: return SnesMemoryType::GsuMemory;
case CpuType::Cx4: break;
case CpuType::NecDsp: break;
}
@ -25,6 +27,6 @@ public:
static constexpr CpuType GetLastCpuType()
{
return CpuType::Gsu;
return CpuType::Cx4;
}
};

View file

@ -7,6 +7,7 @@
#include "Spc.h"
#include "Sa1.h"
#include "Gsu.h"
#include "Cx4.h"
#include "NecDsp.h"
#include "CpuDebugger.h"
#include "SpcDebugger.h"
@ -196,6 +197,24 @@ void Debugger::ProcessNecDspExec(uint32_t addr, uint32_t value)
}
}
void Debugger::ProcessCx4Exec()
{
if(_traceLogger->IsCpuLogged(CpuType::Cx4)) {
DebugState debugState;
GetState(debugState, true);
uint32_t addr = (debugState.Cx4.Cache.Address[debugState.Cx4.Cache.Page] + (debugState.Cx4.PC * 2)) & 0xFFFFFF;
AddressInfo addressInfo = _memoryManager->GetMemoryMappings()->GetAbsoluteAddress(addr);
if(addressInfo.Address >= 0) {
_disassembler->BuildCache(addressInfo, 0, CpuType::Cx4);
DisassemblyInfo disInfo = _disassembler->GetDisassemblyInfo(addressInfo);
_traceLogger->Log(CpuType::Cx4, debugState, disInfo);
}
}
}
void Debugger::SleepUntilResume(BreakSource source, MemoryOperationInfo *operation, int breakpointId)
{
if(_suspendRequestCount) {
@ -321,7 +340,10 @@ void Debugger::Step(CpuType cpuType, int32_t stepCount, StepType type)
case CpuType::Spc: _spcDebugger->Step(stepCount, type); break;
case CpuType::Sa1: _sa1Debugger->Step(stepCount, type); break;
case CpuType::Gsu: _gsuDebugger->Step(stepCount, type); break;
case CpuType::NecDsp: throw std::runtime_error("Step(): Unsupported CPU type.");
case CpuType::NecDsp:
case CpuType::Cx4:
throw std::runtime_error("Step(): Unsupported CPU type.");
}
break;
}
@ -368,6 +390,9 @@ void Debugger::GetState(DebugState &state, bool partialPpuState)
if(_cart->GetGsu()) {
state.Gsu = _cart->GetGsu()->GetState();
}
if(_cart->GetCx4()) {
state.Cx4 = _cart->GetCx4()->GetState();
}
}
AddressInfo Debugger::GetAbsoluteAddress(AddressInfo relAddress)
@ -491,7 +516,9 @@ shared_ptr<CallstackManager> Debugger::GetCallstackManager(CpuType cpuType)
case CpuType::Sa1: return _sa1Debugger->GetCallstackManager();
case CpuType::Gsu:
case CpuType::NecDsp: break;
case CpuType::NecDsp:
case CpuType::Cx4:
break;
}
throw std::runtime_error("GetCallstackManager() - Unsupported CPU type");
}

View file

@ -3,6 +3,7 @@
#include "CpuTypes.h"
#include "PpuTypes.h"
#include "DebugTypes.h"
#include "DebugUtilities.h"
class Console;
class Cpu;
@ -58,7 +59,7 @@ private:
shared_ptr<EventManager> _eventManager;
shared_ptr<LabelManager> _labelManager;
unique_ptr<ExpressionEvaluator> _watchExpEval[(int)CpuType::Gsu + 1];
unique_ptr<ExpressionEvaluator> _watchExpEval[(int)DebugUtilities::GetLastCpuType() + 1];
atomic<bool> _executionStopped;
atomic<uint32_t> _breakRequestCount;
@ -90,6 +91,7 @@ public:
void ProcessPpuCycle();
void ProcessNecDspExec(uint32_t addr, uint32_t value);
void ProcessCx4Exec();
template<CpuType type>
void ProcessInterrupt(uint32_t originalPc, uint32_t currentPc, bool forNmi);

View file

@ -131,6 +131,7 @@ vector<DisassemblyResult>& Disassembler::GetDisassemblyList(CpuType type)
case CpuType::Sa1: return _sa1Disassembly;
case CpuType::Gsu: return _gsuDisassembly;
case CpuType::NecDsp: break;
case CpuType::Cx4: break;
}
throw std::runtime_error("Disassembly::GetDisassemblyList(): Invalid cpu type");
}
@ -178,8 +179,6 @@ void Disassembler::SetDisassembleFlag(CpuType type)
_needDisassemble[(int)CpuType::Gsu] = true;
} else if(type == CpuType::Spc) {
_needDisassemble[(int)CpuType::Spc] = true;
} else {
throw std::runtime_error("Invalid cpu type");
}
}
@ -564,6 +563,7 @@ bool Disassembler::GetLineData(CpuType type, uint32_t lineIndex, CodeLineData &d
break;
}
case CpuType::Cx4:
case CpuType::NecDsp:
throw std::runtime_error("GetLineData - CPU type not supported");
}

View file

@ -7,6 +7,7 @@
#include "SpcDisUtils.h"
#include "GsuDisUtils.h"
#include "NecDspDisUtils.h"
#include "Cx4DisUtils.h"
#include "../Utilities/HexUtilities.h"
#include "../Utilities/FastString.h"
@ -55,6 +56,7 @@ void DisassemblyInfo::GetDisassembly(string &out, uint32_t memoryAddr, LabelMana
case CpuType::Spc: SpcDisUtils::GetDisassembly(*this, out, memoryAddr, labelManager); break;
case CpuType::NecDsp: NecDspDisUtils::GetDisassembly(*this, out, memoryAddr, labelManager); break;
case CpuType::Gsu: GsuDisUtils::GetDisassembly(*this, out, memoryAddr, labelManager); break;
case CpuType::Cx4: Cx4DisUtils::GetDisassembly(*this, out, memoryAddr, labelManager); break;
}
}
@ -68,7 +70,9 @@ int32_t DisassemblyInfo::GetEffectiveAddress(Console *console, void *cpuState)
case CpuType::Spc: return SpcDisUtils::GetEffectiveAddress(*this, console, *(SpcState*)cpuState);
case CpuType::Gsu:
case CpuType::NecDsp: return -1;
case CpuType::Cx4:
case CpuType::NecDsp:
return -1;
}
return -1;
}
@ -135,6 +139,7 @@ uint8_t DisassemblyInfo::GetOpSize(uint8_t opCode, uint8_t flags, CpuType type)
return 1;
case CpuType::NecDsp: return 4;
case CpuType::Cx4: return 2;
}
return 0;
}
@ -149,7 +154,9 @@ bool DisassemblyInfo::IsJumpToSub(uint8_t opCode, CpuType type)
case CpuType::Spc: return opCode == 0x3F || opCode == 0x0F; //JSR, BRK
case CpuType::Gsu:
case CpuType::NecDsp: return false;
case CpuType::NecDsp:
case CpuType::Cx4:
return false;
}
return false;
}
@ -165,7 +172,9 @@ bool DisassemblyInfo::IsReturnInstruction(uint8_t opCode, CpuType type)
case CpuType::Spc: return opCode == 0x6F || opCode == 0x7F;
case CpuType::Gsu:
case CpuType::NecDsp: return false;
case CpuType::NecDsp:
case CpuType::Cx4:
return false;
}
return false;
@ -196,7 +205,9 @@ bool DisassemblyInfo::UpdateCpuFlags(uint8_t &cpuFlags)
case CpuType::Gsu:
case CpuType::Spc:
case CpuType::NecDsp: return false;
case CpuType::NecDsp:
case CpuType::Cx4:
return false;
}
return false;

View file

@ -5,6 +5,7 @@
#include "Spc.h"
#include "NecDsp.h"
#include "Sa1.h"
#include "Cx4.h"
#include "Gsu.h"
#include "MemoryDumper.h"
#include "BaseCartridge.h"
@ -47,8 +48,8 @@ void MemoryDumper::SetMemoryState(SnesMemoryType type, uint8_t *buffer, uint32_t
case SnesMemoryType::DspDataRam: memcpy(_cartridge->GetDsp()->DebugGetDataRam(), buffer, length); break;
case SnesMemoryType::Sa1InternalRam: memcpy(_cartridge->GetSa1()->DebugGetInternalRam(), buffer, length); break;
case SnesMemoryType::GsuWorkRam: memcpy(_cartridge->GetGsu()->DebugGetWorkRam(), buffer, length); break;
case SnesMemoryType::Cx4DataRam: memcpy(_cartridge->GetCx4()->DebugGetDataRam(), buffer, length); break;
}
}
@ -75,8 +76,8 @@ uint32_t MemoryDumper::GetMemorySize(SnesMemoryType type)
case SnesMemoryType::DspDataRam: return _cartridge->GetDsp() ? _cartridge->GetDsp()->DebugGetDataRamSize() : 0;
case SnesMemoryType::Sa1InternalRam: return _cartridge->GetSa1() ? _cartridge->GetSa1()->DebugGetInternalRamSize() : 0;;
case SnesMemoryType::GsuWorkRam: return _cartridge->GetGsu() ? _cartridge->GetGsu()->DebugGetWorkRamSize() : 0;;
case SnesMemoryType::Cx4DataRam: return _cartridge->GetCx4() ? _cartridge->GetCx4()->DebugGetDataRamSize() : 0;;
}
}
@ -123,8 +124,8 @@ void MemoryDumper::GetMemoryState(SnesMemoryType type, uint8_t *buffer)
case SnesMemoryType::DspDataRam: memcpy(buffer, _cartridge->GetDsp()->DebugGetDataRam(), _cartridge->GetDsp()->DebugGetDataRamSize()); break;
case SnesMemoryType::Sa1InternalRam: memcpy(buffer, _cartridge->GetSa1()->DebugGetInternalRam(), _cartridge->GetSa1()->DebugGetInternalRamSize()); break;
case SnesMemoryType::GsuWorkRam: memcpy(buffer, _cartridge->GetGsu()->DebugGetWorkRam(), _cartridge->GetGsu()->DebugGetWorkRamSize()); break;
case SnesMemoryType::Cx4DataRam: memcpy(buffer, _cartridge->GetCx4()->DebugGetDataRam(), _cartridge->GetCx4()->DebugGetDataRamSize()); break;
}
}
@ -165,8 +166,8 @@ void MemoryDumper::SetMemoryValue(SnesMemoryType memoryType, uint32_t address, u
case SnesMemoryType::DspDataRam: _cartridge->GetDsp()->DebugGetDataRam()[address] = value;
case SnesMemoryType::Sa1InternalRam: _cartridge->GetSa1()->DebugGetInternalRam()[address] = value;
case SnesMemoryType::GsuWorkRam: _cartridge->GetGsu()->DebugGetWorkRam()[address] = value;
case SnesMemoryType::Cx4DataRam: _cartridge->GetCx4()->DebugGetDataRam()[address] = value;
}
}
@ -199,8 +200,8 @@ uint8_t MemoryDumper::GetMemoryValue(SnesMemoryType memoryType, uint32_t address
case SnesMemoryType::DspDataRam: return _cartridge->GetDsp()->DebugGetDataRam()[address];
case SnesMemoryType::Sa1InternalRam: return _cartridge->GetSa1()->DebugGetInternalRam()[address];
case SnesMemoryType::GsuWorkRam: return _cartridge->GetGsu()->DebugGetWorkRam()[address];
case SnesMemoryType::Cx4DataRam: return _cartridge->GetCx4()->DebugGetDataRam()[address];
}
}

View file

@ -13,6 +13,7 @@
#include "EmuSettings.h"
#include "Sa1.h"
#include "Gsu.h"
#include "Cx4.h"
#include "BaseCoprocessor.h"
#include "../Utilities/Serializer.h"
#include "../Utilities/HexUtilities.h"
@ -198,6 +199,8 @@ void MemoryManager::SyncCoprocessors()
_cart->GetGsu()->Run();
} else if(_cart->GetSa1()) {
_cart->GetSa1()->Run();
} else if(_cart->GetCx4()) {
_cart->GetCx4()->Run();
}
}
}

View file

@ -66,6 +66,7 @@ void TraceLogger::SetOptions(TraceLoggerOptions options)
_logCpu[(int)CpuType::NecDsp] = options.LogNecDsp;
_logCpu[(int)CpuType::Sa1] = options.LogSa1;
_logCpu[(int)CpuType::Gsu] = options.LogGsu;
_logCpu[(int)CpuType::Cx4] = options.LogCx4;
string condition = _options.Condition;
string format = _options.Format;
@ -84,6 +85,7 @@ void TraceLogger::SetOptions(TraceLoggerOptions options)
ParseFormatString(_spcRowParts, "[PC,4h] [ByteCode,11h] [Disassembly][EffectiveAddress] [MemoryValue,h][Align,48] A:[A,2h] X:[X,2h] Y:[Y,2h] S:[SP,2h] P:[P,8] H:[Cycle,3] V:[Scanline,3]");
ParseFormatString(_dspRowParts, "[PC,4h] [ByteCode,11h] [Disassembly] [Align,65] [A,2h] S:[SP,2h] H:[Cycle,3] V:[Scanline,3]");
ParseFormatString(_gsuRowParts, "[PC,6h] [ByteCode,11h] [Disassembly] [Align,50] SRC:[X,2] DST:[Y,2] R0:[A,2h] H:[Cycle,3] V:[Scanline,3]");
ParseFormatString(_cx4RowParts, "[PC,6h] [ByteCode,11h] [Disassembly] [Align,45] [A,2h] H:[Cycle,3] V:[Scanline,3]");
}
void TraceLogger::ParseFormatString(vector<RowPart> &rowParts, string format)
@ -418,6 +420,44 @@ void TraceLogger::GetTraceRow(string &output, GsuState &gsuState, PpuState &ppuS
output += _options.UseWindowsEol ? "\r\n" : "\n";
}
void TraceLogger::GetTraceRow(string &output, Cx4State &cx4State, PpuState &ppuState, DisassemblyInfo &disassemblyInfo)
{
int originalSize = (int)output.size();
uint32_t pcAddress = (cx4State.Cache.Address[cx4State.Cache.Page] + (cx4State.PC * 2)) & 0xFFFFFF;
for(RowPart& rowPart : _cx4RowParts) {
switch(rowPart.DataType) {
case RowDataType::Text: output += rowPart.Text; break;
case RowDataType::ByteCode: WriteByteCode(disassemblyInfo, rowPart, output); break;
case RowDataType::Disassembly: WriteDisassembly(disassemblyInfo, rowPart, 0, pcAddress, output); break;
case RowDataType::Align: WriteAlign(originalSize, rowPart, output); break;
case RowDataType::PC: WriteValue(output, HexUtilities::ToHex24(pcAddress), rowPart); break;
case RowDataType::A:
output += " A:" + HexUtilities::ToHex24(cx4State.A);
output += string(" ") + (cx4State.Carry ? "C" : "c") + (cx4State.Zero ? "Z" : "z") + (cx4State.Overflow ? "V" : "v") + (cx4State.Negative ? "N" : "n");
output += " PC:" + HexUtilities::ToHex(cx4State.PC);
output += " MAR:" + HexUtilities::ToHex24(cx4State.MemoryAddressReg);
output += " MDR:" + HexUtilities::ToHex24(cx4State.MemoryDataReg);
output += " DPR:" + HexUtilities::ToHex24(cx4State.DataPointerReg);
output += " ML:" + HexUtilities::ToHex24((uint32_t)cx4State.Mult & 0xFFFFFF);
output += " MH:" + HexUtilities::ToHex24((uint32_t)(cx4State.Mult >> 24) & 0xFFFFFF);
for(int i = 0; i < 16; i++) {
output += " R" + std::to_string(i) + ":" + HexUtilities::ToHex24(cx4State.Regs[i]);
}
break;
case RowDataType::Cycle: WriteValue(output, ppuState.Cycle, rowPart); break;
case RowDataType::Scanline: WriteValue(output, ppuState.Scanline, rowPart); break;
case RowDataType::HClock: WriteValue(output, ppuState.HClock, rowPart); break;
case RowDataType::FrameCount: WriteValue(output, ppuState.FrameCount, rowPart); break;
default: break;
}
}
output += _options.UseWindowsEol ? "\r\n" : "\n";
}
/*
bool TraceLogger::ConditionMatches(DebugState &state, DisassemblyInfo &disassemblyInfo, OperationInfo &operationInfo)
{
@ -445,6 +485,7 @@ void TraceLogger::GetTraceRow(string &output, CpuType cpuType, DisassemblyInfo &
case CpuType::NecDsp: GetTraceRow(output, state.Dsp, state.Ppu, disassemblyInfo); break;
case CpuType::Sa1: GetTraceRow(output, state.Sa1, state.Ppu, disassemblyInfo, SnesMemoryType::Sa1Memory); break;
case CpuType::Gsu: GetTraceRow(output, state.Gsu, state.Ppu, disassemblyInfo); break;
case CpuType::Cx4: GetTraceRow(output, state.Cx4, state.Ppu, disassemblyInfo); break;
}
}
@ -533,12 +574,14 @@ const char* TraceLogger::GetExecutionTrace(uint32_t lineCount)
continue;
}
DebugState &state = _stateCacheCopy[index];
switch(cpuType) {
case CpuType::Cpu: _executionTrace += "\x2\x1" + HexUtilities::ToHex24((_stateCacheCopy[index].Cpu.K << 16) | _stateCacheCopy[index].Cpu.PC) + "\x1"; break;
case CpuType::Spc: _executionTrace += "\x3\x1" + HexUtilities::ToHex(_stateCacheCopy[index].Spc.PC) + "\x1"; break;
case CpuType::NecDsp: _executionTrace += "\x4\x1" + HexUtilities::ToHex(_stateCacheCopy[index].Dsp.PC) + "\x1"; break;
case CpuType::Sa1: _executionTrace += "\x4\x1" + HexUtilities::ToHex24((_stateCacheCopy[index].Sa1.K << 16) | _stateCacheCopy[index].Sa1.PC) + "\x1"; break;
case CpuType::Gsu: _executionTrace += "\x4\x1" + HexUtilities::ToHex24((_stateCacheCopy[index].Gsu.ProgramBank << 16) | _stateCacheCopy[index].Gsu.R[15]) + "\x1"; break;
case CpuType::Cpu: _executionTrace += "\x2\x1" + HexUtilities::ToHex24((state.Cpu.K << 16) | state.Cpu.PC) + "\x1"; break;
case CpuType::Spc: _executionTrace += "\x3\x1" + HexUtilities::ToHex(state.Spc.PC) + "\x1"; break;
case CpuType::NecDsp: _executionTrace += "\x4\x1" + HexUtilities::ToHex(state.Dsp.PC) + "\x1"; break;
case CpuType::Sa1: _executionTrace += "\x4\x1" + HexUtilities::ToHex24((state.Sa1.K << 16) | state.Sa1.PC) + "\x1"; break;
case CpuType::Gsu: _executionTrace += "\x4\x1" + HexUtilities::ToHex24((state.Gsu.ProgramBank << 16) | state.Gsu.R[15]) + "\x1"; break;
case CpuType::Cx4: _executionTrace += "\x4\x1" + HexUtilities::ToHex24((state.Cx4.Cache.Address[state.Cx4.Cache.Page] + (state.Cx4.PC * 2)) & 0xFFFFFF) + "\x1"; break;
}
string byteCode;

View file

@ -5,6 +5,7 @@
#include "SpcTypes.h"
#include "DebugTypes.h"
#include "DisassemblyInfo.h"
#include "DebugUtilities.h"
#include "../Utilities/SimpleLock.h"
class Console;
@ -20,6 +21,7 @@ struct TraceLoggerOptions
bool LogNecDsp;
bool LogSa1;
bool LogGsu;
bool LogCx4;
bool ShowExtraInfo;
bool IndentCode;
@ -82,8 +84,9 @@ private:
vector<RowPart> _spcRowParts;
vector<RowPart> _dspRowParts;
vector<RowPart> _gsuRowParts;
vector<RowPart> _cx4RowParts;
bool _logCpu[(int)CpuType::Gsu + 1] = {};
bool _logCpu[(int)DebugUtilities::GetLastCpuType() + 1] = {};
bool _pendingLog;
//CpuState _lastState;
@ -119,6 +122,7 @@ private:
void GetTraceRow(string &output, SpcState &cpuState, PpuState &ppuState, DisassemblyInfo &disassemblyInfo);
void GetTraceRow(string &output, NecDspState &cpuState, PpuState &ppuState, DisassemblyInfo &disassemblyInfo);
void GetTraceRow(string &output, GsuState &gsuState, PpuState &ppuState, DisassemblyInfo &disassemblyInfo);
void GetTraceRow(string &output, Cx4State &cx4State, PpuState &ppuState, DisassemblyInfo &disassemblyInfo);
template<typename T> void WriteValue(string &output, T value, RowPart& rowPart);

View file

@ -37,6 +37,9 @@ SOURCES_CXX := $(LIBRETRO_DIR)/libretro.cpp \
$(CORE_DIR)/Cpu.cpp \
$(CORE_DIR)/CpuDebugger.cpp \
$(CORE_DIR)/CpuDisUtils.cpp \
$(CORE_DIR)/Cx4.cpp \
$(CORE_DIR)/Cx4.Instructions.cpp \
$(CORE_DIR)/Cx4DisUtils.cpp \
$(CORE_DIR)/Debugger.cpp \
$(CORE_DIR)/DebugHud.cpp \
$(CORE_DIR)/DebugStats.cpp \

View file

@ -50,6 +50,7 @@ namespace Mesen.GUI.Config
public bool LogNecDsp;
public bool LogSa1;
public bool LogGsu;
public bool LogCx4;
public bool ShowByteCode;
public bool ShowRegisters;

View file

@ -179,6 +179,11 @@ namespace Mesen.GUI.Debugger
cboMemoryType.Items.Add(ResourceHelper.GetEnumText(SnesMemoryType.GsuWorkRam));
}
if(DebugApi.GetMemorySize(SnesMemoryType.Cx4DataRam) > 0) {
cboMemoryType.Items.Add("-");
cboMemoryType.Items.Add(ResourceHelper.GetEnumText(SnesMemoryType.Cx4DataRam));
}
cboMemoryType.SelectedIndex = 0;
cboMemoryType.SetEnumValue(originalValue);
cboMemoryType.SelectedIndexChanged += this.cboMemoryType_SelectedIndexChanged;

View file

@ -67,6 +67,7 @@ namespace Mesen.GUI.Debugger
this.chkLogCpu = new System.Windows.Forms.CheckBox();
this.chkLogSpc = new System.Windows.Forms.CheckBox();
this.chkLogNecDsp = new System.Windows.Forms.CheckBox();
this.chkLogGsu = new System.Windows.Forms.CheckBox();
this.btnClearLog = new System.Windows.Forms.Button();
this.tableLayoutPanel3 = new System.Windows.Forms.TableLayoutPanel();
this.grpExecutionLog = new System.Windows.Forms.GroupBox();
@ -93,7 +94,7 @@ namespace Mesen.GUI.Debugger
this.mnuAutoRefresh = new System.Windows.Forms.ToolStripMenuItem();
this.toolStripMenuItem1 = new System.Windows.Forms.ToolStripSeparator();
this.mnuRefresh = new System.Windows.Forms.ToolStripMenuItem();
this.chkLogGsu = new System.Windows.Forms.CheckBox();
this.chkLogCx4 = new System.Windows.Forms.CheckBox();
this.tableLayoutPanel1.SuspendLayout();
this.grpLogOptions.SuspendLayout();
this.tableLayoutPanel2.SuspendLayout();
@ -554,18 +555,21 @@ namespace Mesen.GUI.Debugger
//
// tableLayoutPanel6
//
this.tableLayoutPanel6.ColumnCount = 5;
this.tableLayoutPanel6.ColumnCount = 7;
this.tableLayoutPanel2.SetColumnSpan(this.tableLayoutPanel6, 5);
this.tableLayoutPanel6.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle());
this.tableLayoutPanel6.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle());
this.tableLayoutPanel6.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle());
this.tableLayoutPanel6.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle());
this.tableLayoutPanel6.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle());
this.tableLayoutPanel6.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle());
this.tableLayoutPanel6.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F));
this.tableLayoutPanel6.Controls.Add(this.chkLogSa1, 3, 0);
this.tableLayoutPanel6.Controls.Add(this.chkLogCpu, 0, 0);
this.tableLayoutPanel6.Controls.Add(this.chkLogSpc, 1, 0);
this.tableLayoutPanel6.Controls.Add(this.chkLogNecDsp, 2, 0);
this.tableLayoutPanel6.Controls.Add(this.chkLogGsu, 4, 0);
this.tableLayoutPanel6.Controls.Add(this.chkLogCx4, 5, 0);
this.tableLayoutPanel6.Dock = System.Windows.Forms.DockStyle.Fill;
this.tableLayoutPanel6.Location = new System.Drawing.Point(81, 0);
this.tableLayoutPanel6.Margin = new System.Windows.Forms.Padding(0);
@ -625,6 +629,18 @@ namespace Mesen.GUI.Debugger
this.chkLogNecDsp.Text = "DSP-n";
this.chkLogNecDsp.UseVisualStyleBackColor = true;
//
// chkLogGsu
//
this.chkLogGsu.AutoSize = true;
this.chkLogGsu.Checked = true;
this.chkLogGsu.CheckState = System.Windows.Forms.CheckState.Checked;
this.chkLogGsu.Location = new System.Drawing.Point(267, 3);
this.chkLogGsu.Name = "chkLogGsu";
this.chkLogGsu.Size = new System.Drawing.Size(49, 16);
this.chkLogGsu.TabIndex = 26;
this.chkLogGsu.Text = "GSU";
this.chkLogGsu.UseVisualStyleBackColor = true;
//
// btnClearLog
//
this.btnClearLog.Location = new System.Drawing.Point(205, 3);
@ -860,17 +876,17 @@ namespace Mesen.GUI.Debugger
this.mnuRefresh.Text = "Refresh";
this.mnuRefresh.Click += new System.EventHandler(this.mnuRefresh_Click);
//
// chkLogGsu
// chkLogCx4
//
this.chkLogGsu.AutoSize = true;
this.chkLogGsu.Checked = true;
this.chkLogGsu.CheckState = System.Windows.Forms.CheckState.Checked;
this.chkLogGsu.Location = new System.Drawing.Point(267, 3);
this.chkLogGsu.Name = "chkLogGsu";
this.chkLogGsu.Size = new System.Drawing.Size(49, 16);
this.chkLogGsu.TabIndex = 26;
this.chkLogGsu.Text = "GSU";
this.chkLogGsu.UseVisualStyleBackColor = true;
this.chkLogCx4.AutoSize = true;
this.chkLogCx4.Checked = true;
this.chkLogCx4.CheckState = System.Windows.Forms.CheckState.Checked;
this.chkLogCx4.Location = new System.Drawing.Point(322, 3);
this.chkLogCx4.Name = "chkLogCx4";
this.chkLogCx4.Size = new System.Drawing.Size(46, 16);
this.chkLogCx4.TabIndex = 27;
this.chkLogCx4.Text = "CX4";
this.chkLogCx4.UseVisualStyleBackColor = true;
//
// frmTraceLogger
//
@ -974,5 +990,6 @@ namespace Mesen.GUI.Debugger
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel6;
private System.Windows.Forms.CheckBox chkLogSa1;
private System.Windows.Forms.CheckBox chkLogGsu;
private System.Windows.Forms.CheckBox chkLogCx4;
}
}

View file

@ -25,6 +25,7 @@ namespace Mesen.GUI.Debugger
private UInt64 _previousCycleCount;
private volatile bool _refreshRunning;
private bool _initialized;
private NotificationListener _notifListener;
public frmTraceLogger()
{
@ -50,6 +51,7 @@ namespace Mesen.GUI.Debugger
_entityBinder.AddBinding(nameof(TraceLoggerOptions.LogNecDsp), chkLogNecDsp);
_entityBinder.AddBinding(nameof(TraceLoggerOptions.LogSa1), chkLogSa1);
_entityBinder.AddBinding(nameof(TraceLoggerOptions.LogGsu), chkLogGsu);
_entityBinder.AddBinding(nameof(TraceLoggerOptions.LogCx4), chkLogCx4);
_entityBinder.AddBinding(nameof(TraceLoggerOptions.ShowByteCode), chkShowByteCode);
//_entityBinder.AddBinding(nameof(TraceLoggerOptions.ShowCpuCycles), chkShowCpuCycles);
@ -94,6 +96,9 @@ namespace Mesen.GUI.Debugger
"[Align,50]: Align is a special tag that is useful when trying to align some content. [Align,50] will make the next tag start on column 50."
);
_notifListener = new NotificationListener();
_notifListener.OnNotification += OnNotificationReceived;
this._initialized = true;
}
@ -124,6 +129,8 @@ namespace Mesen.GUI.Debugger
protected override void OnFormClosing(FormClosingEventArgs e)
{
_notifListener?.Dispose();
tmrUpdateLog.Stop();
while(_refreshRunning) {
System.Threading.Thread.Sleep(50);
@ -154,6 +161,16 @@ namespace Mesen.GUI.Debugger
}
}
private void OnNotificationReceived(NotificationEventArgs e)
{
switch(e.NotificationType) {
case ConsoleNotificationType.GameLoaded:
//Configuration is lost when debugger is restarted (when switching game or power cycling)
this.Invoke((Action)(() => SetOptions()));
break;
}
}
protected void UpdateFormatOptions()
{
if(!chkOverrideFormat.Checked) {
@ -223,6 +240,7 @@ namespace Mesen.GUI.Debugger
interopOptions.LogNecDsp = !disableLogging && options.LogNecDsp;
interopOptions.LogSa1 = !disableLogging && options.LogSa1;
interopOptions.LogGsu = !disableLogging && options.LogGsu;
interopOptions.LogCx4 = !disableLogging && options.LogCx4;
interopOptions.IndentCode = options.IndentCode;
interopOptions.ShowExtraInfo = options.ShowExtraInfo;
interopOptions.UseLabels = options.UseLabels;

View file

@ -962,6 +962,7 @@
<Value ID="DspDataRam">DSP-n Data RAM</Value>
<Value ID="Sa1InternalRam">SA-1 IRAM</Value>
<Value ID="GsuWorkRam">GSU Work RAM</Value>
<Value ID="Cx4DataRam">CX4 Data RAM</Value>
<Value ID="Register">Register</Value>
</Enum>
<Enum ID="TileFormat">

View file

@ -176,6 +176,7 @@ namespace Mesen.GUI
DspDataRam,
Sa1InternalRam,
GsuWorkRam,
Cx4DataRam,
Register,
}
@ -653,6 +654,7 @@ namespace Mesen.GUI
[MarshalAs(UnmanagedType.I1)] public bool LogNecDsp;
[MarshalAs(UnmanagedType.I1)] public bool LogSa1;
[MarshalAs(UnmanagedType.I1)] public bool LogGsu;
[MarshalAs(UnmanagedType.I1)] public bool LogCx4;
[MarshalAs(UnmanagedType.I1)] public bool ShowExtraInfo;
[MarshalAs(UnmanagedType.I1)] public bool IndentCode;