PPU VRAM, partially fixes tests

This commit is contained in:
Souryo 2014-06-15 21:45:36 -04:00
parent cc674a7467
commit 3ad6418726
9 changed files with 160 additions and 71 deletions

View file

@ -27,6 +27,7 @@ class BaseMapper : public IMemoryHandler
class DefaultMapper : public BaseMapper class DefaultMapper : public BaseMapper
{ {
vector<MemoryBank*> _mappedRomBanks; vector<MemoryBank*> _mappedRomBanks;
MemoryBank *_mappedVromBank;
private: private:
void InitMapper() void InitMapper()
{ {
@ -35,23 +36,40 @@ class DefaultMapper : public BaseMapper
} else { } else {
_mappedRomBanks = { &_romBanks[0], &_romBanks[1] }; _mappedRomBanks = { &_romBanks[0], &_romBanks[1] };
} }
_mappedVromBank = &_vromBanks[0];
} }
public: public:
std::array<int, 2> GetIOAddresses() vector<std::array<uint16_t, 2>> GetRAMAddresses()
{ {
return std::array<int, 2> {{ 0x8000, 0xFFFF }}; return { { { 0x8000, 0xFFFF } } };
}
vector<std::array<uint16_t, 2>> GetVRAMAddresses()
{
return { { { 0x0000, 0x1FFF } } };
} }
uint8_t MemoryRead(uint16_t addr) uint8_t ReadRAM(uint16_t addr)
{ {
return (*_mappedRomBanks[(addr >> 14) & 0x01])[addr & 0x3FFF]; return (*_mappedRomBanks[(addr >> 14) & 0x01])[addr & 0x3FFF];
} }
void MemoryWrite(uint16_t addr, uint8_t value) void WriteRAM(uint16_t addr, uint8_t value)
{ {
(*_mappedRomBanks[(addr >> 14) & 0x01])[addr & 0x3FFF] = value; (*_mappedRomBanks[(addr >> 14) & 0x01])[addr & 0x3FFF] = value;
} }
uint8_t ReadVRAM(uint16_t addr)
{
return (*_mappedVromBank)[addr & 0x1FFF];
}
void WriteVRAM(uint16_t addr, uint8_t value)
{
(*_mappedVromBank)[addr & 0x1FFF] = value;
}
}; };
class MapperFactory class MapperFactory

View file

