Core: Added SNES controller support

This commit is contained in:
Sour 2019-02-17 19:54:29 -05:00
parent aaf147b53b
commit b806b3d96e
21 changed files with 1008 additions and 45 deletions

279
Core/BaseControlDevice.cpp Normal file
View file

@ -0,0 +1,279 @@
#include "stdafx.h"
#include "BaseControlDevice.h"
#include "KeyManager.h"
#include "../Utilities/StringUtilities.h"
#include "Console.h"
//#include "EmulationSettings.h"
BaseControlDevice::BaseControlDevice(shared_ptr<Console> console, uint8_t port, KeyMappingSet keyMappingSet)
{
_console = console;
_port = port;
_strobe = false;
_keyMappings = keyMappingSet.GetKeyMappingArray();
}
BaseControlDevice::~BaseControlDevice()
{
}
uint8_t BaseControlDevice::GetPort()
{
return _port;
}
void BaseControlDevice::SetStateFromInput()
{
ClearState();
InternalSetStateFromInput();
}
void BaseControlDevice::InternalSetStateFromInput()
{
}
//TODO
/*
void BaseControlDevice::StreamState(bool saving)
{
auto lock = _stateLock.AcquireSafe();
VectorInfo<uint8_t> state{ &_state.State };
Stream(_strobe, state);
}
*/
bool BaseControlDevice::IsCurrentPort(uint16_t addr)
{
return _port == (addr - 0x4016);
}
bool BaseControlDevice::IsExpansionDevice()
{
return _port == BaseControlDevice::ExpDevicePort;
}
void BaseControlDevice::StrobeProcessRead()
{
if(_strobe) {
RefreshStateBuffer();
}
}
void BaseControlDevice::StrobeProcessWrite(uint8_t value)
{
bool prevStrobe = _strobe;
_strobe = (value & 0x01) == 0x01;
if(prevStrobe && !_strobe) {
RefreshStateBuffer();
}
}
void BaseControlDevice::ClearState()
{
auto lock = _stateLock.AcquireSafe();
_state = ControlDeviceState();
}
ControlDeviceState BaseControlDevice::GetRawState()
{
auto lock = _stateLock.AcquireSafe();
return _state;
}
void BaseControlDevice::SetRawState(ControlDeviceState state)
{
auto lock = _stateLock.AcquireSafe();
_state = state;
}
void BaseControlDevice::SetTextState(string textState)
{
auto lock = _stateLock.AcquireSafe();
ClearState();
if(IsRawString()) {
_state.State.insert(_state.State.end(), textState.begin(), textState.end());
} else {
if(HasCoordinates()) {
vector<string> data = StringUtilities::Split(textState, ' ');
if(data.size() >= 3) {
MousePosition pos;
try {
pos.X = (int16_t)std::stol(data[0]);
pos.Y = (int16_t)std::stol(data[1]);
} catch(std::exception ex) {
pos.X = -1;
pos.Y = -1;
}
SetCoordinates(pos);
textState = data[2];
}
}
int i = 0;
for(char c : textState) {
if(c != '.') {
SetBit(i);
}
i++;
}
}
}
string BaseControlDevice::GetTextState()
{
auto lock = _stateLock.AcquireSafe();
if(IsRawString()) {
return string((char*)_state.State.data(), _state.State.size());
} else {
string keyNames = GetKeyNames();
string output = "";
if(HasCoordinates()) {
MousePosition pos = GetCoordinates();
output += std::to_string(pos.X) + " " + std::to_string(pos.Y) + " ";
}
for(size_t i = 0; i < keyNames.size(); i++) {
output += IsPressed((uint8_t)i) ? keyNames[i] : '.';
}
return output;
}
}
void BaseControlDevice::EnsureCapacity(int32_t minBitCount)
{
auto lock = _stateLock.AcquireSafe();
uint32_t minByteCount = minBitCount / 8 + 1 + (HasCoordinates() ? 32 : 0);
int32_t gap = minByteCount - (int32_t)_state.State.size();
if(gap > 0) {
_state.State.insert(_state.State.end(), gap, 0);
}
}
bool BaseControlDevice::HasCoordinates()
{
return false;
}
bool BaseControlDevice::IsRawString()
{
return false;
}
uint32_t BaseControlDevice::GetByteIndex(uint8_t bit)
{
return bit / 8 + (HasCoordinates() ? 4 : 0);
}
bool BaseControlDevice::IsPressed(uint8_t bit)
{
auto lock = _stateLock.AcquireSafe();
EnsureCapacity(bit);
uint8_t bitMask = 1 << (bit % 8);
return (_state.State[GetByteIndex(bit)] & bitMask) != 0;
}
void BaseControlDevice::SetBitValue(uint8_t bit, bool set)
{
if(set) {
SetBit(bit);
} else {
ClearBit(bit);
}
}
void BaseControlDevice::SetBit(uint8_t bit)
{
auto lock = _stateLock.AcquireSafe();
EnsureCapacity(bit);
uint8_t bitMask = 1 << (bit % 8);
_state.State[GetByteIndex(bit)] |= bitMask;
}
void BaseControlDevice::ClearBit(uint8_t bit)
{
auto lock = _stateLock.AcquireSafe();
EnsureCapacity(bit);
uint8_t bitMask = 1 << (bit % 8);
_state.State[GetByteIndex(bit)] &= ~bitMask;
}
void BaseControlDevice::InvertBit(uint8_t bit)
{
if(IsPressed(bit)) {
ClearBit(bit);
} else {
SetBit(bit);
}
}
void BaseControlDevice::SetPressedState(uint8_t bit, uint32_t keyCode)
{
if(KeyManager::IsKeyPressed(keyCode)) {
SetBit(bit);
}
}
void BaseControlDevice::SetPressedState(uint8_t bit, bool enabled)
{
if(enabled) {
SetBit(bit);
}
}
void BaseControlDevice::SetCoordinates(MousePosition pos)
{
auto lock = _stateLock.AcquireSafe();
EnsureCapacity(-1);
_state.State[0] = pos.X & 0xFF;
_state.State[1] = (pos.X >> 8) & 0xFF;
_state.State[2] = pos.Y & 0xFF;
_state.State[3] = (pos.Y >> 8) & 0xFF;
}
MousePosition BaseControlDevice::GetCoordinates()
{
auto lock = _stateLock.AcquireSafe();
EnsureCapacity(-1);
MousePosition pos;
pos.X = _state.State[0] | (_state.State[1] << 8);
pos.Y = _state.State[2] | (_state.State[3] << 8);
return pos;
}
void BaseControlDevice::SetMovement(MouseMovement mov)
{
MouseMovement prev = GetMovement();
mov.dx += prev.dx;
mov.dy += prev.dy;
SetCoordinates({ mov.dx, mov.dy });
}
MouseMovement BaseControlDevice::GetMovement()
{
MousePosition pos = GetCoordinates();
SetCoordinates({ 0, 0 });
return { pos.X, pos.Y };
}
void BaseControlDevice::SwapButtons(shared_ptr<BaseControlDevice> state1, uint8_t button1, shared_ptr<BaseControlDevice> state2, uint8_t button2)
{
bool pressed1 = state1->IsPressed(button1);
bool pressed2 = state2->IsPressed(button2);
state1->ClearBit(button1);
state2->ClearBit(button2);
if(pressed1) {
state2->SetBit(button2);
}
if(pressed2) {
state1->SetBit(button1);
}
}

83
Core/BaseControlDevice.h Normal file
View file

@ -0,0 +1,83 @@
#pragma once
#include "stdafx.h"
#include "ControlDeviceState.h"
#include "SettingTypes.h"
#include "IKeyManager.h"
#include "../Utilities/SimpleLock.h"
class Console;
class BaseControlDevice
{
private:
ControlDeviceState _state;
protected:
shared_ptr<Console> _console;
vector<KeyMapping> _keyMappings;
bool _strobe;
uint8_t _port;
SimpleLock _stateLock;
virtual void RefreshStateBuffer() { }
//TODO
//virtual void StreamState(bool saving);
void EnsureCapacity(int32_t minBitCount);
uint32_t GetByteIndex(uint8_t bit);
virtual bool HasCoordinates();
virtual bool IsRawString();
bool IsCurrentPort(uint16_t addr);
bool IsExpansionDevice();
void StrobeProcessRead();
void StrobeProcessWrite(uint8_t value);
virtual string GetKeyNames() { return ""; }
void SetPressedState(uint8_t bit, uint32_t keyCode);
void SetPressedState(uint8_t bit, bool enabled);
void SetCoordinates(MousePosition pos);
void SetMovement(MouseMovement mov);
MouseMovement GetMovement();
virtual void InternalSetStateFromInput();
public:
static constexpr uint8_t ExpDevicePort = 4;
static constexpr uint8_t ConsoleInputPort = 5;
static constexpr uint8_t MapperInputPort = 6;
static constexpr uint8_t ExpDevicePort2 = 7;
static constexpr uint8_t PortCount = ExpDevicePort2 + 1;
BaseControlDevice(shared_ptr<Console> console, uint8_t port, KeyMappingSet keyMappingSet = KeyMappingSet());
virtual ~BaseControlDevice();
uint8_t GetPort();
bool IsPressed(uint8_t bit);
MousePosition GetCoordinates();
void ClearState();
void SetBit(uint8_t bit);
void ClearBit(uint8_t bit);
void InvertBit(uint8_t bit);
void SetBitValue(uint8_t bit, bool set);
void SetTextState(string state);
string GetTextState();
void SetStateFromInput();
virtual void OnAfterSetState() { }
void SetRawState(ControlDeviceState state);
ControlDeviceState GetRawState();
virtual uint8_t ReadRam(uint16_t addr) = 0;
virtual void WriteRam(uint16_t addr, uint8_t value) = 0;
void static SwapButtons(shared_ptr<BaseControlDevice> state1, uint8_t button1, shared_ptr<BaseControlDevice> state2, uint8_t button2);
};

View file

