#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(new RamHandler(_gsuRam, i * 0x1000, _gsuRamSize, SnesMemoryType::GsuWorkRam))); _gsuCpuRamHandlers.push_back(unique_ptr(new GsuRamHandler(_state, _gsuRamHandlers.back().get()))); } //CPU mappings MemoryMappings *cpuMappings = _memoryManager->GetMemoryMappings(); vector> &prgRomHandlers = _console->GetCartridge()->GetPrgRomHandlers(); for(unique_ptr &handler : prgRomHandlers) { _gsuCpuRomHandlers.push_back(unique_ptr(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(_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(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(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(_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)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)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(reg) & 0xF] = value; } break; case GsuRegister::GsuRegSFR: { _state.SFR.SetFlags(value); } break; } }