GB: Various emulation improvements
-Better OAM DMA emulation -Better STAT IRQ emulation -Better LCD power on state -Moved DMA logic to GbDmaController
This commit is contained in:
parent
c32472913c
commit
10be89012c
13 changed files with 263 additions and 94 deletions
|
@ -73,6 +73,7 @@
|
||||||
<ClInclude Include="GbCartFactory.h" />
|
<ClInclude Include="GbCartFactory.h" />
|
||||||
<ClInclude Include="GbCpu.h" />
|
<ClInclude Include="GbCpu.h" />
|
||||||
<ClInclude Include="GbDebugger.h" />
|
<ClInclude Include="GbDebugger.h" />
|
||||||
|
<ClInclude Include="GbDmaController.h" />
|
||||||
<ClInclude Include="GbEventManager.h" />
|
<ClInclude Include="GbEventManager.h" />
|
||||||
<ClInclude Include="GbMbc1.h" />
|
<ClInclude Include="GbMbc1.h" />
|
||||||
<ClInclude Include="GbMbc2.h" />
|
<ClInclude Include="GbMbc2.h" />
|
||||||
|
@ -280,6 +281,7 @@
|
||||||
<ClCompile Include="GbApu.cpp" />
|
<ClCompile Include="GbApu.cpp" />
|
||||||
<ClCompile Include="GbCpu.cpp" />
|
<ClCompile Include="GbCpu.cpp" />
|
||||||
<ClCompile Include="GbDebugger.cpp" />
|
<ClCompile Include="GbDebugger.cpp" />
|
||||||
|
<ClCompile Include="GbDmaController.cpp" />
|
||||||
<ClCompile Include="GbEventManager.cpp" />
|
<ClCompile Include="GbEventManager.cpp" />
|
||||||
<ClCompile Include="GbMemoryManager.cpp" />
|
<ClCompile Include="GbMemoryManager.cpp" />
|
||||||
<ClCompile Include="GbPpu.cpp" />
|
<ClCompile Include="GbPpu.cpp" />
|
||||||
|
|
|
@ -587,6 +587,9 @@
|
||||||
<ClInclude Include="BaseEventManager.h">
|
<ClInclude Include="BaseEventManager.h">
|
||||||
<Filter>Debugger\EventManager</Filter>
|
<Filter>Debugger\EventManager</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="GbDmaController.h">
|
||||||
|
<Filter>GB</Filter>
|
||||||
|
</ClInclude>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClCompile Include="stdafx.cpp" />
|
<ClCompile Include="stdafx.cpp" />
|
||||||
|
@ -932,6 +935,9 @@
|
||||||
<ClCompile Include="GbEventManager.cpp">
|
<ClCompile Include="GbEventManager.cpp">
|
||||||
<Filter>Debugger\EventManager</Filter>
|
<Filter>Debugger\EventManager</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="GbDmaController.cpp">
|
||||||
|
<Filter>GB</Filter>
|
||||||
|
</ClCompile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Filter Include="SNES">
|
<Filter Include="SNES">
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
#include "GbApu.h"
|
#include "GbApu.h"
|
||||||
#include "GbCart.h"
|
#include "GbCart.h"
|
||||||
#include "GbTimer.h"
|
#include "GbTimer.h"
|
||||||
|
#include "GbDmaController.h"
|
||||||
#include "DebugTypes.h"
|
#include "DebugTypes.h"
|
||||||
#include "GbMemoryManager.h"
|
#include "GbMemoryManager.h"
|
||||||
#include "GbCartFactory.h"
|
#include "GbCartFactory.h"
|
||||||
|
@ -88,6 +89,7 @@ void Gameboy::PowerOn()
|
||||||
|
|
||||||
//VRAM is filled with 0s by the boot rom
|
//VRAM is filled with 0s by the boot rom
|
||||||
memset(_videoRam, 0, _videoRamSize);
|
memset(_videoRam, 0, _videoRamSize);
|
||||||
|
memset(_spriteRam, 0, Gameboy::SpriteRamSize);
|
||||||
|
|
||||||
LoadBattery();
|
LoadBattery();
|
||||||
|
|
||||||
|
@ -95,8 +97,9 @@ void Gameboy::PowerOn()
|
||||||
_apu.reset(new GbApu(_console, this));
|
_apu.reset(new GbApu(_console, this));
|
||||||
_memoryManager.reset(new GbMemoryManager());
|
_memoryManager.reset(new GbMemoryManager());
|
||||||
_timer.reset(new GbTimer(_memoryManager.get(), _apu.get()));
|
_timer.reset(new GbTimer(_memoryManager.get(), _apu.get()));
|
||||||
|
_dmaController.reset(new GbDmaController(_memoryManager.get()));
|
||||||
_cart->Init(this, _memoryManager.get());
|
_cart->Init(this, _memoryManager.get());
|
||||||
_memoryManager->Init(_console, this, _cart.get(), _ppu.get(), _apu.get(), _timer.get());
|
_memoryManager->Init(_console, this, _cart.get(), _ppu.get(), _apu.get(), _timer.get(), _dmaController.get());
|
||||||
|
|
||||||
_cpu.reset(new GbCpu(_console, this, _memoryManager.get()));
|
_cpu.reset(new GbCpu(_console, this, _memoryManager.get()));
|
||||||
_ppu->Init(_console, this, _memoryManager.get(), _videoRam, _spriteRam);
|
_ppu->Init(_console, this, _memoryManager.get(), _videoRam, _spriteRam);
|
||||||
|
@ -249,6 +252,7 @@ void Gameboy::Serialize(Serializer& s)
|
||||||
s.Stream(_memoryManager.get());
|
s.Stream(_memoryManager.get());
|
||||||
s.Stream(_cart.get());
|
s.Stream(_cart.get());
|
||||||
s.Stream(_timer.get());
|
s.Stream(_timer.get());
|
||||||
|
s.Stream(_dmaController.get());
|
||||||
s.Stream(_hasBattery);
|
s.Stream(_hasBattery);
|
||||||
|
|
||||||
s.StreamArray(_cartRam, _cartRamSize);
|
s.StreamArray(_cartRam, _cartRamSize);
|
||||||
|
|
|
@ -10,6 +10,7 @@ class GbCpu;
|
||||||
class GbCart;
|
class GbCart;
|
||||||
class GbTimer;
|
class GbTimer;
|
||||||
class GbMemoryManager;
|
class GbMemoryManager;
|
||||||
|
class GbDmaController;
|
||||||
class VirtualFile;
|
class VirtualFile;
|
||||||
|
|
||||||
class Gameboy : public ISerializable
|
class Gameboy : public ISerializable
|
||||||
|
@ -26,6 +27,7 @@ private:
|
||||||
unique_ptr<GbApu> _apu;
|
unique_ptr<GbApu> _apu;
|
||||||
unique_ptr<GbCart> _cart;
|
unique_ptr<GbCart> _cart;
|
||||||
unique_ptr<GbTimer> _timer;
|
unique_ptr<GbTimer> _timer;
|
||||||
|
unique_ptr<GbDmaController> _dmaController;
|
||||||
|
|
||||||
bool _hasBattery;
|
bool _hasBattery;
|
||||||
bool _cgbMode;
|
bool _cgbMode;
|
||||||
|
|
|
@ -1010,9 +1010,9 @@ void GbCpu::CALL(uint16_t dstAddr)
|
||||||
void GbCpu::CALL(bool condition, uint16_t dstAddr)
|
void GbCpu::CALL(bool condition, uint16_t dstAddr)
|
||||||
{
|
{
|
||||||
if(condition) {
|
if(condition) {
|
||||||
|
IncCycleCount();
|
||||||
PushWord(_state.PC);
|
PushWord(_state.PC);
|
||||||
_state.PC = dstAddr;
|
_state.PC = dstAddr;
|
||||||
IncCycleCount();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
93
Core/GbDmaController.cpp
Normal file
93
Core/GbDmaController.cpp
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
#include "stdafx.h"
|
||||||
|
#include "GbDmaController.h"
|
||||||
|
#include "MessageManager.h"
|
||||||
|
#include "CpuTypes.h"
|
||||||
|
#include "GbMemoryManager.h"
|
||||||
|
|
||||||
|
GbDmaController::GbDmaController(GbMemoryManager* memoryManager)
|
||||||
|
{
|
||||||
|
_memoryManager = memoryManager;
|
||||||
|
_state = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
void GbDmaController::Exec()
|
||||||
|
{
|
||||||
|
if(_state.DmaCounter > 0) {
|
||||||
|
if(_state.DmaCounter <= 160) {
|
||||||
|
_memoryManager->Write(0xFE00 + (160 - _state.DmaCounter), _state.DmaReadBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
_state.DmaCounter--;
|
||||||
|
_state.DmaReadBuffer = _memoryManager->ReadDma((_state.OamDmaDest << 8) + (160 - _state.DmaCounter));
|
||||||
|
}
|
||||||
|
|
||||||
|
if(_state.DmaStartDelay > 0) {
|
||||||
|
if(--_state.DmaStartDelay == 0) {
|
||||||
|
_state.InternalDest = _state.OamDmaDest;
|
||||||
|
_state.DmaCounter = 161;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GbDmaController::IsOamDmaRunning()
|
||||||
|
{
|
||||||
|
return _state.DmaCounter > 0 && _state.DmaCounter < 161;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t GbDmaController::Read()
|
||||||
|
{
|
||||||
|
return _state.OamDmaDest;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GbDmaController::Write(uint8_t value)
|
||||||
|
{
|
||||||
|
_state.DmaStartDelay = 1;
|
||||||
|
_state.OamDmaDest = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t GbDmaController::ReadCgb(uint16_t addr)
|
||||||
|
{
|
||||||
|
switch(addr) {
|
||||||
|
case 0xFF51: return _state.CgbDmaSource >> 8;
|
||||||
|
case 0xFF52: return _state.CgbDmaSource & 0xFF;
|
||||||
|
case 0xFF53: return _state.CgbDmaDest >> 8;
|
||||||
|
case 0xFF54: return _state.CgbDmaDest & 0xFF;
|
||||||
|
case 0xFF55: return _state.CgbDmaLength | (_state.CgbHdmaMode ? 0x80 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 0xFF53: _state.CgbDmaDest = (_state.CgbDmaDest & 0xFF) | (value << 8); break;
|
||||||
|
case 0xFF54: _state.CgbDmaDest = (_state.CgbDmaDest & 0xFF00) | value; 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));
|
||||||
|
}
|
||||||
|
_state.CgbDmaLength = 0x7F;
|
||||||
|
} else {
|
||||||
|
MessageManager::Log("TODO HDMA");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GbDmaController::Serialize(Serializer& s)
|
||||||
|
{
|
||||||
|
s.Stream(
|
||||||
|
_state.OamDmaDest, _state.DmaStartDelay, _state.InternalDest, _state.DmaCounter, _state.DmaReadBuffer,
|
||||||
|
_state.CgbDmaDest, _state.CgbDmaLength, _state.CgbDmaSource, _state.CgbHdmaMode
|
||||||
|
);
|
||||||
|
}
|
27
Core/GbDmaController.h
Normal file
27
Core/GbDmaController.h
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
#pragma once
|
||||||
|
#include "stdafx.h"
|
||||||
|
#include "GbTypes.h"
|
||||||
|
#include "../Utilities/ISerializable.h"
|
||||||
|
|
||||||
|
class GbMemoryManager;
|
||||||
|
|
||||||
|
class GbDmaController : public ISerializable
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
GbDmaControllerState _state;
|
||||||
|
GbMemoryManager* _memoryManager;
|
||||||
|
|
||||||
|
public:
|
||||||
|
GbDmaController(GbMemoryManager* memoryManager);
|
||||||
|
void Exec();
|
||||||
|
|
||||||
|
bool IsOamDmaRunning();
|
||||||
|
|
||||||
|
uint8_t Read();
|
||||||
|
void Write(uint8_t value);
|
||||||
|
|
||||||
|
uint8_t ReadCgb(uint16_t addr);
|
||||||
|
void WriteCgb(uint16_t addr, uint8_t value);
|
||||||
|
|
||||||
|
void Serialize(Serializer& s) override;
|
||||||
|
};
|
|
@ -7,6 +7,7 @@
|
||||||
#include "GbTimer.h"
|
#include "GbTimer.h"
|
||||||
#include "GbTypes.h"
|
#include "GbTypes.h"
|
||||||
#include "GbCart.h"
|
#include "GbCart.h"
|
||||||
|
#include "GbDmaController.h"
|
||||||
#include "EmuSettings.h"
|
#include "EmuSettings.h"
|
||||||
#include "ControlManager.h"
|
#include "ControlManager.h"
|
||||||
#include "SnesController.h"
|
#include "SnesController.h"
|
||||||
|
@ -15,10 +16,9 @@
|
||||||
#include "../Utilities/Serializer.h"
|
#include "../Utilities/Serializer.h"
|
||||||
#include "../Utilities/HexUtilities.h"
|
#include "../Utilities/HexUtilities.h"
|
||||||
|
|
||||||
void GbMemoryManager::Init(Console* console, Gameboy* gameboy, GbCart* cart, GbPpu* ppu, GbApu* apu, GbTimer* timer)
|
void GbMemoryManager::Init(Console* console, Gameboy* gameboy, GbCart* cart, GbPpu* ppu, GbApu* apu, GbTimer* timer, GbDmaController* dmaController)
|
||||||
{
|
{
|
||||||
_state = {};
|
_state = {};
|
||||||
_state.DisableBootRom = true;
|
|
||||||
_state.CgbWorkRamBank = 1;
|
_state.CgbWorkRamBank = 1;
|
||||||
|
|
||||||
_prgRom = gameboy->DebugGetMemory(SnesMemoryType::GbPrgRom);
|
_prgRom = gameboy->DebugGetMemory(SnesMemoryType::GbPrgRom);
|
||||||
|
@ -29,12 +29,21 @@ void GbMemoryManager::Init(Console* console, Gameboy* gameboy, GbCart* cart, GbP
|
||||||
_workRamSize = gameboy->DebugGetMemorySize(SnesMemoryType::GbWorkRam);
|
_workRamSize = gameboy->DebugGetMemorySize(SnesMemoryType::GbWorkRam);
|
||||||
_highRam = gameboy->DebugGetMemory(SnesMemoryType::GbHighRam);
|
_highRam = gameboy->DebugGetMemory(SnesMemoryType::GbHighRam);
|
||||||
|
|
||||||
|
#ifdef USEBOOTROM
|
||||||
|
VirtualFile bootRom("Firmware\\boot.bin");
|
||||||
|
bootRom.ReadFile(_bootRom, 256);
|
||||||
|
_state.DisableBootRom = false;
|
||||||
|
#else
|
||||||
|
_state.DisableBootRom = true;
|
||||||
|
#endif
|
||||||
|
|
||||||
_apu = apu;
|
_apu = apu;
|
||||||
_ppu = ppu;
|
_ppu = ppu;
|
||||||
_gameboy = gameboy;
|
_gameboy = gameboy;
|
||||||
_cart = cart;
|
_cart = cart;
|
||||||
_timer = timer;
|
_timer = timer;
|
||||||
_console = console;
|
_console = console;
|
||||||
|
_dmaController = dmaController;
|
||||||
_controlManager = console->GetControlManager().get();
|
_controlManager = console->GetControlManager().get();
|
||||||
_settings = console->GetSettings().get();
|
_settings = console->GetSettings().get();
|
||||||
|
|
||||||
|
@ -56,17 +65,16 @@ GbMemoryManagerState GbMemoryManager::GetState()
|
||||||
|
|
||||||
void GbMemoryManager::RefreshMappings()
|
void GbMemoryManager::RefreshMappings()
|
||||||
{
|
{
|
||||||
if(!_state.DisableBootRom) {
|
|
||||||
//TODO
|
|
||||||
//Map(0x0000, 0x00FF, GbMemoryType::WorkRam, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
Map(0xC000, 0xCFFF, GbMemoryType::WorkRam, 0, false);
|
Map(0xC000, 0xCFFF, GbMemoryType::WorkRam, 0, false);
|
||||||
Map(0xD000, 0xDFFF, GbMemoryType::WorkRam, _state.CgbWorkRamBank * 0x1000, false);
|
Map(0xD000, 0xDFFF, GbMemoryType::WorkRam, _state.CgbWorkRamBank * 0x1000, false);
|
||||||
Map(0xE000, 0xEFFF, GbMemoryType::WorkRam, 0, false);
|
Map(0xE000, 0xEFFF, GbMemoryType::WorkRam, 0, false);
|
||||||
Map(0xF000, 0xFCFF, GbMemoryType::WorkRam, _state.CgbWorkRamBank * 0x1000, false);
|
Map(0xF000, 0xFFFF, GbMemoryType::WorkRam, _state.CgbWorkRamBank * 0x1000, false);
|
||||||
|
|
||||||
_cart->RefreshMappings();
|
_cart->RefreshMappings();
|
||||||
|
|
||||||
|
if(!_state.DisableBootRom) {
|
||||||
|
_reads[0] = _bootRom;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GbMemoryManager::Exec()
|
void GbMemoryManager::Exec()
|
||||||
|
@ -74,6 +82,7 @@ void GbMemoryManager::Exec()
|
||||||
_state.ApuCycleCount += _state.CgbHighSpeed ? 2 : 4;
|
_state.ApuCycleCount += _state.CgbHighSpeed ? 2 : 4;
|
||||||
_timer->Exec();
|
_timer->Exec();
|
||||||
_ppu->Exec();
|
_ppu->Exec();
|
||||||
|
_dmaController->Exec();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GbMemoryManager::MapRegisters(uint16_t start, uint16_t end, RegisterAccess access)
|
void GbMemoryManager::MapRegisters(uint16_t start, uint16_t end, RegisterAccess access)
|
||||||
|
@ -137,6 +146,23 @@ uint8_t GbMemoryManager::Read(uint16_t addr, MemoryOperationType opType)
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool GbMemoryManager::IsOamDmaRunning()
|
||||||
|
{
|
||||||
|
return _dmaController->IsOamDmaRunning();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t GbMemoryManager::ReadDma(uint16_t addr)
|
||||||
|
{
|
||||||
|
uint8_t value = 0;
|
||||||
|
if(_reads[addr >> 8]) {
|
||||||
|
value = _reads[addr >> 8][(uint8_t)addr];
|
||||||
|
} else if(addr >= 0x8000 && addr <= 0x9FFF) {
|
||||||
|
value = ReadRegister(addr);
|
||||||
|
}
|
||||||
|
_console->ProcessMemoryRead<CpuType::Gameboy>(addr, value, MemoryOperationType::DmaRead);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
void GbMemoryManager::Write(uint16_t addr, uint8_t value)
|
void GbMemoryManager::Write(uint16_t addr, uint8_t value)
|
||||||
{
|
{
|
||||||
_console->ProcessMemoryWrite<CpuType::Gameboy>(addr, value, MemoryOperationType::Write);
|
_console->ProcessMemoryWrite<CpuType::Gameboy>(addr, value, MemoryOperationType::Write);
|
||||||
|
@ -184,6 +210,8 @@ uint8_t GbMemoryManager::ReadRegister(uint16_t addr)
|
||||||
if(addr >= 0xFF00) {
|
if(addr >= 0xFF00) {
|
||||||
if(addr == 0xFFFF) {
|
if(addr == 0xFFFF) {
|
||||||
return _state.IrqEnabled; //IE - Interrupt Enable (R/W)
|
return _state.IrqEnabled; //IE - Interrupt Enable (R/W)
|
||||||
|
} else if(addr == 0xFF46) {
|
||||||
|
return _dmaController->Read();
|
||||||
} else if(addr >= 0xFF80) {
|
} else if(addr >= 0xFF80) {
|
||||||
return _highRam[addr & 0x7F]; //80-FE
|
return _highRam[addr & 0x7F]; //80-FE
|
||||||
} else if(addr >= 0xFF4D) {
|
} else if(addr >= 0xFF4D) {
|
||||||
|
@ -191,9 +219,11 @@ uint8_t GbMemoryManager::ReadRegister(uint16_t addr)
|
||||||
switch(addr) {
|
switch(addr) {
|
||||||
//FF4D - KEY1 - CGB Mode Only - Prepare Speed Switch
|
//FF4D - KEY1 - CGB Mode Only - Prepare Speed Switch
|
||||||
case 0xFF4D: return _state.CgbHighSpeed ? 0x80 : 0;
|
case 0xFF4D: return _state.CgbHighSpeed ? 0x80 : 0;
|
||||||
|
|
||||||
|
case 0xFF51: case 0xFF52: case 0xFF53: case 0xFF54: case 0xFF55: //CGB - DMA
|
||||||
|
return _dmaController->ReadCgb(addr);
|
||||||
|
|
||||||
case 0xFF4F: //CGB - VRAM bank
|
case 0xFF4F: //CGB - VRAM bank
|
||||||
case 0xFF51: case 0xFF52: case 0xFF53: case 0xFF54: case 0xFF55: //CGB - DMA
|
|
||||||
case 0xFF68: case 0xFF69: case 0xFF6A: case 0xFF6B: //CGB - Palette
|
case 0xFF68: case 0xFF69: case 0xFF6A: case 0xFF6B: //CGB - Palette
|
||||||
return _ppu->ReadCgbRegister(addr);
|
return _ppu->ReadCgbRegister(addr);
|
||||||
|
|
||||||
|
@ -202,7 +232,7 @@ uint8_t GbMemoryManager::ReadRegister(uint16_t addr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
LogDebug("[Debug] GB - Missing read handler: $" + HexUtilities::ToHex(addr));
|
LogDebug("[Debug] GB - Missing read handler: $" + HexUtilities::ToHex(addr));
|
||||||
return 0; //4D-7F
|
return 0xFF; //4D-7F, open bus
|
||||||
} else if(addr >= 0xFF40) {
|
} else if(addr >= 0xFF40) {
|
||||||
return _ppu->Read(addr); //40-4C
|
return _ppu->Read(addr); //40-4C
|
||||||
} else if(addr >= 0xFF10) {
|
} else if(addr >= 0xFF10) {
|
||||||
|
@ -211,12 +241,16 @@ uint8_t GbMemoryManager::ReadRegister(uint16_t addr)
|
||||||
//00-0F
|
//00-0F
|
||||||
switch(addr) {
|
switch(addr) {
|
||||||
case 0xFF00: return ReadInputPort(); break;
|
case 0xFF00: return ReadInputPort(); break;
|
||||||
case 0xFF01: break; //Serial
|
|
||||||
|
case 0xFF01: return 0; //SB - Serial transfer data (R/W)
|
||||||
|
case 0xFF02: return 0x7E; //SC - Serial Transfer Control (R/W)
|
||||||
|
|
||||||
case 0xFF04: case 0xFF05: case 0xFF06: case 0xFF07:
|
case 0xFF04: case 0xFF05: case 0xFF06: case 0xFF07:
|
||||||
return _timer->Read(addr);
|
return _timer->Read(addr);
|
||||||
|
|
||||||
case 0xFF0F: return _state.IrqRequests; break; //IF - Interrupt flags (R/W)
|
case 0xFF0F: return _state.IrqRequests; break; //IF - Interrupt flags (R/W)
|
||||||
|
|
||||||
|
default: return 0xFF; //Open bus
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if(addr >= 0xFE00) {
|
} else if(addr >= 0xFE00) {
|
||||||
|
@ -233,6 +267,8 @@ void GbMemoryManager::WriteRegister(uint16_t addr, uint8_t value)
|
||||||
if(addr >= 0xFF00) {
|
if(addr >= 0xFF00) {
|
||||||
if(addr == 0xFFFF) {
|
if(addr == 0xFFFF) {
|
||||||
_state.IrqEnabled = value; //IE register
|
_state.IrqEnabled = value; //IE register
|
||||||
|
} else if(addr == 0xFF46) {
|
||||||
|
_dmaController->Write(value);
|
||||||
} else if(addr >= 0xFF80) {
|
} else if(addr >= 0xFF80) {
|
||||||
_highRam[addr & 0x7F] = value; //80-FE
|
_highRam[addr & 0x7F] = value; //80-FE
|
||||||
} else if(addr >= 0xFF4D) {
|
} else if(addr >= 0xFF4D) {
|
||||||
|
@ -249,8 +285,11 @@ void GbMemoryManager::WriteRegister(uint16_t addr, uint8_t value)
|
||||||
_state.CgbSwitchSpeedRequest = (value & 0x01) != 0;
|
_state.CgbSwitchSpeedRequest = (value & 0x01) != 0;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0xFF4F: //CGB - VRAM banking
|
|
||||||
case 0xFF51: case 0xFF52: case 0xFF53: case 0xFF54: case 0xFF55: //CGB - DMA
|
case 0xFF51: case 0xFF52: case 0xFF53: case 0xFF54: case 0xFF55: //CGB - DMA
|
||||||
|
_dmaController->WriteCgb(addr, value);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0xFF4F: //CGB - VRAM banking
|
||||||
case 0xFF68: case 0xFF69: case 0xFF6A: case 0xFF6B: //CGB - Palette
|
case 0xFF68: case 0xFF69: case 0xFF6A: case 0xFF6B: //CGB - Palette
|
||||||
_ppu->WriteCgbRegister(addr, value);
|
_ppu->WriteCgbRegister(addr, value);
|
||||||
break;
|
break;
|
||||||
|
@ -364,7 +403,7 @@ uint8_t GbMemoryManager::ReadInputPort()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return result | (_state.InputSelect & 0x30);
|
return result | (_state.InputSelect & 0x30) | 0xC0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GbMemoryManager::Serialize(Serializer& s)
|
void GbMemoryManager::Serialize(Serializer& s)
|
||||||
|
@ -384,5 +423,6 @@ void GbMemoryManager::Serialize(Serializer& s)
|
||||||
for(int i = 0; i < 0x100; i++) {
|
for(int i = 0; i < 0x100; i++) {
|
||||||
Map(i*0x100, i*0x100+0xFF, _state.MemoryType[i], _state.MemoryOffset[i], _state.MemoryAccessType[i] == RegisterAccess::ReadWrite ? false : true);
|
Map(i*0x100, i*0x100+0xFF, _state.MemoryType[i], _state.MemoryOffset[i], _state.MemoryAccessType[i] == RegisterAccess::ReadWrite ? false : true);
|
||||||
}
|
}
|
||||||
|
RefreshMappings();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ class GbCart;
|
||||||
class GbPpu;
|
class GbPpu;
|
||||||
class GbApu;
|
class GbApu;
|
||||||
class GbTimer;
|
class GbTimer;
|
||||||
|
class GbDmaController;
|
||||||
|
|
||||||
class EmuSettings;
|
class EmuSettings;
|
||||||
class Console;
|
class Console;
|
||||||
|
@ -25,7 +26,9 @@ private:
|
||||||
GbApu* _apu = nullptr;
|
GbApu* _apu = nullptr;
|
||||||
GbPpu* _ppu = nullptr;
|
GbPpu* _ppu = nullptr;
|
||||||
GbTimer* _timer;
|
GbTimer* _timer;
|
||||||
|
GbDmaController* _dmaController;
|
||||||
|
|
||||||
|
uint8_t _bootRom[256];
|
||||||
uint8_t* _prgRom = nullptr;
|
uint8_t* _prgRom = nullptr;
|
||||||
uint32_t _prgRomSize = 0;
|
uint32_t _prgRomSize = 0;
|
||||||
uint8_t* _workRam = nullptr;
|
uint8_t* _workRam = nullptr;
|
||||||
|
@ -44,7 +47,7 @@ public:
|
||||||
|
|
||||||
GbMemoryManagerState GetState();
|
GbMemoryManagerState GetState();
|
||||||
|
|
||||||
void Init(Console* console, Gameboy* gameboy, GbCart* cart, GbPpu* ppu, GbApu* apu, GbTimer* timer);
|
void Init(Console* console, Gameboy* gameboy, GbCart* cart, GbPpu* ppu, GbApu* apu, GbTimer* timer, GbDmaController* dmaController);
|
||||||
void MapRegisters(uint16_t start, uint16_t end, RegisterAccess access);
|
void MapRegisters(uint16_t start, uint16_t end, RegisterAccess access);
|
||||||
void Map(uint16_t start, uint16_t end, GbMemoryType type, uint32_t offset, bool readonly);
|
void Map(uint16_t start, uint16_t end, GbMemoryType type, uint32_t offset, bool readonly);
|
||||||
void Unmap(uint16_t start, uint16_t end);
|
void Unmap(uint16_t start, uint16_t end);
|
||||||
|
@ -53,6 +56,8 @@ public:
|
||||||
void Exec();
|
void Exec();
|
||||||
|
|
||||||
uint8_t Read(uint16_t addr, MemoryOperationType opType);
|
uint8_t Read(uint16_t addr, MemoryOperationType opType);
|
||||||
|
bool IsOamDmaRunning();
|
||||||
|
uint8_t ReadDma(uint16_t addr);
|
||||||
void Write(uint16_t addr, uint8_t value);
|
void Write(uint16_t addr, uint8_t value);
|
||||||
|
|
||||||
uint8_t ReadRegister(uint16_t addr);
|
uint8_t ReadRegister(uint16_t addr);
|
||||||
|
|
119
Core/GbPpu.cpp
119
Core/GbPpu.cpp
|
@ -21,7 +21,7 @@ void GbPpu::Init(Console* console, Gameboy* gameboy, GbMemoryManager* memoryMana
|
||||||
_oam = oam;
|
_oam = oam;
|
||||||
|
|
||||||
_state = {};
|
_state = {};
|
||||||
_state.Mode = PpuMode::OamEvaluation;
|
_state.Mode = PpuMode::HBlank;
|
||||||
_lastFrameTime = 0;
|
_lastFrameTime = 0;
|
||||||
|
|
||||||
_outputBuffers[0] = new uint16_t[256 * 240];
|
_outputBuffers[0] = new uint16_t[256 * 240];
|
||||||
|
@ -92,13 +92,20 @@ void GbPpu::Exec()
|
||||||
void GbPpu::ExecCycle()
|
void GbPpu::ExecCycle()
|
||||||
{
|
{
|
||||||
_state.Cycle++;
|
_state.Cycle++;
|
||||||
|
|
||||||
|
PpuMode mode = _state.Mode;
|
||||||
|
bool lyCoincidenceFlag = _state.LyCoincidenceFlag;
|
||||||
|
_state.LyCoincidenceFlag = (_state.LyCompare == _state.Scanline);
|
||||||
|
|
||||||
if(_state.Cycle == 456) {
|
if(_state.Cycle == 456) {
|
||||||
_state.Cycle = 0;
|
_state.Cycle = 0;
|
||||||
|
|
||||||
_state.Scanline++;
|
_state.Scanline++;
|
||||||
_spriteCount = 0;
|
_spriteCount = 0;
|
||||||
|
|
||||||
if(_state.Scanline == 144) {
|
if(_state.Scanline < 144) {
|
||||||
|
ChangeMode(PpuMode::OamEvaluation);
|
||||||
|
} else if(_state.Scanline == 144) {
|
||||||
ChangeMode(PpuMode::VBlank);
|
ChangeMode(PpuMode::VBlank);
|
||||||
_memoryManager->RequestIrq(GbIrqSource::VerticalBlank);
|
_memoryManager->RequestIrq(GbIrqSource::VerticalBlank);
|
||||||
|
|
||||||
|
@ -113,23 +120,17 @@ void GbPpu::ExecCycle()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if(_state.Scanline < 144) {
|
if(mode != _state.Mode || _state.LyCoincidenceFlag != lyCoincidenceFlag) {
|
||||||
ChangeMode(PpuMode::OamEvaluation);
|
UpdateStatIrq();
|
||||||
}
|
|
||||||
|
|
||||||
if(_state.LyCompare == _state.Scanline && (_state.Status & GbPpuStatusFlags::CoincidenceIrq)) {
|
|
||||||
_memoryManager->RequestIrq(GbIrqSource::LcdStat);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_console->ProcessPpuCycle(_state.Scanline, _state.Cycle);
|
_console->ProcessPpuCycle(_state.Scanline, _state.Cycle);
|
||||||
|
|
||||||
//TODO: Dot-based renderer, currently draws at the end of the scanline
|
//TODO: Dot-based renderer, currently draws at the end of the scanline
|
||||||
if(_state.Scanline < 144) {
|
if(_state.Scanline < 144) {
|
||||||
if(_state.Cycle < 80) {
|
if(_state.Mode == PpuMode::Drawing) {
|
||||||
RunSpriteEvaluation();
|
|
||||||
} else if(_state.Mode == PpuMode::Drawing) {
|
|
||||||
bool fetchWindow = _state.WindowEnabled && _shiftedPixels >= _state.WindowX - 7 && _state.Scanline >= _state.WindowY;
|
bool fetchWindow = _state.WindowEnabled && _shiftedPixels >= _state.WindowX - 7 && _state.Scanline >= _state.WindowY;
|
||||||
if(_fetchWindow != fetchWindow) {
|
if(_fetchWindow != fetchWindow) {
|
||||||
//Switched between window & background, reset fetcher & pixel FIFO
|
//Switched between window & background, reset fetcher & pixel FIFO
|
||||||
|
@ -167,7 +168,8 @@ void GbPpu::ExecCycle()
|
||||||
}
|
}
|
||||||
rgbColor = palette[entry.Color];
|
rgbColor = palette[entry.Color];
|
||||||
}
|
}
|
||||||
_currentBuffer[outOffset] = rgbColor;
|
|
||||||
|
_currentBuffer[outOffset] = rgbColor;
|
||||||
_fifoPosition = (_fifoPosition + 1) & 0x0F;
|
_fifoPosition = (_fifoPosition + 1) & 0x0F;
|
||||||
|
|
||||||
if(_console->IsDebugging()) {
|
if(_console->IsDebugging()) {
|
||||||
|
@ -183,6 +185,8 @@ void GbPpu::ExecCycle()
|
||||||
if(_drawnPixels >= 160) {
|
if(_drawnPixels >= 160) {
|
||||||
ChangeMode(PpuMode::HBlank);
|
ChangeMode(PpuMode::HBlank);
|
||||||
}
|
}
|
||||||
|
} else if(_state.Cycle < 80) {
|
||||||
|
RunSpriteEvaluation();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -202,22 +206,26 @@ void GbPpu::RunSpriteEvaluation()
|
||||||
|
|
||||||
if(_state.Cycle == 79) {
|
if(_state.Cycle == 79) {
|
||||||
ChangeMode(PpuMode::Drawing);
|
ChangeMode(PpuMode::Drawing);
|
||||||
|
ResetRenderer();
|
||||||
//Reset fetcher & pixel FIFO
|
|
||||||
_fetcherStep = 0;
|
|
||||||
_fifoPosition = 0;
|
|
||||||
_fifoSize = 0;
|
|
||||||
_shiftedPixels = 0;
|
|
||||||
_drawnPixels = 0;
|
|
||||||
_fetchSprite = -1;
|
|
||||||
_fetchWindow = false;
|
|
||||||
_fetchColumn = _state.ScrollX / 8;
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
//Hardware probably reads sprite Y and loads the X counter with the value on the next cycle
|
//Hardware probably reads sprite Y and loads the X counter with the value on the next cycle
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GbPpu::ResetRenderer()
|
||||||
|
{
|
||||||
|
//Reset fetcher & pixel FIFO
|
||||||
|
_fetcherStep = 0;
|
||||||
|
_fifoPosition = 0;
|
||||||
|
_fifoSize = 0;
|
||||||
|
_shiftedPixels = 0;
|
||||||
|
_drawnPixels = 0;
|
||||||
|
_fetchSprite = -1;
|
||||||
|
_fetchWindow = false;
|
||||||
|
_fetchColumn = _state.ScrollX / 8;
|
||||||
|
}
|
||||||
|
|
||||||
void GbPpu::ClockTileFetcher()
|
void GbPpu::ClockTileFetcher()
|
||||||
{
|
{
|
||||||
if(_fetchSprite < 0 && _fifoSize >= 8) {
|
if(_fetchSprite < 0 && _fifoSize >= 8) {
|
||||||
|
@ -240,7 +248,7 @@ void GbPpu::ClockTileFetcher()
|
||||||
uint8_t sprTile = _oam[_fetchSprite + 2];
|
uint8_t sprTile = _oam[_fetchSprite + 2];
|
||||||
uint8_t sprAttr = _oam[_fetchSprite + 3];
|
uint8_t sprAttr = _oam[_fetchSprite + 3];
|
||||||
bool vMirror = (sprAttr & 0x40) != 0;
|
bool vMirror = (sprAttr & 0x40) != 0;
|
||||||
uint16_t tileBank = (sprAttr & 0x08) ? 0x2000 : 0x0000;
|
uint16_t tileBank = _gameboy->IsCgb() ? ((sprAttr & 0x08) ? 0x2000 : 0x0000) : 0;
|
||||||
|
|
||||||
uint8_t sprOffsetY = vMirror ? (_state.LargeSprites ? 15 : 7) - (_state.Scanline - sprY) : (_state.Scanline - sprY);
|
uint8_t sprOffsetY = vMirror ? (_state.LargeSprites ? 15 : 7) - (_state.Scanline - sprY) : (_state.Scanline - sprY);
|
||||||
if(_state.LargeSprites) {
|
if(_state.LargeSprites) {
|
||||||
|
@ -353,7 +361,6 @@ void GbPpu::PushTileToPixelFifo()
|
||||||
void GbPpu::ChangeMode(PpuMode mode)
|
void GbPpu::ChangeMode(PpuMode mode)
|
||||||
{
|
{
|
||||||
_state.Mode = mode;
|
_state.Mode = mode;
|
||||||
UpdateStatIrq();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GbPpu::UpdateStatIrq()
|
void GbPpu::UpdateStatIrq()
|
||||||
|
@ -415,8 +422,9 @@ uint8_t GbPpu::Read(uint16_t addr)
|
||||||
case 0xFF41:
|
case 0xFF41:
|
||||||
//FF41 - STAT - LCDC Status (R/W)
|
//FF41 - STAT - LCDC Status (R/W)
|
||||||
return (
|
return (
|
||||||
(_state.Status & 0xF8) |
|
0x80 |
|
||||||
((_state.LyCompare == _state.Scanline) ? 0x04 : 0x00) |
|
(_state.Status & 0x78) |
|
||||||
|
(_state.LyCoincidenceFlag ? 0x04 : 0x00) |
|
||||||
(int)_state.Mode
|
(int)_state.Mode
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -444,7 +452,7 @@ void GbPpu::Write(uint16_t addr, uint8_t value)
|
||||||
_state.LcdEnabled = (value & 0x80) != 0;
|
_state.LcdEnabled = (value & 0x80) != 0;
|
||||||
|
|
||||||
if(!_state.LcdEnabled) {
|
if(!_state.LcdEnabled) {
|
||||||
//Reset LCD to top of screen when it gets turned on
|
//Reset LCD to top of screen when it gets turned off
|
||||||
_state.Cycle = 0;
|
_state.Cycle = 0;
|
||||||
_state.Scanline = 0;
|
_state.Scanline = 0;
|
||||||
ChangeMode(PpuMode::HBlank);
|
ChangeMode(PpuMode::HBlank);
|
||||||
|
@ -454,6 +462,10 @@ void GbPpu::Write(uint16_t addr, uint8_t value)
|
||||||
std::fill(_outputBuffers[0], _outputBuffers[0] + 256 * 239, 0x7FFF);
|
std::fill(_outputBuffers[0], _outputBuffers[0] + 256 * 239, 0x7FFF);
|
||||||
std::fill(_outputBuffers[1], _outputBuffers[1] + 256 * 239, 0x7FFF);
|
std::fill(_outputBuffers[1], _outputBuffers[1] + 256 * 239, 0x7FFF);
|
||||||
SendFrame();
|
SendFrame();
|
||||||
|
} else {
|
||||||
|
_state.Cycle = 6;
|
||||||
|
_state.Scanline = 0;
|
||||||
|
ChangeMode(PpuMode::Drawing);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_state.WindowTilemapSelect = (value & 0x40) != 0;
|
_state.WindowTilemapSelect = (value & 0x40) != 0;
|
||||||
|
@ -465,24 +477,10 @@ void GbPpu::Write(uint16_t addr, uint8_t value)
|
||||||
_state.BgEnabled = (value & 0x01) != 0;
|
_state.BgEnabled = (value & 0x01) != 0;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0xFF41:
|
case 0xFF41: _state.Status = value & 0xF8; break;
|
||||||
_state.Status = value & 0xF8;
|
|
||||||
UpdateStatIrq();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 0xFF42: _state.ScrollY = value; break;
|
case 0xFF42: _state.ScrollY = value; break;
|
||||||
case 0xFF43: _state.ScrollX = value; break;
|
case 0xFF43: _state.ScrollX = value; break;
|
||||||
case 0xFF45: _state.LyCompare = value; break;
|
case 0xFF45: _state.LyCompare = value; break;
|
||||||
|
|
||||||
case 0xFF46:
|
|
||||||
//OAM DMA
|
|
||||||
//TODO restrict CPU accesses to high ram during this
|
|
||||||
//TODO timing
|
|
||||||
for(int i = 0; i < 0xA0; i++) {
|
|
||||||
_memoryManager->Write(0xFE00 | i, _memoryManager->Read((value << 8) | i, MemoryOperationType::DmaRead));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 0xFF47: _state.BgPalette = value; break;
|
case 0xFF47: _state.BgPalette = value; break;
|
||||||
case 0xFF48: _state.ObjPalette0 = value; break;
|
case 0xFF48: _state.ObjPalette0 = value; break;
|
||||||
case 0xFF49: _state.ObjPalette1 = value; break;
|
case 0xFF49: _state.ObjPalette1 = value; break;
|
||||||
|
@ -514,10 +512,10 @@ void GbPpu::WriteVram(uint16_t addr, uint8_t value)
|
||||||
uint8_t GbPpu::ReadOam(uint8_t addr)
|
uint8_t GbPpu::ReadOam(uint8_t addr)
|
||||||
{
|
{
|
||||||
if(addr < 0xA0) {
|
if(addr < 0xA0) {
|
||||||
if((int)_state.Mode <= (int)PpuMode::VBlank) {
|
if((int)_state.Mode >= (int)PpuMode::OamEvaluation || _memoryManager->IsOamDmaRunning()) {
|
||||||
return _oam[addr];
|
|
||||||
} else {
|
|
||||||
return 0xFF;
|
return 0xFF;
|
||||||
|
} else {
|
||||||
|
return _oam[addr];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -534,11 +532,6 @@ uint8_t GbPpu::ReadCgbRegister(uint16_t addr)
|
||||||
{
|
{
|
||||||
switch(addr) {
|
switch(addr) {
|
||||||
case 0xFF4F: return _state.CgbVramBank;
|
case 0xFF4F: return _state.CgbVramBank;
|
||||||
case 0xFF51: return _state.CgbDmaSource >> 8;
|
|
||||||
case 0xFF52: return _state.CgbDmaSource & 0xFF;
|
|
||||||
case 0xFF53: return _state.CgbDmaDest >> 8;
|
|
||||||
case 0xFF54: return _state.CgbDmaDest & 0xFF;
|
|
||||||
case 0xFF55: return _state.CgbDmaLength | (_state.CgbHdmaMode ? 0x80 : 0);
|
|
||||||
case 0xFF68: return _state.CgbBgPalPosition | (_state.CgbBgPalAutoInc ? 0x80 : 0);
|
case 0xFF68: return _state.CgbBgPalPosition | (_state.CgbBgPalAutoInc ? 0x80 : 0);
|
||||||
case 0xFF69: return (_state.CgbBgPalettes[_state.CgbBgPalPosition >> 1] >> ((_state.CgbBgPalPosition & 0x01) ? 8 : 0) & 0xFF);
|
case 0xFF69: return (_state.CgbBgPalettes[_state.CgbBgPalPosition >> 1] >> ((_state.CgbBgPalPosition & 0x01) ? 8 : 0) & 0xFF);
|
||||||
case 0xFF6A: return _state.CgbObjPalPosition | (_state.CgbObjPalAutoInc ? 0x80 : 0);
|
case 0xFF6A: return _state.CgbObjPalPosition | (_state.CgbObjPalAutoInc ? 0x80 : 0);
|
||||||
|
@ -552,27 +545,7 @@ void GbPpu::WriteCgbRegister(uint16_t addr, uint8_t value)
|
||||||
{
|
{
|
||||||
switch(addr) {
|
switch(addr) {
|
||||||
case 0xFF4F: _state.CgbVramBank = value & 0x01; break;
|
case 0xFF4F: _state.CgbVramBank = value & 0x01; break;
|
||||||
case 0xFF51: _state.CgbDmaSource = (_state.CgbDmaSource & 0xFF) | (value << 8); break;
|
|
||||||
case 0xFF52: _state.CgbDmaSource = (_state.CgbDmaSource & 0xFF00) | value; break;
|
|
||||||
case 0xFF53: _state.CgbDmaDest = (_state.CgbDmaDest & 0xFF) | (value << 8); break;
|
|
||||||
case 0xFF54: _state.CgbDmaDest = (_state.CgbDmaDest & 0xFF00) | value; 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));
|
|
||||||
}
|
|
||||||
_state.CgbDmaLength = 0x7F;
|
|
||||||
} else {
|
|
||||||
MessageManager::Log("TODO HDMA");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 0xFF68:
|
case 0xFF68:
|
||||||
//FF68 - BCPS/BGPI - CGB Mode Only - Background Palette Index
|
//FF68 - BCPS/BGPI - CGB Mode Only - Background Palette Index
|
||||||
_state.CgbBgPalPosition = value & 0x3F;
|
_state.CgbBgPalPosition = value & 0x3F;
|
||||||
|
@ -624,7 +597,7 @@ void GbPpu::Serialize(Serializer& s)
|
||||||
_state.ScrollX, _state.ScrollY, _state.WindowX, _state.WindowY, _state.Control, _state.LcdEnabled, _state.WindowTilemapSelect,
|
_state.ScrollX, _state.ScrollY, _state.WindowX, _state.WindowY, _state.Control, _state.LcdEnabled, _state.WindowTilemapSelect,
|
||||||
_state.WindowEnabled, _state.BgTileSelect, _state.BgTilemapSelect, _state.LargeSprites, _state.SpritesEnabled, _state.BgEnabled,
|
_state.WindowEnabled, _state.BgTileSelect, _state.BgTilemapSelect, _state.LargeSprites, _state.SpritesEnabled, _state.BgEnabled,
|
||||||
_state.Status, _state.FrameCount, _lastFrameTime,
|
_state.Status, _state.FrameCount, _lastFrameTime,
|
||||||
_state.CgbBgPalAutoInc, _state.CgbBgPalPosition, _state.CgbDmaDest, _state.CgbDmaLength, _state.CgbDmaSource, _state.CgbHdmaMode,
|
_state.CgbBgPalAutoInc, _state.CgbBgPalPosition,
|
||||||
_state.CgbObjPalAutoInc, _state.CgbObjPalPosition, _state.CgbVramBank
|
_state.CgbObjPalAutoInc, _state.CgbObjPalPosition, _state.CgbVramBank
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -53,6 +53,7 @@ private:
|
||||||
|
|
||||||
void ExecCycle();
|
void ExecCycle();
|
||||||
void RunSpriteEvaluation();
|
void RunSpriteEvaluation();
|
||||||
|
void ResetRenderer();
|
||||||
void ClockTileFetcher();
|
void ClockTileFetcher();
|
||||||
void PushSpriteToPixelFifo();
|
void PushSpriteToPixelFifo();
|
||||||
void PushTileToPixelFifo();
|
void PushTileToPixelFifo();
|
||||||
|
|
|
@ -8,6 +8,11 @@ GbTimer::GbTimer(GbMemoryManager* memoryManager, GbApu* apu)
|
||||||
{
|
{
|
||||||
_apu = apu;
|
_apu = apu;
|
||||||
_memoryManager = memoryManager;
|
_memoryManager = memoryManager;
|
||||||
|
|
||||||
|
|
||||||
|
//Passes boot_div-dmgABCmgb
|
||||||
|
//But that test depends on LCD power on timings, so may be wrong.
|
||||||
|
_divider = 0x06;
|
||||||
}
|
}
|
||||||
|
|
||||||
GbTimer::~GbTimer()
|
GbTimer::~GbTimer()
|
||||||
|
|
|
@ -109,6 +109,7 @@ struct GbPpuState
|
||||||
bool StatIrqFlag;
|
bool StatIrqFlag;
|
||||||
|
|
||||||
uint8_t LyCompare;
|
uint8_t LyCompare;
|
||||||
|
bool LyCoincidenceFlag;
|
||||||
uint8_t BgPalette;
|
uint8_t BgPalette;
|
||||||
uint8_t ObjPalette0;
|
uint8_t ObjPalette0;
|
||||||
uint8_t ObjPalette1;
|
uint8_t ObjPalette1;
|
||||||
|
@ -131,11 +132,7 @@ struct GbPpuState
|
||||||
uint32_t FrameCount;
|
uint32_t FrameCount;
|
||||||
|
|
||||||
uint8_t CgbVramBank;
|
uint8_t CgbVramBank;
|
||||||
uint16_t CgbDmaSource;
|
|
||||||
uint16_t CgbDmaDest;
|
|
||||||
uint8_t CgbDmaLength;
|
|
||||||
bool CgbHdmaMode;
|
|
||||||
|
|
||||||
uint8_t CgbBgPalPosition;
|
uint8_t CgbBgPalPosition;
|
||||||
bool CgbBgPalAutoInc;
|
bool CgbBgPalAutoInc;
|
||||||
uint16_t CgbBgPalettes[4 * 8];
|
uint16_t CgbBgPalettes[4 * 8];
|
||||||
|
@ -145,6 +142,20 @@ struct GbPpuState
|
||||||
uint16_t CgbObjPalettes[4 * 8];
|
uint16_t CgbObjPalettes[4 * 8];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct GbDmaControllerState
|
||||||
|
{
|
||||||
|
uint8_t OamDmaDest;
|
||||||
|
uint8_t DmaStartDelay;
|
||||||
|
uint8_t InternalDest;
|
||||||
|
uint8_t DmaCounter;
|
||||||
|
uint8_t DmaReadBuffer;
|
||||||
|
|
||||||
|
uint16_t CgbDmaSource;
|
||||||
|
uint16_t CgbDmaDest;
|
||||||
|
uint8_t CgbDmaLength;
|
||||||
|
bool CgbHdmaMode;
|
||||||
|
};
|
||||||
|
|
||||||
struct GbSquareState
|
struct GbSquareState
|
||||||
{
|
{
|
||||||
uint16_t SweepPeriod;
|
uint16_t SweepPeriod;
|
||||||
|
|
Loading…
Add table
Reference in a new issue