Mesen-S/Core/Gsu.cpp
Sour 6539b92502 SA-1: Fixed timing regressions
Memory handlers did not have the _memoryType value set, which caused some memory accesses to be much faster than expected
2020-03-01 18:24:33 -05:00

654 lines
18 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;
}