This commit is contained in:
Souryo 2014-06-14 18:20:56 -04:00
parent 630db30484
commit 177d53e8bf
12 changed files with 314 additions and 49 deletions

View file

@ -11,18 +11,31 @@ class BaseMapper : public IMemoryHandler
vector<MemoryBank> _romBanks;
vector<MemoryBank> _vromBanks;
virtual void InitMapper() = 0;
public:
void Initialize(NESHeader header, vector<MemoryBank> romBanks, vector<MemoryBank> vromBanks)
{
_header = header;
_romBanks = romBanks;
_vromBanks = vromBanks;
InitMapper();
}
};
class DefaultMapper : public BaseMapper
{
vector<MemoryBank*> _mappedRomBanks;
private:
void InitMapper()
{
if(_romBanks.size() == 1) {
_mappedRomBanks = { &_romBanks[0], &_romBanks[0] };
} else {
_mappedRomBanks = { &_romBanks[0], &_romBanks[1] };
}
}
public:
std::array<int, 2> GetIOAddresses()
@ -32,19 +45,19 @@ class DefaultMapper : public BaseMapper
uint8_t MemoryRead(uint16_t addr)
{
return _romBanks[(addr >> 14) & 0x01][addr & 0x3FFF];
return (*_mappedRomBanks[(addr >> 14) & 0x01])[addr & 0x3FFF];
}
void MemoryWrite(uint16_t addr, uint8_t value)
{
_romBanks[(addr >> 14) & 0x01][addr & 0x3FFF] = value;
(*_mappedRomBanks[(addr >> 14) & 0x01])[addr & 0x3FFF] = value;
}
};
class MapperFactory
{
public:
static shared_ptr<BaseMapper> InitializeFromFile(char *filename)
static shared_ptr<BaseMapper> InitializeFromFile(string filename)
{
ROMLoader loader(filename);

View file

@ -2,6 +2,8 @@
#include "CPU.h"
#include "Timer.h"
uint64_t CPU::CycleCount = 0;
CPU::CPU(MemoryManager *memoryManager) : _memoryManager(memoryManager)
{
Reset();
@ -50,6 +52,7 @@ CPU::CPU(MemoryManager *memoryManager) : _memoryManager(memoryManager)
void CPU::Reset()
{
CPU::CycleCount = 0;
_state.A = 0;
_state.PC = MemoryReadWord(0xFFFC);
_state.SP = 0xFF;
@ -60,25 +63,11 @@ void CPU::Reset()
void CPU::Exec()
{
uint32_t cycleCount = 0;
Timer timer;
while(true) {
_currentPC = _state.PC;
uint8_t opCode = ReadByte();
if(_opTable[opCode] != nullptr) {
(this->*_opTable[opCode])();
cycleCount += this->_cycles[opCode];
//std::cout << "OPCode: " << std::hex << (short)opCode << " PC:" << _currentPC << std::endl;
CPU::CycleCount += this->_cycles[opCode];
} else {
//std::cout << "Invalid opcode: PC:" << _currentPC << std::endl;
throw std::exception("Invalid opcode");
//throw std::exception("Invalid opcode");
}
if(cycleCount >= 200000000) {
break;
}
}
std::wstring result = L"Frequency: " + std::to_wstring((int)(cycleCount / timer.GetElapsedMS() * 1000 / 1000000)) + L"mhz\n";
OutputDebugString(result.c_str());
}

View file

@ -40,6 +40,8 @@ private:
MemoryManager *_memoryManager = nullptr;
static uint64_t CycleCount;
uint16_t _currentPC = 0;
uint8_t _cyclePenalty = 0;
@ -599,6 +601,13 @@ private:
public:
CPU(MemoryManager *memoryManager);
static uint64_t GetCycleCount() {
return CPU::CycleCount;
}
void Reset();
void Exec();
State GetState()
{
return _state;
}
};

View file

@ -1,7 +1,8 @@
#include "stdafx.h"
#include "Console.h"
#include "Timer.h"
Console::Console(char* filename)
Console::Console(string filename)
{
_mapper = MapperFactory::InitializeFromFile(filename);
_memoryManager.RegisterIODevice(_mapper.get());
@ -21,11 +22,61 @@ void Console::Reset()
void Console::Run()
{
while(true) {
_cpu->Exec();
_ppu.Exec();
}
}
void Console::RunBenchmark()
void Console::RunTest(bool callback(Console*))
{
Console console("mario.nes");
console.Run();
while(true) {
_cpu->Exec();
_ppu.Exec();
if(callback(this)) {
break;
}
}
}
void Console::RunTests()
{
/*Console *console = new Console("mario.nes");
console->Run();
delete console;*/
vector<std::string> testROMs = { { "nestest", "01-basics", "02-implied", "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", "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());
return false;
});
} else {
console->RunTest([] (Console *console) {
static bool testStarted = false;
uint8_t testStatus = console->_memoryManager.Read(0x6000);
if(testStatus == 0x81) {
//need reset
throw std::exception("reset needed");
} else if(testStatus == 0x80) {
testStarted = true;
} else if(testStatus < 0x80 && testStarted) {
char *result = console->_memoryManager.GetTestResult();
OutputDebugStringA(result);
delete[] result;
testStarted = false;
return true;
}
return false;
});
}
delete console;
}
}

View file

@ -13,9 +13,10 @@ class Console
MemoryManager _memoryManager;
public:
Console(char* filename);
Console(string filename);
~Console();
void Run();
void RunTest(bool callback(Console*));
void Reset();
static void RunBenchmark();
static void RunTests();
};

View file

@ -21,10 +21,12 @@ MemoryManager::MemoryManager()
{
_internalRAM = new uint8_t[InternalRAMSize];
_SRAM = new uint8_t[SRAMSize];
ZeroMemory(_SRAM, SRAMSize);
_expansionRAM = new uint8_t[0x2000];
ZeroMemory(_internalRAM, InternalRAMSize);
ZeroMemory(_SRAM, SRAMSize);
ZeroMemory(_expansionRAM, 0x2000);
for(int i = 0; i < 0xFFFF; i++) {
for(int i = 0; i <= 0xFFFF; i++) {
_registerHandlers.push_back(nullptr);
}
}
@ -33,6 +35,7 @@ MemoryManager::~MemoryManager()
{
delete[] _internalRAM;
delete[] _SRAM;
delete[] _expansionRAM;
}
void MemoryManager::RegisterIODevice(IMemoryHandler *handler)
@ -50,10 +53,9 @@ uint8_t MemoryManager::Read(uint16_t addr)
} else if(addr <= 0x401F) {
return ReadRegister(addr);
} else if(addr <= 0x5FFF) {
throw std::exception("Not implemented yet");
//return ReadExpansionROM();
return _expansionRAM[addr & 0x1FFF];
} else if(addr <= 0x7FFF) {
return _SRAM[addr];
return _SRAM[addr & 0x1FFF];
} else {
return ReadRegister(addr);
}
@ -61,15 +63,14 @@ uint8_t MemoryManager::Read(uint16_t addr)
void MemoryManager::Write(uint16_t addr, uint8_t value)
{
if(addr <= 0x1FFFF) {
if(addr <= 0x1FFF) {
_internalRAM[addr & 0x07FF] = value;
} else if(addr <= 0x401F) {
WriteRegister(addr, value);
} else if(addr <= 0x5FFF) {
throw std::exception("Not implemented yet");
//return ReadExpansionROM();
_expansionRAM[addr & 0x1FFF] = value;
} else if(addr <= 0x7FFF) {
_SRAM[addr] = value;
_SRAM[addr & 0x1FFF] = value;
} else {
WriteRegister(addr, value);
}

View file

@ -7,9 +7,10 @@ class MemoryManager
{
private:
const int InternalRAMSize = 0x800;
const int SRAMSize = 0x800;
const int SRAMSize = 0x2000;
uint8_t *_internalRAM;
uint8_t *_expansionRAM;
uint8_t *_SRAM;
vector<IMemoryHandler*> _registerHandlers;
@ -26,5 +27,14 @@ class MemoryManager
uint8_t Read(uint16_t addr);
void Write(uint16_t addr, uint8_t value);
uint16_t ReadWord(uint16_t addr);
char* GetTestResult()
{
char *buffer = new char[0x2000];
for(int i = 0; i < 0x1000; i++) {
buffer[i] = Read(0x6004 + i);
}
return buffer;
}
};

View file

@ -1,10 +1,22 @@
#include "stdafx.h"
#include "PPU.h"
#include "CPU.h"
PPU::PPU()
{
_state = {};
_state.Status |= 0xFF;
_outputBuffer = new uint8_t[256 * 240 * 4];
}
PPU::~PPU()
{
delete[] _outputBuffer;
}
bool PPU::CheckFlag(PPUControlFlags flag)
{
return false;
}
uint8_t PPU::MemoryRead(uint16_t addr)
@ -13,18 +25,149 @@ uint8_t PPU::MemoryRead(uint16_t addr)
case PPURegisters::Control:
return (uint8_t)_state.Control;
case PPURegisters::Control2:
return (uint8_t)(_state.Control >> 8);
return (uint8_t)_state.Control2;
case PPURegisters::Status:
UpdateStatusFlag();
return _state.Status;
case PPURegisters::SpriteData:
return _spriteRAM[_state.SpriteRamAddr];
case PPURegisters::VideoMemoryData:
return _videoRAM[_state.VideoRamAddr];
uint8_t returnValue = _memoryReadBuffer;
_memoryReadBuffer = _videoRAM[_state.VideoRamAddr];
return returnValue;
}
return 0;
}
void PPU::MemoryWrite(uint16_t addr, uint8_t value)
{
static bool videoLow = true;
switch(GetRegisterID(addr)) {
case PPURegisters::Control:
_state.Control = value;
UpdateFlags();
break;
case PPURegisters::Control2:
_state.Control2 = value;
UpdateFlags();
break;
case PPURegisters::SpriteAddr:
_state.SpriteRamAddr = value;
break;
case PPURegisters::SpriteData:
_spriteRAM[_state.SpriteRamAddr] = value;
break;
case PPURegisters::ScrollOffsets:
break;
case PPURegisters::VideoMemoryAddr:
if(videoLow) {
_state.VideoRamAddr &= 0xFF00;
_state.VideoRamAddr |= value;
videoLow = false;
} else {
_state.VideoRamAddr |= value<<8;
videoLow = true;
}
break;
case PPURegisters::VideoMemoryData:
_videoRAM[_state.VideoRamAddr] = value;
break;
}
}
void PPU::UpdateFlags()
{
uint8_t nameTable = (_state.Control & 0x03);
switch(nameTable) {
case 0: _flags.NameTableAddr = 0x2000; break;
case 1: _flags.NameTableAddr = 0x2400; break;
case 2: _flags.NameTableAddr = 0x2800; break;
case 3: _flags.NameTableAddr = 0x2C00; break;
}
_flags.VerticalWrite = (_state.Control & 0x04) == 0x04;
_flags.SpritePatternAddr = ((_state.Control & 0x08) == 0x08) ? 0x1000 : 0x0000;
_flags.BackgroundPatternAddr = ((_state.Control & 0x10) == 0x10) ? 0x1000 : 0x0000;
_flags.LargeSprites = (_state.Control & 0x20) == 0x20;
_flags.VBlank = (_state.Control & 0x80) == 0x80;
_flags.Grayscale = (_state.Control2 & 0x01) == 0x01;
_flags.BackgroundMask = (_state.Control2 & 0x02) == 0x02;
_flags.SpriteMask = (_state.Control2 & 0x04) == 0x04;
_flags.BackgroundEnabled = (_state.Control2 & 0x08) == 0x08;
_flags.SpritesEnabled = (_state.Control2 & 0x10) == 0x10;
_flags.IntensifyRed = (_state.Control2 & 0x20) == 0x20;
_flags.IntensifyGreen = (_state.Control2 & 0x40) == 0x40;
_flags.IntensifyBlue = (_state.Control2 & 0x80) == 0x80;
}
void PPU::UpdateStatusFlag()
{
_state.Status = ((uint8_t)_statusFlags.SpriteOverflow << 5) |
((uint8_t)_statusFlags.Sprite0Hit << 6) |
((uint8_t)_statusFlags.VerticalBlank << 7));
}
void PPU::Exec()
{
uint64_t equivalentCycleCount = CPU::GetCycleCount() * 3;
while(_cycleCount < equivalentCycleCount) {
if(_scanline == -1) {
if(_cycle == 1) {
_statusFlags.SpriteOverflow = false;
_statusFlags.Sprite0Hit = false;
} else if(_cycle == 304) {
// Copy scroll latch into VRAMADDR register
if(_flags.BackgroundEnabled || _flags.SpritesEnabled) {
//p->registers.vramAddress = p->registers.vramLatch;
}
}
} else if(_scanline < 240) {
if(_cycle == 254) {
if(_flags.BackgroundEnabled) {
//Ppu_renderTileRow(p);
}
if(_flags.SpritesEnabled) {
//Ppu_evaluateScanlineSprites(p, p->scanline);
}
} else if(_cycle == 256) {
if(_flags.BackgroundEnabled) {
//Ppu_updateEndScanlineRegisters(p);
}
}
} else if(_scanline == 240) {
if(_cycle == 1) {
if(!_suppressVBlank) {
// We're in VBlank
Ppu_setStatus(p, STATUS_VBLANK_STARTED);
p->cycleCount = 0;
}
if(_flags.VBlank && !_suppressNMI) {
VBlankInterrupt();
}
//Ppu_raster(p);
}
} else if(_scanline == 260) {
// End of vblank
if(_cycle == 1) {
// Clear VBlank flag
Ppu_clearStatus(p, STATUS_VBLANK_STARTED);
_cycleCount = 0;
} else if(_cycle == 341) {
_scanline = -1;
_cycle = 1;
_frameCount++;
return;
}
}
if(_cycle == 341) {
_cycle = 0;
_scanline++;
}
_cycle++;
_cycleCount++;
}
}

