Mesen2/Core/GBA/GbaMemoryManager.cpp
Sour 8d6830a70a SNES: Add support for ST018 coprocessor
Used by a single game: Hayazashi Nidan Morita Shougi 2
2024-12-29 23:41:52 +09:00

904 lines
No EOL
25 KiB
C++

#include "pch.h"
#include "GBA/GbaMemoryManager.h"
#include "GBA/GbaConsole.h"
#include "GBA/GbaPpu.h"
#include "GBA/GbaCpu.h"
#include "GBA/GbaDmaController.h"
#include "GBA/GbaTimer.h"
#include "GBA/GbaSerial.h"
#include "GBA/GbaControlManager.h"
#include "GBA/GbaRomPrefetch.h"
#include "GBA/Debugger/MgbaLogHandler.h"
#include "GBA/APU/GbaApu.h"
#include "GBA/Cart/GbaCart.h"
#include "Shared/Emulator.h"
#include "Shared/EmuSettings.h"
#include "Shared/MemoryType.h"
#include "Shared/KeyManager.h"
#include "Shared/MessageManager.h"
#include "Utilities/HexUtilities.h"
#include "Utilities/BitUtilities.h"
#include "Utilities/Serializer.h"
GbaMemoryManager::GbaMemoryManager(Emulator* emu, GbaConsole* console, GbaPpu* ppu, GbaDmaController* dmaController, GbaControlManager* controlManager, GbaTimer* timer, GbaApu* apu, GbaCart* cart, GbaSerial* serial, GbaRomPrefetch* prefetch)
{
_emu = emu;
_console = console;
_ppu = ppu;
_dmaController = dmaController;
_controlManager = controlManager;
_timer = timer;
_apu = apu;
_cart = cart;
_serial = serial;
_prefetch = prefetch;
_mgbaLog.reset(new MgbaLogHandler());
_prgRom = (uint8_t*)emu->GetMemory(MemoryType::GbaPrgRom).Memory;
_prgRomSize = emu->GetMemory(MemoryType::GbaPrgRom).Size;
_bootRom = (uint8_t*)emu->GetMemory(MemoryType::GbaBootRom).Memory;
_intWorkRam = (uint8_t*)emu->GetMemory(MemoryType::GbaIntWorkRam).Memory;
_extWorkRam = (uint8_t*)emu->GetMemory(MemoryType::GbaExtWorkRam).Memory;
_vram = (uint8_t*)emu->GetMemory(MemoryType::GbaVideoRam).Memory;
_oam = (uint8_t*)emu->GetMemory(MemoryType::GbaSpriteRam).Memory;
_palette = (uint8_t*)emu->GetMemory(MemoryType::GbaPaletteRam).Memory;
_saveRam = (uint8_t*)emu->GetMemory(MemoryType::GbaSaveRam).Memory;
_saveRamSize = emu->GetMemory(MemoryType::GbaSaveRam).Size;
_waitStatesLut = new uint8_t[0x400];
GenerateWaitStateLut();
//Used to get the correct timing for the timer prescaler, based on the "timer" test
_masterClock = 48;
if(_emu->GetSettings()->GetGbaConfig().SkipBootScreen) {
_biosLocked = true;
_state.BootRomOpenBus[1] = 0xF0;
_state.BootRomOpenBus[2] = 0x29;
_state.BootRomOpenBus[3] = 0xE1;
}
}
GbaMemoryManager::~GbaMemoryManager()
{
delete[] _waitStatesLut;
}
void GbaMemoryManager::ProcessIdleCycle()
{
_prefetch->Exec(1, _state.PrefetchEnabled);
ProcessInternalCycle<true>();
}
void GbaMemoryManager::ProcessStoppedCycle()
{
//What exactly is disabled when stopped?
//This at least works the games that were tested (but is very likely inaccurate)
_masterClock++;
_ppu->Exec(); //keep PPU running to keep emulation running properly
_timer->Exec(_masterClock);
}
void GbaMemoryManager::ProcessPendingUpdates(bool allowStartDma)
{
if(_dmaController->HasPendingDma()) {
_dmaController->RunPendingDma(allowStartDma);
}
_masterClock++;
if(_state.IrqUpdateCounter) {
_state.IrqUpdateCounter--;
_state.IrqPending <<= 1;
_state.IrqPending |= (uint8_t)(bool)(_state.IE & _state.IF);
_state.IrqLine <<= 1;
_state.IrqLine |= (uint8_t)((bool)(_state.IE & _state.IF) && _state.IME);
_state.IE = _state.NewIE;
_state.IF = _state.NewIF;
_state.IME = _state.NewIME;
}
if(_pendingIrqSourceDelay && --_pendingIrqSourceDelay == 0) {
_state.NewIF |= (int)_pendingIrqSource;
TriggerIrqUpdate();
}
if(_timer->HasPendingTimers()) {
_timer->ProcessPendingTimers();
}
_ppu->Exec();
_timer->Exec(_masterClock);
if(_serial->HasPendingIrq()) {
_serial->CheckForIrq(_masterClock);
}
_hasPendingUpdates = (
_dmaController->HasPendingDma() ||
_timer->HasPendingTimers() ||
_state.IrqUpdateCounter ||
_pendingIrqSourceDelay ||
_serial->HasPendingIrq()
);
}
void GbaMemoryManager::ProcessPendingLateUpdates()
{
if(_timer->HasPendingWrites()) {
_timer->ProcessPendingWrites();
}
_hasPendingLateUpdates = false;
}
void GbaMemoryManager::GenerateWaitStateLut()
{
for(GbaAccessModeVal mode = 0; mode < 4; mode++) {
for(int i = 0; i <= 0xFF; i++) {
uint8_t waitStates = 1;
switch(i) {
case 0x02:
//External work ram
waitStates = (mode & GbaAccessMode::Word) ? 6 : 3;
break;
case 0x05:
case 0x06:
//VRAM/Palette
waitStates = (mode & GbaAccessMode::Word) ? 2 : 1;
break;
case 0x08:
case 0x09:
waitStates = _state.PrgWaitStates0[mode & GbaAccessMode::Sequential] + ((mode & GbaAccessMode::Word) ? _state.PrgWaitStates0[1] : 0);
break;
case 0x0A:
case 0x0B:
waitStates = _state.PrgWaitStates1[mode & GbaAccessMode::Sequential] + ((mode & GbaAccessMode::Word) ? _state.PrgWaitStates1[1] : 0);
break;
case 0x0C:
case 0x0D:
waitStates = _state.PrgWaitStates2[mode & GbaAccessMode::Sequential] + ((mode & GbaAccessMode::Word) ? _state.PrgWaitStates2[1] : 0);
break;
case 0x0E:
case 0x0F:
//SRAM
waitStates = _state.SramWaitStates;
break;
}
_waitStatesLut[(i << 2) | mode] = waitStates;
}
}
}
uint8_t GbaMemoryManager::GetWaitStates(GbaAccessModeVal mode, uint32_t addr)
{
return _waitStatesLut[((addr >> 22) & 0x3FC) | (mode & (GbaAccessMode::Word | ((addr & 0x1FFFF) ? GbaAccessMode::Sequential : 0)))];
}
void GbaMemoryManager::ProcessWaitStates(GbaAccessModeVal mode, uint32_t addr)
{
uint8_t waitStates;
if(addr < 0x8000000 || addr >= 0x10000000) {
waitStates = GetWaitStates(mode, addr);
_prefetch->Exec(waitStates, _state.PrefetchEnabled);
} else if((mode & GbaAccessMode::Dma) || !(mode & GbaAccessMode::Prefetch)) {
//Accesses to ROM from DMA or reads not caused by the CPU loading opcodes will reset the cartridge prefetcher
//When the prefetch is reset on its last cycle, the ROM access takes an extra cycle to complete
waitStates = GetWaitStates(mode, addr) + (int)_prefetch->Reset();
} else {
waitStates = _state.PrefetchEnabled ? _prefetch->Read<true>(mode, addr) : _prefetch->Read<false>(mode, addr);
}
ProcessInternalCycle<true>();
waitStates--;
while(waitStates >= 3) {
ProcessInternalCycle();
ProcessInternalCycle();
ProcessInternalCycle();
waitStates -= 3;
}
switch(waitStates) {
case 2: ProcessInternalCycle(); [[fallthrough]];
case 1: ProcessInternalCycle(); break;
}
}
void GbaMemoryManager::ProcessVramStalling(uint32_t addr)
{
//TODOGBA 32-bit vram/palette writes should be split across 2 clocks (and stalled independently)
uint8_t memType = (addr - 0x4000000) >> 24;
if(memType == 3) {
memType = GbaPpuMemAccess::Oam;
} else if(memType == GbaPpuMemAccess::Vram && addr & 0x10000) {
memType = GbaPpuMemAccess::VramObj;
}
_ppu->RenderScanline(true);
while(_ppu->IsAccessingMemory(memType)) {
//Block CPU until PPU is done accessing ram
ProcessInternalCycle();
_ppu->RenderScanline(true);
}
}
template<uint8_t width>
void GbaMemoryManager::UpdateOpenBus(uint32_t addr, uint32_t value)
{
if((addr & 0xFF000000) == 0x03000000) {
//IWRAM appears to have its own open bus value, which overwrites
//the main bus' open bus value whenever IWRAM is read
//This is unverified, but passes the openbuster test
for(int i = 0; i < width; i++) {
_state.IwramOpenBus[(addr & (4 - width)) + i] = value;
value >>= 8;
}
memcpy(_state.InternalOpenBus, _state.IwramOpenBus, sizeof(_state.IwramOpenBus));
} else if constexpr(width == 4) {
_state.InternalOpenBus[0] = value;
_state.InternalOpenBus[1] = value >> 8;
_state.InternalOpenBus[2] = value >> 16;
_state.InternalOpenBus[3] = value >> 24;
} else if constexpr(width == 2) {
_state.InternalOpenBus[2] = _state.InternalOpenBus[0] = value;
_state.InternalOpenBus[3] = _state.InternalOpenBus[1] = value >> 8;
} else if constexpr(width == 1) {
_state.InternalOpenBus[0] = value;
_state.InternalOpenBus[1] = value;
_state.InternalOpenBus[2] = value;
_state.InternalOpenBus[3] = value;
}
}
uint32_t GbaMemoryManager::Read(GbaAccessModeVal mode, uint32_t addr)
{
ProcessWaitStates(mode, addr);
if(addr >= 0x5000000 && addr <= 0x7000000) {
ProcessVramStalling(addr);
}
uint32_t value;
if(mode & GbaAccessMode::Prefetch) {
_biosLocked = addr >= GbaConsole::BootRomSize;
}
bool isSigned = mode & GbaAccessMode::Signed;
if(mode & GbaAccessMode::Byte) {
value = InternalRead(mode, addr, addr);
UpdateOpenBus<1>(addr, value);
value = isSigned ? (uint32_t)(int8_t)value : (uint8_t)value;
_emu->ProcessMemoryRead<CpuType::Gba, 1>(addr, value, MemoryOperationType::Read);
} else if(mode & GbaAccessMode::HalfWord) {
uint8_t b0 = InternalRead(mode, addr & ~0x01, addr);
uint8_t b1 = InternalRead(mode, addr | 1, addr);
value = b0 | (b1 << 8);
UpdateOpenBus<2>(addr, value);
value = isSigned ? (uint32_t)(int16_t)value : (uint16_t)value;
if(!(mode & GbaAccessMode::NoRotate)) {
value = RotateValue(mode, addr, value, isSigned);
}
_emu->ProcessMemoryRead<CpuType::Gba, 2>(addr & ~0x01, value, mode & GbaAccessMode::Prefetch ? MemoryOperationType::ExecOpCode : MemoryOperationType::Read);
} else {
uint8_t b0 = InternalRead(mode, addr & ~0x03, addr);
uint8_t b1 = InternalRead(mode, (addr & ~0x03) | 1, addr);
uint8_t b2 = InternalRead(mode, (addr & ~0x03) | 2, addr);
uint8_t b3 = InternalRead(mode, addr | 3, addr);
value = b0 | (b1 << 8) | (b2 << 16) | (b3 << 24);
UpdateOpenBus<4>(addr, value);
if(!(mode & GbaAccessMode::NoRotate)) {
value = RotateValue(mode, addr, value, isSigned);
}
_emu->ProcessMemoryRead<CpuType::Gba, 4>(addr & ~0x03, value, mode & GbaAccessMode::Prefetch ? MemoryOperationType::ExecOpCode : MemoryOperationType::Read);
}
return value;
}
template<bool debug>
uint32_t GbaMemoryManager::RotateValue(GbaAccessModeVal mode, uint32_t addr, uint32_t value, bool isSigned)
{
uint8_t shift = (addr & ((mode & GbaAccessMode::HalfWord) ? 0x01 : 0x03)) << 3;
if(shift) {
if constexpr(!debug) {
_emu->BreakIfDebugging(CpuType::Gba, BreakSource::GbaUnalignedMemoryAccess);
}
if(isSigned) {
return (int32_t)value >> shift;
} else {
return (value << (32 - shift)) | (value >> shift);
}
}
return value;
}
void GbaMemoryManager::Write(GbaAccessModeVal mode, uint32_t addr, uint32_t value)
{
ProcessWaitStates(mode, addr);
if(addr >= 0x5000000 && addr <= 0x7000000) {
ProcessVramStalling(addr);
}
if(mode & GbaAccessMode::Byte) {
if(_emu->ProcessMemoryWrite<CpuType::Gba, 1>(addr, value, MemoryOperationType::Write)) {
InternalWrite(mode, addr, (uint8_t)value, addr, value);
}
} else if(mode & GbaAccessMode::HalfWord) {
if(_emu->ProcessMemoryWrite<CpuType::Gba, 2>(addr & ~0x01, value, MemoryOperationType::Write)) {
InternalWrite(mode, addr & ~0x01, (uint8_t)value, addr, value);
InternalWrite(mode, (addr & ~0x01) | 0x01, (uint8_t)(value >> 8), addr, value);
}
} else {
if(_emu->ProcessMemoryWrite<CpuType::Gba, 4>(addr & ~0x03, value, MemoryOperationType::Write)) {
InternalWrite(mode, (addr & ~0x03), (uint8_t)value, addr, value);
InternalWrite(mode, (addr & ~0x03) | 0x01, (uint8_t)(value >> 8), addr, value);
InternalWrite(mode, (addr & ~0x03) | 0x02, (uint8_t)(value >> 16), addr, value);
InternalWrite(mode, (addr & ~0x03) | 0x03, (uint8_t)(value >> 24), addr, value);
}
}
}
uint8_t GbaMemoryManager::InternalRead(GbaAccessModeVal mode, uint32_t addr, uint32_t readAddr)
{
uint8_t bank = (addr >> 24);
addr &= 0xFFFFFF;
switch(bank) {
case 0x00:
//bootrom
if(addr < GbaConsole::BootRomSize) {
if(!_biosLocked) {
return _state.BootRomOpenBus[addr & 0x03] = _bootRom[addr];
} else {
return _state.BootRomOpenBus[addr & 0x03];
}
}
return _state.InternalOpenBus[addr & 0x03];
case 0x02: return _extWorkRam[addr & (GbaConsole::ExtWorkRamSize - 1)];
case 0x03: return _intWorkRam[addr & (GbaConsole::IntWorkRamSize - 1)];
case 0x04: return ReadRegister(addr);
case 0x05: return _palette[addr & (GbaConsole::PaletteRamSize - 1)];
case 0x06:
if(addr & 0x10000) {
if(addr >= 0x18000 && _ppu->IsBitmapMode() && !(addr & 0x4000)) {
//reads to mirrors of the first 0x4000 when in bitmap mode return 0 instead
return 0;
} else {
return _vram[addr & 0x17FFF];
}
} else {
return _vram[addr & 0xFFFF];
}
case 0x07: return _oam[addr & (GbaConsole::SpriteRamSize - 1)];
case 0x08:
case 0x09:
case 0x0A:
case 0x0B:
case 0x0C:
return _cart->ReadRom<false>((bank << 24) | addr);
case 0x0D:
return _cart->ReadRom<true>((bank << 24) | addr);
case 0x0E:
case 0x0F:
return _cart->ReadRam((bank << 24) | addr, readAddr);
}
return _state.InternalOpenBus[addr & 0x03];
}
void GbaMemoryManager::InternalWrite(GbaAccessModeVal mode, uint32_t addr, uint8_t value, uint32_t writeAddr, uint32_t fullValue)
{
uint8_t bank = (addr >> 24);
addr &= 0xFFFFFF;
_state.InternalOpenBus[addr & 0x03] = value;
switch(bank) {
case 0x00:
//bootrom
break;
case 0x02: _extWorkRam[addr & (GbaConsole::ExtWorkRamSize - 1)] = value; break;
case 0x03:
_intWorkRam[addr & (GbaConsole::IntWorkRamSize - 1)] = value;
_state.IwramOpenBus[addr & 0x03] = value;
break;
case 0x04:
//registers
if(addr < 0x3FF) {
WriteRegister(mode, addr, value);
} else if(addr >= 0xFFF600 && addr <= 0xFFF783) {
if(_emu->GetSettings()->GetGbaConfig().EnableMgbaLogApi) {
_mgbaLog->Write(addr, value);
}
}
break;
case 0x05:
if(!(mode & GbaAccessMode::Byte)) {
_palette[addr & (GbaConsole::PaletteRamSize - 1)] = value;
} else {
//Mirror the value over a half-word
_palette[(addr & (GbaConsole::PaletteRamSize - 1)) & ~0x01] = value;
_palette[(addr & (GbaConsole::PaletteRamSize - 1)) | 0x01] = value;
}
break;
case 0x06:
//vram
if(addr & 0x10000) {
if(addr >= 0x18000 && _ppu->IsBitmapMode() && !(addr & 0x4000)) {
//Ignore writes to mirrors of the first 0x4000 when in bitmap mode
//Fixes a visual glitch on the title screen of Acrobat Kid
break;
}
if(!(mode & GbaAccessMode::Byte)) {
_vram[addr & 0x17FFF] = value;
} else if(_ppu->IsBitmapMode() && addr < 0x14000) {
//Ignore byte mode writes above 0x10000 in non-bitmap modes and above 0x14000 in bitmap modes
//Mirror the value over a half-word
_vram[addr & 0x17FFE] = value;
_vram[(addr & 0x17FFF) | 0x01] = value;
}
} else {
if(mode & GbaAccessMode::Byte) {
_vram[addr & 0xFFFE] = value;
_vram[(addr & 0xFFFF) | 0x01] = value;
} else {
_vram[addr & 0xFFFF] = value;
}
}
break;
case 0x07:
if(!(mode & GbaAccessMode::Byte)) {
//OAM ignores all byte mode writes
_oam[addr & (GbaConsole::SpriteRamSize - 1)] = value;
}
break;
case 0x08:
case 0x09:
case 0x0A:
case 0x0B:
case 0x0C:
case 0x0D:
//ROM
_cart->WriteRom((bank << 24) | addr, value);
break;
case 0x0E:
case 0x0F:
_cart->WriteRam(mode, addr, value, writeAddr, fullValue);
break;
}
}
uint32_t GbaMemoryManager::ReadRegister(uint32_t addr)
{
if(addr < 0x60) {
return _ppu->ReadRegister(addr);
} else if(addr < 0xB0) {
return _apu->ReadRegister(addr);
} else if(addr < 0xE0) {
return _dmaController->ReadRegister(addr);
} else if(addr >= 0x100 && addr < 0x110) {
return _timer->ReadRegister(addr);
} else if((addr >= 0x120 && addr <= 0x12B) || (addr >= 0x134 && addr <= 0x159)) {
return _serial->ReadRegister(addr, false);
} else {
switch(addr) {
case 0x130:
case 0x131:
case 0x132:
case 0x133:
return _controlManager->ReadInputPort(addr);
case 0x15A: return 0;
case 0x15B: return 0;
case 0x200: return (uint8_t)_state.IE;
case 0x201: return (uint8_t)(_state.IE >> 8);
case 0x202: return (uint8_t)_state.IF;
case 0x203: return (uint8_t)(_state.IF >> 8);
case 0x204: return (uint8_t)_state.WaitControl;
case 0x205: return (uint8_t)(_state.WaitControl >> 8);
case 0x206: return 0;
case 0x207: return 0;
case 0x208: return _state.IME;
case 0x209: return 0;
case 0x20A: return 0;
case 0x20B: return 0;
case 0x300: return (uint8_t)_state.PostBootFlag;
case 0x301: return 0;
case 0x302: return 0;
case 0x303: return 0;
default:
if(addr >= 0xFFF780 && addr <= 0xFFF783) {
if(_emu->GetSettings()->GetGbaConfig().EnableMgbaLogApi) {
return _mgbaLog->Read(addr);
}
}
LogDebug("Read unimplemented register: " + HexUtilities::ToHex32(addr));
return _state.InternalOpenBus[addr & 0x01];
}
}
}
void GbaMemoryManager::WriteRegister(GbaAccessModeVal mode, uint32_t addr, uint8_t value)
{
static constexpr uint8_t defaultWaitStates[4] = { 4,3,2,8 };
switch(addr) {
case 0x132:
case 0x133:
_controlManager->WriteInputPort(mode, addr, value);
break;
case 0x200: _state.NewIE = (_state.NewIE & 0xFF00) | value; TriggerIrqUpdate(); break;
case 0x201: _state.NewIE = (_state.NewIE & 0xFF) | (value << 8); TriggerIrqUpdate(); break;
case 0x202: _state.NewIF = (_state.NewIF & 0xFF00) | ((_state.NewIF & 0xFF) & ~value); TriggerIrqUpdate(); break;
case 0x203: _state.NewIF = ((_state.NewIF & 0xFF00) & ~(value << 8)) | (_state.NewIF & 0xFF); TriggerIrqUpdate(); break;
case 0x204:
BitUtilities::SetBits<0>(_state.WaitControl, value);
_state.SramWaitStates = defaultWaitStates[value & 0x03] + 1;
_state.PrgWaitStates0[0] = defaultWaitStates[(value >> 2) & 0x03] + 1;
_state.PrgWaitStates0[1] = (value & 0x10) ? 2 : 3;
_state.PrgWaitStates1[0] = (defaultWaitStates[(value >> 5) & 0x03] + 1);
_state.PrgWaitStates1[1] = (value & 0x80) ? 2 : 5;
GenerateWaitStateLut();
break;
case 0x205:
BitUtilities::SetBits<8>(_state.WaitControl, value & 0x7F);
_state.PrgWaitStates2[0] = defaultWaitStates[value & 0x03] + 1;
_state.PrgWaitStates2[1] = (value & 0x04) ? 2 : 9;
_state.PrefetchEnabled = (value & 0x40);
GenerateWaitStateLut();
break;
case 0x206: break; //waitcontrol
case 0x207: break; //waitcontrol
case 0x208: _state.NewIME = value & 0x01; TriggerIrqUpdate(); break;
case 0x209: break; //ime
case 0x20A: break; //ime
case 0x20B: break; //ime
case 0x300:
if(!_biosLocked && !_state.PostBootFlag) {
//Only accessible from BIOS, and can't be cleared once set
_state.PostBootFlag = value & 0x01;
}
break;
case 0x301:
if(!_biosLocked) {
_haltModeUsed = true;
_console->GetCpu()->SetStopFlag();
_state.StopMode = value & 0x80;
}
break;
//TODOGBA case 0x800: break;
default:
if(addr < 0x60) {
_ppu->WriteRegister(addr, value);
} else if(addr < 0xB0) {
_apu->WriteRegister(mode, addr, value);
} else if(addr < 0xE0) {
_dmaController->WriteRegister(addr, value);
} else if(addr >= 0x100 && addr < 0x110) {
_timer->WriteRegister(addr, value);
} else if((addr >= 0x120 && addr <= 0x12B) || (addr >= 0x134 && addr <= 0x159)) {
_serial->WriteRegister(addr, value);
} else {
LogDebug("Write unimplemented register: " + HexUtilities::ToHex32(addr) + " = " + HexUtilities::ToHex(value));
}
break;
}
}
void GbaMemoryManager::TriggerIrqUpdate()
{
_state.IrqUpdateCounter = 3;
SetPendingUpdateFlag();
}
void GbaMemoryManager::SetDelayedIrqSource(GbaIrqSource source, uint8_t delay)
{
_pendingIrqSource = source;
_pendingIrqSourceDelay = delay;
SetPendingUpdateFlag();
}
void GbaMemoryManager::SetIrqSource(GbaIrqSource source)
{
_state.NewIF |= (int)source;
TriggerIrqUpdate();
}
bool GbaMemoryManager::HasPendingIrq()
{
//Keep track of whether or not a pending IRQ exists - used to break out of HALT mode
return _state.IrqPending & 0x01;
}
bool GbaMemoryManager::ProcessIrq()
{
//Keep track of the IRQ line used by the CPU to decide if the IRQ handler should be executed
//(requires the IF+IE IRQ flags and IME to all be set)
return _state.IrqLine & 0x02;
}
bool GbaMemoryManager::IsHaltOver()
{
return _state.StopMode ? _controlManager->CheckInputCondition() : HasPendingIrq();
}
bool GbaMemoryManager::UseInlineHalt()
{
bool result = _haltModeUsed;
_haltModeUsed = false;
return result;
}
uint8_t GbaMemoryManager::GetOpenBus(uint32_t addr)
{
return _state.InternalOpenBus[addr & 0x01];
}
uint32_t GbaMemoryManager::DebugCpuRead(GbaAccessModeVal mode, uint32_t addr)
{
uint32_t value;
bool isSigned = mode & GbaAccessMode::Signed;
if(mode & GbaAccessMode::Byte) {
value = DebugRead(addr);
value = isSigned ? (uint32_t)(int8_t)value : (uint8_t)value;
} else if(mode & GbaAccessMode::HalfWord) {
uint8_t b0 = DebugRead(addr & ~0x01);
uint8_t b1 = DebugRead(addr | 1);
value = b0 | (b1 << 8);
value = isSigned ? (uint32_t)(int16_t)value : (uint16_t)value;
if(!(mode & GbaAccessMode::NoRotate)) {
value = RotateValue<true>(mode, addr, value, isSigned);
}
} else {
uint8_t b0 = DebugRead(addr & ~0x03);
uint8_t b1 = DebugRead((addr & ~0x03) | 1);
uint8_t b2 = DebugRead((addr & ~0x03) | 2);
uint8_t b3 = DebugRead(addr | 3);
value = b0 | (b1 << 8) | (b2 << 16) | (b3 << 24);
if(!(mode & GbaAccessMode::NoRotate)) {
value = RotateValue<true>(mode, addr, value, isSigned);
}
}
return value;
}
uint8_t GbaMemoryManager::DebugRead(uint32_t addr)
{
uint8_t bank = (addr >> 24);
addr &= 0xFFFFFF;
switch(bank) {
case 0x00:
if(addr < GbaConsole::BootRomSize) {
return _bootRom[addr];
}
break;
case 0x02: return _extWorkRam[addr & (GbaConsole::ExtWorkRamSize - 1)];
case 0x03: return _intWorkRam[addr & (GbaConsole::IntWorkRamSize - 1)];
case 0x04:
if(addr < 0x3FF) {
if((addr >= 0x120 && addr <= 0x12B) || (addr >= 0x134 && addr <= 0x159)) {
return _serial->ReadRegister(addr, true);
} else {
return ReadRegister(addr);
}
}
break;
case 0x05: return _palette[addr & (GbaConsole::PaletteRamSize - 1)];
case 0x06:
if(addr & 0x10000) {
return _vram[addr & 0x17FFF];
} else {
return _vram[addr & 0xFFFF];
}
case 0x07: return _oam[addr & (GbaConsole::SpriteRamSize - 1)];
case 0x08: case 0x09: case 0x0A:
case 0x0B: case 0x0C: case 0x0D: {
uint32_t romAddr = ((bank & 0x01) << 24) | addr;
if(romAddr < _prgRomSize) {
return _prgRom[romAddr];
}
break;
}
case 0x0E: case 0x0F:
return _cart->ReadRam(addr, addr);
}
return _state.InternalOpenBus[addr & 0x01];
}
void GbaMemoryManager::DebugWrite(uint32_t addr, uint8_t value)
{
uint8_t bank = (addr >> 24);
addr &= 0xFFFFFF;
switch(bank) {
case 0x00:
if(addr < GbaConsole::BootRomSize) {
_bootRom[addr] = value;
}
break;
case 0x02: _extWorkRam[addr & (GbaConsole::ExtWorkRamSize - 1)] = value; break;
case 0x03: _intWorkRam[addr & (GbaConsole::IntWorkRamSize - 1)] = value; break;
case 0x04:
//todogba debugger - allow writing to registers
break;
case 0x05: _palette[addr & (GbaConsole::PaletteRamSize - 1)] = value; break;
case 0x06:
if(addr & 0x10000) {
_vram[addr & 0x17FFF] = value; break;
} else {
_vram[addr & 0xFFFF] = value; break;
}
case 0x07: _oam[addr & (GbaConsole::SpriteRamSize - 1)] = value; break;
case 0x08: case 0x09: case 0x0A:
case 0x0B: case 0x0C: case 0x0D:
if(addr < _prgRomSize) {
_prgRom[addr] = value;
}
break;
case 0x0E: case 0x0F:
_cart->DebugWriteRam(addr, value);
break;
}
}
AddressInfo GbaMemoryManager::GetAbsoluteAddress(uint32_t addr)
{
uint8_t bank = (addr >> 24);
addr &= 0xFFFFFF;
switch(bank) {
case 0x00:
if(addr < GbaConsole::BootRomSize) {
return { (int32_t)addr, MemoryType::GbaBootRom };
}
break;
case 0x02: return { (int32_t)addr & (GbaConsole::ExtWorkRamSize - 1), MemoryType::GbaExtWorkRam };
case 0x03: return { (int32_t)addr & (GbaConsole::IntWorkRamSize - 1), MemoryType::GbaIntWorkRam };
case 0x04:
//registers
if(addr < 0x3FF) {
return { -1, MemoryType::None };
}
break;
case 0x05: return { (int32_t)addr & (GbaConsole::PaletteRamSize - 1), MemoryType::GbaPaletteRam };
case 0x06:
if(addr & 0x10000) {
return { (int32_t)addr & 0x17FFF, MemoryType::GbaVideoRam };
} else {
return { (int32_t)addr & 0xFFFF, MemoryType::GbaVideoRam };
}
case 0x07: return { (int32_t)addr & (GbaConsole::SpriteRamSize - 1), MemoryType::GbaSpriteRam };
case 0x08:
case 0x09:
case 0x0A:
case 0x0B:
case 0x0C:
case 0x0D:
addr |= (bank & 0x01) << 24;
if(addr < _prgRomSize) {
return { (int32_t)addr, MemoryType::GbaPrgRom };
}
break;
case 0x0E:
case 0x0F:
return _cart->GetRamAbsoluteAddress(addr);
}
return { -1, MemoryType::None };
}
int64_t GbaMemoryManager::GetRelativeAddress(AddressInfo& absAddress)
{
switch(absAddress.Type) {
default:
case MemoryType::GbaBootRom: return absAddress.Address;
case MemoryType::GbaExtWorkRam: return 0x2000000 | absAddress.Address;
case MemoryType::GbaIntWorkRam: return 0x3000000 | absAddress.Address;
case MemoryType::GbaPaletteRam: return 0x5000000 | absAddress.Address;
case MemoryType::GbaPrgRom: return 0x8000000 | absAddress.Address;
case MemoryType::GbaSaveRam: return _cart->GetRamRelativeAddress(absAddress);
case MemoryType::GbaSpriteRam: return 0x7000000 | absAddress.Address;
case MemoryType::GbaVideoRam: return 0x6000000 | absAddress.Address;
case MemoryType::GbaMemory: return absAddress.Address;
}
}
void GbaMemoryManager::Serialize(Serializer& s)
{
SV(_masterClock);
SV(_hasPendingUpdates);
SV(_hasPendingLateUpdates);
SV(_pendingIrqSource);
SV(_pendingIrqSourceDelay);
SV(_haltModeUsed);
SV(_biosLocked);
SV(_state.IE);
SV(_state.IF);
SV(_state.NewIE);
SV(_state.NewIF);
SV(_state.NewIME);
SV(_state.WaitControl);
SVArray(_state.PrgWaitStates0, 2);
SVArray(_state.PrgWaitStates1, 2);
SVArray(_state.PrgWaitStates2, 2);
SV(_state.SramWaitStates);
SV(_state.PrefetchEnabled);
SV(_state.IME);
SV(_state.IrqUpdateCounter);
SV(_state.IrqPending);
SV(_state.IrqLine);
SV(_state.BusLocked);
SV(_state.StopMode);
SV(_state.PostBootFlag);
SVArray(_state.BootRomOpenBus, 4);
SVArray(_state.InternalOpenBus, 4);
SVArray(_state.IwramOpenBus, 4);
if(!s.IsSaving()) {
GenerateWaitStateLut();
}
}