From f3df2ecf175e1984d4ce418bd2b2da3c66eac34d Mon Sep 17 00:00:00 2001 From: Souryo Date: Sat, 4 Jul 2015 22:21:14 -0400 Subject: [PATCH] -CPU code changes to implement all dummy reads/writes. Each memory access runs the PPU for 3 cycles (PPU is now controlled by the CPU) -Optimizations + PGO (profile guided optimization) build support --- Core/APU.cpp | 4 +- Core/BaseMapper.h | 33 +++--- Core/CPU.cpp | 115 ++++++++------------- Core/CPU.h | 154 +++++++++++++++++----------- Core/Console.cpp | 11 +- Core/MemoryManager.cpp | 46 ++++----- Core/MemoryManager.h | 2 - Core/PPU.cpp | 102 ++++++++---------- Core/PPU.h | 14 +-- NES.sln | 18 ++++ PGOHelper/PGOHelper.cpp | 14 +++ PGOHelper/PGOHelper.vcxproj | 94 +++++++++++++++++ PGOHelper/PGOHelper.vcxproj.filters | 14 +++ 13 files changed, 367 insertions(+), 254 deletions(-) create mode 100644 PGOHelper/PGOHelper.cpp create mode 100644 PGOHelper/PGOHelper.vcxproj create mode 100644 PGOHelper/PGOHelper.vcxproj.filters diff --git a/Core/APU.cpp b/Core/APU.cpp index e25e08d9..4c40adef 100644 --- a/Core/APU.cpp +++ b/Core/APU.cpp @@ -59,9 +59,9 @@ void APU::WriteRAM(uint16_t addr, uint8_t value) } } -bool APU::Exec(uint32_t executedCycles) +bool APU::Exec(uint32_t currentCPUCycle) { - _currentClock += executedCycles; + _currentClock = currentCPUCycle; if(_currentClock >= 29780) { _apu.end_frame(_currentClock); diff --git a/Core/BaseMapper.h b/Core/BaseMapper.h index bc7e80bf..4a5f9593 100644 --- a/Core/BaseMapper.h +++ b/Core/BaseMapper.h @@ -35,8 +35,13 @@ class BaseMapper : public IMemoryHandler, public Snapshotable uint32_t* _chrSlotPages; uint32_t _chrShift = -1; + uint32_t _chrSlotMaxIndex = -1; uint32_t _prgShift = -1; + uint32_t _prgSlotMaxIndex = -1; + uint32_t _chrPageMask = -1; + uint32_t _prgPageMask = -1; + virtual void InitMapper() = 0; public: @@ -93,20 +98,12 @@ class BaseMapper : public IMemoryHandler, public Snapshotable uint32_t AddrToPRGSlot(uint16_t addr) { - if(_prgShift == -1) { - _prgShift = this->log2(GetPRGSlotCount()); - } - - return (addr >> (15 - _prgShift)) & (GetPRGSlotCount() - 1); + return (addr >> _prgShift) & _prgSlotMaxIndex; } uint32_t AddrToCHRSlot(uint16_t addr) { - if(_chrShift == -1) { - _chrShift = this->log2(GetCHRSlotCount()); - } - - return (addr >> (13 - _chrShift)) & (GetCHRSlotCount() - 1); + return (addr >> _chrShift) & _chrSlotMaxIndex; } wstring GetBatteryFilename() @@ -184,6 +181,14 @@ class BaseMapper : public IMemoryHandler, public Snapshotable _prgSlotPages = new uint32_t[GetPRGSlotCount()]; _chrSlotPages = new uint32_t[GetCHRSlotCount()]; + _prgShift = 15 - this->log2(GetPRGSlotCount()); + _prgSlotMaxIndex = GetPRGSlotCount() - 1; + _chrShift = 13 - this->log2(GetCHRSlotCount()); + _chrSlotMaxIndex = GetCHRSlotCount() - 1; + + _chrPageMask = GetCHRPageSize() - 1; + _prgPageMask = GetPRGPageSize() - 1; + InitMapper(); } @@ -245,7 +250,7 @@ class BaseMapper : public IMemoryHandler, public Snapshotable virtual uint8_t ReadRAM(uint16_t addr) { if(addr >= 0x8000) { - return _prgPages[AddrToPRGSlot(addr)][addr & (GetPRGPageSize() - 1)]; + return _prgPages[AddrToPRGSlot(addr)][addr & _prgPageMask]; } else if(addr >= 0x6000) { return _SRAM[addr & 0x1FFF]; } else if(addr >= 0x4000) { @@ -268,7 +273,7 @@ class BaseMapper : public IMemoryHandler, public Snapshotable uint32_t ToAbsoluteAddress(uint16_t addr) { - return GetPRGPageSize() * (_prgSlotPages[AddrToPRGSlot(addr)] % GetPRGPageCount()) + (addr & (GetPRGPageSize() - 1)); + return GetPRGPageSize() * (_prgSlotPages[AddrToPRGSlot(addr)] % GetPRGPageCount()) + (addr & _prgPageMask); } int32_t FromAbsoluteAddress(uint32_t addr) @@ -319,13 +324,13 @@ class BaseMapper : public IMemoryHandler, public Snapshotable virtual uint8_t ReadVRAM(uint16_t addr) { - return _chrPages[AddrToCHRSlot(addr)][addr & (GetCHRPageSize() - 1)]; + return _chrPages[AddrToCHRSlot(addr)][addr & _chrPageMask]; } virtual void WriteVRAM(uint16_t addr, uint8_t value) { if(_hasCHRRAM) { - _chrPages[AddrToCHRSlot(addr)][addr & (GetCHRPageSize() - 1)] = value; + _chrPages[AddrToCHRSlot(addr)][addr & _chrPageMask] = value; } else { //assert(false); } diff --git a/Core/CPU.cpp b/Core/CPU.cpp index b1611e8f..9f52dfe4 100644 --- a/Core/CPU.cpp +++ b/Core/CPU.cpp @@ -1,5 +1,6 @@ #include "stdafx.h" #include "CPU.h" +#include "PPU.h" CPU* CPU::Instance = nullptr; @@ -28,66 +29,27 @@ CPU::CPU(MemoryManager *memoryManager) : _memoryManager(memoryManager) }; AddrMode addrMode[] = { - Imm, IndX, None, IndX, Zero, Zero, Zero, Zero, Imp, Imm, Imp, Imm, Abs, Abs, Abs, Abs, - Rel, IndY, None, IndYW, ZeroX, ZeroX, ZeroX, ZeroX, Imp, AbsY, Imp, AbsYW, AbsX, AbsX, AbsXW, AbsXW, - Abs, IndX, None, IndX, Zero, Zero, Zero, Zero, Imp, Imm, Imp, Imm, Abs, Abs, Abs, Abs, - Rel, IndY, None, IndYW, ZeroX, ZeroX, ZeroX, ZeroX, Imp, AbsY, Imp, AbsYW, AbsX, AbsX, AbsXW, AbsXW, - Imp, IndX, None, IndX, Zero, Zero, Zero, Zero, Imp, Imm, Imp, Imm, Abs, Abs, Abs, Abs, - Rel, IndY, None, IndYW, ZeroX, ZeroX, ZeroX, ZeroX, Imp, AbsY, Imp, AbsYW, AbsX, AbsX, AbsXW, AbsXW, - Imp, IndX, None, IndX, Zero, Zero, Zero, Zero, Imp, Imm, Imp, Imm, Abs, Abs, Abs, Abs, - Rel, IndY, None, IndYW, ZeroX, ZeroX, ZeroX, ZeroX, Imp, AbsY, Imp, AbsYW, AbsX, AbsX, AbsXW, AbsXW, - Imm, IndX, Imm, IndX, Zero, Zero, Zero, Zero, Imp, Imm, Imp, Imm, Abs, Abs, Abs, Abs, - Rel, IndYW, None, IndY, ZeroX, ZeroX, ZeroY, ZeroY, Imp, AbsYW, Imp, AbsY, AbsXW, AbsXW, AbsYW, AbsY, - Imm, IndX, Imm, IndX, Zero, Zero, Zero, Zero, Imp, Imm, Imp, Imm, Abs, Abs, Abs, Abs, - Rel, IndY, None, IndY, ZeroX, ZeroX, ZeroY, ZeroY, Imp, AbsY, Imp, AbsY, AbsX, AbsX, AbsY, AbsY, - Imm, IndX, Imm, IndX, Zero, Zero, Zero, Zero, Imp, Imm, Imp, Imm, Abs, Abs, Abs, Abs, - Rel, IndY, None, IndYW, ZeroX, ZeroX, ZeroX, ZeroX, Imp, AbsY, Imp, AbsYW, AbsX, AbsX, AbsXW, AbsXW, - Imm, IndX, Imm, IndX, Zero, Zero, Zero, Zero, Imp, Imm, Imp, Imm, Abs, Abs, Abs, Abs, - Rel, IndY, None, IndYW, ZeroX, ZeroX, ZeroX, ZeroX, Imp, AbsY, Imp, AbsYW, AbsX, AbsX, AbsXW, AbsXW, + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + Imp, IndX, None, IndX, Zero, Zero, Zero, Zero, Imp, Imm, Imp, Imm, Abs, Abs, Abs, Abs, //0 + Rel, IndY, None, IndYW, ZeroX, ZeroX, ZeroX, ZeroX, Imp, AbsY, Imp, AbsYW, AbsX, AbsX, AbsXW, AbsXW, //1 + Abs, IndX, None, IndX, Zero, Zero, Zero, Zero, Imp, Imm, Imp, Imm, Abs, Abs, Abs, Abs, //2 + Rel, IndY, None, IndYW, ZeroX, ZeroX, ZeroX, ZeroX, Imp, AbsY, Imp, AbsYW, AbsX, AbsX, AbsXW, AbsXW, //3 + Imp, IndX, None, IndX, Zero, Zero, Zero, Zero, Imp, Imm, Imp, Imm, Abs, Abs, Abs, Abs, //4 + Rel, IndY, None, IndYW, ZeroX, ZeroX, ZeroX, ZeroX, Imp, AbsY, Imp, AbsYW, AbsX, AbsX, AbsXW, AbsXW, //5 + Imp, IndX, None, IndX, Zero, Zero, Zero, Zero, Imp, Imm, Imp, Imm, Abs, Abs, Abs, Abs, //6 + Rel, IndY, None, IndYW, ZeroX, ZeroX, ZeroX, ZeroX, Imp, AbsY, Imp, AbsYW, AbsX, AbsX, AbsXW, AbsXW, //7 + Imm, IndX, Imm, IndX, Zero, Zero, Zero, Zero, Imp, Imm, Imp, Imm, Abs, Abs, Abs, Abs, //8 + Rel, IndYW, None, IndY, ZeroX, ZeroX, ZeroY, ZeroY, Imp, AbsYW, Imp, AbsY, AbsXW, AbsXW, AbsYW, AbsY, //9 + Imm, IndX, Imm, IndX, Zero, Zero, Zero, Zero, Imp, Imm, Imp, Imm, Abs, Abs, Abs, Abs, //A + Rel, IndY, None, IndY, ZeroX, ZeroX, ZeroY, ZeroY, Imp, AbsY, Imp, AbsY, AbsX, AbsX, AbsY, AbsY, //B + Imm, IndX, Imm, IndX, Zero, Zero, Zero, Zero, Imp, Imm, Imp, Imm, Abs, Abs, Abs, Abs, //C + Rel, IndY, None, IndYW, ZeroX, ZeroX, ZeroX, ZeroX, Imp, AbsY, Imp, AbsYW, AbsX, AbsX, AbsXW, AbsXW, //D + Imm, IndX, Imm, IndX, Zero, Zero, Zero, Zero, Imp, Imm, Imp, Imm, Abs, Abs, Abs, Abs, //E + Rel, IndY, None, IndYW, ZeroX, ZeroX, ZeroX, ZeroX, Imp, AbsY, Imp, AbsYW, AbsX, AbsX, AbsXW, AbsXW, //F }; - - uint8_t cycles[] { - 7, 6, 2, 8, 3, 3, 5, 5, 3, 2, 2, 2, 4, 4, 6, 6, - 2, 5, 2, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 7, 7, - 6, 6, 2, 8, 3, 3, 5, 5, 4, 2, 2, 2, 4, 4, 6, 6, - 2, 5, 2, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 7, 7, - 6, 6, 2, 8, 3, 3, 5, 5, 3, 2, 2, 2, 3, 4, 6, 6, - 2, 5, 2, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 7, 7, - 6, 6, 2, 8, 3, 3, 5, 5, 4, 2, 2, 2, 5, 4, 6, 6, - 2, 5, 2, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 7, 7, - 2, 6, 2, 6, 3, 3, 3, 3, 2, 2, 2, 2, 4, 4, 4, 4, - 2, 6, 2, 6, 4, 4, 4, 4, 2, 5, 2, 5, 5, 5, 5, 5, - 2, 6, 2, 6, 3, 3, 3, 3, 2, 2, 2, 2, 4, 4, 4, 4, - 2, 5, 2, 5, 4, 4, 4, 4, 2, 4, 2, 4, 4, 4, 4, 4, - 2, 6, 2, 8, 3, 3, 5, 5, 2, 2, 2, 2, 4, 4, 6, 6, - 2, 5, 2, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 7, 7, - 2, 6, 3, 8, 3, 3, 5, 5, 2, 2, 2, 2, 4, 4, 6, 6, - 2, 5, 2, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 7, 7, - }; - - uint8_t cyclesPageCrossed[] { - 7, 6, 2, 8, 3, 3, 5, 5, 3, 2, 2, 2, 4, 4, 6, 6, - 3, 6, 2, 8, 4, 4, 6, 6, 2, 5, 2, 7, 5, 5, 7, 7, - 6, 6, 2, 8, 3, 3, 5, 5, 4, 2, 2, 2, 4, 4, 6, 6, - 3, 6, 2, 8, 4, 4, 6, 6, 2, 5, 2, 7, 5, 5, 7, 7, - 6, 6, 2, 8, 3, 3, 5, 5, 3, 2, 2, 2, 3, 4, 6, 6, - 3, 6, 2, 8, 4, 4, 6, 6, 2, 5, 2, 7, 5, 5, 7, 7, - 6, 6, 2, 8, 3, 3, 5, 5, 4, 2, 2, 2, 5, 4, 6, 6, - 3, 6, 2, 8, 4, 4, 6, 6, 2, 5, 2, 7, 5, 5, 7, 7, - 2, 6, 2, 6, 3, 3, 3, 3, 2, 2, 2, 2, 4, 4, 4, 4, - 3, 6, 2, 6, 4, 4, 4, 4, 2, 5, 2, 5, 5, 5, 5, 5, - 2, 6, 2, 6, 3, 3, 3, 3, 2, 2, 2, 2, 4, 4, 4, 4, - 3, 6, 2, 5, 4, 4, 4, 4, 2, 5, 2, 5, 5, 5, 5, 5, - 2, 6, 2, 8, 3, 3, 5, 5, 2, 2, 2, 2, 4, 4, 6, 6, - 3, 6, 2, 8, 4, 4, 6, 6, 2, 5, 2, 7, 5, 5, 7, 7, - 2, 6, 3, 8, 3, 3, 5, 5, 2, 2, 2, 2, 4, 4, 6, 6, - 3, 6, 2, 8, 4, 4, 6, 6, 2, 5, 2, 7, 5, 5, 7, 7, - }; - + memcpy(_opTable, opTable, sizeof(opTable)); - memcpy(_cycles, cycles, sizeof(cycles)); memcpy(_addrMode, addrMode, sizeof(addrMode)); - memcpy(_cyclesPageCrossed, cyclesPageCrossed, sizeof(cyclesPageCrossed)); } void CPU::Reset(bool softReset) @@ -96,7 +58,6 @@ void CPU::Reset(bool softReset) _state.IRQFlag = 0; _cycleCount = 0; _relativeCycleCount = 0; - _cyclePenalty = 0; _state.PC = MemoryReadWord(CPU::ResetVector); if(softReset) { @@ -116,11 +77,8 @@ void CPU::Reset(bool softReset) uint32_t CPU::Exec() { - //static ofstream log("log.txt", ios::out | ios::binary); - uint32_t executedCycles = 0; if(!_runNMI && !_runIRQ) { uint8_t opCode = GetOPCode(); - if(_state.NMIFlag) { _runNMI = true; } else if(opCode != 0x40 && _state.IRQFlag > 0 && !CheckFlag(PSFlags::Interrupt)) { @@ -128,14 +86,12 @@ uint32_t CPU::Exec() } _instAddrMode = _addrMode[opCode]; + _operand = FetchOperand(); + if(_opTable[opCode] != nullptr) { - //std::cout << std::hex << (_state.PC - 1) << ": " << (short)opCode << std::endl; (this->*_opTable[opCode])(); - executedCycles = (IsPageCrossed() ? _cyclesPageCrossed[opCode] : _cycles[opCode]); } else { - GetOperandAddr(); std::cout << "Invalid opcode: " << std::hex << (short)opCode; - //throw exception("Invalid opcode"); } if(!_runIRQ && opCode == 0x40 && _state.IRQFlag > 0 && !CheckFlag(PSFlags::Interrupt)) { @@ -151,12 +107,9 @@ uint32_t CPU::Exec() IRQ(); } _runIRQ = false; - - executedCycles = 7; } - - _cycleCount += executedCycles; - return executedCycles + GetCyclePenalty(); + + return _cycleCount; } void CPU::EndFrame() @@ -165,6 +118,28 @@ void CPU::EndFrame() _cycleCount = 0; } +void CPU::RunDMATransfer(uint8_t* spriteRAM, uint32_t &spriteRamAddr, uint8_t offsetValue) +{ + //"the DMA procedure takes 513 CPU cycles (+1 on odd CPU cycles)" + if((CPU::GetRelativeCycleCount() + Instance->_cycleCount) % 2 != 0) { + Instance->_cycleCount++; + } + Instance->_cycleCount++; + + //DMA transfer starts at SpriteRamAddr and wraps around + for(int i = 0; i < 0x100; i++) { + //Read value + uint8_t readValue = Instance->_memoryManager->Read(offsetValue*0x100 + i); + Instance->_cycleCount++; + + //Write to ram + spriteRAM[(spriteRamAddr+i) & 0xFF] = readValue; + PPU::ExecStatic(); + Instance->_cycleCount++; + } +} + + void CPU::StreamState(bool saving) { Stream(_state.PC); diff --git a/Core/CPU.h b/Core/CPU.h index 59bc984d..fcb9c091 100644 --- a/Core/CPU.h +++ b/Core/CPU.h @@ -59,7 +59,7 @@ private: int32_t _cycleCount; int32_t _relativeCycleCount; - uint32_t _cyclePenalty; + uint16_t _operand; Func _opTable[256]; uint8_t _cycles[256]; @@ -77,10 +77,17 @@ private: uint8_t GetOPCode() { uint8_t value = _memoryManager->Read(_state.PC, true); + _cycleCount++; _state.PC++; return value; } + void DummyRead() + { + _memoryManager->Read(_state.PC, false); + _cycleCount++; + } + uint8_t ReadByte() { return MemoryRead(_state.PC++); @@ -132,14 +139,19 @@ private: void MemoryWrite(uint16_t addr, uint8_t value) { _memoryManager->Write(addr, value); + _cycleCount++; } uint8_t MemoryRead(uint16_t addr) { - return _memoryManager->Read(addr); + uint8_t value = _memoryManager->Read(addr); + _cycleCount++; + return value; } uint16_t MemoryReadWord(uint16_t addr) { - return _memoryManager->ReadWord(addr); + uint16_t value = _memoryManager->ReadWord(addr); + _cycleCount+=2; + return value; } void SetRegister(uint8_t ®, uint8_t value) { @@ -183,40 +195,56 @@ private: uint16_t PC() { return _state.PC; } void SetPC(uint16_t value) { _state.PC = value; } - uint16_t GetOperandAddr() + uint16_t FetchOperand() { switch(_instAddrMode) { + case AddrMode::Acc: + case AddrMode::Imp: DummyRead(); return 0; + case AddrMode::Imm: + case AddrMode::Rel: return GetImmediate(); + case AddrMode::Zero: return GetZeroAddr(); + case AddrMode::ZeroX: return GetZeroXAddr(); + case AddrMode::ZeroY: return GetZeroYAddr(); + case AddrMode::Ind: return GetIndAddr(); + case AddrMode::IndX: return GetIndXAddr(); + case AddrMode::IndY: return GetIndYAddr(false); + case AddrMode::IndYW: return GetIndYAddr(true); case AddrMode::Abs: return GetAbsAddr(); case AddrMode::AbsX: return GetAbsXAddr(false); case AddrMode::AbsXW: return GetAbsXAddr(true); case AddrMode::AbsY: return GetAbsYAddr(false); case AddrMode::AbsYW: return GetAbsYAddr(true); - case AddrMode::Imm: return GetImmediate(); - case AddrMode::IndX: return GetIndXAddr(); - case AddrMode::IndY: return GetIndYAddr(false); - case AddrMode::IndYW: return GetIndYAddr(true); - case AddrMode::Zero: return GetZeroAddr(); - case AddrMode::ZeroX: return GetZeroXAddr(); - case AddrMode::ZeroY: return GetZeroYAddr(); } - return 0; + throw new exception(); } - uint8_t GetOperand() + uint16_t GetOperand() { - uint16_t addr = GetOperandAddr(); + return _operand; + } - if(_instAddrMode != AddrMode::Imm) { - return MemoryRead(addr); + uint8_t GetOperandValue() + { + if(_instAddrMode >= AddrMode::Zero) { + return MemoryRead(GetOperand()); } else { - return (uint8_t)addr; + return (uint8_t)GetOperand(); } } + uint16_t GetIndAddr() { return ReadWord(); } uint8_t GetImmediate() { return ReadByte(); } uint8_t GetZeroAddr() { return ReadByte(); } - uint8_t GetZeroXAddr() { return ReadByte() + X(); } - uint8_t GetZeroYAddr() { return ReadByte() + Y(); } + uint8_t GetZeroXAddr() { + uint8_t value = ReadByte(); + MemoryRead(value); //Dummy read + return value + X(); + } + uint8_t GetZeroYAddr() { + uint8_t value = ReadByte(); + MemoryRead(value); //Dummy read + return value + Y(); + } uint16_t GetAbsAddr() { return ReadWord(); } uint16_t GetAbsXAddr(bool dummyRead = true) { @@ -243,7 +271,7 @@ private: } uint16_t GetInd() { - uint16_t addr = ReadWord(); + uint16_t addr = GetOperand(); if((addr & 0xFF) == 0xFF) { auto lo = MemoryRead(addr); auto hi = MemoryRead(addr - 0xFF); @@ -288,9 +316,9 @@ private: return addr + Y(); } - void AND() { SetA(A() & GetOperand()); } - void EOR() { SetA(A() ^ GetOperand()); } - void ORA() { SetA(A() | GetOperand()); } + void AND() { SetA(A() & GetOperandValue()); } + void EOR() { SetA(A() ^ GetOperandValue()); } + void ORA() { SetA(A() | GetOperandValue()); } void ADD(uint8_t value) { @@ -307,8 +335,8 @@ private: SetA((uint8_t)result); } - void ADC() { ADD(GetOperand()); } - void SBC() { ADD(GetOperand() ^ 0xFF); } + void ADC() { ADD(GetOperandValue()); } + void SBC() { ADD(GetOperandValue() ^ 0xFF); } void CMP(uint8_t reg, uint8_t value) { @@ -327,13 +355,13 @@ private: } } - void CPA() { CMP(A(), GetOperand()); } - void CPX() { CMP(X(), GetOperand()); } - void CPY() { CMP(Y(), GetOperand()); } + void CPA() { CMP(A(), GetOperandValue()); } + void CPX() { CMP(X(), GetOperandValue()); } + void CPY() { CMP(Y(), GetOperandValue()); } void INC() { - uint16_t addr = GetOperandAddr(); + uint16_t addr = GetOperand(); ClearFlags(PSFlags::Negative | PSFlags::Zero); uint8_t value = MemoryRead(addr); @@ -346,7 +374,7 @@ private: void DEC() { - uint16_t addr = GetOperandAddr(); + uint16_t addr = GetOperand(); ClearFlags(PSFlags::Negative | PSFlags::Zero); uint8_t value = MemoryRead(addr); MemoryWrite(addr, value); //Dummy write @@ -405,28 +433,28 @@ private: } void ASLAddr() { - uint16_t addr = GetOperandAddr(); + uint16_t addr = GetOperand(); uint8_t value = MemoryRead(addr); MemoryWrite(addr, value); //Dummy write MemoryWrite(addr, ASL(value)); } void LSRAddr() { - uint16_t addr = GetOperandAddr(); + uint16_t addr = GetOperand(); uint8_t value = MemoryRead(addr); MemoryWrite(addr, value); //Dummy write MemoryWrite(addr, LSR(value)); } void ROLAddr() { - uint16_t addr = GetOperandAddr(); + uint16_t addr = GetOperand(); uint8_t value = MemoryRead(addr); MemoryWrite(addr, value); //Dummy write MemoryWrite(addr, ROL(value)); } void RORAddr() { - uint16_t addr = GetOperandAddr(); + uint16_t addr = GetOperand(); uint8_t value = MemoryRead(addr); MemoryWrite(addr, value); //Dummy write MemoryWrite(addr, ROR(value)); @@ -437,17 +465,19 @@ private: } void BranchRelative(bool branch) { - int8_t offset = GetImmediate(); + int8_t offset = (int8_t)GetOperand(); if(branch) { - CheckPageCrossed(PC(), offset); - IncCycleCount(1); + if(CheckPageCrossed(PC(), offset)) { + DummyRead(); + } + DummyRead(); SetPC(PC() + offset); } } void BIT() { - uint8_t value = GetOperand(); + uint8_t value = GetOperandValue(); ClearFlags(PSFlags::Zero | PSFlags::Overflow | PSFlags::Negative); if((A() & value) == 0) { SetFlags(PSFlags::Zero); @@ -466,20 +496,14 @@ private: return pageCrossed; } - uint32_t GetCyclePenalty() { - uint32_t cyclePenalty = _cyclePenalty; - _cyclePenalty = 0; - return cyclePenalty; - } - #pragma region OP Codes - void LDA() { SetA(GetOperand()); } - void LDX() { SetX(GetOperand()); } - void LDY() { SetY(GetOperand()); } + void LDA() { SetA(GetOperandValue()); } + void LDX() { SetX(GetOperandValue()); } + void LDY() { SetY(GetOperandValue()); } - void STA() { MemoryWrite(GetOperandAddr(), A()); } - void STX() { MemoryWrite(GetOperandAddr(), X()); } - void STY() { MemoryWrite(GetOperandAddr(), Y()); } + void STA() { MemoryWrite(GetOperand(), A()); } + void STX() { MemoryWrite(GetOperand(), X()); } + void STY() { MemoryWrite(GetOperand(), Y()); } void TAX() { SetX(A()); } void TAY() { SetY(A()); } @@ -493,8 +517,14 @@ private: uint8_t flags = PS() | PSFlags::Break; Push((uint8_t)flags); } - void PLA() { SetA(Pop()); } - void PLP() { SetPS(Pop()); } + void PLA() { + DummyRead(); + SetA(Pop()); + } + void PLP() { + DummyRead(); + SetPS(Pop()); + } void INX() { SetX(X() + 1); } void INY() { SetY(Y() + 1); } @@ -515,16 +545,19 @@ private: void ROR_Memory() { RORAddr(); } void JMP_Abs() { - JMP(GetAbsAddr()); + JMP(GetOperand()); } void JMP_Ind() { JMP(GetInd()); } void JSR() { - uint16_t addr = GetAbsAddr(); + uint16_t addr = GetOperand(); + DummyRead(); Push((uint16_t)(PC() - 1)); JMP(addr); } void RTS() { uint16_t addr = PopWord(); + DummyRead(); + DummyRead(); SetPC(addr + 1); } @@ -587,6 +620,8 @@ private: } void NMI() { + DummyRead(); //fetch opcode (and discard it - $00 (BRK) is forced into the opcode register instead) + DummyRead(); //read next instruction byte (actually the same as above, since PC increment is suppressed. Also discarded.) Push((uint16_t)(PC())); Push((uint8_t)PS()); SetFlags(PSFlags::Interrupt); @@ -594,6 +629,8 @@ private: } void IRQ() { + DummyRead(); //fetch opcode (and discard it - $00 (BRK) is forced into the opcode register instead) + DummyRead(); //read next instruction byte (actually the same as above, since PC increment is suppressed. Also discarded.) Push((uint16_t)(PC())); if(_state.NMIFlag) { @@ -609,14 +646,14 @@ private: } void RTI() { + DummyRead(); SetPS(Pop()); SetPC(PopWord()); } void NOP() { - GetOperand(); } - + #pragma endregion protected: @@ -628,14 +665,11 @@ public: CPU(MemoryManager *memoryManager); static int32_t GetCycleCount() { return CPU::Instance->_cycleCount; } static int32_t GetRelativeCycleCount() { return CPU::Instance->_relativeCycleCount + CPU::Instance->_cycleCount; } - static void IncCycleCount(uint32_t cycles) { - CPU::Instance->_cyclePenalty += cycles; - CPU::Instance->_cycleCount += cycles; - } static void SetNMIFlag() { CPU::Instance->_state.NMIFlag = true; } static void ClearNMIFlag() { CPU::Instance->_state.NMIFlag = false; } static void SetIRQSource(IRQSource source) { CPU::Instance->_state.IRQFlag |= (int)source; } static void ClearIRQSource(IRQSource source) { CPU::Instance->_state.IRQFlag &= ~(int)source; } + static void RunDMATransfer(uint8_t* spriteRAM, uint32_t &spriteRamAddr, uint8_t offsetValue); void Reset(bool softReset); uint32_t Exec(); diff --git a/Core/Console.cpp b/Core/Console.cpp index ce545e46..419a8611 100644 --- a/Core/Console.cpp +++ b/Core/Console.cpp @@ -159,13 +159,10 @@ void Console::Run() Console::RunningLock.Acquire(); while(true) { - uint32_t executedCycles = _cpu->Exec(); - _ppu->Exec(); - bool frameDone = _apu->Exec(executedCycles); + bool frameDone = _apu->Exec(_cpu->Exec()); if(frameDone) { _cpu->EndFrame(); - _ppu->EndFrame(); if(CheckFlag(EmulationFlags::LimitFPS) && frameDone) { elapsedTime = clockTimer.GetElapsedMS(); @@ -273,12 +270,8 @@ bool Console::RunTest(uint8_t *expectedResult) Console::RunningLock.Acquire(); while(true) { - uint32_t executedCycles = _cpu->Exec(); - _ppu->Exec(); - - if(_apu->Exec(executedCycles)) { + if(_apu->Exec(_cpu->Exec())) { _cpu->EndFrame(); - _ppu->EndFrame(); } if(timer.GetElapsedMS() > 100) { diff --git a/Core/MemoryManager.cpp b/Core/MemoryManager.cpp index 0e98327d..bdf6fc5a 100644 --- a/Core/MemoryManager.cpp +++ b/Core/MemoryManager.cpp @@ -59,18 +59,12 @@ void MemoryManager::WriteRegister(uint16_t addr, uint8_t value) uint8_t MemoryManager::ReadMappedVRAM(uint16_t addr) { - if(_vramReadHandlers[addr]) { - return _vramReadHandlers[addr]->ReadVRAM(addr); - } else { - return 0; - } + return _mapper->ReadVRAM(addr); } void MemoryManager::WriteMappedVRAM(uint16_t addr, uint8_t value) { - if(_vramWriteHandlers[addr]) { - _vramWriteHandlers[addr]->WriteVRAM(addr, value); - } + return _mapper->WriteVRAM(addr, value); } void MemoryManager::InitializeMemoryHandlers(IMemoryHandler** memoryHandlers, IMemoryHandler* handler, vector *addresses) @@ -115,7 +109,7 @@ uint8_t MemoryManager::Read(uint16_t addr, bool forExecution) Debugger::CheckBreakpoint(forExecution ? BreakpointType::Execute : BreakpointType::Read, addr); uint8_t value; - PPU::ExecStatic(3); + PPU::ExecStatic(); if(addr <= 0x1FFF) { value = _internalRAM[addr & 0x07FF]; } else { @@ -128,7 +122,7 @@ void MemoryManager::Write(uint16_t addr, uint8_t value) { Debugger::CheckBreakpoint(BreakpointType::Write, addr); - PPU::ExecStatic(3); + PPU::ExecStatic(); if(addr <= 0x1FFF) { _internalRAM[addr & 0x07FF] = value; } else { @@ -143,24 +137,20 @@ uint16_t MemoryManager::ReadWord(uint16_t addr) return lo | hi << 8; } -void MemoryManager::ProcessVRAMAccess(uint16_t &addr) -{ - addr &= 0x3FFF; - if(addr >= 0x3000) { - //Need to mirror 0x3000 writes to 0x2000, this appears to be how hardware behaves - //Required for proper MMC3 IRQ timing in Burai Fighter - addr -= 0x1000; - } - _mapper->NotifyVRAMAddressChange(addr); -} - uint8_t MemoryManager::ReadVRAM(uint16_t addr) { - ProcessVRAMAccess(addr); + addr &= 0x3FFF; if(addr <= 0x1FFF) { - return ReadMappedVRAM(addr & 0x1FFF); + _mapper->NotifyVRAMAddressChange(addr); + return ReadMappedVRAM(addr); } else { + if(addr >= 0x3000) { + //Need to mirror 0x3000 writes to 0x2000, this appears to be how hardware behaves + //Required for proper MMC3 IRQ timing in Burai Fighter + addr -= 0x1000; + } + _mapper->NotifyVRAMAddressChange(addr); switch(_mapper->GetMirroringType()) { case MirroringType::Vertical: return _nametableRAM[(addr&0x400)>>10][addr & 0x3FF]; @@ -178,17 +168,23 @@ uint8_t MemoryManager::ReadVRAM(uint16_t addr) case MirroringType::ScreenBOnly: return _nametableRAM[1][addr & 0x3FF]; } - } } void MemoryManager::WriteVRAM(uint16_t addr, uint8_t value) { - ProcessVRAMAccess(addr); + addr &= 0x3FFF; if(addr <= 0x1FFF) { + _mapper->NotifyVRAMAddressChange(addr); WriteMappedVRAM(addr, value); } else { + if(addr >= 0x3000) { + //Need to mirror 0x3000 writes to 0x2000, this appears to be how hardware behaves + //Required for proper MMC3 IRQ timing in Burai Fighter + addr -= 0x1000; + } + _mapper->NotifyVRAMAddressChange(addr); switch(_mapper->GetMirroringType()) { case MirroringType::Vertical: _nametableRAM[(addr&0x400)>>10][addr & 0x3FF] = value; diff --git a/Core/MemoryManager.h b/Core/MemoryManager.h index d6207ae7..a3962ecf 100644 --- a/Core/MemoryManager.h +++ b/Core/MemoryManager.h @@ -34,8 +34,6 @@ class MemoryManager: public Snapshotable uint8_t ReadMappedVRAM(uint16_t addr); void WriteMappedVRAM(uint16_t addr, uint8_t value); - void ProcessVRAMAccess(uint16_t &addr); - protected: void StreamState(bool saving); diff --git a/Core/PPU.cpp b/Core/PPU.cpp index c75379b5..b0baa6f9 100644 --- a/Core/PPU.cpp +++ b/Core/PPU.cpp @@ -46,7 +46,6 @@ void PPU::Reset() _scanline = 0; _cycle = 0; _frameCount = 0; - _cycleCount = 0; _memoryReadBuffer = 0; memset(_spriteRAM, 0xFF, 0x100); @@ -154,13 +153,7 @@ void PPU::WriteRAM(uint16_t addr, uint8_t value) UpdateVideoRamAddr(); break; case PPURegisters::SpriteDMA: - //DMA transfer starts at SpriteRamAddr and wraps around - for(int i = 0; i < 0x100; i++) { - _spriteRAM[(_state.SpriteRamAddr+i)&0xFF] = _memoryManager->Read(value*0x100 + i); - } - - //"the DMA procedure takes 513 CPU cycles (+1 on odd CPU cycles)" - CPU::IncCycleCount((CPU::GetCycleCount() % 2 == 0) ? 513 : 514); + CPU::RunDMATransfer(_spriteRAM, _state.SpriteRamAddr, value); break; } } @@ -399,21 +392,20 @@ void PPU::DrawPixel() //This is called 3.7 million times per second - needs to be as fast as possible. uint8_t offset = _state.XScroll; - bool useBackground = true; uint32_t backgroundColor = 0; - uint32_t spriteColor = 0; - + uint32_t &pixel = (((uint32_t*)_outputBuffer)[(_scanline << 8) + _cycle - 1]); + if((_cycle > 8 || _flags.BackgroundMask) && _flags.BackgroundEnabled) { //BackgroundMask = false: Hide background in leftmost 8 pixels of screen backgroundColor = (((_state.LowBitShift << offset) & 0x8000) >> 15) | (((_state.HighBitShift << offset) & 0x8000) >> 14); } - uint8_t i; if((_cycle > 8 || _flags.SpriteMask) && _flags.SpritesEnabled) { //SpriteMask = true: Hide sprites in leftmost 8 pixels of screen - for(i = 0; i < _spriteCount; i++) { + for(uint8_t i = 0; i < _spriteCount; i++) { int32_t shift = -((int32_t)_spriteX[i] - (int32_t)_cycle + 1); if(shift >= 0 && shift < 8) { + uint32_t spriteColor; if(_spriteTiles[i].HorizontalMirror) { spriteColor = ((_spriteTiles[i].LowByte >> shift) & 0x01) | ((_spriteTiles[i].HighByte >> shift) & 0x01) << 1; } else { @@ -424,7 +416,7 @@ void PPU::DrawPixel() //First sprite without a 00 color, use it. if(backgroundColor == 0 || !_spriteTiles[i].BackgroundPriority) { //Check sprite priority - useBackground = false; + pixel = PPU_PALETTE_RGB[GetSpritePaletteEntry(_spriteTiles[i].PaletteOffset, spriteColor)]; } if(i == 0 && backgroundColor != 0 && _sprite0Visible && _cycle != 256 && _flags.BackgroundEnabled) { @@ -434,22 +426,12 @@ void PPU::DrawPixel() //"Should always miss when Y >= 239" _statusFlags.Sprite0Hit = true; } - break; + return; } } } } - - uint32_t bufferPosition = _scanline * 256 + (_cycle - 1); - if(useBackground) { - // If we're grabbing the pixel from the high part of the shift register, use the previous tile's palette, not the current one - ((uint32_t*)_outputBuffer)[bufferPosition] = PPU_PALETTE_RGB[GetBGPaletteEntry(offset + ((_cycle - 1) % 8) < 8 ? _previousTile.PaletteOffset : _currentTile.PaletteOffset, backgroundColor)]; - } else { - ((uint32_t*)_outputBuffer)[bufferPosition] = PPU_PALETTE_RGB[GetSpritePaletteEntry(_spriteTiles[i].PaletteOffset, spriteColor)]; - } - - //Shift the tile registers to prepare for the next cycle - ShiftTileRegisters(); + pixel = PPU_PALETTE_RGB[GetBGPaletteEntry(offset + ((_cycle - 1) % 8) < 8 ? _previousTile.PaletteOffset : _currentTile.PaletteOffset, backgroundColor)]; } void PPU::ProcessPreVBlankScanline() @@ -457,13 +439,13 @@ void PPU::ProcessPreVBlankScanline() //For pre-render scanline & all visible scanlines if(IsRenderingEnabled()) { //Update video ram address according to scrolling logic - if(_cycle == 256) { + if((_cycle > 0 && _cycle < 256 && _cycle % 8 == 0) || _cycle == 328 || _cycle == 336) { + IncHorizontalScrolling(); + } else if(_cycle == 256) { IncVerticalScrolling(); } else if(_cycle == 257) { //copy horizontal scrolling value from t _state.VideoRamAddr = (_state.VideoRamAddr & ~0x041F) | (_state.TmpVideoRamAddr & 0x041F); - } else if((_cycle % 8 == 0 && _cycle > 0 && _cycle < 256) || _cycle == 328 || _cycle == 336) { - IncHorizontalScrolling(); } } @@ -477,7 +459,7 @@ void PPU::ProcessPrerenderScanline() { ProcessPreVBlankScanline(); - if(_cycle == 0) { + if(_cycle == 1) { _statusFlags.SpriteOverflow = false; _statusFlags.Sprite0Hit = false; _statusFlags.VerticalBlank = false; @@ -520,6 +502,7 @@ void PPU::ProcessVisibleScanline() } DrawPixel(); + ShiftTileRegisters(); if(IsRenderingEnabled()) { CopyOAMData(); @@ -527,9 +510,12 @@ void PPU::ProcessVisibleScanline() if(_cycle == 256 && _scanline == 239) { //Send frame to GUI once the last pixel has been output - PPU::VideoDevice->UpdateFrame(_outputBuffer); + if(PPU::VideoDevice) { + PPU::VideoDevice->UpdateFrame(_outputBuffer); + } } } else if((_cycle - 261) % 8 == 0 && _cycle <= 320) { + //Cycle 261, 269, etc. uint32_t spriteIndex = (_cycle - 261) / 8; LoadSpriteTileInfo(spriteIndex); } else if(_cycle == 321 || _cycle == 329) { @@ -615,7 +601,7 @@ void PPU::CopyOAMData() void PPU::BeginVBlank() { - if(_cycle == 0) { + if(_cycle == 1) { if(!_doNotSetVBFlag) { _statusFlags.VerticalBlank = true; if(_flags.VBlank) { @@ -633,39 +619,34 @@ void PPU::EndVBlank() } } -void PPU::Exec(uint32_t extraCycles) +void PPU::Exec() { - int32_t gap = CPU::GetCycleCount() * 3 - _cycleCount; - if(gap < 0) { - gap = 0; + if(_scanline != -1 && _scanline < 240) { + ProcessVisibleScanline(); + } else if(_scanline == -1) { + ProcessPrerenderScanline(); + } else if(_scanline == 241) { + BeginVBlank(); + } else if(_scanline == 260) { + EndVBlank(); } - gap += extraCycles; - _cycleCount += gap; - while(gap > 0) { - if(_scanline == -1) { - ProcessPrerenderScanline(); - } else if(_scanline < 240) { - ProcessVisibleScanline(); - } else if(_scanline == 241) { - BeginVBlank(); - } else if(_scanline == 260) { - EndVBlank(); + if(_cycle == 340) { + _cycle = -1; + _scanline++; + + if(_scanline == 261) { + _scanline = -1; } - - if(_cycle == 340) { - _cycle = 0; - _scanline++; - - if(_scanline == 261) { - _scanline = -1; - } - } else { - _cycle++; - } - - gap--; } + _cycle++; +} + +void PPU::ExecStatic() +{ + PPU::Instance->Exec(); + PPU::Instance->Exec(); + PPU::Instance->Exec(); } void PPU::StreamState(bool saving) @@ -703,7 +684,6 @@ void PPU::StreamState(bool saving) Stream(_scanline); Stream(_cycle); Stream(_frameCount); - Stream(_cycleCount); Stream(_memoryReadBuffer); StreamArray(_paletteRAM, 0x100); diff --git a/Core/PPU.h b/Core/PPU.h index 0ade76d4..f508b82a 100644 --- a/Core/PPU.h +++ b/Core/PPU.h @@ -124,7 +124,7 @@ class PPU : public IMemoryHandler, public Snapshotable bool _writeOAMData; uint32_t _overflowCounter; bool _sprite0Added; - + void UpdateStatusFlag(); void SetControlRegister(uint8_t value); @@ -193,7 +193,7 @@ class PPU : public IMemoryHandler, public Snapshotable uint8_t ReadRAM(uint16_t addr); void WriteRAM(uint16_t addr, uint8_t value); - void Exec(uint32_t extraCycles = 0); + void Exec(); static void RegisterVideoDevice(IVideoDevice *videoDevice) { @@ -225,13 +225,5 @@ class PPU : public IMemoryHandler, public Snapshotable return PPU::Instance->_scanline; } - static void ExecStatic(uint32_t cycles) - { - PPU::Instance->Exec(cycles); - } - - void EndFrame() - { - _cycleCount = 0; - } + static void ExecStatic(); }; diff --git a/NES.sln b/NES.sln index fc92643e..b9b90fcc 100644 --- a/NES.sln +++ b/NES.sln @@ -24,6 +24,11 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "InteropDLL", "InteropDLL\In {78FEF1A1-6DF1-4CBB-A373-AE6FA7CE5CE0} = {78FEF1A1-6DF1-4CBB-A373-AE6FA7CE5CE0} EndProjectSection EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PGOHelper", "PGOHelper\PGOHelper.vcxproj", "{38D74EE1-5276-4D24-AABC-104B912A27D2}" + ProjectSection(ProjectDependencies) = postProject + {37749BB2-FA78-4EC9-8990-5628FC0BBA19} = {37749BB2-FA78-4EC9-8990-5628FC0BBA19} + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -84,8 +89,21 @@ Global {37749BB2-FA78-4EC9-8990-5628FC0BBA19}.Release|Mixed Platforms.Build.0 = Release|Win32 {37749BB2-FA78-4EC9-8990-5628FC0BBA19}.Release|Win32.ActiveCfg = Release|Win32 {37749BB2-FA78-4EC9-8990-5628FC0BBA19}.Release|Win32.Build.0 = Release|Win32 + {38D74EE1-5276-4D24-AABC-104B912A27D2}.Debug|Any CPU.ActiveCfg = Debug|Win32 + {38D74EE1-5276-4D24-AABC-104B912A27D2}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32 + {38D74EE1-5276-4D24-AABC-104B912A27D2}.Debug|Mixed Platforms.Build.0 = Debug|Win32 + {38D74EE1-5276-4D24-AABC-104B912A27D2}.Debug|Win32.ActiveCfg = Debug|Win32 + {38D74EE1-5276-4D24-AABC-104B912A27D2}.Debug|Win32.Build.0 = Debug|Win32 + {38D74EE1-5276-4D24-AABC-104B912A27D2}.Release|Any CPU.ActiveCfg = Release|Win32 + {38D74EE1-5276-4D24-AABC-104B912A27D2}.Release|Mixed Platforms.ActiveCfg = Release|Win32 + {38D74EE1-5276-4D24-AABC-104B912A27D2}.Release|Mixed Platforms.Build.0 = Release|Win32 + {38D74EE1-5276-4D24-AABC-104B912A27D2}.Release|Win32.ActiveCfg = Release|Win32 + {38D74EE1-5276-4D24-AABC-104B912A27D2}.Release|Win32.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(Performance) = preSolution + HasPerformanceSessions = true + EndGlobalSection EndGlobal diff --git a/PGOHelper/PGOHelper.cpp b/PGOHelper/PGOHelper.cpp new file mode 100644 index 00000000..5a9648d4 --- /dev/null +++ b/PGOHelper/PGOHelper.cpp @@ -0,0 +1,14 @@ +#include + +extern "C" { + void __stdcall LoadROM(wchar_t* filename); + void __stdcall Run(); +} + +int _tmain(int argc, _TCHAR* argv[]) +{ + LoadROM(argv[1]); + Run(); + return 0; +} + diff --git a/PGOHelper/PGOHelper.vcxproj b/PGOHelper/PGOHelper.vcxproj new file mode 100644 index 00000000..d3043e62 --- /dev/null +++ b/PGOHelper/PGOHelper.vcxproj @@ -0,0 +1,94 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {38D74EE1-5276-4D24-AABC-104B912A27D2} + Win32Proj + PGOHelper + + + + Application + true + v120 + Unicode + + + Application + false + v120 + true + Unicode + + + + + + + + + + + + + true + + + false + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) + + + Console + true + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) + + + Console + true + true + true + + + + + + + + {37749bb2-fa78-4ec9-8990-5628fc0bba19} + false + true + false + true + true + + + + + + \ No newline at end of file diff --git a/PGOHelper/PGOHelper.vcxproj.filters b/PGOHelper/PGOHelper.vcxproj.filters new file mode 100644 index 00000000..f52130df --- /dev/null +++ b/PGOHelper/PGOHelper.vcxproj.filters @@ -0,0 +1,14 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + + + Source Files + + + \ No newline at end of file