Keyboard support + FPS limit
This commit is contained in:
parent
1def59c730
commit
1765b9a690
14 changed files with 247 additions and 15 deletions
10
Core/CPU.cpp
10
Core/CPU.cpp
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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; }
|
||||
|
||||
};
|
|
@ -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*))
|
||||
|
|
|
@ -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
91
Core/ControlManager.cpp
Normal 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
30
Core/ControlManager.h
Normal 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);
|
||||
};
|
|
@ -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" />
|
||||
|
|
|
@ -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
23
Core/IControlDevice.h
Normal 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;
|
||||
};
|
|
@ -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">
|
||||
|
|
|
@ -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
22
GUI/InputManager.cpp
Normal 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
13
GUI/InputManager.h
Normal 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();
|
||||
};
|
|
@ -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;
|
||||
|
|
Loading…
Add table
Reference in a new issue