@ -120,7 +120,7 @@ void BaseRenderer::ShowFpsCounter(int lineNumber)
if(_fpsTimer.GetElapsedMS() > 1000) {
//Update fps every sec
shared_ptr<Ppu> ppu = _console->GetPpu();
uint32_t frameCount = ppu ? ppu->GetState().FrameCount : 0;
uint32_t frameCount = ppu ? ppu->GetFrameCount() : 0;
if(_lastFrameCount > frameCount) {
_currentFPS = 0;
} else {
@ -167,7 +167,7 @@ void BaseRenderer::ShowFrameCounter(int lineNumber)
int yPos = 13 + 24 * lineNumber;
shared_ptr<Ppu> ppu = _console->GetPpu();
string frameCounter = MessageManager::Localize("Frame") + ": " + std::to_string(ppu ? ppu->GetState().FrameCount : 0);
string frameCounter = MessageManager::Localize("Frame") + ": " + std::to_string(ppu ? ppu->GetFrameCount() : 0);
DrawString(frameCounter, _screenWidth - 146, yPos, 250, 235, 215);
}

View file

@ -4,6 +4,7 @@
#include "Ppu.h"
#include "Spc.h"
#include "InternalRegisters.h"
#include "ControlManager.h"
#include "MemoryManager.h"
#include "Debugger.h"
#include "NotificationManager.h"
@ -52,12 +53,6 @@ void Console::Run()
while(!_stopFlag) {
_cpu->Exec();
}
//Timer timer;
/*std::cout << "Time: " << std::to_string(timer.GetElapsedMS()) << std::endl;
std::cout << "OP Count: " << std::to_string(_cpu->opCount) << std::endl;
std::cout << "OP/sec: " << std::to_string(_cpu->opCount * 1000 / timer.GetElapsedMS()) << std::endl;
while(true);*/
}
void Console::Stop()
@ -76,6 +71,7 @@ void Console::Stop()
_spc.reset();
_cart.reset();
_internalRegisters.reset();
_controlManager.reset();
_memoryManager.reset();
}
@ -86,10 +82,11 @@ void Console::LoadRom(VirtualFile romFile, VirtualFile patchFile)
shared_ptr<BaseCartridge> cart = BaseCartridge::CreateCartridge(romFile, patchFile);
if(cart) {
MessageManager::ClearLog();
_internalRegisters.reset(new InternalRegisters());
_internalRegisters.reset(new InternalRegisters(shared_from_this()));
_ppu.reset(new Ppu(shared_from_this()));
_spc.reset(new Spc(shared_from_this()));
_cart = cart;
_controlManager.reset(new ControlManager(shared_from_this()));
_memoryManager.reset(new MemoryManager());
_memoryManager->Initialize(shared_from_this());
@ -159,6 +156,11 @@ shared_ptr<InternalRegisters> Console::GetInternalRegisters()
return _internalRegisters;
}
shared_ptr<ControlManager> Console::GetControlManager()
{
return _controlManager;
}
shared_ptr<Debugger> Console::GetDebugger(bool autoStart)
{
shared_ptr<Debugger> debugger = _debugger;

View file

@ -9,6 +9,7 @@ class Spc;
class BaseCartridge;
class MemoryManager;
class InternalRegisters;
class ControlManager;
class Debugger;
class DebugHud;
class SoundMixer;
@ -26,6 +27,7 @@ private:
shared_ptr<MemoryManager> _memoryManager;
shared_ptr<BaseCartridge> _cart;
shared_ptr<InternalRegisters> _internalRegisters;
shared_ptr<ControlManager> _controlManager;
shared_ptr<Debugger> _debugger;
@ -61,6 +63,7 @@ public:
shared_ptr<BaseCartridge> GetCartridge();
shared_ptr<MemoryManager> GetMemoryManager();
shared_ptr<InternalRegisters> GetInternalRegisters();
shared_ptr<ControlManager> GetControlManager();
shared_ptr<Debugger> GetDebugger(bool autoStart = true);
bool IsRunning();

13
Core/ControlDeviceState.h Normal file
View file

@ -0,0 +1,13 @@
#pragma once
#include "stdafx.h"
#include <cstring>
struct ControlDeviceState
{
vector<uint8_t> State;
bool operator!=(ControlDeviceState &other)
{
return State.size() != other.State.size() || memcmp(State.data(), other.State.data(), State.size()) != 0;
}
};

219
Core/ControlManager.cpp Normal file
View file

@ -0,0 +1,219 @@
#include "stdafx.h"
#include "ControlManager.h"
#include "Console.h"
#include "MemoryManager.h"
#include "KeyManager.h"
#include "IKeyManager.h"
#include "IInputProvider.h"
#include "IInputRecorder.h"
#include "SnesController.h"
ControlManager::ControlManager(shared_ptr<Console> console)
{
_console = console;
_pollCounter = 0;
UpdateControlDevices();
}
ControlManager::~ControlManager()
{
}
void ControlManager::RegisterInputProvider(IInputProvider* provider)
{
auto lock = _deviceLock.AcquireSafe();
_inputProviders.push_back(provider);
}
void ControlManager::UnregisterInputProvider(IInputProvider* provider)
{
auto lock = _deviceLock.AcquireSafe();
vector<IInputProvider*> &vec = _inputProviders;
vec.erase(std::remove(vec.begin(), vec.end(), provider), vec.end());
}
void ControlManager::RegisterInputRecorder(IInputRecorder* provider)
{
auto lock = _deviceLock.AcquireSafe();
_inputRecorders.push_back(provider);
}
void ControlManager::UnregisterInputRecorder(IInputRecorder* provider)
{
auto lock = _deviceLock.AcquireSafe();
vector<IInputRecorder*> &vec = _inputRecorders;
vec.erase(std::remove(vec.begin(), vec.end(), provider), vec.end());
}
vector<ControlDeviceState> ControlManager::GetPortStates()
{
auto lock = _deviceLock.AcquireSafe();
vector<ControlDeviceState> states;
for(int i = 0; i < 4; i++) {
shared_ptr<BaseControlDevice> device = GetControlDevice(i);
if(device) {
states.push_back(device->GetRawState());
} else {
states.push_back(ControlDeviceState());
}
}
return states;
}
shared_ptr<BaseControlDevice> ControlManager::GetControlDevice(uint8_t port)
{
auto lock = _deviceLock.AcquireSafe();
auto result = std::find_if(_controlDevices.begin(), _controlDevices.end(), [port](const shared_ptr<BaseControlDevice> control) { return control->GetPort() == port; });
if(result != _controlDevices.end()) {
return *result;
}
return nullptr;
}
vector<shared_ptr<BaseControlDevice>> ControlManager::GetControlDevices()
{
return _controlDevices;
}
void ControlManager::RegisterControlDevice(shared_ptr<BaseControlDevice> controlDevice)
{
_controlDevices.push_back(controlDevice);
}
ControllerType ControlManager::GetControllerType(uint8_t port)
{
return ControllerType::SnesController;
//TODO _console->GetSettings()->GetControllerType(port);
}
shared_ptr<BaseControlDevice> ControlManager::CreateControllerDevice(ControllerType type, uint8_t port, shared_ptr<Console> console)
{
shared_ptr<BaseControlDevice> device;
KeyMappingSet keyMappings = {};
if(port == 0) {
keyMappings.Mapping1.Up = KeyManager::GetKeyCode("Pad1 Up");
keyMappings.Mapping1.Down = KeyManager::GetKeyCode("Pad1 Down");
keyMappings.Mapping1.Left = KeyManager::GetKeyCode("Pad1 Left");
keyMappings.Mapping1.Right = KeyManager::GetKeyCode("Pad1 Right");
keyMappings.Mapping1.A = KeyManager::GetKeyCode("Pad1 B");
keyMappings.Mapping1.B = KeyManager::GetKeyCode("Pad1 A");
keyMappings.Mapping1.X = KeyManager::GetKeyCode("Pad1 Y");
keyMappings.Mapping1.Y = KeyManager::GetKeyCode("Pad1 X");
keyMappings.Mapping1.L = KeyManager::GetKeyCode("Pad1 L1");
keyMappings.Mapping1.R = KeyManager::GetKeyCode("Pad1 R1");
keyMappings.Mapping1.Select = KeyManager::GetKeyCode("Pad1 Back");
keyMappings.Mapping1.Start = KeyManager::GetKeyCode("Pad1 Start");
keyMappings.Mapping2.Up = KeyManager::GetKeyCode("Up Arrow");
keyMappings.Mapping2.Down = KeyManager::GetKeyCode("Down Arrow");
keyMappings.Mapping2.Left = KeyManager::GetKeyCode("Left Arrow");
keyMappings.Mapping2.Right = KeyManager::GetKeyCode("Right Arrow");
keyMappings.Mapping2.A = KeyManager::GetKeyCode("Z");
keyMappings.Mapping2.B = KeyManager::GetKeyCode("X");
keyMappings.Mapping2.X = KeyManager::GetKeyCode("S");
keyMappings.Mapping2.Y = KeyManager::GetKeyCode("A");
keyMappings.Mapping2.L = KeyManager::GetKeyCode("Q");
keyMappings.Mapping2.R = KeyManager::GetKeyCode("W");
keyMappings.Mapping2.Select = KeyManager::GetKeyCode("E");
keyMappings.Mapping2.Start = KeyManager::GetKeyCode("D");
}
switch(type) {
case ControllerType::None: break;
case ControllerType::SnesController: device.reset(new SnesController(console, port, keyMappings)); break;
}
return device;
}
void ControlManager::UpdateControlDevices()
{
auto lock = _deviceLock.AcquireSafe();
_controlDevices.clear();
for(int i = 0; i < 4; i++) {
shared_ptr<BaseControlDevice> device = CreateControllerDevice(GetControllerType(i), i, _console);
if(device) {
RegisterControlDevice(device);
}
}
}
uint8_t ControlManager::GetOpenBusMask(uint8_t port)
{
return 0;
}
void ControlManager::UpdateInputState()
{
KeyManager::RefreshKeyState();
//auto lock = _deviceLock.AcquireSafe();
string log = "";
for(shared_ptr<BaseControlDevice> &device : _controlDevices) {
device->ClearState();
bool inputSet = false;
for(size_t i = 0; i < _inputProviders.size(); i++) {
IInputProvider* provider = _inputProviders[i];
if(provider->SetInput(device.get())) {
inputSet = true;
break;
}
}
if(!inputSet) {
device->SetStateFromInput();
}
device->OnAfterSetState();
//log += "|" + device->GetTextState();
}
//TODO
/*
shared_ptr<Debugger> debugger = _console->GetDebugger(false);
if(debugger) {
debugger->ProcessEvent(EventType::InputPolled);
}*/
for(IInputRecorder* recorder : _inputRecorders) {
recorder->RecordInput(_controlDevices);
}
//MessageManager::Log(log);
_pollCounter++;
}
uint32_t ControlManager::GetPollCounter()
{
return ControlManager::_pollCounter;
}
void ControlManager::SetPollCounter(uint32_t value)
{
_pollCounter = value;
}
uint8_t ControlManager::Read(uint16_t addr)
{
uint8_t value = 0; //TODO _console->GetMemoryManager()->GetOpenBus(GetOpenBusMask(addr - 0x4016));
for(shared_ptr<BaseControlDevice> &device : _controlDevices) {
value |= device->ReadRam(addr);
}
return value;
}
void ControlManager::Write(uint16_t addr, uint8_t value)
{
for(shared_ptr<BaseControlDevice> &device : _controlDevices) {
device->WriteRam(addr, value);
}
}

60
Core/ControlManager.h Normal file
View file

@ -0,0 +1,60 @@
#pragma once
#include "stdafx.h"
#include "../Utilities/SimpleLock.h"
#include "IMemoryHandler.h"
class BaseControlDevice;
class IInputRecorder;
class IInputProvider;
class Console;
struct ControlDeviceState;
enum class ControllerType;
enum class ExpansionPortDevice;
class ControlManager
{
private:
vector<IInputRecorder*> _inputRecorders;
vector<IInputProvider*> _inputProviders;
//Static so that power cycle does not reset its value
uint32_t _pollCounter;
protected:
shared_ptr<Console> _console;
SimpleLock _deviceLock;
vector<shared_ptr<BaseControlDevice>> _controlDevices;
shared_ptr<BaseControlDevice> _systemActionManager;
void RegisterControlDevice(shared_ptr<BaseControlDevice> controlDevice);
ControllerType GetControllerType(uint8_t port);
uint8_t GetOpenBusMask(uint8_t port);
public:
ControlManager(shared_ptr<Console> console);
~ControlManager();
void UpdateControlDevices();
void UpdateInputState();
uint32_t GetPollCounter();
void SetPollCounter(uint32_t value);
void RegisterInputProvider(IInputProvider* provider);
void UnregisterInputProvider(IInputProvider* provider);
void RegisterInputRecorder(IInputRecorder* recorder);
void UnregisterInputRecorder(IInputRecorder* recorder);
vector<ControlDeviceState> GetPortStates();
shared_ptr<BaseControlDevice> GetControlDevice(uint8_t port);
vector<shared_ptr<BaseControlDevice>> GetControlDevices();
static shared_ptr<BaseControlDevice> CreateControllerDevice(ControllerType type, uint8_t port, shared_ptr<Console> console);
uint8_t Read(uint16_t addr);
void Write(uint16_t addr, uint8_t value);
};

View file

@ -44,6 +44,7 @@
</ItemGroup>
<ItemGroup>
<ClInclude Include="BaseCartridge.h" />
<ClInclude Include="BaseControlDevice.h" />
<ClInclude Include="BaseRenderer.h" />
<ClInclude Include="BaseSoundManager.h" />
<ClInclude Include="BaseVideoFilter.h" />
@ -52,6 +53,8 @@
<ClInclude Include="blargg_endian.h" />
<ClInclude Include="blargg_source.h" />
<ClInclude Include="Console.h" />
<ClInclude Include="ControlDeviceState.h" />
<ClInclude Include="ControlManager.h" />
<ClInclude Include="Cpu.h" />
<ClInclude Include="CpuTypes.h" />
<ClInclude Include="Debugger.h" />
@ -86,6 +89,7 @@
<ClInclude Include="RamHandler.h" />
<ClInclude Include="RomHandler.h" />
<ClInclude Include="SettingTypes.h" />
<ClInclude Include="SnesController.h" />
<ClInclude Include="SNES_SPC.h" />
<ClInclude Include="SoundMixer.h" />
<ClInclude Include="Spc.h" />
@ -99,10 +103,12 @@
</ItemGroup>
<ItemGroup>
<ClCompile Include="BaseCartridge.cpp" />
<ClCompile Include="BaseControlDevice.cpp" />
<ClCompile Include="BaseRenderer.cpp" />
<ClCompile Include="BaseSoundManager.cpp" />
<ClCompile Include="BaseVideoFilter.cpp" />
<ClCompile Include="Console.cpp" />
<ClCompile Include="ControlManager.cpp" />
<ClCompile Include="Cpu.cpp" />
<ClCompile Include="Cpu.Instructions.cpp" />
<ClCompile Include="Debugger.cpp" />

View file

@ -158,6 +158,18 @@
<ClInclude Include="InternalRegisters.h">
<Filter>SNES</Filter>
</ClInclude>
<ClInclude Include="BaseControlDevice.h">
<Filter>SNES\Input</Filter>
</ClInclude>
<ClInclude Include="SnesController.h">
<Filter>SNES\Input</Filter>
</ClInclude>
<ClInclude Include="ControlDeviceState.h">
<Filter>SNES\Input</Filter>
</ClInclude>
<ClInclude Include="ControlManager.h">
<Filter>SNES\Input</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="stdafx.cpp" />
@ -245,6 +257,12 @@
<ClCompile Include="InternalRegisters.cpp">
<Filter>SNES</Filter>
</ClCompile>
<ClCompile Include="BaseControlDevice.cpp">
<Filter>SNES\Input</Filter>
</ClCompile>
<ClCompile Include="ControlManager.cpp">
<Filter>SNES\Input</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<Filter Include="SNES">
@ -268,5 +286,8 @@
<Filter Include="SNES\SPC">
<UniqueIdentifier>{e0e6a67b-620e-4fe4-9846-26a6c80ca6b6}</UniqueIdentifier>
</Filter>
<Filter Include="SNES\Input">
<UniqueIdentifier>{3a9ea5fe-e818-4e65-bd50-cb9591de3e0d}</UniqueIdentifier>
</Filter>
</ItemGroup>
</Project>

View file

@ -1,17 +1,90 @@
#include "stdafx.h"
#include "InternalRegisters.h"
#include "Console.h"
#include "Cpu.h"
#include "Ppu.h"
#include "ControlManager.h"
#include "MessageManager.h"
#include "../Utilities/HexUtilities.h"
InternalRegisters::InternalRegisters(shared_ptr<Console> console)
{
_console = console;
}
void InternalRegisters::ProcessAutoJoypadRead()
{
if(!_enableAutoJoypadRead) {
return;
}
shared_ptr<ControlManager> controlManager = _console->GetControlManager();
controlManager->Write(0x4016, 1);
controlManager->Write(0x4016, 0);
for(int i = 0; i < 4; i++) {
_controllerData[i] = 0;
}
for(int i = 0; i < 16; i++) {
uint8_t port1 = controlManager->Read(0x4016);
uint8_t port2 = controlManager->Read(0x4017);
_controllerData[0] <<= 1;
_controllerData[1] <<= 1;
_controllerData[2] <<= 1;
_controllerData[3] <<= 1;
_controllerData[0] |= (port1 & 0x01);
_controllerData[1] |= (port2 & 0x01);
_controllerData[2] |= (port1 & 0x02) >> 1;
_controllerData[3] |= (port2 & 0x02) >> 1;
}
}
void InternalRegisters::SetNmiFlag(bool nmiFlag)
{
_nmiFlag = nmiFlag;
}
uint8_t InternalRegisters::Read(uint16_t addr)
{
switch(addr) {
case 0x4210: {
//open bus implementation here is needed to pass CPUPHL test
uint8_t value = (_nmiFlag ? 0x80 : 0) | ((addr >> 8) & 0x70);
_nmiFlag = false;
return value;
}
case 0x4211: {
uint8_t value = (_console->GetCpu()->CheckIrqSource(IrqSource::Ppu) ? 0x80 : 0) | ((addr >> 8) & 0x7F);
_console->GetCpu()->ClearIrqSource(IrqSource::Ppu);
return value;
}
case 0x4212: {
PpuState state = _console->GetPpu()->GetState();
return (
(state.Scanline >= 225 ? 0x80 : 0) |
((state.Cycle >= 0x121 || state.Cycle <= 0x15) ? 0x40 : 0) |
((_enableAutoJoypadRead && state.Scanline >= 225 && state.Scanline <= 227) ? 0x01 : 0) //Auto joypad read in progress
);
}
case 0x4214: return (uint8_t)_divResult;
case 0x4215: return (uint8_t)(_divResult >> 8);
case 0x4216: return (uint8_t)_multOrRemainderResult;
case 0x4217: return (uint8_t)(_multOrRemainderResult >> 8);
case 0x4218: case 0x421A: case 0x421C: case 0x421E:
return (uint8_t)_controllerData[((addr & 0x0E) - 8) >> 1];
case 0x4219: case 0x421B: case 0x421D: case 0x421F:
return (uint8_t)(_controllerData[((addr & 0x0E) - 8) >> 1] >> 8);
default:
MessageManager::DisplayMessage("Debug", "Unimplemented register read: " + HexUtilities::ToHex(addr));
return 0;
@ -26,8 +99,7 @@ void InternalRegisters::Write(uint16_t addr, uint8_t value)
_enableVerticalIrq = (value & 0x20) != 0;
_enableHorizontalIrq = (value & 0x10) != 0;
//TODO
//_autoJoypadRead = (value & 0x01) != 0;
_enableAutoJoypadRead = (value & 0x01) != 0;
break;
case 0x4202: _multOperand1 = value; break;

View file

@ -1,9 +1,13 @@
#pragma once
#include "stdafx.h"
class Console;
class InternalRegisters
{
private:
shared_ptr<Console> _console;
uint8_t _multOperand1 = 0;
uint8_t _multOperand2 = 0;
uint16_t _multOrRemainderResult = 0;
@ -12,19 +16,29 @@ private:
uint8_t _divisor = 0;
uint16_t _divResult = 0;
bool _enableAutoJoypadRead = false;
bool _nmiFlag = false;
bool _enableNmi = false;
bool _enableHorizontalIrq = false;
bool _enableVerticalIrq = false;
uint16_t _horizontalTimer = 0x1FF;
uint16_t _verticalTimer = 0x1FF;
uint16_t _controllerData[4];
public:
InternalRegisters(shared_ptr<Console> console);
void ProcessAutoJoypadRead();
void SetNmiFlag(bool nmiFlag);
bool IsVerticalIrqEnabled() { return _enableVerticalIrq; }
bool IsHorizontalIrqEnabled() { return _enableHorizontalIrq; }
bool IsNmiEnabled() { return _enableNmi; }
uint16_t GetHorizontalTimer() { return _horizontalTimer; }
uint16_t GetVerticalTimer() { return _verticalTimer; }
uint8_t Read(uint16_t addr);
void Write(uint16_t addr, uint8_t value);
};

View file

@ -23,10 +23,9 @@ void KeyManager::RefreshKeyState()
bool KeyManager::IsKeyPressed(uint32_t keyCode)
{
//TODO
/*if(_keyManager != nullptr) {
return _settings->InputEnabled() && _keyManager->IsKeyPressed(keyCode);
}*/
if(_keyManager != nullptr) {
return _keyManager->IsKeyPressed(keyCode);
}
return false;
}

View file

@ -6,6 +6,7 @@
#include "RamHandler.h"
#include "DmaController.h"
#include "BaseCartridge.h"
#include "ControlManager.h"
#include "InternalRegisters.h"
#include "IMemoryHandler.h"
#include "MessageManager.h"
@ -18,16 +19,18 @@ private:
Spc *_spc;
DmaController *_dmaController;
InternalRegisters *_regs;
ControlManager *_controlManager;
uint8_t *_workRam;
uint32_t _wramPosition;
public:
CpuRegisterHandler(Ppu *ppu, Spc *spc, DmaController *dmaController, InternalRegisters *regs, uint8_t *workRam)
CpuRegisterHandler(Ppu *ppu, Spc *spc, DmaController *dmaController, InternalRegisters *regs, ControlManager *controlManager, uint8_t *workRam)
{
_ppu = ppu;
_spc = spc;
_regs = regs;
_dmaController = dmaController;
_controlManager = controlManager;
_workRam = workRam;
_wramPosition = 0;
@ -38,6 +41,8 @@ public:
addr &= 0xFFFF;
if(addr >= 0x2140 && addr <= 0x217F) {
return _spc->Read(addr & 0x03);
} else if(addr == 0x4016 || addr == 0x4017) {
return _controlManager->Read(addr);
} else if(addr < 0x4200) {
return _ppu->Read(addr);
} else {
@ -61,6 +66,8 @@ public:
case 0x2182: _wramPosition = (_wramPosition & 0x100FF) | (value << 8); break;
case 0x2183: _wramPosition = (_wramPosition & 0xFFFF) | ((value & 0x01) << 16); break;
}
} else if(addr == 0x4016) {
return _controlManager->Write(addr, value);
} else if(addr == 0x420B || addr == 0x420C || addr >= 0x4300) {
_dmaController->Write(addr, value);
} else if(addr < 0x4200) {
@ -104,7 +111,7 @@ public:
_workRam = new uint8_t[MemoryManager::WorkRamSize];
_dmaController.reset(new DmaController(console->GetMemoryManager().get()));
_cpuRegisterHandler.reset(new CpuRegisterHandler(_ppu.get(), console->GetSpc().get(), _dmaController.get(), console->GetInternalRegisters().get(), _workRam));
_cpuRegisterHandler.reset(new CpuRegisterHandler(_ppu.get(), console->GetSpc().get(), _dmaController.get(), console->GetInternalRegisters().get(), console->GetControlManager().get(), _workRam));
memset(_handlers, 0, sizeof(_handlers));
//memset(_workRam, 0, 128 * 1024);

View file

@ -5,6 +5,7 @@
#include "Cpu.h"
#include "Spc.h"
#include "InternalRegisters.h"
#include "ControlManager.h"
#include "VideoDecoder.h"
#include "NotificationManager.h"
@ -41,6 +42,11 @@ Ppu::~Ppu()
delete[] _outputBuffers[1];
}
uint32_t Ppu::GetFrameCount()
{
return _frameCount;
}
PpuState Ppu::GetState()
{
return {
@ -57,17 +63,19 @@ void Ppu::Exec()
_scanline++;
if(_scanline == 225) {
_frameCount++;
_console->GetSpc()->ProcessEndFrame();
_nmiFlag = true;
_console->GetControlManager()->UpdateInputState();
_regs->ProcessAutoJoypadRead();
_regs->SetNmiFlag(true);
SendFrame();
if(_regs->IsNmiEnabled()) {
_console->GetCpu()->SetNmiFlag();
}
} else if(_scanline == 261) {
_nmiFlag = false;
_regs->SetNmiFlag(false);
_scanline = 0;
_frameCount++;
}
if(_regs->IsVerticalIrqEnabled() && !_regs->IsHorizontalIrqEnabled() && _scanline == _regs->GetVerticalTimer()) {
@ -175,25 +183,6 @@ uint8_t* Ppu::GetSpriteRam()
uint8_t Ppu::Read(uint16_t addr)
{
switch(addr) {
case 0x4210: {
//open bus implementation here is needed to pass CPUPHL test
uint8_t value = (_nmiFlag ? 0x80 : 0) | ((addr >> 8) & 0x70);
_nmiFlag = false;
return value;
}
case 0x4211: {
uint8_t value = (_console->GetCpu()->CheckIrqSource(IrqSource::Ppu) ? 0x80 : 0) | ((addr >> 8) & 0x7F);
_console->GetCpu()->ClearIrqSource(IrqSource::Ppu);
return value;
}
case 0x4212:
return (
(_scanline >= 225 ? 0x80 : 0) |
((_cycle >= 0x121 || _cycle <= 0x15) ? 0x40 : 0)
);
default:
MessageManager::DisplayMessage("Debug", "Unimplemented register read: " + HexUtilities::ToHex(addr));
break;
@ -205,6 +194,30 @@ uint8_t Ppu::Read(uint16_t addr)
void Ppu::Write(uint32_t addr, uint8_t value)
{
switch(addr) {
case 0x2101:
//TODO
//_spriteMode = (value & 0xE0) >> 5;
//_spriteBaseAddress = (value & 0x07) << 13;
//_spriteAddressOffset = (value & 0x18) << 9;
break;
case 0x2102:
//TODO
//_oamAddress = (value << 1);
break;
case 0x2103:
//TODO
//_oamTableSelect = value & 0x01;
//_enableOamPriority = (value & 0x80) != 0;
break;
case 0x2104:
//TODO
//_oam[_oamAddress] = value;
//_oamAddress++;
break;
case 0x2105:
_bgMode = value & 0x07;

View file

@ -23,8 +23,6 @@ private:
uint8_t _bgMode = 0;
LayerConfig _layerConfig[4];
bool _nmiFlag = false;
uint8_t *_vram;
uint16_t _vramAddress;
uint8_t _vramIncrementValue;
@ -45,6 +43,7 @@ public:
Ppu(shared_ptr<Console> console);
~Ppu();
uint32_t GetFrameCount();
PpuState GetState();
void Exec();

View file

@ -127,3 +127,80 @@ struct ScreenSize
int32_t Height;
double Scale;
};
struct KeyMapping
{
uint32_t A = 0;
uint32_t B = 0;
uint32_t X = 0;
uint32_t Y = 0;
uint32_t L = 0;
uint32_t R = 0;
uint32_t Up = 0;
uint32_t Down = 0;
uint32_t Left = 0;
uint32_t Right = 0;
uint32_t Start = 0;
uint32_t Select = 0;
uint32_t TurboA = 0;
uint32_t TurboB = 0;
uint32_t TurboX = 0;
uint32_t TurboY = 0;
uint32_t TurboL = 0;
uint32_t TurboR = 0;
uint32_t TurboSelect = 0;
uint32_t TurboStart = 0;
bool HasKeySet()
{
if(A || B || X || Y || L || R || Up || Down || Left || Right || Start || Select || TurboA || TurboB || TurboX || TurboY || TurboL || TurboR || TurboStart || TurboSelect) {
return true;
}
return false;
}
private:
bool HasKeyBinding(uint32_t* buttons, uint32_t count)
{
for(uint32_t i = 0; i < count; i++) {
if(buttons[i] != 0) {
return true;
}
}
return false;
}
};
struct KeyMappingSet
{
KeyMapping Mapping1;
KeyMapping Mapping2;
KeyMapping Mapping3;
KeyMapping Mapping4;
uint32_t TurboSpeed = 0;
vector<KeyMapping> GetKeyMappingArray()
{
vector<KeyMapping> keyMappings;
if(Mapping1.HasKeySet()) {
keyMappings.push_back(Mapping1);
}
if(Mapping2.HasKeySet()) {
keyMappings.push_back(Mapping2);
}
if(Mapping3.HasKeySet()) {
keyMappings.push_back(Mapping3);
}
if(Mapping4.HasKeySet()) {
keyMappings.push_back(Mapping4);
}
return keyMappings;
}
};
enum class ControllerType
{
None = 0,
SnesController = 1
};

98
Core/SnesController.h Normal file
View file

@ -0,0 +1,98 @@
#pragma once
#include "stdafx.h"
#include "BaseControlDevice.h"
class SnesController : public BaseControlDevice
{
private:
uint32_t _stateBuffer = 0;
protected:
enum Buttons { A = 0, B, Select, Start, Up, Down, Left, Right, Y, X, L, R };
string GetKeyNames() override
{
return "ABSTUDLRYXLR";
}
void InternalSetStateFromInput() override
{
for(KeyMapping keyMapping : _keyMappings) {
SetPressedState(Buttons::A, keyMapping.A);
SetPressedState(Buttons::B, keyMapping.B);
SetPressedState(Buttons::Start, keyMapping.Start);
SetPressedState(Buttons::Select, keyMapping.Select);
SetPressedState(Buttons::Up, keyMapping.Up);
SetPressedState(Buttons::Down, keyMapping.Down);
SetPressedState(Buttons::Left, keyMapping.Left);
SetPressedState(Buttons::Right, keyMapping.Right);
SetPressedState(Buttons::X, keyMapping.X);
SetPressedState(Buttons::Y, keyMapping.Y);
SetPressedState(Buttons::L, keyMapping.L);
SetPressedState(Buttons::R, keyMapping.R);
}
}
uint16_t ToByte()
{
//"A Super NES controller returns a 16-bit report in a similar order: B, Y, Select, Start, Up, Down, Left, Right, A, X, L, R, then four 0 bits."
return
(uint8_t)IsPressed(Buttons::B) |
((uint8_t)IsPressed(Buttons::Y) << 1) |
((uint8_t)IsPressed(Buttons::Select) << 2) |
((uint8_t)IsPressed(Buttons::Start) << 3) |
((uint8_t)IsPressed(Buttons::Up) << 4) |
((uint8_t)IsPressed(Buttons::Down) << 5) |
((uint8_t)IsPressed(Buttons::Left) << 6) |
((uint8_t)IsPressed(Buttons::Right) << 7) |
((uint8_t)IsPressed(Buttons::A) << 8) |
((uint8_t)IsPressed(Buttons::X) << 9) |
((uint8_t)IsPressed(Buttons::L) << 10) |
((uint8_t)IsPressed(Buttons::R) << 11);
}
//TODO
/*
void StreamState(bool saving) override
{
BaseControlDevice::StreamState(saving);
Stream(_stateBuffer);
}*/
void RefreshStateBuffer() override
{
_stateBuffer = (uint32_t)ToByte();
}
public:
SnesController(shared_ptr<Console> console, uint8_t port, KeyMappingSet keyMappings) : BaseControlDevice(console, port, keyMappings)
{
}
uint8_t ReadRam(uint16_t addr) override
{
uint8_t output = 0;
if(IsCurrentPort(addr)) {
if(_port >= 2) {
output = (_stateBuffer & 0x01) << 1; //P3/P4 are reported in bit 2
} else {
output = _stateBuffer & 0x01;
}
_stateBuffer >>= 1;
//"All subsequent reads will return D=1 on an authentic controller but may return D=0 on third party controllers."
_stateBuffer |= 0x8000;
StrobeProcessRead();
}
return output;
}
void WriteRam(uint16_t addr, uint8_t value) override
{
StrobeProcessWrite(value);
}
};

View file

@ -3,7 +3,6 @@
#include "SNES_SPC.h"
#include "Console.h"
#include "MemoryManager.h"
#include "Ppu.h"
#include "SoundMixer.h"
#include "../Utilities/VirtualFile.h"

View file

@ -1,6 +1,5 @@
#pragma once
#include "stdafx.h"
#include "PpuTypes.h"
class Console;
struct SNES_SPC;

View file

@ -13,7 +13,7 @@ using System.Windows.Forms;
namespace Mesen.GUI.Forms
{
public partial class frmMain : BaseForm
public partial class frmMain : BaseInputForm
{
private NotificationListener _notifListener;