View file

@ -15,9 +15,36 @@ enum PPURegisters
VideoMemoryData = 0x07
};
struct PPUControlFlags
{
uint16_t NameTableAddr;
bool VerticalWrite;
uint16_t SpritePatternAddr;
uint16_t BackgroundPatternAddr;
bool LargeSprites;
bool VBlank;
bool Grayscale;
bool BackgroundMask;
bool SpriteMask;
bool BackgroundEnabled;
bool SpritesEnabled;
bool IntensifyRed;
bool IntensifyGreen;
bool IntensifyBlue;
};
struct PPUStatusFlags
{
bool SpriteOverflow;
bool Sprite0Hit;
bool VerticalBlank;
};
struct PPUState
{
uint16_t Control;
uint8_t Control;
uint8_t Control2;
uint8_t Status;
uint8_t SpriteRamAddr;
uint16_t VideoRamAddr;
@ -27,9 +54,25 @@ class PPU : public IMemoryHandler
{
private:
PPUState _state;
uint64_t _cycleCount;
uint8_t _memoryReadBuffer;
uint8_t _spriteRAM[256];
uint8_t _videoRAM[16*1024];
uint8_t *_outputBuffer;
int16_t _scanline = -1;
uint16_t _cycle = 0;
PPUControlFlags _flags = {};
PPUStatusFlags _statusFlags = {};
void PPU::UpdateStatusFlag();
void PPU::UpdateFlags();
bool PPU::CheckFlag(PPUControlFlags flag);
PPURegisters GetRegisterID(uint16_t addr)
{
return (PPURegisters)(addr & 0x07);
@ -37,6 +80,7 @@ class PPU : public IMemoryHandler
public:
PPU();
~PPU();
std::array<int, 2> GetIOAddresses()
{
@ -45,4 +89,6 @@ class PPU : public IMemoryHandler
uint8_t MemoryRead(uint16_t addr);
void MemoryWrite(uint16_t addr, uint8_t value);
void Exec();
};

View file

@ -30,7 +30,7 @@ class ROMLoader
vector<MemoryBank> _vromBanks;
public:
ROMLoader(const char* filename)
ROMLoader(string filename)
{
_romBanks.clear();
_vromBanks.clear();
@ -38,7 +38,7 @@ class ROMLoader
ifstream romFile(filename, ios::in | ios::binary);
if(!romFile) {
return;
throw std::exception("File could not be read");
}
romFile.read((char*)&_header, sizeof(NESHeader));

View file

@ -20,6 +20,7 @@
#include <thread>
#include <vector>
#include <array>
#include <sstream>
#include <windows.h>
@ -28,5 +29,6 @@ using std::shared_ptr;
using std::unique_ptr;
using std::ios;
using std::ifstream;
using std::string;
// TODO: reference additional headers your program requires here

View file

@ -41,7 +41,7 @@ namespace NES
frameCount = 0;
}*/
}
//std::this_thread::sleep_for(std::chrono::duration<int, std::milli>(50));
std::this_thread::sleep_for(std::chrono::duration<int, std::milli>(50));
}
return (int)msg.wParam;
@ -106,7 +106,7 @@ namespace NES
void MainWindow::RunBenchmark()
{
std::thread bmThread(&Console::RunBenchmark);
std::thread bmThread(&Console::RunTests);
bmThread.detach();
}