Fixed remaining color issues, added horizontal/vertical mirroring support
This commit is contained in:
parent
8b5618168b
commit
fdb5776856
10 changed files with 190 additions and 121 deletions
|
@ -22,6 +22,11 @@ class BaseMapper : public IMemoryHandler
|
|||
|
||||
InitMapper();
|
||||
}
|
||||
|
||||
const NESHeader GetHeader()
|
||||
{
|
||||
return _header;
|
||||
}
|
||||
};
|
||||
|
||||
class DefaultMapper : public BaseMapper
|
||||
|
@ -75,16 +80,16 @@ class DefaultMapper : public BaseMapper
|
|||
class MapperFactory
|
||||
{
|
||||
public:
|
||||
static shared_ptr<BaseMapper> InitializeFromFile(string filename)
|
||||
static unique_ptr<BaseMapper> InitializeFromFile(string filename)
|
||||
{
|
||||
ROMLoader loader(filename);
|
||||
|
||||
NESHeader header = loader.GetHeader();
|
||||
|
||||
uint8_t mapperID = header.GetMapperID();
|
||||
shared_ptr<BaseMapper> mapper = nullptr;
|
||||
unique_ptr<BaseMapper> mapper = nullptr;
|
||||
switch(mapperID) {
|
||||
case 0: mapper = shared_ptr<BaseMapper>(new DefaultMapper()); break;
|
||||
case 0: mapper = unique_ptr<BaseMapper>(new DefaultMapper()); break;
|
||||
}
|
||||
|
||||
if(!mapper) {
|
||||
|
|
|
@ -7,7 +7,6 @@ bool CPU::NMIFlag = false;
|
|||
|
||||
CPU::CPU(MemoryManager *memoryManager) : _memoryManager(memoryManager)
|
||||
{
|
||||
Reset();
|
||||
Func opTable[] = {
|
||||
// 0 1 2 3 4 5 6 7 8 9 A B C D E F
|
||||
&CPU::BRK, &CPU::ORA_IndX, nullptr, nullptr, nullptr, &CPU::ORA_Zero, &CPU::ASL_Zero, nullptr, &CPU::PHP, &CPU::ORA_Imm, &CPU::ASL_Acc, nullptr, nullptr, &CPU::ORA_Abs, &CPU::ASL_Abs, nullptr, //0
|
||||
|
|
|
@ -5,10 +5,13 @@
|
|||
Console::Console(string filename)
|
||||
{
|
||||
_mapper = MapperFactory::InitializeFromFile(filename);
|
||||
_memoryManager.RegisterIODevice(_mapper.get());
|
||||
_cpu.reset(new CPU(&_memoryManager));
|
||||
_ppu.reset(new PPU(&_memoryManager));
|
||||
_memoryManager.RegisterIODevice(_ppu.get());
|
||||
_memoryManager.reset(new MemoryManager(_mapper->GetHeader()));
|
||||
_cpu.reset(new CPU(_memoryManager.get()));
|
||||
_ppu.reset(new PPU(_memoryManager.get()));
|
||||
_memoryManager->RegisterIODevice(_mapper.get());
|
||||
_memoryManager->RegisterIODevice(_ppu.get());
|
||||
|
||||
Reset();
|
||||
}
|
||||
|
||||
Console::~Console()
|
||||
|
@ -40,7 +43,7 @@ void Console::RunTest(bool callback(Console*))
|
|||
_cpu->Exec();
|
||||
_ppu->Exec();
|
||||
|
||||
if(timer.GetElapsedMS() > 5000) {
|
||||
if(timer.GetElapsedMS() > 2000) {
|
||||
uint32_t frameCount = _ppu->GetFrameCount();
|
||||
std::cout << ((frameCount - lastFrameCount) / (timer.GetElapsedMS() / 1000)) << std::endl;
|
||||
timer.Reset();
|
||||
|
@ -51,8 +54,14 @@ void Console::RunTest(bool callback(Console*))
|
|||
|
||||
void Console::RunTests()
|
||||
{
|
||||
//(new Console("TestSuite/mario.nes"))->Run();
|
||||
|
||||
|
||||
vector<string> testROMs {
|
||||
"dk",
|
||||
//"Bomberman",
|
||||
"IceClimber",
|
||||
//"Excitebike",
|
||||
//"dk",
|
||||
"mario",
|
||||
"01-basics",
|
||||
"02-implied",
|
||||
|
@ -90,7 +99,7 @@ void Console::RunTests()
|
|||
console->RunTest([] (Console *console) {
|
||||
//static std::ofstream output("test.log", ios::out | ios::binary);
|
||||
static bool testStarted = false;
|
||||
uint8_t testStatus = console->_memoryManager.Read(0x6000);
|
||||
uint8_t testStatus = console->_memoryManager->Read(0x6000);
|
||||
|
||||
State state = console->_cpu->GetState();
|
||||
/*output << std::hex << std::uppercase <<
|
||||
|
@ -107,7 +116,7 @@ void Console::RunTests()
|
|||
} else if(testStatus == 0x80) {
|
||||
testStarted = true;
|
||||
} else if(testStatus < 0x80 && testStarted) {
|
||||
char *result = console->_memoryManager.GetTestResult();
|
||||
char *result = console->_memoryManager->GetTestResult();
|
||||
std::cout << result;
|
||||
delete[] result;
|
||||
testStarted = false;
|
||||
|
|
|
@ -9,8 +9,8 @@ class Console
|
|||
private:
|
||||
unique_ptr<CPU> _cpu;
|
||||
unique_ptr<PPU> _ppu;
|
||||
shared_ptr<BaseMapper> _mapper;
|
||||
MemoryManager _memoryManager;
|
||||
unique_ptr<BaseMapper> _mapper;
|
||||
unique_ptr<MemoryManager> _memoryManager;
|
||||
|
||||
public:
|
||||
Console(string filename);
|
||||
|
|
|
@ -1,5 +1,36 @@
|
|||
#include "stdafx.h"
|
||||
#include "MemoryManager.h"
|
||||
#include "ROMLoader.h"
|
||||
|
||||
MemoryManager::MemoryManager(NESHeader header)
|
||||
{
|
||||
_header = header;
|
||||
_mirroringType = _header.GetMirroringType();
|
||||
|
||||
_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++) {
|
||||
_ramHandlers.push_back(nullptr);
|
||||
}
|
||||
|
||||
for(int i = 0; i <= 0x3FFF; i++) {
|
||||
_vramHandlers.push_back(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
MemoryManager::~MemoryManager()
|
||||
{
|
||||
delete[] _internalRAM;
|
||||
delete[] _SRAM;
|
||||
delete[] _expansionRAM;
|
||||
}
|
||||
|
||||
uint8_t MemoryManager::ReadRegister(uint16_t addr)
|
||||
{
|
||||
|
@ -33,33 +64,6 @@ void MemoryManager::WriteMappedVRAM(uint16_t addr, uint8_t value)
|
|||
}
|
||||
}
|
||||
|
||||
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++) {
|
||||
_ramHandlers.push_back(nullptr);
|
||||
}
|
||||
|
||||
for(int i = 0; i <= 0x3FFF; i++) {
|
||||
_vramHandlers.push_back(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
MemoryManager::~MemoryManager()
|
||||
{
|
||||
delete[] _internalRAM;
|
||||
delete[] _SRAM;
|
||||
delete[] _expansionRAM;
|
||||
}
|
||||
|
||||
void MemoryManager::RegisterIODevice(IMemoryHandler *handler)
|
||||
{
|
||||
vector<std::array<uint16_t, 2>> addresses = handler->GetRAMAddresses();
|
||||
|
@ -124,6 +128,8 @@ uint8_t MemoryManager::ReadVRAM(uint16_t addr)
|
|||
if(addr == 0x3F10 || addr == 0x3F14 || addr == 0x3F18 || addr == 0x3F1C) {
|
||||
addr &= ~0x0010;
|
||||
}
|
||||
} else if(addr >= 0x3000) {
|
||||
addr -= 0x1000;
|
||||
}
|
||||
return _videoRAM[addr & 0x3FFF];
|
||||
}
|
||||
|
@ -140,21 +146,34 @@ void MemoryManager::WriteVRAM(uint16_t addr, uint8_t value)
|
|||
if(addr == 0x3F10 || addr == 0x3F14 || addr == 0x3F18 || addr == 0x3F1C) {
|
||||
addr &= ~0x0010;
|
||||
}
|
||||
//std::cout << "palette:" << std::hex << (short)addr << " = " << (short)value << std::endl;
|
||||
}
|
||||
if(addr == 0x2000) {
|
||||
//std::cout << "test" << std::endl;
|
||||
} else if(addr >= 0x3000) {
|
||||
addr -= 0x1000;
|
||||
}
|
||||
|
||||
_videoRAM[addr] = value;
|
||||
|
||||
if(addr >= 0x2000 && addr < 0x2400) {
|
||||
_videoRAM[addr + 0x800] = value;
|
||||
} else if(addr >= 0x2400 && addr < 0x2800) {
|
||||
_videoRAM[addr + 0x800] = value;
|
||||
} else if(addr >= 0x2800 && addr < 0x2C00) {
|
||||
_videoRAM[addr - 0x800] = value;
|
||||
} else if(addr >= 0x2C00 && addr < 0x3000) {
|
||||
_videoRAM[addr - 0x800] = value;
|
||||
if(_mirroringType == MirroringType::Vertical) {
|
||||
if(addr >= 0x2000 && addr < 0x2400) {
|
||||
_videoRAM[addr + 0x800] = value;
|
||||
} else if(addr >= 0x2400 && addr < 0x2800) {
|
||||
_videoRAM[addr + 0x800] = value;
|
||||
} else if(addr >= 0x2800 && addr < 0x2C00) {
|
||||
_videoRAM[addr - 0x800] = value;
|
||||
} else if(addr >= 0x2C00 && addr < 0x3000) {
|
||||
_videoRAM[addr - 0x800] = value;
|
||||
}
|
||||
} else if(_mirroringType == MirroringType::Horizontal) {
|
||||
if(addr >= 0x2000 && addr < 0x2400) {
|
||||
_videoRAM[addr + 0x400] = value;
|
||||
} else if(addr >= 0x2400 && addr < 0x2800) {
|
||||
_videoRAM[addr - 0x400] = value;
|
||||
} else if(addr >= 0x2800 && addr < 0x2C00) {
|
||||
_videoRAM[addr + 0x400] = value;
|
||||
} else if(addr >= 0x2C00 && addr < 0x3000) {
|
||||
_videoRAM[addr - 0x400] = value;
|
||||
}
|
||||
} else {
|
||||
throw exception("Not implemented yet");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
#include "stdafx.h"
|
||||
#include "IMemoryHandler.h"
|
||||
#include "ROMLoader.h"
|
||||
|
||||
class MemoryManager
|
||||
{
|
||||
|
@ -10,6 +11,9 @@ class MemoryManager
|
|||
const int SRAMSize = 0x2000;
|
||||
const int VRAMSize = 0x4000;
|
||||
|
||||
NESHeader _header;
|
||||
MirroringType _mirroringType;
|
||||
|
||||
uint8_t *_internalRAM;
|
||||
uint8_t *_expansionRAM;
|
||||
uint8_t *_SRAM;
|
||||
|
@ -25,7 +29,7 @@ class MemoryManager
|
|||
void WriteMappedVRAM(uint16_t addr, uint8_t value);
|
||||
|
||||
public:
|
||||
MemoryManager();
|
||||
MemoryManager(NESHeader header);
|
||||
~MemoryManager();
|
||||
|
||||
void RegisterIODevice(IMemoryHandler *handler);
|
||||
|
@ -37,7 +41,6 @@ class MemoryManager
|
|||
uint8_t ReadVRAM(uint16_t addr);
|
||||
void WriteVRAM(uint16_t addr, uint8_t value);
|
||||
|
||||
|
||||
char* GetTestResult()
|
||||
{
|
||||
char *buffer = new char[0x2000];
|
||||
|
|
136
Core/PPU.cpp
136
Core/PPU.cpp
|
@ -195,7 +195,7 @@ void PPU::IncHorizontalScrolling()
|
|||
}
|
||||
|
||||
//Take from http://wiki.nesdev.com/w/index.php/The_skinny_on_NES_scrolling#Tile_and_attribute_fetching
|
||||
uint16_t PPU::GetTileAddr()
|
||||
uint16_t PPU::GetNameTableAddr()
|
||||
{
|
||||
return 0x2000 | (_state.VideoRamAddr & 0x0FFF);
|
||||
}
|
||||
|
@ -206,6 +206,69 @@ uint16_t PPU::GetAttributeAddr()
|
|||
return 0x23C0 | (_state.VideoRamAddr & 0x0C00) | ((_state.VideoRamAddr >> 4) & 0x38) | ((_state.VideoRamAddr >> 2) & 0x07);
|
||||
}
|
||||
|
||||
void PPU::LoadTileInfo()
|
||||
{
|
||||
_previousTile = _currentTile;
|
||||
_currentTile = _nextTile;
|
||||
|
||||
uint16_t tileIndex = _memoryManager->ReadVRAM(GetNameTableAddr());
|
||||
uint16_t tileAddr = (tileIndex << 4) | (_state.VideoRamAddr >> 12) | _flags.BackgroundPatternAddr;
|
||||
|
||||
uint16_t shift = ((_state.VideoRamAddr >> 4) & 0x04) | (_state.VideoRamAddr & 0x02);
|
||||
_nextTile.PaletteOffset = ((_memoryManager->ReadVRAM(GetAttributeAddr()) >> shift) & 0x03) << 2;
|
||||
_nextTile.LowByte = _memoryManager->ReadVRAM(tileAddr);
|
||||
_nextTile.HighByte = _memoryManager->ReadVRAM(tileAddr + 8);
|
||||
}
|
||||
|
||||
void PPU::LoadNextTile()
|
||||
{
|
||||
_state.LowBitShift |= _nextTile.LowByte;
|
||||
_state.HighBitShift |= _nextTile.HighByte;
|
||||
}
|
||||
|
||||
void PPU::InitializeShiftRegisters()
|
||||
{
|
||||
_state.LowBitShift = (_currentTile.LowByte << 8) | _nextTile.LowByte;
|
||||
_state.HighBitShift = (_currentTile.HighByte << 8) | _nextTile.HighByte;
|
||||
}
|
||||
|
||||
void PPU::ShiftTileRegisters()
|
||||
{
|
||||
_state.LowBitShift <<= 1;
|
||||
_state.HighBitShift <<= 1;
|
||||
}
|
||||
|
||||
void PPU::DrawPixel()
|
||||
{
|
||||
uint32_t offset = _state.XScroll;
|
||||
uint32_t pixelColor = (((_state.LowBitShift << offset) & 0x8000) >> 15) | (((_state.HighBitShift << offset) & 0x8000) >> 14);
|
||||
|
||||
// If we're grabbing the pixel from the high part of the shift register, use the buffered palette, not the current one
|
||||
uint8_t palette = GetBGPaletteEntry(offset < 8 ? _previousTile.PaletteOffset : _currentTile.PaletteOffset, pixelColor);
|
||||
|
||||
/*
|
||||
if(p->palettebuffer[fbRow].value != 0) {
|
||||
// Pixel is already rendered and priority
|
||||
// 1 means show behind background
|
||||
continue;
|
||||
}*/
|
||||
|
||||
//p->palettebuffer[fbRow].color = PPU_PALETTE_RGB[palette % 64];
|
||||
uint32_t bufferPosition = _scanline * 256 + (_cycle - 1);
|
||||
((uint32_t*)_outputBuffer)[bufferPosition] = 0xFF000000 | PPU_PALETTE_RGB[palette % 64];
|
||||
//p->palettebuffer[fbRow].value = pixel;
|
||||
//p->palettebuffer[fbRow].pindex = -1;
|
||||
}
|
||||
|
||||
uint8_t PPU::GetBGPaletteEntry(uint8_t paletteOffset, uint8_t pixel)
|
||||
{
|
||||
if(pixel == 0) {
|
||||
return _memoryManager->ReadVRAM(0x3F00);
|
||||
} else {
|
||||
return _memoryManager->ReadVRAM(0x3F00 + paletteOffset + pixel);
|
||||
}
|
||||
}
|
||||
|
||||
void PPU::UpdateScrolling()
|
||||
{
|
||||
//For pre-render scanline & all visible scanlines
|
||||
|
@ -240,71 +303,29 @@ void PPU::ProcessPrerenderScanline()
|
|||
_scanline = 0;
|
||||
} else if(_cycle == 321 || _cycle == 329) {
|
||||
LoadTileInfo();
|
||||
LoadShiftRegisters();
|
||||
if(_cycle == 329) {
|
||||
InitializeShiftRegisters();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PPU::LoadTileInfo()
|
||||
{
|
||||
_currentTile = _nextTile;
|
||||
|
||||
uint16_t tileIndex = _memoryManager->ReadVRAM(GetTileAddr());
|
||||
uint16_t tileAddr = (tileIndex << 4) | (_state.VideoRamAddr >> 12) | _flags.BackgroundPatternAddr;
|
||||
|
||||
uint16_t addrMask = _state.VideoRamAddr & 0x3FF;
|
||||
uint16_t shift = ((addrMask >> 4) & 0x04) | (addrMask & 0x02);
|
||||
_nextTile.Attributes = ((_memoryManager->ReadVRAM(GetAttributeAddr()) >> shift) & 0x03) << 2;
|
||||
_nextTile.LowByte = _memoryManager->ReadVRAM(tileAddr);
|
||||
_nextTile.HighByte = _memoryManager->ReadVRAM(tileAddr + 8);
|
||||
}
|
||||
|
||||
void PPU::LoadShiftRegisters()
|
||||
{
|
||||
_state.LowBitShift = (_state.LowBitShift << 8) | _nextTile.LowByte;
|
||||
_state.HighBitShift = (_state.HighBitShift << 8) | _nextTile.HighByte;
|
||||
}
|
||||
|
||||
void PPU::DrawPixel()
|
||||
{
|
||||
uint32_t tileXPixel = (_cycle - 1) % 8;
|
||||
uint32_t offset = (15 - tileXPixel - _state.XScroll);
|
||||
|
||||
uint8_t pixelColor = ((_state.LowBitShift >> offset) & 0x01) | (((_state.HighBitShift >> offset) & 0x01) << 1);
|
||||
|
||||
// If we're grabbing the pixel from the high part of the shift register, use the buffered palette, not the current one
|
||||
uint32_t palette = 0;
|
||||
if(offset < 8) {
|
||||
palette = GetBGPaletteEntry(_nextTile.Attributes, pixelColor);
|
||||
} else {
|
||||
palette = GetBGPaletteEntry(_currentTile.Attributes, pixelColor);
|
||||
}
|
||||
/*
|
||||
if(p->palettebuffer[fbRow].value != 0) {
|
||||
// Pixel is already rendered and priority
|
||||
// 1 means show behind background
|
||||
continue;
|
||||
}*/
|
||||
|
||||
//p->palettebuffer[fbRow].color = PPU_PALETTE_RGB[palette % 64];
|
||||
uint32_t bufferPosition = _scanline * 256 + (_cycle - 1);
|
||||
uint32_t paletteColor = PPU_PALETTE_RGB[palette % 64];
|
||||
((uint32_t*)_outputBuffer)[bufferPosition] = paletteColor | (0xFF << 24);
|
||||
//p->palettebuffer[fbRow].value = pixel;
|
||||
//p->palettebuffer[fbRow].pindex = -1;
|
||||
}
|
||||
|
||||
void PPU::ProcessVisibleScanline()
|
||||
{
|
||||
if((_cycle - 1) % 8 == 0 && _cycle <= 250) {
|
||||
LoadShiftRegisters();
|
||||
if(_cycle > 1) {
|
||||
LoadNextTile();
|
||||
}
|
||||
LoadTileInfo();
|
||||
} else if(_cycle == 321 || _cycle == 329) {
|
||||
LoadShiftRegisters();
|
||||
LoadTileInfo();
|
||||
if(_cycle == 329) {
|
||||
InitializeShiftRegisters();
|
||||
}
|
||||
}
|
||||
|
||||
if(_cycle > 0 && _cycle <= 256) {
|
||||
DrawPixel();
|
||||
ShiftTileRegisters();
|
||||
}
|
||||
|
||||
if(IsRenderingEnabled()) {
|
||||
|
@ -325,15 +346,6 @@ void PPU::ProcessVisibleScanline()
|
|||
}
|
||||
}
|
||||
|
||||
uint8_t PPU::GetBGPaletteEntry(uint8_t a, uint16_t pix)
|
||||
{
|
||||
if(pix == 0) {
|
||||
return _memoryManager->ReadVRAM(0x3F00);
|
||||
} else {
|
||||
return _memoryManager->ReadVRAM(0x3F00 + a + pix);
|
||||
}
|
||||
}
|
||||
|
||||
void PPU::BeginVBlank()
|
||||
{
|
||||
if(_cycle == 1) {
|
||||
|
|
11
Core/PPU.h
11
Core/PPU.h
|
@ -61,7 +61,7 @@ struct TileInfo
|
|||
{
|
||||
uint8_t LowByte;
|
||||
uint8_t HighByte;
|
||||
uint8_t Attributes;
|
||||
uint8_t PaletteOffset;
|
||||
};
|
||||
|
||||
class PPU : public IMemoryHandler
|
||||
|
@ -87,6 +87,7 @@ class PPU : public IMemoryHandler
|
|||
|
||||
TileInfo _currentTile;
|
||||
TileInfo _nextTile;
|
||||
TileInfo _previousTile;
|
||||
|
||||
void UpdateStatusFlag();
|
||||
|
||||
|
@ -97,7 +98,7 @@ class PPU : public IMemoryHandler
|
|||
|
||||
void IncVerticalScrolling();
|
||||
void IncHorizontalScrolling();
|
||||
uint16_t GetTileAddr();
|
||||
uint16_t GetNameTableAddr();
|
||||
uint16_t GetAttributeAddr();
|
||||
|
||||
void UpdateScrolling();
|
||||
|
@ -107,10 +108,12 @@ class PPU : public IMemoryHandler
|
|||
void BeginVBlank();
|
||||
void EndVBlank();
|
||||
|
||||
uint8_t GetBGPaletteEntry(uint8_t a, uint16_t pix);
|
||||
uint8_t GetBGPaletteEntry(uint8_t paletteOffset, uint8_t pixel);
|
||||
|
||||
void LoadTileInfo();
|
||||
void LoadShiftRegisters();
|
||||
void ShiftTileRegisters();
|
||||
void InitializeShiftRegisters();
|
||||
void LoadNextTile();
|
||||
void DrawPixel();
|
||||
|
||||
void CopyFrame();
|
||||
|
|
|
@ -2,6 +2,13 @@
|
|||
|
||||
#include "stdafx.h"
|
||||
|
||||
enum class MirroringType
|
||||
{
|
||||
Horizontal,
|
||||
Vertical,
|
||||
FourScreens,
|
||||
};
|
||||
|
||||
struct NESHeader
|
||||
{
|
||||
char NES[4];
|
||||
|
@ -17,6 +24,15 @@ struct NESHeader
|
|||
{
|
||||
return (Flags2 & 0xF0) | (Flags1 >> 4);
|
||||
}
|
||||
|
||||
MirroringType GetMirroringType()
|
||||
{
|
||||
if(Flags1 & 0x08) {
|
||||
return MirroringType::FourScreens;
|
||||
} else {
|
||||
return Flags1 & 0x01 ? MirroringType::Vertical : MirroringType::Horizontal;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
typedef vector<uint8_t> MemoryBank;
|
||||
|
|
3
NES.sln
3
NES.sln
|
@ -28,4 +28,7 @@ Global
|
|||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(Performance) = preSolution
|
||||
HasPerformanceSessions = true
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
|
Loading…
Add table
Reference in a new issue