Keyboard support + FPS limit

This commit is contained in:
Souryo 2014-06-21 15:43:41 -04:00
parent 1def59c730
commit 1765b9a690
14 changed files with 247 additions and 15 deletions

View file

@ -81,8 +81,9 @@ void CPU::Reset()
_state.PS = PSFlags::Zero | PSFlags::Reserved;// | PSFlags::Interrupt;
}
void CPU::Exec()
uint32_t CPU::Exec()
{
uint32_t executedCycles = 0;
if(!_runNMI) {
if(CPU::NMIFlag) {
_runNMI = true;
@ -91,15 +92,18 @@ void CPU::Exec()
if(_opTable[opCode] != nullptr) {
(this->*_opTable[opCode])();
CPU::CycleCount += GetPageCrossed() ? this->_cyclesPageCrossed[opCode] : this->_cycles[opCode];
executedCycles = GetPageCrossed() ? this->_cyclesPageCrossed[opCode] : this->_cycles[opCode];
} else {
//std::cout << "Invalid opcode: " << std::hex << (short)opCode;
//throw exception("Invalid opcode");
}
} else {
NMI();
CPU::CycleCount += 7;
_runNMI = false;
executedCycles = 7;
CPU::NMIFlag = false;
}
CPU::CycleCount += executedCycles;
return executedCycles;
}

View file

@ -618,7 +618,7 @@ public:
static uint64_t GetCycleCount() { return CPU::CycleCount; }
static void SetNMIFlag() { CPU::NMIFlag = true; }
void Reset();
void Exec();
uint32_t Exec();
State GetState() { return _state; }
};

View file

@ -8,8 +8,12 @@ Console::Console(wstring filename)
_memoryManager.reset(new MemoryManager(_mapper->GetHeader()));
_cpu.reset(new CPU(_memoryManager.get()));
_ppu.reset(new PPU(_memoryManager.get()));
_controlManager.reset(new ControlManager());
_memoryManager->RegisterIODevice(_mapper.get());
_memoryManager->RegisterIODevice(_ppu.get());
_memoryManager->RegisterIODevice(_controlManager.get());
Reset();
}
@ -31,24 +35,41 @@ void Console::Stop()
void Console::Run()
{
Timer timer;
Timer clockTimer;
Timer fpsTimer;
uint32_t lastFrameCount = 0;
while(true) {
_cpu->Exec();
double elapsedTime = 0.0;
uint32_t executedCycles = 0;
bool limitFrameRate = true;
while(true) {
executedCycles += _cpu->Exec();
_ppu->Exec();
if(_stop) {
break;
if(limitFrameRate && executedCycles > 30000) {
double targetTime = 16.77;
elapsedTime = clockTimer.GetElapsedMS();
while(targetTime > elapsedTime) {
if(targetTime - elapsedTime > 2) {
std::this_thread::sleep_for(std::chrono::duration<int, std::milli>(1));
}
elapsedTime = clockTimer.GetElapsedMS();
}
executedCycles = 0;
clockTimer.Reset();
}
if(fpsTimer.GetElapsedMS() > 2000) {
uint32_t frameCount = _ppu->GetFrameCount();
std::cout << ((frameCount - lastFrameCount) / (fpsTimer.GetElapsedMS() / 1000)) << std::endl;
lastFrameCount = frameCount;
fpsTimer.Reset();
}
if(timer.GetElapsedMS() > 2000) {
uint32_t frameCount = _ppu->GetFrameCount();
std::cout << ((frameCount - lastFrameCount) / (timer.GetElapsedMS() / 1000)) << std::endl;
timer.Reset();
lastFrameCount = frameCount;
if(_stop) {
_stopped = true;
break;
}
}
_stopped = true;
}
void Console::RunTest(bool callback(Console*))

View file

@ -5,6 +5,7 @@
#include "PPU.h"
#include "BaseMapper.h"
#include "MemoryManager.h"
#include "ControlManager.h"
class Console
{
@ -12,6 +13,7 @@ class Console
unique_ptr<CPU> _cpu;
unique_ptr<PPU> _ppu;
unique_ptr<BaseMapper> _mapper;
unique_ptr<ControlManager> _controlManager;
unique_ptr<MemoryManager> _memoryManager;
bool _stop = false;

91
Core/ControlManager.cpp Normal file
View file

@ -0,0 +1,91 @@
#include "stdafx.h"
#include "ControlManager.h"
IControlDevice* ControlManager::ControlDevices[] = { nullptr, nullptr, nullptr, nullptr };
ControlManager::ControlManager()
{
}
void ControlManager::RegisterControlDevice(IControlDevice* controlDevice, uint8_t port)
{
ControlManager::ControlDevices[port] = controlDevice;
}
void ControlManager::RefreshAllPorts()
{
RefreshStateBuffer(0);
RefreshStateBuffer(1);
RefreshStateBuffer(2);
RefreshStateBuffer(3);
}
void ControlManager::RefreshStateBuffer(uint8_t port)
{
if(port >= 4) {
throw exception("Invalid port");
}
IControlDevice* controlDevice = ControlManager::ControlDevices[port];
if(controlDevice) {
ButtonState buttonState = controlDevice->GetButtonState();
//"Button status for each controller is returned as an 8-bit report in the following order: A, B, Select, Start, Up, Down, Left, Right."
uint8_t state = buttonState.A | (buttonState.B << 1) | (buttonState.Select << 2) | (buttonState.Start << 3) |
(buttonState.Up << 4) | (buttonState.Down << 5) | (buttonState.Left << 6) | (buttonState.Right << 7);
_stateBuffer[port] = state;
} else {
_stateBuffer[port] = 0x00;
}
}
uint8_t ControlManager::GetPortValue(uint8_t port)
{
if(port >= 4) {
throw exception("Invalid port");
}
if(_refreshState) {
RefreshStateBuffer(port);
}
uint8_t returnValue = _stateBuffer[port] & 0x01;
_stateBuffer[port] >>= 1;
//"All subsequent reads will return D=1 on an authentic controller but may return D=0 on third party controllers."
_stateBuffer[port] |= 0x80;
//"In the NES and Famicom, the top three (or five) bits are not driven, and so retain the bits of the previous byte on the bus.
//Usually this is the most significant byte of the address of the controller port - 0x40.
//Paperboy relies on this behavior and requires that reads from the controller ports return exactly $40 or $41 as appropriate."
return 0x40 | returnValue;
}
uint8_t ControlManager::ReadRAM(uint16_t addr)
{
switch(addr) {
case 0x4016:
return GetPortValue(0);
case 0x4017:
return GetPortValue(1);
}
return 0;
}
void ControlManager::WriteRAM(uint16_t addr, uint8_t value)
{
switch(addr) {
case 0x4016:
_refreshState = (value & 0x01) == 0x01;
if(_refreshState) {
RefreshAllPorts();
}
break;
}
}

30
Core/ControlManager.h Normal file
View file

@ -0,0 +1,30 @@
#pragma once
#include "stdafx.h"
#include "IControlDevice.h"
#include "IMemoryHandler.h"
class ControlManager : public IMemoryHandler
{
private:
static IControlDevice* ControlDevices[4];
bool _refreshState = false;
uint8_t _stateBuffer[4];
void RefreshAllPorts();
void RefreshStateBuffer(uint8_t port);
uint8_t GetPortValue(uint8_t port);
public:
ControlManager();
static void RegisterControlDevice(IControlDevice* controlDevice, uint8_t port);
virtual vector<std::array<uint16_t, 2>> GetRAMAddresses()
{
return { { { 0x4016, 0x4017 } } };
}
uint8_t ReadRAM(uint16_t addr);
void WriteRAM(uint16_t addr, uint8_t value);
};

View file

@ -88,6 +88,8 @@
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="BaseMapper.h" />
<ClInclude Include="ControlManager.h" />
<ClInclude Include="IControlDevice.h" />
<ClInclude Include="IMemoryHandler.h" />
<ClInclude Include="Console.h" />
<ClInclude Include="IVideoDevice.h" />
@ -100,6 +102,7 @@
</ItemGroup>
<ItemGroup>
<ClCompile Include="Console.cpp" />
<ClCompile Include="ControlManager.cpp" />
<ClCompile Include="MemoryManager.cpp" />
<ClCompile Include="PPU.cpp" />
<ClCompile Include="CPU.cpp" />

View file

@ -45,6 +45,12 @@
<ClInclude Include="IVideoDevice.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="IControlDevice.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="ControlManager.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="Timer.h">
@ -65,5 +71,8 @@
<ClCompile Include="Console.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="ControlManager.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
</Project>

23
Core/IControlDevice.h Normal file
View file

@ -0,0 +1,23 @@
#pragma once
#include "stdafx.h"
struct ButtonState
{
bool Up;
bool Down;
bool Left;
bool Right;
bool A;
bool B;
bool Select;
bool Start;
};
class IControlDevice
{
public:
virtual ButtonState GetButtonState() = 0;
};

View file

@ -113,6 +113,7 @@
<ClInclude Include="DirectXTK\VertexTypes.h" />
<ClInclude Include="DirectXTK\WICTextureLoader.h" />
<ClInclude Include="DirectXTK\XboxDDSTextureLoader.h" />
<ClInclude Include="InputManager.h" />
<ClInclude Include="Renderer.h" />
<ClInclude Include="Resource.h" />
<ClInclude Include="stdafx.h" />
@ -120,6 +121,7 @@
<ClInclude Include="MainWindow.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="InputManager.cpp" />
<ClCompile Include="main.cpp" />
<ClCompile Include="Renderer.cpp" />
<ClCompile Include="stdafx.cpp">

View file

@ -81,6 +81,9 @@
<ClInclude Include="DirectXTK\XboxDDSTextureLoader.h">
<Filter>Header Files\DirectXTK</Filter>
</ClInclude>
<ClInclude Include="InputManager.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="stdafx.cpp">
@ -95,6 +98,9 @@
<ClCompile Include="Renderer.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="InputManager.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="GUI.rc">

22
GUI/InputManager.cpp Normal file
View file

@ -0,0 +1,22 @@
#include "stdafx.h"
#include "InputManager.h"
bool InputManager::IsKeyPressed(int key)
{
return (GetAsyncKeyState(key) & 0x8000) == 0x8000;
}
ButtonState InputManager::GetButtonState()
{
ButtonState state;
state.A = IsKeyPressed('A');
state.B = IsKeyPressed('S');
state.Select = IsKeyPressed('W');
state.Start = IsKeyPressed('Q');
state.Up = IsKeyPressed(VK_UP);
state.Down = IsKeyPressed(VK_DOWN);
state.Left = IsKeyPressed(VK_LEFT);
state.Right = IsKeyPressed(VK_RIGHT);
return state;
}

13
GUI/InputManager.h Normal file
View file

@ -0,0 +1,13 @@
#pragma once
#include "stdafx.h"
#include "..\Core\IControlDevice.h"
class InputManager : public IControlDevice
{
private:
bool IsKeyPressed(int key);
public:
ButtonState GetButtonState();
};

View file

@ -5,6 +5,8 @@
#include "MainWindow.h"
#include "..\Core\Console.h"
#include "..\Core\Timer.h"
#include "InputManager.h"
using namespace DirectX;
namespace NES
@ -65,6 +67,10 @@ namespace NES
Initialize();
InputManager inputManager;
ControlManager::RegisterControlDevice(&inputManager, 0);
MSG msg = { 0 };
Timer timer;
int frameCount = 0;