GB: Improved DMA support for GBC (timing + bug fixes)
Fixes issues in Star Wars Racer
This commit is contained in:
parent
dae99e0a75
commit
06376f8ca9
9 changed files with 44 additions and 26 deletions
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
);
|
||||
|
|
|
@ -25,7 +25,6 @@ public:
|
|||
virtual ~GbCpu();
|
||||
|
||||
GbCpuState GetState();
|
||||
uint64_t GetCycleCount();
|
||||
|
||||
void Exec();
|
||||
void ExecOpCode(uint8_t opCode);
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -63,6 +63,7 @@ public:
|
|||
|
||||
void ToggleSpeed();
|
||||
bool IsHighSpeed();
|
||||
uint64_t GetCycleCount();
|
||||
uint64_t GetApuCycleCount();
|
||||
|
||||
uint8_t ReadInputPort();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue