ROM loader, Mappers, Memory manager
This commit is contained in:
parent
cca56693f3
commit
ebb1182453
8 changed files with 253 additions and 87 deletions
25
Core/CPU.cpp
25
Core/CPU.cpp
|
@ -51,7 +51,7 @@ CPU::CPU(MemoryManager *memoryManager) : _memoryManager(memoryManager)
|
|||
void CPU::Reset()
|
||||
{
|
||||
_state.A = 0;
|
||||
_state.PC = 0x0400;
|
||||
_state.PC = MemoryReadWord(0xFFFC);
|
||||
_state.SP = 0xFF;
|
||||
_state.X = 0;
|
||||
_state.Y = 0;
|
||||
|
@ -72,8 +72,8 @@ void CPU::Exec()
|
|||
cycleCount += this->_cycles[opCode];
|
||||
//std::cout << "OPCode: " << std::hex << (short)opCode << " PC:" << _currentPC << std::endl;
|
||||
} else {
|
||||
std::cout << "Invalid opcode: PC:" << _currentPC << std::endl;
|
||||
throw;
|
||||
//std::cout << "Invalid opcode: PC:" << _currentPC << std::endl;
|
||||
throw std::exception("Invalid opcode");
|
||||
}
|
||||
lastPC = _currentPC;
|
||||
|
||||
|
@ -85,19 +85,6 @@ void CPU::Exec()
|
|||
OutputDebugString(result.c_str());
|
||||
}
|
||||
|
||||
class Test : public IMemoryHandler
|
||||
{
|
||||
public:
|
||||
int _counter = 0;
|
||||
public:
|
||||
void MemoryRead(uint16_t aa) {
|
||||
//_counter++;
|
||||
}
|
||||
void MemoryWrite(uint16_t aa) {
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
void CPU::RunBenchmark()
|
||||
{
|
||||
std::ifstream romFile("6502_functional_test.bin", std::ios::in | std::ios::binary);
|
||||
|
@ -108,10 +95,10 @@ void CPU::RunBenchmark()
|
|||
uint8_t *romMemory = new uint8_t[65536];
|
||||
romFile.read((char *)romMemory, 65536);
|
||||
|
||||
Test a;
|
||||
//Test a;
|
||||
|
||||
MemoryManager memoryManager(romMemory);
|
||||
memoryManager.OnMemoryRead()->RegisterHandler(&a, &IMemoryHandler::MemoryRead);
|
||||
MemoryManager memoryManager(MapperFactory::InitializeFromFile("mario.nes"));
|
||||
//memoryManager.OnMemoryRead()->RegisterHandler(&a, &IMemoryHandler::MemoryRead);
|
||||
CPU core(&memoryManager);
|
||||
core.Exec();
|
||||
}
|
235
Core/Memory.h
235
Core/Memory.h
|
@ -1,28 +1,234 @@
|
|||
#include "stdafx.h"
|
||||
#include "EventHandler.h"
|
||||
#include "PPU.h"
|
||||
|
||||
class IMemoryHandler
|
||||
using std::vector;
|
||||
using std::shared_ptr;
|
||||
using std::unique_ptr;
|
||||
using std::ios;
|
||||
using std::ifstream;
|
||||
|
||||
struct NESHeader
|
||||
{
|
||||
char NES[4];
|
||||
uint8_t ROMCount;
|
||||
uint8_t VROMCount;
|
||||
uint8_t Flags1;
|
||||
uint8_t Flags2;
|
||||
uint8_t RAMCount;
|
||||
uint8_t CartType;
|
||||
uint8_t Reserved[6];
|
||||
|
||||
uint8_t GetMapperID()
|
||||
{
|
||||
return (Flags2 & 0xF0) | (Flags1 >> 4);
|
||||
}
|
||||
};
|
||||
|
||||
typedef vector<uint8_t> MemoryBank;
|
||||
|
||||
class ROMLoader
|
||||
{
|
||||
private:
|
||||
const int ROMBankSize = 0x4000;
|
||||
const int VROMBankSize = 0x2000;
|
||||
NESHeader _header;
|
||||
vector<MemoryBank> _romBanks;
|
||||
vector<MemoryBank> _vromBanks;
|
||||
|
||||
public:
|
||||
ROMLoader(const char* filename)
|
||||
{
|
||||
_romBanks.clear();
|
||||
_vromBanks.clear();
|
||||
|
||||
ifstream romFile(filename, ios::in | ios::binary);
|
||||
|
||||
if(!romFile) {
|
||||
return;
|
||||
}
|
||||
|
||||
romFile.read((char*)&_header, sizeof(NESHeader));
|
||||
|
||||
uint8_t *buffer = new uint8_t[max(ROMBankSize, VROMBankSize)];
|
||||
for(int i = 0; i < _header.ROMCount; i++) {
|
||||
romFile.read((char*)buffer, ROMBankSize);
|
||||
_romBanks.push_back(MemoryBank(buffer, buffer + ROMBankSize));
|
||||
}
|
||||
|
||||
for(int i = 0; i < _header.VROMCount; i++) {
|
||||
romFile.read((char*)buffer, VROMBankSize);
|
||||
_vromBanks.push_back(MemoryBank(buffer, buffer + VROMBankSize));
|
||||
}
|
||||
|
||||
delete[] buffer;
|
||||
|
||||
romFile.close();
|
||||
}
|
||||
|
||||
vector<MemoryBank> GetROMBanks()
|
||||
{
|
||||
return _romBanks;
|
||||
}
|
||||
|
||||
vector<MemoryBank> GetVROMBanks()
|
||||
{
|
||||
return _vromBanks;
|
||||
}
|
||||
|
||||
NESHeader GetHeader()
|
||||
{
|
||||
return _header;
|
||||
}
|
||||
};
|
||||
|
||||
class BaseMapper : public IMemoryHandler
|
||||
{
|
||||
protected:
|
||||
NESHeader _header;
|
||||
vector<MemoryBank> _romBanks;
|
||||
vector<MemoryBank> _vromBanks;
|
||||
|
||||
public:
|
||||
void Initialize(NESHeader header, vector<MemoryBank> romBanks, vector<MemoryBank> vromBanks)
|
||||
{
|
||||
_header = header;
|
||||
_romBanks = romBanks;
|
||||
_vromBanks = vromBanks;
|
||||
}
|
||||
};
|
||||
|
||||
class DefaultMapper : public BaseMapper
|
||||
{
|
||||
private:
|
||||
|
||||
|
||||
public:
|
||||
uint8_t MemoryRead(uint16_t addr)
|
||||
{
|
||||
return _romBanks[addr <= 0xC000 ? 0 : 1][addr & 0x3FFF];
|
||||
}
|
||||
|
||||
void MemoryWrite(uint16_t addr, uint8_t value)
|
||||
{
|
||||
_romBanks[addr <= 0xC000 ? 0 : 1][addr & 0x3FFF] = value;
|
||||
}
|
||||
};
|
||||
|
||||
class MapperFactory
|
||||
{
|
||||
public:
|
||||
virtual void MemoryRead(uint16_t aa) = 0;
|
||||
virtual void MemoryWrite(uint16_t aa) = 0;
|
||||
static shared_ptr<BaseMapper> InitializeFromFile(char *filename)
|
||||
{
|
||||
ROMLoader loader(filename);
|
||||
|
||||
NESHeader header = loader.GetHeader();
|
||||
|
||||
uint8_t mapperID = header.GetMapperID();
|
||||
shared_ptr<BaseMapper> mapper = nullptr;
|
||||
switch(mapperID) {
|
||||
case 0: mapper = shared_ptr<BaseMapper>(new DefaultMapper()); break;
|
||||
}
|
||||
|
||||
if(!mapper) {
|
||||
throw std::exception("Unsupported mapper");
|
||||
}
|
||||
|
||||
mapper->Initialize(header, loader.GetROMBanks(), loader.GetVROMBanks());
|
||||
return mapper;
|
||||
}
|
||||
};
|
||||
|
||||
class MemoryManager
|
||||
{
|
||||
private:
|
||||
EventHandler<IMemoryHandler, uint16_t> _readHandler;
|
||||
EventHandler<IMemoryHandler, uint16_t> _writeHandler;
|
||||
uint8_t *_memory = nullptr;
|
||||
|
||||
const int InternalRAMSize = 0x800;
|
||||
const int SRAMSize = 0x800;
|
||||
|
||||
/*EventHandler<IMemoryHandler, uint16_t> _readRegisterHandler;
|
||||
EventHandler<IMemoryHandler, uint16_t> _writeRegisterHandler;*/
|
||||
|
||||
shared_ptr<BaseMapper> _mapper;
|
||||
uint8_t *_internalRAM;
|
||||
uint8_t *_SRAM;
|
||||
|
||||
PPU _ppu;
|
||||
|
||||
vector<IMemoryHandler*> _registerHandlers;
|
||||
|
||||
void RegisterIODevice(IMemoryHandler *handler, uint16_t startAddr, uint16_t endAddr)
|
||||
{
|
||||
for(int i = startAddr; i < endAddr; i++) {
|
||||
_registerHandlers[i] = handler;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t ReadRegister(uint16_t addr)
|
||||
{
|
||||
if(_registerHandlers[addr] != nullptr) {
|
||||
return _registerHandlers[addr]->MemoryRead(addr);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void WriteRegister(uint16_t addr, uint8_t value)
|
||||
{
|
||||
if(_registerHandlers[addr] != nullptr) {
|
||||
_registerHandlers[addr]->MemoryWrite(addr, value);
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
MemoryManager(uint8_t *memory) : _memory(memory) { }
|
||||
EventHandler<IMemoryHandler, uint16_t> *OnMemoryRead() { return &_readHandler; }
|
||||
EventHandler<IMemoryHandler, uint16_t> *OnMemoryWrite() { return &_writeHandler; }
|
||||
MemoryManager(shared_ptr<BaseMapper> mapper) : _mapper(mapper)
|
||||
{
|
||||
_internalRAM = new uint8_t[InternalRAMSize];
|
||||
_SRAM = new uint8_t[SRAMSize];
|
||||
ZeroMemory(_SRAM, SRAMSize);
|
||||
ZeroMemory(_internalRAM, InternalRAMSize);
|
||||
|
||||
for(int i = 0; i < 0xFFFF; i++) {
|
||||
_registerHandlers.push_back(nullptr);
|
||||
}
|
||||
|
||||
RegisterIODevice(&_ppu, 0x2000, 0x3FFF);
|
||||
}
|
||||
|
||||
~MemoryManager()
|
||||
{
|
||||
delete[] _internalRAM;
|
||||
delete[] _SRAM;
|
||||
}
|
||||
|
||||
uint8_t Read(uint16_t addr) {
|
||||
//_readHandler(addr);
|
||||
return _memory[addr];
|
||||
if(addr <= 0x1FFF) {
|
||||
return _internalRAM[addr & 0x07FF];
|
||||
} else if(addr <= 0x401F) {
|
||||
return ReadRegister(addr);
|
||||
} else if(addr <= 0x5FFF) {
|
||||
throw std::exception("Not implemented yet");
|
||||
//return ReadExpansionROM();
|
||||
} else if(addr <= 0x7FFF) {
|
||||
return _SRAM[addr];
|
||||
} else {
|
||||
return _mapper->MemoryRead(addr);
|
||||
}
|
||||
}
|
||||
|
||||
void Write(uint16_t addr, uint8_t value) {
|
||||
//_writeHandler(addr);
|
||||
if(addr <= 0x1FFFF) {
|
||||
_internalRAM[addr & 0x07FF] = value;
|
||||
} else if(addr <= 0x401F) {
|
||||
WriteRegister(addr, value);
|
||||
} else if(addr <= 0x5FFF) {
|
||||
throw std::exception("Not implemented yet");
|
||||
//return ReadExpansionROM();
|
||||
} else if(addr <= 0x7FFF) {
|
||||
_SRAM[addr] = value;
|
||||
} else {
|
||||
_mapper->MemoryWrite(addr, value);
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t ReadWord(uint16_t addr) {
|
||||
|
@ -30,10 +236,5 @@ class MemoryManager
|
|||
uint8_t hi = Read(addr+1);
|
||||
return lo | hi << 8;
|
||||
}
|
||||
|
||||
void Write(uint16_t addr, uint8_t value) {
|
||||
//_writeHandler(addr);
|
||||
_memory[addr] = value;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -19,8 +19,17 @@
|
|||
#include <memory>
|
||||
#include <thread>
|
||||
#include <list>
|
||||
#include <vector>
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
|
||||
class IMemoryHandler
|
||||
{
|
||||
public:
|
||||
virtual uint8_t MemoryRead(uint16_t addr) = 0;
|
||||
virtual void MemoryWrite(uint16_t addr, uint8_t value) = 0;
|
||||
};
|
||||
|
||||
|
||||
// TODO: reference additional headers your program requires here
|
||||
|
|
|
@ -59,7 +59,13 @@
|
|||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>DirectXTK.lib;d3d11.lib;d3dcompiler.lib;dxguid.lib;winmm.lib;comctl32.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalDependencies>d3d11.lib;d3dcompiler.lib;dxguid.lib;winmm.lib;comctl32.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<OptimizeReferences>
|
||||
</OptimizeReferences>
|
||||
<EnableCOMDATFolding>
|
||||
</EnableCOMDATFolding>
|
||||
<ImageHasSafeExceptionHandlers>
|
||||
</ImageHasSafeExceptionHandlers>
|
||||
</Link>
|
||||
<FxCompile>
|
||||
<DisableOptimizations>false</DisableOptimizations>
|
||||
|
@ -83,7 +89,7 @@
|
|||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<AdditionalDependencies>DirectXTK.lib;d3d11.lib;d3dcompiler.lib;dxguid.lib;winmm.lib;comctl32.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalDependencies>d3d11.lib;d3dcompiler.lib;dxguid.lib;winmm.lib;comctl32.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
<FxCompile>
|
||||
<ShaderType>
|
||||
|
|
|
@ -32,14 +32,14 @@ namespace NES
|
|||
TranslateMessage(&msg);
|
||||
DispatchMessage(&msg);
|
||||
} else {
|
||||
_renderer.Render();
|
||||
/*_renderer.Render();
|
||||
frameCount++;
|
||||
if(frameCount == 500) {
|
||||
double fps = (double)frameCount / (timer.GetElapsedMS() / 1000);
|
||||
OutputDebugString((std::to_wstring((int)fps) + L"\n").c_str());
|
||||
timer.Reset();
|
||||
frameCount = 0;
|
||||
}
|
||||
}*/
|
||||
}
|
||||
//std::this_thread::sleep_for(std::chrono::duration<int, std::milli>(50));
|
||||
}
|
||||
|
|
|
@ -16,40 +16,6 @@ namespace NES
|
|||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------
|
||||
// Helper for compiling shaders with D3DCompile
|
||||
//
|
||||
// With VS 11, we could load up prebuilt .cso files instead...
|
||||
//--------------------------------------------------------------------------------------
|
||||
HRESULT Renderer::CompileShaderFromFile(WCHAR* szFileName, LPCSTR szEntryPoint, LPCSTR szShaderModel, ID3DBlob** ppBlobOut)
|
||||
{
|
||||
HRESULT hr = S_OK;
|
||||
|
||||
DWORD dwShaderFlags = D3DCOMPILE_ENABLE_STRICTNESS;
|
||||
#if defined( DEBUG ) || defined( _DEBUG )
|
||||
// Set the D3DCOMPILE_DEBUG flag to embed debug information in the shaders.
|
||||
// Setting this flag improves the shader debugging experience, but still allows
|
||||
// the shaders to be optimized and to run exactly the way they will run in
|
||||
// the release configuration of this program.
|
||||
dwShaderFlags |= D3DCOMPILE_DEBUG;
|
||||
#endif
|
||||
|
||||
ID3DBlob* pErrorBlob = nullptr;
|
||||
hr = D3DCompileFromFile(szFileName, nullptr, nullptr, szEntryPoint, szShaderModel,
|
||||
dwShaderFlags, 0, ppBlobOut, &pErrorBlob);
|
||||
if(FAILED(hr)) {
|
||||
if(pErrorBlob) {
|
||||
OutputDebugStringA(reinterpret_cast<const char*>(pErrorBlob->GetBufferPointer()));
|
||||
pErrorBlob->Release();
|
||||
}
|
||||
return hr;
|
||||
}
|
||||
if(pErrorBlob) pErrorBlob->Release();
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------------------
|
||||
// Create Direct3D device and swap chain
|
||||
//--------------------------------------------------------------------------------------
|
||||
|
@ -218,12 +184,7 @@ namespace NES
|
|||
void Renderer::Render()
|
||||
{
|
||||
// Clear the back buffer
|
||||
_pImmediateContext->ClearRenderTargetView(_pRenderTargetView, Colors::MidnightBlue);
|
||||
|
||||
// Render a triangle
|
||||
/*_pImmediateContext->VSSetShader(_pVertexShader, nullptr, 0);
|
||||
_pImmediateContext->PSSetShader(_pPixelShader, nullptr, 0);
|
||||
_pImmediateContext->Draw(3, 0);*/
|
||||
//_pImmediateContext->ClearRenderTargetView(_pRenderTargetView, Colors::MidnightBlue);
|
||||
|
||||
UINT screenwidth = 320, screenheight = 240;
|
||||
|
||||
|
@ -239,10 +200,7 @@ namespace NES
|
|||
dd.DepthPitch = screenwidth* screenheight * 4;
|
||||
|
||||
_pImmediateContext->Map(_pTexture, 0, D3D11_MAP_WRITE_DISCARD, 0, &dd);
|
||||
|
||||
memcpy(dd.pData, _videoRAM, screenwidth*screenheight * 4);
|
||||
|
||||
|
||||
_pImmediateContext->Unmap(_pTexture, 0);
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -259,11 +217,11 @@ namespace NES
|
|||
ID3D11ShaderResourceView *pSRView = NULL;
|
||||
_pd3dDevice->CreateShaderResourceView(_pTexture, &srvDesc, &pSRView);
|
||||
|
||||
|
||||
/*
|
||||
D3D11_RENDER_TARGET_VIEW_DESC rtDesc;
|
||||
rtDesc.Format = desc.Format;
|
||||
rtDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;
|
||||
rtDesc.Texture2D.MipSlice = 0;
|
||||
rtDesc.Texture2D.MipSlice = 0;*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
|
|
@ -30,10 +30,9 @@ namespace NES {
|
|||
ID3D11Texture2D* _pTexture = nullptr;
|
||||
|
||||
byte* _videoRAM;
|
||||
//SpriteBatch* _sprites;
|
||||
|
||||
std::unique_ptr<SpriteBatch> _sprites;
|
||||
|
||||
HRESULT Renderer::CompileShaderFromFile(WCHAR* szFileName, LPCSTR szEntryPoint, LPCSTR szShaderModel, ID3DBlob** ppBlobOut);
|
||||
HRESULT Renderer::InitDevice();
|
||||
void Renderer::CleanupDevice();
|
||||
|
||||
|
|
|
@ -11,6 +11,12 @@
|
|||
// Windows Header Files:
|
||||
#include <windows.h>
|
||||
|
||||
#ifdef _DEBUG
|
||||
#pragma comment( lib, "DirectXTK.debug.lib" )
|
||||
#else
|
||||
#pragma comment( lib, "DirectXTK.lib" )
|
||||
#endif
|
||||
|
||||
// C RunTime Header Files
|
||||
#include <stdlib.h>
|
||||
#include <malloc.h>
|
||||
|
|
Loading…
Add table
Reference in a new issue