GB: Improved DMA support for GBC (timing + bug fixes)

Fixes issues in Star Wars Racer
This commit is contained in:
Sour 2020-05-27 22:50:27 -04:00
parent dae99e0a75
commit 06376f8ca9
9 changed files with 44 additions and 26 deletions

View file

@ -133,7 +133,7 @@ void Gameboy::Exec()
void Gameboy::Run(uint64_t masterClock)
{
while(_cpu->GetState().CycleCount < masterClock) {
while(_memoryManager->GetCycleCount() < masterClock) {
_cpu->Exec();
}
}
@ -269,7 +269,7 @@ bool Gameboy::UseBootRom()
uint64_t Gameboy::GetCycleCount()
{
return _cpu->GetCycleCount();
return _memoryManager->GetCycleCount();
}
uint64_t Gameboy::GetApuCycleCount()

View file

@ -16,7 +16,6 @@ GbCpu::GbCpu(Console* console, Gameboy* gameboy, GbMemoryManager* memoryManager)
if(_gameboy->UseBootRom()) {
_state.PC = 0;
_state.SP = 0xFFFF;
_state.CycleCount = 8; //Makes boot_sclk_align serial test pass
} else {
_state.PC = 0x100;
_state.SP = 0xFFFE;
@ -28,7 +27,6 @@ GbCpu::GbCpu(Console* console, Gameboy* gameboy, GbMemoryManager* memoryManager)
_state.H = _gameboy->IsCgb() ? 0x00 : 0x01;
_state.L = _gameboy->IsCgb() ? 0x7C : 0x4D;
_state.Flags = _gameboy->IsCgb() ? 0x80 : 0xB0;
_state.CycleCount = _gameboy->IsCgb() ? 13051516 : 23440332;
}
}
@ -41,11 +39,6 @@ GbCpuState GbCpu::GetState()
return _state;
}
uint64_t GbCpu::GetCycleCount()
{
return _state.CycleCount;
}
void GbCpu::Exec()
{
uint8_t irqVector = _memoryManager->ProcessIrqRequests();
@ -361,7 +354,6 @@ void GbCpu::ExecOpCode(uint8_t opCode)
void GbCpu::IncCycleCount()
{
_state.CycleCount += 4;
_memoryManager->Exec();
}
@ -1378,7 +1370,7 @@ void GbCpu::PREFIX()
void GbCpu::Serialize(Serializer& s)
{
s.Stream(
_state.CycleCount, _state.PC, _state.SP, _state.A, _state.Flags, _state.B,
_state.PC, _state.SP, _state.A, _state.Flags, _state.B,
_state.C, _state.D, _state.E, _state.H, _state.L, _state.IME, _state.Halted,
_state.EiPending
);

View file

@ -25,7 +25,6 @@ public:
virtual ~GbCpu();
GbCpuState GetState();
uint64_t GetCycleCount();
void Exec();
void ExecOpCode(uint8_t opCode);

View file

@ -62,20 +62,32 @@ void GbDmaController::WriteCgb(uint16_t addr, uint8_t value)
{
switch(addr) {
case 0xFF51: _state.CgbDmaSource = (_state.CgbDmaSource & 0xFF) | (value << 8); break;
case 0xFF52: _state.CgbDmaSource = (_state.CgbDmaSource & 0xFF00) | value; break;
case 0xFF52: _state.CgbDmaSource = (_state.CgbDmaSource & 0xFF00) | (value & 0xF0); break;
case 0xFF53: _state.CgbDmaDest = (_state.CgbDmaDest & 0xFF) | (value << 8); break;
case 0xFF54: _state.CgbDmaDest = (_state.CgbDmaDest & 0xFF00) | value; break;
case 0xFF54: _state.CgbDmaDest = (_state.CgbDmaDest & 0xFF00) | (value & 0xF0); break;
case 0xFF55:
_state.CgbDmaLength = value & 0x7F;
_state.CgbHdmaMode = (value & 0x80) != 0;
if(!_state.CgbHdmaMode) {
//TODO check invalid dma sources/etc.
//TODO timing
for(int i = 0; i < _state.CgbDmaLength * 16; i++) {
uint16_t dst = 0x8000 | (((_state.CgbDmaDest & 0x1FF0) + i) & 0x1FFF);
_memoryManager->Write(dst, _memoryManager->Read((_state.CgbDmaSource & 0xFFF0) + i, MemoryOperationType::DmaRead));
//4 cycles for setup
_memoryManager->Exec();
int count = (_state.CgbDmaLength + 1) * 16;
for(int i = 0; i < count; i++) {
uint16_t dst = 0x8000 | ((_state.CgbDmaDest + i) & 0x1FFF);
//2 or 4 cycles per byte transfered (2x more cycles in high speed mode - effective speed is the same in both modes
if(_memoryManager->IsHighSpeed() || (i & 0x01)) {
//TODO: Not perfectly accurate (in "slow" mode mode both occur on the same cycle)
_memoryManager->Exec();
}
_memoryManager->Write(dst, _memoryManager->Read(_state.CgbDmaSource + i, MemoryOperationType::DmaRead));
}
//Source/Dest/Length are all modified by the DMA process and keep their last value after DMA completes
_state.CgbDmaSource += count;
_state.CgbDmaDest += count;
_state.CgbDmaLength = 0x7F;
} else {
MessageManager::Log("TODO HDMA");

View file

@ -33,6 +33,11 @@ void GbMemoryManager::Init(Console* console, Gameboy* gameboy, GbCart* cart, GbP
_state = {};
_state.CgbWorkRamBank = 1;
_state.DisableBootRom = !_gameboy->UseBootRom();
if(_gameboy->UseBootRom()) {
_state.CycleCount = 8; //Makes boot_sclk_align serial test pass
} else {
_state.CycleCount = _gameboy->IsCgb() ? 13051516 : 23440332;
}
MapRegisters(0x8000, 0x9FFF, RegisterAccess::ReadWrite);
MapRegisters(0xFE00, 0xFFFF, RegisterAccess::ReadWrite);
@ -69,12 +74,13 @@ void GbMemoryManager::RefreshMappings()
void GbMemoryManager::Exec()
{
_state.CycleCount += 4;
_state.ApuCycleCount += _state.CgbHighSpeed ? 2 : 4;
_timer->Exec();
_ppu->Exec();
_dmaController->Exec();
if(_state.SerialBitCount && (_gameboy->GetCycleCount() & 0x1FF) == 0) {
if(_state.SerialBitCount && (_state.CycleCount & 0x1FF) == 0) {
_state.SerialData = (_state.SerialData << 1) | 0x01;
if(--_state.SerialBitCount == 0) {
RequestIrq(GbIrqSource::Serial);
@ -389,6 +395,11 @@ bool GbMemoryManager::IsHighSpeed()
return _state.CgbHighSpeed;
}
uint64_t GbMemoryManager::GetCycleCount()
{
return _state.CycleCount;
}
uint64_t GbMemoryManager::GetApuCycleCount()
{
return _state.ApuCycleCount;
@ -429,7 +440,7 @@ void GbMemoryManager::Serialize(Serializer& s)
s.Stream(
_state.DisableBootRom, _state.IrqEnabled, _state.IrqRequests, _state.InputSelect,
_state.ApuCycleCount, _state.CgbHighSpeed, _state.CgbSwitchSpeedRequest, _state.CgbWorkRamBank,
_state.SerialData, _state.SerialControl, _state.SerialBitCount
_state.SerialData, _state.SerialControl, _state.SerialBitCount, _state.CycleCount
);
s.StreamArray(_state.MemoryType, 0x100);
s.StreamArray(_state.MemoryOffset, 0x100);

View file

@ -63,6 +63,7 @@ public:
void ToggleSpeed();
bool IsHighSpeed();
uint64_t GetCycleCount();
uint64_t GetApuCycleCount();
uint8_t ReadInputPort();

View file

@ -4,7 +4,6 @@
struct GbCpuState
{
uint64_t CycleCount;
uint16_t PC;
uint16_t SP;
@ -340,10 +339,13 @@ enum class GbMemoryType
struct GbMemoryManagerState
{
uint64_t CycleCount;
uint64_t ApuCycleCount;
uint8_t CgbWorkRamBank;
bool CgbSwitchSpeedRequest;
bool CgbHighSpeed;
uint64_t ApuCycleCount;
bool DisableBootRom;
uint8_t IrqRequests;
uint8_t IrqEnabled;

View file

@ -37,8 +37,7 @@ namespace Mesen.GUI.Debugger.Controls
_cpuBinder.AddBinding(nameof(GbCpuState.Halted), chkHalted);
_cpuBinder.AddBinding(nameof(GbCpuState.IME), chkIme);
_cpuBinder.AddBinding(nameof(GbCpuState.CycleCount), txtCycleCount, eNumberFormat.Decimal);
_ppuBinder.Entity = new GbPpuState();
_ppuBinder.AddBinding(nameof(GbPpuState.Cycle), txtCycle, eNumberFormat.Decimal);
_ppuBinder.AddBinding(nameof(GbPpuState.Scanline), txtScanline, eNumberFormat.Decimal);
@ -51,6 +50,7 @@ namespace Mesen.GUI.Debugger.Controls
_cpuBinder.Entity = state.Cpu;
_cpuBinder.UpdateUI();
txtCycleCount.Text = state.MemoryManager.CycleCount.ToString();
txtHL.Text = ((state.Cpu.H << 8) | state.Cpu.L).ToString("X4");
_ppuBinder.Entity = state.Ppu;

View file

@ -625,10 +625,12 @@ namespace Mesen.GUI
public struct GbMemoryManagerState
{
public UInt64 CycleCount;
public UInt64 ApuCycleCount;
public byte CgbWorkRamBank;
[MarshalAs(UnmanagedType.I1)] public bool CgbSwitchSpeedRequest;
[MarshalAs(UnmanagedType.I1)] public bool CgbHighSpeed;
public UInt64 ApuCycleCount;
[MarshalAs(UnmanagedType.I1)] public bool DisableBootRom;
public byte IrqRequests;
@ -682,7 +684,6 @@ namespace Mesen.GUI
public struct GbCpuState
{
public UInt64 CycleCount;
public UInt16 PC;
public UInt16 SP;