Mesen-SX/Core/Gsu.cpp
2020-12-19 23:30:09 +03:00

1039 lines
19 KiB
C++

#include "stdafx.h"
#include "Gsu.h"
#include "Console.h"
#include "Cpu.h"
#include "MemoryManager.h"
#include "BaseCartridge.h"
#include "RamHandler.h"
#include "GsuRomHandler.h"
#include "GsuRamHandler.h"
#include "EmuSettings.h"
#include "MessageManager.h"
#include "BatteryManager.h"
#include "../Utilities/HexUtilities.h"
Gsu::Gsu(Console* console, uint32_t gsuRamSize) : BaseCoprocessor(SnesMemoryType::Register)
{
_console = console;
_memoryManager = console->GetMemoryManager().get();
_cpu = console->GetCpu().get();
_memoryType = SnesMemoryType::Register;
_settings = _console->GetSettings().get();
_clockMultiplier = _settings->GetEmulationConfig().GsuClockSpeed / 100;
_state = {};
_state.ProgramReadBuffer = 0x01; //Run a NOP on first cycle
_settings->InitializeRam(_cache, 512);
_gsuRamSize = gsuRamSize;
_gsuRam = new uint8_t[_gsuRamSize];
_settings->InitializeRam(_gsuRam, _gsuRamSize);
for (uint32_t i = 0; i < _gsuRamSize / 0x1000; i++)
{
_gsuRamHandlers.push_back(
unique_ptr<IMemoryHandler>(new RamHandler(_gsuRam, i * 0x1000, _gsuRamSize, SnesMemoryType::GsuWorkRam)));
_gsuCpuRamHandlers.push_back(unique_ptr<IMemoryHandler>(new GsuRamHandler(_state, _gsuRamHandlers.back().get())));
}
//CPU mappings
MemoryMappings* cpuMappings = _memoryManager->GetMemoryMappings();
vector<unique_ptr<IMemoryHandler>>& prgRomHandlers = _console->GetCartridge()->GetPrgRomHandlers();
for (unique_ptr<IMemoryHandler>& handler : prgRomHandlers)
{
_gsuCpuRomHandlers.push_back(unique_ptr<IMemoryHandler>(new GsuRomHandler(_state, handler.get())));
}
//GSU registers in CPU memory space
cpuMappings->RegisterHandler(0x00, 0x3F, 0x3000, 0x3FFF, this);
cpuMappings->RegisterHandler(0x80, 0xBF, 0x3000, 0x3FFF, this);
for (int i = 0; i < 0x3F; i++)
{
cpuMappings->RegisterHandler(i, i, 0x6000, 0x7FFF, _gsuCpuRamHandlers);
cpuMappings->RegisterHandler(i + 0x80, i + 0x80, 0x6000, 0x7FFF, _gsuCpuRamHandlers);
}
cpuMappings->RegisterHandler(0x70, 0x71, 0x0000, 0xFFFF, _gsuCpuRamHandlers);
cpuMappings->RegisterHandler(0xF0, 0xF1, 0x0000, 0xFFFF, _gsuCpuRamHandlers);
cpuMappings->RegisterHandler(0x00, 0x3F, 0x8000, 0xFFFF, _gsuCpuRomHandlers);
cpuMappings->RegisterHandler(0x80, 0xBF, 0x8000, 0xFFFF, _gsuCpuRomHandlers);
cpuMappings->RegisterHandler(0x40, 0x5F, 0x0000, 0xFFFF, _gsuCpuRomHandlers);
cpuMappings->RegisterHandler(0xC0, 0xDF, 0x0000, 0xFFFF, _gsuCpuRomHandlers);
//GSU mappings
_mappings.RegisterHandler(0x00, 0x3F, 0x8000, 0xFFFF, prgRomHandlers);
_mappings.RegisterHandler(0x00, 0x3F, 0x0000, 0x7FFF, prgRomHandlers); //Mirror
_mappings.RegisterHandler(0x40, 0x5F, 0x0000, 0xFFFF, prgRomHandlers);
_mappings.RegisterHandler(0x70, 0x71, 0x0000, 0xFFFF, _gsuRamHandlers);
}
Gsu::~Gsu()
{
delete[] _gsuRam;
}
void Gsu::ProcessEndOfFrame()
{
uint8_t clockMultiplier = _settings->GetEmulationConfig().GsuClockSpeed / 100;
if (_clockMultiplier != clockMultiplier)
{
_state.CycleCount = _state.CycleCount / _clockMultiplier * clockMultiplier;
_clockMultiplier = clockMultiplier;
}
}
void Gsu::Run()
{
uint64_t targetCycle = _memoryManager->GetMasterClock() * _clockMultiplier;
while (!_stopped && _state.CycleCount < targetCycle)
{
Exec();
}
if (targetCycle > _state.CycleCount)
{
Step(targetCycle - _state.CycleCount);
}
}
void Gsu::Exec()
{
uint8_t opCode = ReadOpCode();
switch (opCode)
{
case 0x00: STOP();
break;
case 0x01: NOP();
break;
case 0x02: CACHE();
break;
case 0x03: LSR();
break;
case 0x04: ROL();
break;
case 0x05: BRA();
break;
case 0x06: BLT();
break;
case 0x07: BGE();
break;
case 0x08: BNE();
break;
case 0x09: BEQ();
break;
case 0x0A: BPL();
break;
case 0x0B: BMI();
break;
case 0x0C: BCC();
break;
case 0x0D: BCS();
break;
case 0x0E: BCV();
break;
case 0x0F: BVS();
break;
case 0x10:
case 0x11:
case 0x12:
case 0x13:
case 0x14:
case 0x15:
case 0x16:
case 0x17:
case 0x18:
case 0x19:
case 0x1A:
case 0x1B:
case 0x1C:
case 0x1D:
case 0x1E:
case 0x1F:
TO(opCode & 0x0F);
break;
case 0x20:
case 0x21:
case 0x22:
case 0x23:
case 0x24:
case 0x25:
case 0x26:
case 0x27:
case 0x28:
case 0x29:
case 0x2A:
case 0x2B:
case 0x2C:
case 0x2D:
case 0x2E:
case 0x2F:
WITH(opCode & 0x0F);
break;
case 0x30:
case 0x31:
case 0x32:
case 0x33:
case 0x34:
case 0x35:
case 0x36:
case 0x37:
case 0x38:
case 0x39:
case 0x3A:
case 0x3B:
STORE(opCode & 0x0F);
break;
case 0x3C: LOOP();
break;
case 0x3D: ALT1();
break;
case 0x3E: ALT2();
break;
case 0x3F: ALT3();
break;
case 0x40:
case 0x41:
case 0x42:
case 0x43:
case 0x44:
case 0x45:
case 0x46:
case 0x47:
case 0x48:
case 0x49:
case 0x4A:
case 0x4B:
LOAD(opCode & 0x0F);
break;
case 0x4C: PlotRpix();
break;
case 0x4D: SWAP();
break;
case 0x4E: ColorCMode();
break;
case 0x4F: NOT();
break;
case 0x50:
case 0x51:
case 0x52:
case 0x53:
case 0x54:
case 0x55:
case 0x56:
case 0x57:
case 0x58:
case 0x59:
case 0x5A:
case 0x5B:
case 0x5C:
case 0x5D:
case 0x5E:
case 0x5F:
Add(opCode & 0x0F);
break;
case 0x60:
case 0x61:
case 0x62:
case 0x63:
case 0x64:
case 0x65:
case 0x66:
case 0x67:
case 0x68:
case 0x69:
case 0x6A:
case 0x6B:
case 0x6C:
case 0x6D:
case 0x6E:
case 0x6F:
SubCompare(opCode & 0x0F);
break;
case 0x70: MERGE();
break;
case 0x71:
case 0x72:
case 0x73:
case 0x74:
case 0x75:
case 0x76:
case 0x77:
case 0x78:
case 0x79:
case 0x7A:
case 0x7B:
case 0x7C:
case 0x7D:
case 0x7E:
case 0x7F:
AndBitClear(opCode & 0x0F);
break;
case 0x80:
case 0x81:
case 0x82:
case 0x83:
case 0x84:
case 0x85:
case 0x86:
case 0x87:
case 0x88:
case 0x89:
case 0x8A:
case 0x8B:
case 0x8C:
case 0x8D:
case 0x8E:
case 0x8F:
MULT(opCode & 0x0F);
break;
case 0x90: SBK();
break;
case 0x91: LINK(1);
break;
case 0x92: LINK(2);
break;
case 0x93: LINK(3);
break;
case 0x94: LINK(4);
break;
case 0x95: SignExtend();
break;
case 0x96: ASR();
break;
case 0x97: ROR();
break;
case 0x98:
case 0x99:
case 0x9A:
case 0x9B:
case 0x9C:
case 0x9D:
JMP(opCode & 0x0F);
break;
case 0x9E: LOB();
break;
case 0x9F: FMultLMult();
break;
case 0xA0:
case 0xA1:
case 0xA2:
case 0xA3:
case 0xA4:
case 0xA5:
case 0xA6:
case 0xA7:
case 0xA8:
case 0xA9:
case 0xAA:
case 0xAB:
case 0xAC:
case 0xAD:
case 0xAE:
case 0xAF:
IbtSmsLms(opCode & 0x0F);
break;
case 0xB0:
case 0xB1:
case 0xB2:
case 0xB3:
case 0xB4:
case 0xB5:
case 0xB6:
case 0xB7:
case 0xB8:
case 0xB9:
case 0xBA:
case 0xBB:
case 0xBC:
case 0xBD:
case 0xBE:
case 0xBF:
FROM(opCode & 0x0F);
break;
case 0xC0: HIB();
break;
case 0xC1:
case 0xC2:
case 0xC3:
case 0xC4:
case 0xC5:
case 0xC6:
case 0xC7:
case 0xC8:
case 0xC9:
case 0xCA:
case 0xCB:
case 0xCC:
case 0xCD:
case 0xCE:
case 0xCF:
OrXor(opCode & 0x0F);
break;
case 0xD0:
case 0xD1:
case 0xD2:
case 0xD3:
case 0xD4:
case 0xD5:
case 0xD6:
case 0xD7:
case 0xD8:
case 0xD9:
case 0xDA:
case 0xDB:
case 0xDC:
case 0xDD:
case 0xDE:
INC(opCode & 0x0F);
break;
case 0xDF: GetCRamBRomB();
break;
case 0xE0:
case 0xE1:
case 0xE2:
case 0xE3:
case 0xE4:
case 0xE5:
case 0xE6:
case 0xE7:
case 0xE8:
case 0xE9:
case 0xEA:
case 0xEB:
case 0xEC:
case 0xED:
case 0xEE:
DEC(opCode & 0x0F);
break;
case 0xEF: GETB();
break;
case 0xF0:
case 0xF1:
case 0xF2:
case 0xF3:
case 0xF4:
case 0xF5:
case 0xF6:
case 0xF7:
case 0xF8:
case 0xF9:
case 0xFA:
case 0xFB:
case 0xFC:
case 0xFD:
case 0xFE:
case 0xFF:
IwtLmSm(opCode & 0x0F);
break;
}
_console->ProcessMemoryRead<CpuType::Gsu>(_lastOpAddr, _state.ProgramReadBuffer, MemoryOperationType::ExecOpCode);
if (!_r15Changed)
{
_state.R[15]++;
}
else
{
_r15Changed = false;
}
}
uint8_t Gsu::ReadGsu(uint32_t addr, MemoryOperationType opType)
{
IMemoryHandler* handler = _mappings.GetHandler(addr);
uint8_t value;
if (handler)
{
value = handler->Read(addr);
}
else
{
//TODO: Open bus?
value = 0;
LogDebug("[Debug] GSU - Missing read handler: " + HexUtilities::ToHex(addr));
}
_console->ProcessMemoryRead<CpuType::Gsu>(addr, value, opType);
return value;
}
void Gsu::WriteGsu(uint32_t addr, uint8_t value, MemoryOperationType opType)
{
IMemoryHandler* handler = _mappings.GetHandler(addr);
if (handler)
{
handler->Write(addr, value);
}
else
{
LogDebug("[Debug] GSU - Missing write handler: " + HexUtilities::ToHex(addr));
}
_console->ProcessMemoryWrite<CpuType::Gsu>(addr, value, opType);
}
void Gsu::InitProgramCache(uint16_t cacheAddr)
{
uint16_t dest = (cacheAddr & 0x01F0);
if (_state.ProgramBank <= 0x5F)
{
WaitRomOperation();
WaitForRomAccess();
}
else
{
WaitRamOperation();
WaitForRamAccess();
}
uint32_t srcBaseAddr = (_state.ProgramBank << 16) + _state.CacheBase + dest;
for (int i = 0; i < 16; i++)
{
_cache[dest + i] = ReadGsu(srcBaseAddr + i, MemoryOperationType::Read);
}
Step(_state.ClockSelect ? 5 * 16 : 6 * 16);
_cacheValid[cacheAddr >> 4] = true;
}
uint8_t Gsu::ReadOperand()
{
uint8_t result = _state.ProgramReadBuffer;
_state.R[15]++;
_state.ProgramReadBuffer = ReadProgramByte(MemoryOperationType::Read);
return result;
}
uint8_t Gsu::ReadOpCode()
{
uint8_t result = _state.ProgramReadBuffer;
_state.ProgramReadBuffer = ReadProgramByte(MemoryOperationType::Read);
return result;
}
uint8_t Gsu::ReadProgramByte(MemoryOperationType opType)
{
_lastOpAddr = (_state.ProgramBank << 16) | _state.R[15];
uint16_t cacheAddr = _state.R[15] - _state.CacheBase;
if (cacheAddr < 512)
{
if (!_cacheValid[cacheAddr >> 4])
{
InitProgramCache(cacheAddr & 0xFFF0);
}
Step(_state.ClockSelect ? 1 : 2);
_console->ProcessMemoryRead<CpuType::Gsu>(_lastOpAddr, _cache[cacheAddr], opType);
return _cache[cacheAddr];
}
else
{
if (_state.ProgramBank <= 0x5F)
{
WaitRomOperation();
WaitForRomAccess();
}
else
{
WaitRamOperation();
WaitForRamAccess();
}
Step(_state.ClockSelect ? 5 : 6);
return ReadGsu(_lastOpAddr, opType);
}
}
uint16_t Gsu::ReadSrcReg()
{
return _state.R[_state.SrcReg];
}
void Gsu::WriteDestReg(uint16_t value)
{
WriteRegister(_state.DestReg, value);
}
void Gsu::WriteRegister(uint8_t reg, uint16_t value)
{
_state.R[reg] = value;
if (reg == 14)
{
_state.SFR.RomReadPending = true;
_state.RomDelay = _state.ClockSelect ? 5 : 6;
}
else if (reg == 15)
{
_r15Changed = true;
}
}
void Gsu::ResetFlags()
{
_state.SFR.Prefix = 0;
_state.SFR.Alt1 = false;
_state.SFR.Alt2 = false;
_state.SrcReg = 0;
_state.DestReg = 0;
}
void Gsu::InvalidateCache()
{
memset(_cacheValid, 0, sizeof(_cacheValid));
}
void Gsu::WaitRomOperation()
{
if (_state.RomDelay)
{
//Wait for existing RAM operation to complete
Step(_state.RomDelay);
}
}
void Gsu::WaitRamOperation()
{
if (_state.RamDelay)
{
//Wait for existing RAM operation to complete
Step(_state.RamDelay);
}
}
void Gsu::WaitForRomAccess()
{
if (!_state.GsuRomAccess)
{
_waitForRomAccess = true;
_stopped = true;
}
}
void Gsu::WaitForRamAccess()
{
if (!_state.GsuRamAccess)
{
_waitForRamAccess = true;
_stopped = true;
}
}
void Gsu::UpdateRunningState()
{
_stopped = !_state.SFR.Running || _waitForRamAccess || _waitForRomAccess;
}
uint8_t Gsu::ReadRomBuffer()
{
WaitRomOperation();
return _state.RomReadBuffer;
}
uint8_t Gsu::ReadRamBuffer(uint16_t addr)
{
WaitRamOperation();
WaitForRamAccess();
return ReadGsu(0x700000 | (_state.RamBank << 16) | addr, MemoryOperationType::Read);
}
void Gsu::WriteRam(uint16_t addr, uint8_t value)
{
WaitRamOperation();
_state.RamDelay = _state.ClockSelect ? 5 : 6;
_state.RamWriteAddress = addr;
_state.RamWriteValue = value;
}
void Gsu::Step(uint64_t cycles)
{
_state.CycleCount += cycles;
if (_state.RomDelay)
{
_state.RomDelay -= std::min<uint8_t>((uint8_t)cycles, _state.RomDelay);
if (_state.RomDelay == 0)
{
WaitForRomAccess();
_state.RomReadBuffer = ReadGsu((_state.RomBank << 16) | _state.R[14], MemoryOperationType::Read);
_state.SFR.RomReadPending = false;
}
}
if (_state.RamDelay)
{
_state.RamDelay -= std::min<uint8_t>((uint8_t)cycles, _state.RamDelay);
if (_state.RamDelay == 0)
{
WaitForRamAccess();
WriteGsu(0x700000 | (_state.RamBank << 16) | _state.RamWriteAddress, _state.RamWriteValue,
MemoryOperationType::Write);
}
}
}
void Gsu::Reset()
{
_state = {};
_state.ProgramReadBuffer = 0x01; //Run a NOP on first cycle
_console->GetSettings()->InitializeRam(_cache, 512);
memset(_cacheValid, 0, sizeof(_cacheValid));
_waitForRomAccess = false;
_waitForRamAccess = false;
_stopped = true;
_lastOpAddr = 0;
}
uint8_t Gsu::Read(uint32_t addr)
{
addr &= 0x33FF;
if (_state.SFR.Running && addr != 0x3030 && addr != 0x3031 && addr != 0x303B)
{
//"During GSU operation, only SFR, SCMR, and VCR may be accessed."
return 0;
}
switch (addr)
{
case 0x3000:
case 0x3002:
case 0x3004:
case 0x3006:
case 0x3008:
case 0x300A:
case 0x300C:
case 0x300E:
case 0x3010:
case 0x3012:
case 0x3014:
case 0x3016:
case 0x3018:
case 0x301A:
case 0x301C:
case 0x301E:
return (uint8_t)_state.R[(addr >> 1) & 0x0F];
case 0x3001:
case 0x3003:
case 0x3005:
case 0x3007:
case 0x3009:
case 0x300B:
case 0x300D:
case 0x300F:
case 0x3011:
case 0x3013:
case 0x3015:
case 0x3017:
case 0x3019:
case 0x301B:
case 0x301D:
case 0x301F:
return _state.R[(addr >> 1) & 0x0F] >> 8;
case 0x3030: return _state.SFR.GetFlagsLow();
case 0x3031:
{
uint8_t flags = _state.SFR.GetFlagsHigh();
_state.SFR.Irq = false;
_cpu->ClearIrqSource(IrqSource::Coprocessor);
return flags;
}
case 0x3034: return _state.ProgramBank;
case 0x3036: return _state.RomBank;
case 0x303B: return 0x04; //Version (can be 1 or 4?)
case 0x303C: return _state.RamBank;
case 0x303E: return (uint8_t)_state.CacheBase;
case 0x303F: return _state.CacheBase >> 8;
}
if (addr >= 0x3100 && addr <= 0x32FF)
{
return _cache[(_state.CacheBase + (addr - 0x3100)) & 0x1FF];
}
LogDebug("[Debug] Missing read handler: $" + HexUtilities::ToHex(addr));
//TODO open bus and proper mirroring?
return 0;
}
void Gsu::Write(uint32_t addr, uint8_t value)
{
addr &= 0x33FF;
if (_state.SFR.Running && addr != 0x3030 && addr != 0x303A)
{
//"During GSU operation, only SFR, SCMR, and VCR may be accessed."
return;
}
switch (addr)
{
case 0x3000:
case 0x3002:
case 0x3004:
case 0x3006:
case 0x3008:
case 0x300A:
case 0x300C:
case 0x300E:
case 0x3010:
case 0x3012:
case 0x3014:
case 0x3016:
case 0x3018:
case 0x301A:
case 0x301C:
case 0x301E:
_state.RegisterLatch = value;
break;
case 0x3001:
case 0x3003:
case 0x3005:
case 0x3007:
case 0x3009:
case 0x300B:
case 0x300D:
case 0x300F:
case 0x3011:
case 0x3013:
case 0x3015:
case 0x3017:
case 0x3019:
case 0x301B:
case 0x301D:
case 0x301F:
{
uint8_t reg = (addr >> 1) & 0x0F;
_state.R[reg] = (value << 8) | _state.RegisterLatch;
if (reg == 14)
{
_state.SFR.RomReadPending = true;
_state.RomDelay = _state.ClockSelect ? 5 : 6;
}
else if (addr == 0x301F)
{
_state.SFR.Running = true;
UpdateRunningState();
}
break;
}
case 0x3030:
{
bool running = _state.SFR.Running;
_state.SFR.Zero = (value & 0x02) != 0;
_state.SFR.Carry = (value & 0x04) != 0;
_state.SFR.Sign = (value & 0x08) != 0;
_state.SFR.Overflow = (value & 0x10) != 0;
_state.SFR.Running = (value & 0x20) != 0;
if (running && !_state.SFR.Running)
{
_state.CacheBase = 0;
InvalidateCache();
}
UpdateRunningState();
break;
}
case 0x3033: _state.BackupRamEnabled = (value & 0x01);
break;
case 0x3034: _state.ProgramBank = (value & 0x7F);
InvalidateCache();
break;
case 0x3037:
_state.HighSpeedMode = (value & 0x20) != 0;
_state.IrqDisabled = (value & 0x80) != 0;
break;
case 0x3038: _state.ScreenBase = value;
break;
case 0x3039: _state.ClockSelect = (value & 0x01);
break;
case 0x303A:
_state.ColorGradient = (value & 0x03);
switch (_state.ColorGradient)
{
case 0: _state.PlotBpp = 2;
break;
case 1: _state.PlotBpp = 4;
break;
case 2: _state.PlotBpp = 4;
break;
case 3: _state.PlotBpp = 8;
break;
}
_state.ScreenHeight = ((value & 0x04) >> 2) | ((value & 0x20) >> 4);
_state.GsuRamAccess = (value & 0x08) != 0;
_state.GsuRomAccess = (value & 0x10) != 0;
if (_state.GsuRamAccess)
{
_waitForRamAccess = false;
}
if (_state.GsuRomAccess)
{
_waitForRomAccess = false;
}
UpdateRunningState();
break;
}
if (addr >= 0x3100 && addr <= 0x32FF)
{
uint16_t cacheAddr = (_state.CacheBase + (addr - 0x3100)) & 0x1FF;
_cache[cacheAddr] = value;
if ((cacheAddr & 0x0F) == 0x0F)
{
_cacheValid[cacheAddr >> 4] = true;
}
}
}
uint8_t Gsu::Peek(uint32_t addr)
{
return 0;
}
void Gsu::PeekBlock(uint32_t addr, uint8_t* output)
{
memset(output, 0, 0x1000);
}
AddressInfo Gsu::GetAbsoluteAddress(uint32_t address)
{
return {-1, SnesMemoryType::Register};
}
void Gsu::Serialize(Serializer& s)
{
s.Stream(
_state.CycleCount, _state.RegisterLatch, _state.ProgramBank, _state.RomBank, _state.RamBank, _state.IrqDisabled,
_state.HighSpeedMode, _state.ClockSelect, _state.BackupRamEnabled, _state.ScreenBase, _state.ColorGradient,
_state.PlotBpp,
_state.ScreenHeight, _state.GsuRamAccess, _state.GsuRomAccess, _state.CacheBase, _state.PlotTransparent,
_state.PlotDither,
_state.ColorHighNibble, _state.ColorFreezeHigh, _state.ObjMode, _state.ColorReg, _state.SrcReg, _state.DestReg,
_state.RomReadBuffer, _state.RomDelay, _state.ProgramReadBuffer, _state.RamWriteAddress, _state.RamWriteValue,
_state.RamDelay,
_state.RamAddress, _state.PrimaryCache.X, _state.PrimaryCache.Y, _state.PrimaryCache.ValidBits,
_state.SecondaryCache.X,
_state.SecondaryCache.Y, _state.SecondaryCache.ValidBits,
_state.SFR.Alt1, _state.SFR.Alt2, _state.SFR.Carry, _state.SFR.ImmHigh, _state.SFR.ImmLow, _state.SFR.Irq,
_state.SFR.Overflow,
_state.SFR.Prefix, _state.SFR.RomReadPending, _state.SFR.Running, _state.SFR.Sign, _state.SFR.Zero
);
s.StreamArray(_state.R, 16);
s.StreamArray(_state.PrimaryCache.Pixels, 8);
s.StreamArray(_state.SecondaryCache.Pixels, 8);
s.Stream(_waitForRamAccess, _waitForRomAccess, _stopped);
s.StreamArray(_cacheValid, 32);
s.StreamArray(_cache, 512);
s.StreamArray(_gsuRam, _gsuRamSize);
}
void Gsu::LoadBattery()
{
_console->GetBatteryManager()->LoadBattery(".srm", (uint8_t*)_gsuRam, _gsuRamSize);
}
void Gsu::SaveBattery()
{
_console->GetBatteryManager()->SaveBattery(".srm", (uint8_t*)_gsuRam, _gsuRamSize);
}
GsuState Gsu::GetState()
{
return _state;
}
MemoryMappings* Gsu::GetMemoryMappings()
{
return &_mappings;
}
uint8_t* Gsu::DebugGetWorkRam()
{
return _gsuRam;
}
uint32_t Gsu::DebugGetWorkRamSize()
{
return _gsuRamSize;
}
void Gsu::SetReg(GsuRegister reg, uint16_t value)
{
switch (reg)
{
case GsuRegister::GsuReg0:
case GsuRegister::GsuReg1:
case GsuRegister::GsuReg2:
case GsuRegister::GsuReg3:
case GsuRegister::GsuReg4:
case GsuRegister::GsuReg5:
case GsuRegister::GsuReg6:
case GsuRegister::GsuReg7:
case GsuRegister::GsuReg8:
case GsuRegister::GsuReg9:
case GsuRegister::GsuRegA:
case GsuRegister::GsuRegB:
case GsuRegister::GsuRegC:
case GsuRegister::GsuRegD:
case GsuRegister::GsuRegE:
case GsuRegister::GsuRegF:
{
_state.R[static_cast<int>(reg) & 0xF] = value;
}
break;
case GsuRegister::GsuRegSFR:
{
_state.SFR.SetFlags(value);
}
break;
}
}