@ -144,7 +144,7 @@ private:
uint8_t SP() { return _state.SP; } uint8_t SP() { return _state.SP; }
void SetSP(uint8_t value) { _state.SP = value; } void SetSP(uint8_t value) { _state.SP = value; }
uint8_t PS() { return _state.PS; } uint8_t PS() { return _state.PS; }
void SetPS(uint8_t value) { _state.PS = value | PSFlags::Reserved; } void SetPS(uint8_t value) { _state.PS = (value & 0xDF) | PSFlags::Reserved; }
uint16_t PC() { return _state.PC; } uint16_t PC() { return _state.PC; }
void SetPC(uint16_t value) { _state.PC = value; } void SetPC(uint16_t value) { _state.PC = value; }
@ -182,8 +182,7 @@ private:
uint16_t GetInd() { uint16_t GetInd() {
uint16_t addr = ReadWord(); uint16_t addr = ReadWord();
if(addr & 0xFF == 0xFF) { if((addr & 0xFF) == 0xFF) {
//Emulate a CPU bug when crossing page boundary
auto lo = MemoryRead(addr); auto lo = MemoryRead(addr);
auto hi = MemoryRead(addr - 0xFF); auto hi = MemoryRead(addr - 0xFF);
return (lo | hi << 8); return (lo | hi << 8);
@ -250,7 +249,7 @@ private:
if(reg == value) { if(reg == value) {
SetFlags(PSFlags::Zero); SetFlags(PSFlags::Zero);
} }
if(result & 0x80 == 0x80) { if((result & 0x80) == 0x80) {
SetFlags(PSFlags::Negative); SetFlags(PSFlags::Negative);
} }
} }
@ -437,8 +436,8 @@ private:
void PHA() { Push(A()); } void PHA() { Push(A()); }
void PHP() { void PHP() {
SetFlags(PSFlags::Break); uint8_t flags = PS() | PSFlags::Break;
Push((uint8_t)PS()); Push((uint8_t)flags);
} }
void PLA() { SetA(Pop()); } void PLA() { SetA(Pop()); }
void PLP() { SetPS(Pop()); } void PLP() { SetPS(Pop()); }
@ -607,11 +606,11 @@ private:
void SEI() { SetFlags(PSFlags::Interrupt); } void SEI() { SetFlags(PSFlags::Interrupt); }
void BRK() { void BRK() {
SetFlags(PSFlags::Break);
Push((uint16_t)(PC() + 1)); Push((uint16_t)(PC() + 1));
Push((uint8_t)PS());
uint8_t flags = PS() | PSFlags::Break;
Push((uint8_t)flags);
SetFlags(PSFlags::Interrupt); SetFlags(PSFlags::Interrupt);
ClearFlags(PSFlags::Break);
SetPC(MemoryReadWord(0xFFFE)); SetPC(MemoryReadWord(0xFFFE));
} }

View file

@ -6,13 +6,13 @@ Console::Console(string filename)
{ {
_mapper = MapperFactory::InitializeFromFile(filename); _mapper = MapperFactory::InitializeFromFile(filename);
_memoryManager.RegisterIODevice(_mapper.get()); _memoryManager.RegisterIODevice(_mapper.get());
_memoryManager.RegisterIODevice(&_ppu);
_cpu.reset(new CPU(&_memoryManager)); _cpu.reset(new CPU(&_memoryManager));
_ppu.reset(new PPU(&_memoryManager));
_memoryManager.RegisterIODevice(_ppu.get());
} }
Console::~Console() Console::~Console()
{ {
_cpu.release();
} }
void Console::Reset() void Console::Reset()
@ -24,65 +24,72 @@ void Console::Run()
{ {
while(true) { while(true) {
_cpu->Exec(); _cpu->Exec();
_ppu.Exec(); _ppu->Exec();
} }
} }
void Console::RunTest(bool callback(Console*)) void Console::RunTest(bool callback(Console*))
{ {
while(true) { while(true) {
_cpu->Exec();
_ppu.Exec();
if(callback(this)) { if(callback(this)) {
break; break;
} }
_cpu->Exec();
_ppu->Exec();
} }
} }
void Console::RunTests() void Console::RunTests()
{ {
/*Console *console = new Console("mario.nes"); vector<string> testROMs {
console->Run(); //"mario"
delete console;*/
vector<std::string> testROMs = { {
"01-basics", "01-basics",
"02-implied", "02-implied",
//"03-immediate", "03-immediate",
//"04-zero_page", "04-zero_page",
//"05-zp_xy", "05-zp_xy",
//"06-absolute", "06-absolute",
//"07-abs_xy", "07-abs_xy",
"08-ind_x", "08-ind_x",
"09-ind_y", "09-ind_y",
"10-branches", "10-branches",
"11-stack", "11-stack",
"12-jmp_jsr", "12-jmp_jsr",
"13-rts", "13-rts",
"14-rti", "14-rti",
//"15-brk", "15-brk",
"16-special" "16-special"
} }; };
for(string testROM : testROMs) { for(string testROM : testROMs) {
Console *console = new Console(string("TestSuite/") + testROM + ".nes"); Console *console = new Console(string("TestSuite/") + testROM + ".nes");
if(testROM == "nestest") { if(testROM == "nestest") {
console->RunTest([] (Console *console) { console->RunTest([] (Console *console) {
auto state = console->_cpu->GetState(); State state = console->_cpu->GetState();
std::stringstream ss; std::cout << std::hex << std::uppercase <<
ss << std::hex << (short)state.PC << " A:" << (short)state.A << " X:" << (short)state.X << " Y:" << (short)state.Y << std::endl; "A:" << std::setfill('0') << std::setw(2) << (short)state.A <<
OutputDebugStringA(ss.str().c_str()); " X:" << std::setfill('0') << std::setw(2) << (short)state.X <<
" Y:" << std::setfill('0') << std::setw(2) << (short)state.Y <<
" S:" << std::setfill('0') << std::setw(2) << (short)state.SP <<
" P:........ $" <<
std::setfill('0') << std::setw(4) << (short)state.PC <<std::endl;
return false; return false;
}); });
} else { } else {
console->RunTest([] (Console *console) { console->RunTest([] (Console *console) {
//static std::ofstream output("test.log", ios::out | ios::binary); static std::ofstream output("test.log", ios::out | ios::binary);
static bool testStarted = false; static bool testStarted = false;
uint8_t testStatus = console->_memoryManager.Read(0x6000); uint8_t testStatus = console->_memoryManager.Read(0x6000);
State state = console->_cpu->GetState(); State state = console->_cpu->GetState();
//output << std::hex << (short)state.PC << " A:" << (short)state.A << " X:" << (short)state.X << " Y:" << (short)state.Y << std::endl; /*output << std::hex << std::uppercase <<
"A:" << std::setfill('0') << std::setw(2) << (short)state.A <<
" X:" << std::setfill('0') << std::setw(2) << (short)state.X <<
" Y:" << std::setfill('0') << std::setw(2) << (short)state.Y <<
" S:" << std::setfill('0') << std::setw(2) << (short)state.SP <<
" P:........ $" <<
std::setfill('0') << std::setw(4) << (short)state.PC <<std::endl;*/
if(testStatus == 0x81) { if(testStatus == 0x81) {
//need reset //need reset

View file

@ -8,7 +8,7 @@ class Console
{ {
private: private:
unique_ptr<CPU> _cpu; unique_ptr<CPU> _cpu;
PPU _ppu; unique_ptr<PPU> _ppu;
shared_ptr<BaseMapper> _mapper; shared_ptr<BaseMapper> _mapper;
MemoryManager _memoryManager; MemoryManager _memoryManager;

View file

@ -4,8 +4,11 @@
class IMemoryHandler class IMemoryHandler
{ {
public: public:
virtual std::array<int, 2> GetIOAddresses() = 0; virtual vector<std::array<uint16_t, 2>> GetRAMAddresses() = 0;
virtual uint8_t MemoryRead(uint16_t addr) = 0; virtual vector<std::array<uint16_t, 2>> GetVRAMAddresses() { return{}; }
virtual void MemoryWrite(uint16_t addr, uint8_t value) = 0; virtual uint8_t ReadRAM(uint16_t addr) = 0;
virtual void WriteRAM(uint16_t addr, uint8_t value) = 0;
virtual uint8_t ReadVRAM(uint16_t addr) { return 0; }
virtual void WriteVRAM(uint16_t addr, uint8_t value) { }
}; };

View file

@ -3,8 +3,8 @@
uint8_t MemoryManager::ReadRegister(uint16_t addr) uint8_t MemoryManager::ReadRegister(uint16_t addr)
{ {
if(_registerHandlers[addr]) { if(_ramHandlers[addr]) {
return _registerHandlers[addr]->MemoryRead(addr); return _ramHandlers[addr]->ReadRAM(addr);
} else { } else {
return 0; return 0;
} }
@ -12,8 +12,24 @@ uint8_t MemoryManager::ReadRegister(uint16_t addr)
void MemoryManager::WriteRegister(uint16_t addr, uint8_t value) void MemoryManager::WriteRegister(uint16_t addr, uint8_t value)
{ {
if(_registerHandlers[addr]) { if(_ramHandlers[addr]) {
_registerHandlers[addr]->MemoryWrite(addr, value); _ramHandlers[addr]->WriteRAM(addr, value);
}
}
uint8_t MemoryManager::ReadMappedVRAM(uint16_t addr)
{
if(_vramHandlers[addr]) {
return _vramHandlers[addr]->ReadVRAM(addr);
} else {
return 0;
}
}
void MemoryManager::WriteMappedVRAM(uint16_t addr, uint8_t value)
{
if(_vramHandlers[addr]) {
_vramHandlers[addr]->WriteVRAM(addr, value);
} }
} }
@ -21,13 +37,19 @@ MemoryManager::MemoryManager()
{ {
_internalRAM = new uint8_t[InternalRAMSize]; _internalRAM = new uint8_t[InternalRAMSize];
_SRAM = new uint8_t[SRAMSize]; _SRAM = new uint8_t[SRAMSize];
_videoRAM = new uint8_t[VRAMSize];
_expansionRAM = new uint8_t[0x2000]; _expansionRAM = new uint8_t[0x2000];
ZeroMemory(_internalRAM, InternalRAMSize); ZeroMemory(_internalRAM, InternalRAMSize);
ZeroMemory(_SRAM, SRAMSize); ZeroMemory(_SRAM, SRAMSize);
ZeroMemory(_videoRAM, VRAMSize);
ZeroMemory(_expansionRAM, 0x2000); ZeroMemory(_expansionRAM, 0x2000);
for(int i = 0; i <= 0xFFFF; i++) { for(int i = 0; i <= 0xFFFF; i++) {
_registerHandlers.push_back(nullptr); _ramHandlers.push_back(nullptr);
}
for(int i = 0; i <= 0x3FFF; i++) {
_vramHandlers.push_back(nullptr);
} }
} }
@ -40,9 +62,18 @@ MemoryManager::~MemoryManager()
void MemoryManager::RegisterIODevice(IMemoryHandler *handler) void MemoryManager::RegisterIODevice(IMemoryHandler *handler)
{ {
std::array<int, 2> addresses = handler->GetIOAddresses(); vector<std::array<uint16_t, 2>> addresses = handler->GetRAMAddresses();
for(int i = addresses[0]; i < addresses[1]; i++) { for(std::array<uint16_t, 2> startEndAddr : addresses) {
_registerHandlers[i] = handler; for(int i = startEndAddr[0]; i <= startEndAddr[1]; i++) {
_ramHandlers[i] = handler;
}
}
addresses = handler->GetVRAMAddresses();
for(std::array<uint16_t, 2> startEndAddr : addresses) {
for(int i = startEndAddr[0]; i <= startEndAddr[1]; i++) {
_vramHandlers[i] = handler;
}
} }
} }
@ -82,4 +113,21 @@ uint16_t MemoryManager::ReadWord(uint16_t addr)
uint8_t hi = Read(addr+1); uint8_t hi = Read(addr+1);
return lo | hi << 8; return lo | hi << 8;
} }
uint8_t MemoryManager::ReadVRAM(uint16_t addr)
{
if(addr <= 0x1FFF) {
return ReadMappedVRAM(addr & 0x1FFF);
} else {
return _videoRAM[addr & 0x3FFF];
}
}
void MemoryManager::WriteVRAM(uint16_t addr, uint8_t value)
{
if(addr <= 0x1FFF) {
WriteMappedVRAM(addr, value);
} else {
_videoRAM[addr & 0x3FFF] = value;
}
}

View file

@ -8,15 +8,21 @@ class MemoryManager
private: private:
const int InternalRAMSize = 0x800; const int InternalRAMSize = 0x800;
const int SRAMSize = 0x2000; const int SRAMSize = 0x2000;
const int VRAMSize = 0x4000;
uint8_t *_internalRAM; uint8_t *_internalRAM;
uint8_t *_expansionRAM; uint8_t *_expansionRAM;
uint8_t *_SRAM; uint8_t *_SRAM;
uint8_t *_videoRAM;
vector<IMemoryHandler*> _registerHandlers; vector<IMemoryHandler*> _ramHandlers;
vector<IMemoryHandler*> _vramHandlers;
inline uint8_t ReadRegister(uint16_t addr); uint8_t ReadRegister(uint16_t addr);
inline void WriteRegister(uint16_t addr, uint8_t value); void WriteRegister(uint16_t addr, uint8_t value);
uint8_t ReadMappedVRAM(uint16_t addr);
void WriteMappedVRAM(uint16_t addr, uint8_t value);
public: public:
MemoryManager(); MemoryManager();
@ -25,8 +31,12 @@ class MemoryManager
void RegisterIODevice(IMemoryHandler *handler); void RegisterIODevice(IMemoryHandler *handler);
uint8_t Read(uint16_t addr); uint8_t Read(uint16_t addr);
void Write(uint16_t addr, uint8_t value);
uint16_t ReadWord(uint16_t addr); uint16_t ReadWord(uint16_t addr);
void Write(uint16_t addr, uint8_t value);
uint8_t ReadVRAM(uint16_t addr);
void WriteVRAM(uint16_t addr, uint8_t value);
char* GetTestResult() char* GetTestResult()
{ {

View file

@ -2,8 +2,9 @@
#include "PPU.h" #include "PPU.h"
#include "CPU.h" #include "CPU.h"
PPU::PPU() PPU::PPU(MemoryManager *memoryManager)
{ {
_memoryManager = memoryManager;
_state = {}; _state = {};
_flags = {}; _flags = {};
_statusFlags = {}; _statusFlags = {};
@ -21,7 +22,7 @@ bool PPU::CheckFlag(PPUControlFlags flag)
return false; return false;
} }
uint8_t PPU::MemoryRead(uint16_t addr) uint8_t PPU::ReadRAM(uint16_t addr)
{ {
switch(GetRegisterID(addr)) { switch(GetRegisterID(addr)) {
case PPURegisters::Control: case PPURegisters::Control:
@ -36,13 +37,14 @@ uint8_t PPU::MemoryRead(uint16_t addr)
return _spriteRAM[_state.SpriteRamAddr]; return _spriteRAM[_state.SpriteRamAddr];
case PPURegisters::VideoMemoryData: case PPURegisters::VideoMemoryData:
uint8_t returnValue = _memoryReadBuffer; uint8_t returnValue = _memoryReadBuffer;
_memoryReadBuffer = _videoRAM[_state.VideoRamAddr]; _memoryReadBuffer = _memoryManager->ReadVRAM(_state.VideoRamAddr);
_state.VideoRamAddr += _flags.VerticalWrite ? 32 : 1;
return returnValue; return returnValue;
} }
return 0; return 0;
} }
void PPU::MemoryWrite(uint16_t addr, uint8_t value) void PPU::WriteRAM(uint16_t addr, uint8_t value)
{ {
switch(GetRegisterID(addr)) { switch(GetRegisterID(addr)) {
case PPURegisters::Control: case PPURegisters::Control:
@ -72,7 +74,8 @@ void PPU::MemoryWrite(uint16_t addr, uint8_t value)
_writeLow = !_writeLow; _writeLow = !_writeLow;
break; break;
case PPURegisters::VideoMemoryData: case PPURegisters::VideoMemoryData:
_videoRAM[_state.VideoRamAddr&0x3FFF] = value; _memoryManager->WriteVRAM(_state.VideoRamAddr, value);
_state.VideoRamAddr += _flags.VerticalWrite ? 32 : 1;
break; break;
} }
} }
@ -125,8 +128,8 @@ void PPU::Exec()
/*if(_flags.BackgroundEnabled || _flags.SpritesEnabled) { /*if(_flags.BackgroundEnabled || _flags.SpritesEnabled) {
//p->registers.vramAddress = p->registers.vramLatch; //p->registers.vramAddress = p->registers.vramLatch;
}*/ }*/
} else if(_cycle == 339 && (_frameCount % 2 == 1)) { } else if(_cycle == 339 && _flags.BackgroundEnabled && (_frameCount % 2 == 1)) {
//Skip a cycle for odd frames //Skip a cycle for odd frames, if background drawing is enabled
_cycle++; _cycle++;
} }
} else if(_scanline < 240) { } else if(_scanline < 240) {

View file

@ -1,7 +1,7 @@
#pragma once #pragma once
#include "stdafx.h" #include "stdafx.h"
#include "IMemoryHandler.h" #include "MemoryManager.h"
enum PPURegisters enum PPURegisters
{ {
@ -53,12 +53,13 @@ struct PPUState
class PPU : public IMemoryHandler class PPU : public IMemoryHandler
{ {
private: private:
MemoryManager *_memoryManager;
PPUState _state; PPUState _state;
uint64_t _cycleCount; uint64_t _cycleCount;
uint8_t _memoryReadBuffer; uint8_t _memoryReadBuffer;
uint8_t _spriteRAM[0x100]; uint8_t _spriteRAM[0x100];
uint8_t _videoRAM[0x4000];
uint8_t *_outputBuffer; uint8_t *_outputBuffer;
@ -82,16 +83,16 @@ class PPU : public IMemoryHandler
} }
public: public:
PPU(); PPU(MemoryManager *memoryManager);
~PPU(); ~PPU();
std::array<int, 2> GetIOAddresses() vector<std::array<uint16_t, 2>> GetRAMAddresses()
{ {
return std::array<int, 2> {{ 0x2000, 0x3FFF }}; return{ { { 0x2000, 0x3FFF } }, { {0x4014, 0x4014 } } };
} }
uint8_t MemoryRead(uint16_t addr); uint8_t ReadRAM(uint16_t addr);
void MemoryWrite(uint16_t addr, uint8_t value); void WriteRAM(uint16_t addr, uint8_t value);
void Exec(); void Exec();
}; };