diff --git a/Core/BaseControlDevice.cpp b/Core/BaseControlDevice.cpp new file mode 100644 index 0000000..a1cbbd1 --- /dev/null +++ b/Core/BaseControlDevice.cpp @@ -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, 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 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 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 state1, uint8_t button1, shared_ptr 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); + } +} diff --git a/Core/BaseControlDevice.h b/Core/BaseControlDevice.h new file mode 100644 index 0000000..414d7fd --- /dev/null +++ b/Core/BaseControlDevice.h @@ -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; + vector _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, 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 state1, uint8_t button1, shared_ptr state2, uint8_t button2); +}; diff --git a/Core/BaseRenderer.cpp b/Core/BaseRenderer.cpp index 742b1c9..6ddc8d9 100644 --- a/Core/BaseRenderer.cpp +++ b/Core/BaseRenderer.cpp @@ -120,7 +120,7 @@ void BaseRenderer::ShowFpsCounter(int lineNumber) if(_fpsTimer.GetElapsedMS() > 1000) { //Update fps every sec shared_ptr 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 = _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); } diff --git a/Core/Console.cpp b/Core/Console.cpp index ad09345..e2330ef 100644 --- a/Core/Console.cpp +++ b/Core/Console.cpp @@ -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 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 Console::GetInternalRegisters() return _internalRegisters; } +shared_ptr Console::GetControlManager() +{ + return _controlManager; +} + shared_ptr Console::GetDebugger(bool autoStart) { shared_ptr debugger = _debugger; diff --git a/Core/Console.h b/Core/Console.h index 2f37267..13b3fdf 100644 --- a/Core/Console.h +++ b/Core/Console.h @@ -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; shared_ptr _cart; shared_ptr _internalRegisters; + shared_ptr _controlManager; shared_ptr _debugger; @@ -61,6 +63,7 @@ public: shared_ptr GetCartridge(); shared_ptr GetMemoryManager(); shared_ptr GetInternalRegisters(); + shared_ptr GetControlManager(); shared_ptr GetDebugger(bool autoStart = true); bool IsRunning(); diff --git a/Core/ControlDeviceState.h b/Core/ControlDeviceState.h new file mode 100644 index 0000000..1a4b0e1 --- /dev/null +++ b/Core/ControlDeviceState.h @@ -0,0 +1,13 @@ +#pragma once +#include "stdafx.h" +#include + +struct ControlDeviceState +{ + vector State; + + bool operator!=(ControlDeviceState &other) + { + return State.size() != other.State.size() || memcmp(State.data(), other.State.data(), State.size()) != 0; + } +}; \ No newline at end of file diff --git a/Core/ControlManager.cpp b/Core/ControlManager.cpp new file mode 100644 index 0000000..97446c1 --- /dev/null +++ b/Core/ControlManager.cpp @@ -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; + _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 &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 &vec = _inputRecorders; + vec.erase(std::remove(vec.begin(), vec.end(), provider), vec.end()); +} + +vector ControlManager::GetPortStates() +{ + auto lock = _deviceLock.AcquireSafe(); + + vector states; + for(int i = 0; i < 4; i++) { + shared_ptr device = GetControlDevice(i); + if(device) { + states.push_back(device->GetRawState()); + } else { + states.push_back(ControlDeviceState()); + } + } + return states; +} + +shared_ptr ControlManager::GetControlDevice(uint8_t port) +{ + auto lock = _deviceLock.AcquireSafe(); + + auto result = std::find_if(_controlDevices.begin(), _controlDevices.end(), [port](const shared_ptr control) { return control->GetPort() == port; }); + if(result != _controlDevices.end()) { + return *result; + } + return nullptr; +} + +vector> ControlManager::GetControlDevices() +{ + return _controlDevices; +} + +void ControlManager::RegisterControlDevice(shared_ptr controlDevice) +{ + _controlDevices.push_back(controlDevice); +} + +ControllerType ControlManager::GetControllerType(uint8_t port) +{ + return ControllerType::SnesController; + //TODO _console->GetSettings()->GetControllerType(port); +} + +shared_ptr ControlManager::CreateControllerDevice(ControllerType type, uint8_t port, shared_ptr console) +{ + shared_ptr 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 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 &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 = _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 &device : _controlDevices) { + value |= device->ReadRam(addr); + } + + return value; +} + +void ControlManager::Write(uint16_t addr, uint8_t value) +{ + for(shared_ptr &device : _controlDevices) { + device->WriteRam(addr, value); + } +} \ No newline at end of file diff --git a/Core/ControlManager.h b/Core/ControlManager.h new file mode 100644 index 0000000..ced4bf3 --- /dev/null +++ b/Core/ControlManager.h @@ -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 _inputRecorders; + vector _inputProviders; + + //Static so that power cycle does not reset its value + uint32_t _pollCounter; + +protected: + shared_ptr _console; + SimpleLock _deviceLock; + vector> _controlDevices; + shared_ptr _systemActionManager; + + void RegisterControlDevice(shared_ptr controlDevice); + + ControllerType GetControllerType(uint8_t port); + uint8_t GetOpenBusMask(uint8_t port); + +public: + ControlManager(shared_ptr 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 GetPortStates(); + + shared_ptr GetControlDevice(uint8_t port); + vector> GetControlDevices(); + + static shared_ptr CreateControllerDevice(ControllerType type, uint8_t port, shared_ptr console); + + uint8_t Read(uint16_t addr); + void Write(uint16_t addr, uint8_t value); +}; diff --git a/Core/Core.vcxproj b/Core/Core.vcxproj index 7bc74a2..8bec523 100644 --- a/Core/Core.vcxproj +++ b/Core/Core.vcxproj @@ -44,6 +44,7 @@ + @@ -52,6 +53,8 @@ + + @@ -86,6 +89,7 @@ + @@ -99,10 +103,12 @@ + + diff --git a/Core/Core.vcxproj.filters b/Core/Core.vcxproj.filters index c0662e7..fdc2901 100644 --- a/Core/Core.vcxproj.filters +++ b/Core/Core.vcxproj.filters @@ -158,6 +158,18 @@ SNES + + SNES\Input + + + SNES\Input + + + SNES\Input + + + SNES\Input + @@ -245,6 +257,12 @@ SNES + + SNES\Input + + + SNES\Input + @@ -268,5 +286,8 @@ {e0e6a67b-620e-4fe4-9846-26a6c80ca6b6} + + {3a9ea5fe-e818-4e65-bd50-cb9591de3e0d} + \ No newline at end of file diff --git a/Core/InternalRegisters.cpp b/Core/InternalRegisters.cpp index 4521fef..7dad669 100644 --- a/Core/InternalRegisters.cpp +++ b/Core/InternalRegisters.cpp @@ -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; +} + +void InternalRegisters::ProcessAutoJoypadRead() +{ + if(!_enableAutoJoypadRead) { + return; + } + + shared_ptr 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; diff --git a/Core/InternalRegisters.h b/Core/InternalRegisters.h index b40bd34..534266d 100644 --- a/Core/InternalRegisters.h +++ b/Core/InternalRegisters.h @@ -1,9 +1,13 @@ #pragma once #include "stdafx.h" +class Console; + class InternalRegisters { private: + shared_ptr _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); + + 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); }; \ No newline at end of file diff --git a/Core/KeyManager.cpp b/Core/KeyManager.cpp index 0d02ec8..137d7a5 100644 --- a/Core/KeyManager.cpp +++ b/Core/KeyManager.cpp @@ -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; } diff --git a/Core/MemoryManager.h b/Core/MemoryManager.h index 5830bf8..3ba3b96 100644 --- a/Core/MemoryManager.h +++ b/Core/MemoryManager.h @@ -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); diff --git a/Core/Ppu.cpp b/Core/Ppu.cpp index bc567ec..dd27c3b 100644 --- a/Core/Ppu.cpp +++ b/Core/Ppu.cpp @@ -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; diff --git a/Core/Ppu.h b/Core/Ppu.h index a61933c..c961ed9 100644 --- a/Core/Ppu.h +++ b/Core/Ppu.h @@ -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); ~Ppu(); + uint32_t GetFrameCount(); PpuState GetState(); void Exec(); diff --git a/Core/SettingTypes.h b/Core/SettingTypes.h index c0338ec..76edf10 100644 --- a/Core/SettingTypes.h +++ b/Core/SettingTypes.h @@ -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 GetKeyMappingArray() + { + vector 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 +}; \ No newline at end of file diff --git a/Core/SnesController.h b/Core/SnesController.h new file mode 100644 index 0000000..0ab5cb2 --- /dev/null +++ b/Core/SnesController.h @@ -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, 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); + } +}; \ No newline at end of file diff --git a/Core/Spc.cpp b/Core/Spc.cpp index 8b15109..e3f152b 100644 --- a/Core/Spc.cpp +++ b/Core/Spc.cpp @@ -3,7 +3,6 @@ #include "SNES_SPC.h" #include "Console.h" #include "MemoryManager.h" -#include "Ppu.h" #include "SoundMixer.h" #include "../Utilities/VirtualFile.h" diff --git a/Core/Spc.h b/Core/Spc.h index 8a33739..74becd0 100644 --- a/Core/Spc.h +++ b/Core/Spc.h @@ -1,6 +1,5 @@ #pragma once #include "stdafx.h" -#include "PpuTypes.h" class Console; struct SNES_SPC; diff --git a/UI/Forms/frmMain.cs b/UI/Forms/frmMain.cs index 0b0086b..69e3458 100644 --- a/UI/Forms/frmMain.cs +++ b/UI/Forms/frmMain.cs @@ -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;