PPU VRAM, partially fixes tests
This commit is contained in:
parent
cc674a7467
commit
3ad6418726
9 changed files with 160 additions and 71 deletions
|
@ -27,6 +27,7 @@ class BaseMapper : public IMemoryHandler
|
|||
class DefaultMapper : public BaseMapper
|
||||
{
|
||||
vector<MemoryBank*> _mappedRomBanks;
|
||||
MemoryBank *_mappedVromBank;
|
||||
private:
|
||||
void InitMapper()
|
||||
{
|
||||
|
@ -35,23 +36,40 @@ class DefaultMapper : public BaseMapper
|
|||
} else {
|
||||
_mappedRomBanks = { &_romBanks[0], &_romBanks[1] };
|
||||
}
|
||||
|
||||
_mappedVromBank = &_vromBanks[0];
|
||||
}
|
||||
|
||||
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];
|
||||
}
|
||||
|
||||
void MemoryWrite(uint16_t addr, uint8_t value)
|
||||
void WriteRAM(uint16_t addr, uint8_t 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
|
||||
|
|
17
Core/CPU.h
17
Core/CPU.h
|
@ -144,7 +144,7 @@ private:
|
|||
uint8_t SP() { return _state.SP; }
|
||||
void SetSP(uint8_t value) { _state.SP = value; }
|
||||
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; }
|
||||
void SetPC(uint16_t value) { _state.PC = value; }
|
||||
|
||||
|
@ -182,8 +182,7 @@ private:
|
|||
|
||||
uint16_t GetInd() {
|
||||
uint16_t addr = ReadWord();
|
||||
if(addr & 0xFF == 0xFF) {
|
||||
//Emulate a CPU bug when crossing page boundary
|
||||
if((addr & 0xFF) == 0xFF) {
|
||||
auto lo = MemoryRead(addr);
|
||||
auto hi = MemoryRead(addr - 0xFF);
|
||||
return (lo | hi << 8);
|
||||
|
@ -250,7 +249,7 @@ private:
|
|||
if(reg == value) {
|
||||
SetFlags(PSFlags::Zero);
|
||||
}
|
||||
if(result & 0x80 == 0x80) {
|
||||
if((result & 0x80) == 0x80) {
|
||||
SetFlags(PSFlags::Negative);
|
||||
}
|
||||
}
|
||||
|
@ -437,8 +436,8 @@ private:
|
|||
|
||||
void PHA() { Push(A()); }
|
||||
void PHP() {
|
||||
SetFlags(PSFlags::Break);
|
||||
Push((uint8_t)PS());
|
||||
uint8_t flags = PS() | PSFlags::Break;
|
||||
Push((uint8_t)flags);
|
||||
}
|
||||
void PLA() { SetA(Pop()); }
|
||||
void PLP() { SetPS(Pop()); }
|
||||
|
@ -607,11 +606,11 @@ private:
|
|||
void SEI() { SetFlags(PSFlags::Interrupt); }
|
||||
|
||||
void BRK() {
|
||||
SetFlags(PSFlags::Break);
|
||||
Push((uint16_t)(PC() + 1));
|
||||
Push((uint8_t)PS());
|
||||
|
||||
uint8_t flags = PS() | PSFlags::Break;
|
||||
Push((uint8_t)flags);
|
||||
SetFlags(PSFlags::Interrupt);
|
||||
ClearFlags(PSFlags::Break);
|
||||
|
||||
SetPC(MemoryReadWord(0xFFFE));
|
||||
}
|
||||
|
|
|
@ -6,13 +6,13 @@ Console::Console(string filename)
|
|||
{
|
||||
_mapper = MapperFactory::InitializeFromFile(filename);
|
||||
_memoryManager.RegisterIODevice(_mapper.get());
|
||||
_memoryManager.RegisterIODevice(&_ppu);
|
||||
_cpu.reset(new CPU(&_memoryManager));
|
||||
_ppu.reset(new PPU(&_memoryManager));
|
||||
_memoryManager.RegisterIODevice(_ppu.get());
|
||||
}
|
||||
|
||||
Console::~Console()
|
||||
{
|
||||
_cpu.release();
|
||||
}
|
||||
|
||||
void Console::Reset()
|
||||
|
@ -24,65 +24,72 @@ void Console::Run()
|
|||
{
|
||||
while(true) {
|
||||
_cpu->Exec();
|
||||
_ppu.Exec();
|
||||
_ppu->Exec();
|
||||
}
|
||||
}
|
||||
|
||||
void Console::RunTest(bool callback(Console*))
|
||||
{
|
||||
while(true) {
|
||||
_cpu->Exec();
|
||||
_ppu.Exec();
|
||||
|
||||
if(callback(this)) {
|
||||
break;
|
||||
}
|
||||
|
||||
_cpu->Exec();
|
||||
_ppu->Exec();
|
||||
}
|
||||
}
|
||||
|
||||
void Console::RunTests()
|
||||
{
|
||||
/*Console *console = new Console("mario.nes");
|
||||
console->Run();
|
||||
delete console;*/
|
||||
|
||||
vector<std::string> testROMs = { {
|
||||
vector<string> testROMs {
|
||||
//"mario"
|
||||
"01-basics",
|
||||
"02-implied",
|
||||
//"03-immediate",
|
||||
//"04-zero_page",
|
||||
//"05-zp_xy",
|
||||
//"06-absolute",
|
||||
//"07-abs_xy",
|
||||
"03-immediate",
|
||||
"04-zero_page",
|
||||
"05-zp_xy",
|
||||
"06-absolute",
|
||||
"07-abs_xy",
|
||||
"08-ind_x",
|
||||
"09-ind_y",
|
||||
"10-branches",
|
||||
"11-stack",
|
||||
"12-jmp_jsr",
|
||||
"13-rts",
|
||||
"14-rti",
|
||||
//"15-brk",
|
||||
"14-rti",
|
||||
"15-brk",
|
||||
"16-special"
|
||||
} };
|
||||
};
|
||||
|
||||
for(string testROM : testROMs) {
|
||||
Console *console = new Console(string("TestSuite/") + testROM + ".nes");
|
||||
if(testROM == "nestest") {
|
||||
console->RunTest([] (Console *console) {
|
||||
auto state = console->_cpu->GetState();
|
||||
std::stringstream ss;
|
||||
ss << std::hex << (short)state.PC << " A:" << (short)state.A << " X:" << (short)state.X << " Y:" << (short)state.Y << std::endl;
|
||||
OutputDebugStringA(ss.str().c_str());
|
||||
State state = console->_cpu->GetState();
|
||||
std::cout << 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;
|
||||
return false;
|
||||
});
|
||||
} else {
|
||||
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;
|
||||
uint8_t testStatus = console->_memoryManager.Read(0x6000);
|
||||
|
||||
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) {
|
||||
//need reset
|
||||
|
|
|
@ -8,7 +8,7 @@ class Console
|
|||
{
|
||||
private:
|
||||
unique_ptr<CPU> _cpu;
|
||||
PPU _ppu;
|
||||
unique_ptr<PPU> _ppu;
|
||||
shared_ptr<BaseMapper> _mapper;
|
||||
MemoryManager _memoryManager;
|
||||
|
||||
|
|
|
@ -4,8 +4,11 @@
|
|||
|
||||
class IMemoryHandler
|
||||
{
|
||||
public:
|
||||
virtual std::array<int, 2> GetIOAddresses() = 0;
|
||||
virtual uint8_t MemoryRead(uint16_t addr) = 0;
|
||||
virtual void MemoryWrite(uint16_t addr, uint8_t value) = 0;
|
||||
public:
|
||||
virtual vector<std::array<uint16_t, 2>> GetRAMAddresses() = 0;
|
||||
virtual vector<std::array<uint16_t, 2>> GetVRAMAddresses() { return{}; }
|
||||
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) { }
|
||||
};
|
|
@ -3,8 +3,8 @@
|
|||
|
||||
uint8_t MemoryManager::ReadRegister(uint16_t addr)
|
||||
{
|
||||
if(_registerHandlers[addr]) {
|
||||
return _registerHandlers[addr]->MemoryRead(addr);
|
||||
if(_ramHandlers[addr]) {
|
||||
return _ramHandlers[addr]->ReadRAM(addr);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
|
@ -12,8 +12,24 @@ uint8_t MemoryManager::ReadRegister(uint16_t addr)
|
|||
|
||||
void MemoryManager::WriteRegister(uint16_t addr, uint8_t value)
|
||||
{
|
||||
if(_registerHandlers[addr]) {
|
||||
_registerHandlers[addr]->MemoryWrite(addr, value);
|
||||
if(_ramHandlers[addr]) {
|
||||
_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];
|
||||
_SRAM = new uint8_t[SRAMSize];
|
||||
_videoRAM = new uint8_t[VRAMSize];
|
||||
_expansionRAM = new uint8_t[0x2000];
|
||||
ZeroMemory(_internalRAM, InternalRAMSize);
|
||||
ZeroMemory(_SRAM, SRAMSize);
|
||||
ZeroMemory(_videoRAM, VRAMSize);
|
||||
ZeroMemory(_expansionRAM, 0x2000);
|
||||
|
||||
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)
|
||||
{
|
||||
std::array<int, 2> addresses = handler->GetIOAddresses();
|
||||
for(int i = addresses[0]; i < addresses[1]; i++) {
|
||||
_registerHandlers[i] = handler;
|
||||
vector<std::array<uint16_t, 2>> addresses = handler->GetRAMAddresses();
|
||||
for(std::array<uint16_t, 2> startEndAddr : addresses) {
|
||||
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);
|
||||
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;
|
||||
}
|
||||
}
|
|
@ -8,15 +8,21 @@ class MemoryManager
|
|||
private:
|
||||
const int InternalRAMSize = 0x800;
|
||||
const int SRAMSize = 0x2000;
|
||||
const int VRAMSize = 0x4000;
|
||||
|
||||
uint8_t *_internalRAM;
|
||||
uint8_t *_expansionRAM;
|
||||
uint8_t *_SRAM;
|
||||
uint8_t *_videoRAM;
|
||||
|
||||
vector<IMemoryHandler*> _registerHandlers;
|
||||
vector<IMemoryHandler*> _ramHandlers;
|
||||
vector<IMemoryHandler*> _vramHandlers;
|
||||
|
||||
inline uint8_t ReadRegister(uint16_t addr);
|
||||
inline void WriteRegister(uint16_t addr, uint8_t value);
|
||||
uint8_t ReadRegister(uint16_t addr);
|
||||
void WriteRegister(uint16_t addr, uint8_t value);
|
||||
|
||||
uint8_t ReadMappedVRAM(uint16_t addr);
|
||||
void WriteMappedVRAM(uint16_t addr, uint8_t value);
|
||||
|
||||
public:
|
||||
MemoryManager();
|
||||
|
@ -25,8 +31,12 @@ class MemoryManager
|
|||
void RegisterIODevice(IMemoryHandler *handler);
|
||||
|
||||
uint8_t Read(uint16_t addr);
|
||||
void Write(uint16_t addr, uint8_t value);
|
||||
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()
|
||||
{
|
||||
|
|
17
Core/PPU.cpp
17
Core/PPU.cpp
|
@ -2,8 +2,9 @@
|
|||
#include "PPU.h"
|
||||
#include "CPU.h"
|
||||
|
||||
PPU::PPU()
|
||||
PPU::PPU(MemoryManager *memoryManager)
|
||||
{
|
||||
_memoryManager = memoryManager;
|
||||
_state = {};
|
||||
_flags = {};
|
||||
_statusFlags = {};
|
||||
|
@ -21,7 +22,7 @@ bool PPU::CheckFlag(PPUControlFlags flag)
|
|||
return false;
|
||||
}
|
||||
|
||||
uint8_t PPU::MemoryRead(uint16_t addr)
|
||||
uint8_t PPU::ReadRAM(uint16_t addr)
|
||||
{
|
||||
switch(GetRegisterID(addr)) {
|
||||
case PPURegisters::Control:
|
||||
|
@ -36,13 +37,14 @@ uint8_t PPU::MemoryRead(uint16_t addr)
|
|||
return _spriteRAM[_state.SpriteRamAddr];
|
||||
case PPURegisters::VideoMemoryData:
|
||||
uint8_t returnValue = _memoryReadBuffer;
|
||||
_memoryReadBuffer = _videoRAM[_state.VideoRamAddr];
|
||||
_memoryReadBuffer = _memoryManager->ReadVRAM(_state.VideoRamAddr);
|
||||
_state.VideoRamAddr += _flags.VerticalWrite ? 32 : 1;
|
||||
return returnValue;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void PPU::MemoryWrite(uint16_t addr, uint8_t value)
|
||||
void PPU::WriteRAM(uint16_t addr, uint8_t value)
|
||||
{
|
||||
switch(GetRegisterID(addr)) {
|
||||
case PPURegisters::Control:
|
||||
|
@ -72,7 +74,8 @@ void PPU::MemoryWrite(uint16_t addr, uint8_t value)
|
|||
_writeLow = !_writeLow;
|
||||
break;
|
||||
case PPURegisters::VideoMemoryData:
|
||||
_videoRAM[_state.VideoRamAddr&0x3FFF] = value;
|
||||
_memoryManager->WriteVRAM(_state.VideoRamAddr, value);
|
||||
_state.VideoRamAddr += _flags.VerticalWrite ? 32 : 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -125,8 +128,8 @@ void PPU::Exec()
|
|||
/*if(_flags.BackgroundEnabled || _flags.SpritesEnabled) {
|
||||
//p->registers.vramAddress = p->registers.vramLatch;
|
||||
}*/
|
||||
} else if(_cycle == 339 && (_frameCount % 2 == 1)) {
|
||||
//Skip a cycle for odd frames
|
||||
} else if(_cycle == 339 && _flags.BackgroundEnabled && (_frameCount % 2 == 1)) {
|
||||
//Skip a cycle for odd frames, if background drawing is enabled
|
||||
_cycle++;
|
||||
}
|
||||
} else if(_scanline < 240) {
|
||||
|
|
17
Core/PPU.h
17
Core/PPU.h
|
@ -1,7 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "IMemoryHandler.h"
|
||||
#include "MemoryManager.h"
|
||||
|
||||
enum PPURegisters
|
||||
{
|
||||
|
@ -53,12 +53,13 @@ struct PPUState
|
|||
class PPU : public IMemoryHandler
|
||||
{
|
||||
private:
|
||||
MemoryManager *_memoryManager;
|
||||
|
||||
PPUState _state;
|
||||
uint64_t _cycleCount;
|
||||
|
||||
uint8_t _memoryReadBuffer;
|
||||
uint8_t _spriteRAM[0x100];
|
||||
uint8_t _videoRAM[0x4000];
|
||||
|
||||
uint8_t *_outputBuffer;
|
||||
|
||||
|
@ -82,16 +83,16 @@ class PPU : public IMemoryHandler
|
|||
}
|
||||
|
||||
public:
|
||||
PPU();
|
||||
PPU(MemoryManager *memoryManager);
|
||||
~PPU();
|
||||
|
||||
std::array<int, 2> GetIOAddresses()
|
||||
{
|
||||
return std::array<int, 2> {{ 0x2000, 0x3FFF }};
|
||||
vector<std::array<uint16_t, 2>> GetRAMAddresses()
|
||||
{
|
||||
return{ { { 0x2000, 0x3FFF } }, { {0x4014, 0x4014 } } };
|
||||
}
|
||||
|
||||
uint8_t MemoryRead(uint16_t addr);
|
||||
void MemoryWrite(uint16_t addr, uint8_t value);
|
||||
uint8_t ReadRAM(uint16_t addr);
|
||||
void WriteRAM(uint16_t addr, uint8_t value);
|
||||
|
||||
void Exec();
|
||||
};
|
Loading…
Add table
Reference in a new issue