From add120abdba5e5cb24b239326bc8d53b09e23a01 Mon Sep 17 00:00:00 2001 From: Sour Date: Tue, 20 Apr 2021 22:51:15 -0400 Subject: [PATCH] Integrate NES input devices --- Core/Core.vcxproj | 24 ++++ Core/Core.vcxproj.filters | 24 ++++ Core/Gameboy/Gameboy.cpp | 5 + Core/Gameboy/Gameboy.h | 3 +- Core/NES/Input/ArkanoidController.h | 90 ++++++++++++++ Core/NES/Input/AsciiTurboFile.h | 66 ++++++++++ Core/NES/Input/BandaiHyperShot.h | 77 ++++++++++++ Core/NES/Input/BarcodeBattlerReader.h | 106 ++++++++++++++++ Core/NES/Input/BattleBox.h | 118 ++++++++++++++++++ Core/NES/Input/ExcitingBoxingController.h | 64 ++++++++++ Core/NES/Input/FamilyBasicDataRecorder.h | 141 ++++++++++++++++++++++ Core/NES/Input/FamilyBasicKeyboard.h | 123 +++++++++++++++++++ Core/NES/Input/FamilyMatTrainer.h | 46 +++++++ Core/NES/Input/FourScore.h | 50 ++++++++ Core/NES/Input/HoriTrack.h | 54 +++++++++ Core/NES/Input/IBarcodeReader.h | 8 ++ Core/NES/Input/JissenMahjongController.h | 99 +++++++++++++++ Core/NES/Input/KonamiHyperShot.h | 86 +++++++++++++ Core/NES/Input/NesController.h | 5 +- Core/NES/Input/OekaKidsTablet.h | 82 +++++++++++++ Core/NES/Input/PachinkoController.h | 60 +++++++++ Core/NES/Input/PartyTap.h | 76 ++++++++++++ Core/NES/Input/PowerPad.h | 81 +++++++++++++ Core/NES/Input/SuborKeyboard.h | 126 +++++++++++++++++++ Core/NES/Input/SuborMouse.h | 110 +++++++++++++++++ Core/NES/Input/VirtualBoyController.h | 109 +++++++++++++++++ Core/NES/Input/VsZapper.h | 45 +++++++ Core/NES/Input/Zapper.h | 98 +++++++++++++++ Core/NES/NesConsole.cpp | 5 + Core/NES/NesConsole.h | 3 +- Core/NES/NesControlManager.cpp | 118 +++++++++--------- Core/SNES/ControlManager.cpp | 2 +- Core/SNES/Input/Multitap.cpp | 7 +- Core/SNES/Input/Multitap.h | 2 - Core/SNES/Input/SnesController.cpp | 7 +- Core/SNES/Input/SnesController.h | 1 - Core/SNES/Input/SnesMouse.h | 8 +- Core/SNES/Input/SuperScope.h | 7 +- Core/Shared/BaseControlDevice.cpp | 8 +- Core/Shared/BaseControlDevice.h | 5 +- Core/Shared/Emulator.cpp | 8 +- Core/Shared/Emulator.h | 1 + Core/Shared/Interfaces/IBattery.h | 7 ++ Core/Shared/Interfaces/IConsole.h | 1 + Core/Shared/SettingTypes.h | 47 +++++++- Core/Shared/SystemActionManager.h | 7 +- NewUI/Config/NesConfig.cs | 3 + 47 files changed, 2110 insertions(+), 113 deletions(-) create mode 100644 Core/NES/Input/ArkanoidController.h create mode 100644 Core/NES/Input/AsciiTurboFile.h create mode 100644 Core/NES/Input/BandaiHyperShot.h create mode 100644 Core/NES/Input/BarcodeBattlerReader.h create mode 100644 Core/NES/Input/BattleBox.h create mode 100644 Core/NES/Input/ExcitingBoxingController.h create mode 100644 Core/NES/Input/FamilyBasicDataRecorder.h create mode 100644 Core/NES/Input/FamilyBasicKeyboard.h create mode 100644 Core/NES/Input/FamilyMatTrainer.h create mode 100644 Core/NES/Input/FourScore.h create mode 100644 Core/NES/Input/HoriTrack.h create mode 100644 Core/NES/Input/IBarcodeReader.h create mode 100644 Core/NES/Input/JissenMahjongController.h create mode 100644 Core/NES/Input/KonamiHyperShot.h create mode 100644 Core/NES/Input/OekaKidsTablet.h create mode 100644 Core/NES/Input/PachinkoController.h create mode 100644 Core/NES/Input/PartyTap.h create mode 100644 Core/NES/Input/PowerPad.h create mode 100644 Core/NES/Input/SuborKeyboard.h create mode 100644 Core/NES/Input/SuborMouse.h create mode 100644 Core/NES/Input/VirtualBoyController.h create mode 100644 Core/NES/Input/VsZapper.h create mode 100644 Core/NES/Input/Zapper.h create mode 100644 Core/Shared/Interfaces/IBattery.h diff --git a/Core/Core.vcxproj b/Core/Core.vcxproj index 40787070..fae7e930 100644 --- a/Core/Core.vcxproj +++ b/Core/Core.vcxproj @@ -44,6 +44,29 @@ + + + + + + + + + + + + + + + + + + + + + + + @@ -51,6 +74,7 @@ + diff --git a/Core/Core.vcxproj.filters b/Core/Core.vcxproj.filters index 1b8d670d..703975c1 100644 --- a/Core/Core.vcxproj.filters +++ b/Core/Core.vcxproj.filters @@ -734,6 +734,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Core/Gameboy/Gameboy.cpp b/Core/Gameboy/Gameboy.cpp index 21debb2d..fae62de1 100644 --- a/Core/Gameboy/Gameboy.cpp +++ b/Core/Gameboy/Gameboy.cpp @@ -463,3 +463,8 @@ uint64_t Gameboy::GetMasterClock() { return _memoryManager->GetCycleCount(); } + +uint32_t Gameboy::GetMasterClockRate() +{ + return 20971520; +} diff --git a/Core/Gameboy/Gameboy.h b/Core/Gameboy/Gameboy.h index 97114e54..2acda52c 100644 --- a/Core/Gameboy/Gameboy.h +++ b/Core/Gameboy/Gameboy.h @@ -117,5 +117,6 @@ public: virtual AddressInfo GetRelativeAddress(AddressInfo absAddress, CpuType cpuType) override; // Inherited via IConsole - virtual uint64_t GetMasterClock() override; + uint64_t GetMasterClock() override; + uint32_t GetMasterClockRate() override; }; \ No newline at end of file diff --git a/Core/NES/Input/ArkanoidController.h b/Core/NES/Input/ArkanoidController.h new file mode 100644 index 00000000..3b824256 --- /dev/null +++ b/Core/NES/Input/ArkanoidController.h @@ -0,0 +1,90 @@ +#pragma once +#include "stdafx.h" +#include "Shared/BaseControlDevice.h" +#include "Shared/KeyManager.h" +#include "Shared/Emulator.h" +#include "Shared/EmuSettings.h" +#include "Utilities/Serializer.h" + +class ArkanoidController : public BaseControlDevice +{ +private: + uint32_t _currentValue = (0xF4 - 0x54) / 2; + uint32_t _stateBuffer = 0; + enum Buttons { Fire }; + +protected: + bool HasCoordinates() override { return true; } + + string GetKeyNames() override + { + return "F"; + } + + void InternalSetStateFromInput() override + { + if(_emu->GetSettings()->IsInputEnabled()) { + SetPressedState(Buttons::Fire, KeyManager::IsMouseButtonPressed(MouseButton::LeftButton)); + SetMovement(KeyManager::GetMouseMovement( + _emu->GetSettings()->GetVideoConfig().VideoScale, + _emu->GetSettings()->GetInputConfig().MouseSensitivity + 1 + )); + } + } + + void Serialize(Serializer& s) override + { + BaseControlDevice::Serialize(s); + s.Stream(_stateBuffer, _currentValue); + } + + void RefreshStateBuffer() override + { + MouseMovement mov = GetMovement(); + + _currentValue += mov.dx; + if(_currentValue < 0x54) { + _currentValue = 0x54; + } else if(_currentValue > 0xF4) { + _currentValue = 0xF4; + } + + _stateBuffer = _currentValue; + } + +public: + ArkanoidController(Emulator* emu, ControllerType type, uint8_t port) : BaseControlDevice(emu, type, port) + { + } + + uint8_t ReadRam(uint16_t addr) override + { + uint8_t output = 0; + if(IsExpansionDevice()) { + if(addr == 0x4016) { + //Fire button is on port 1 + if(IsPressed(ArkanoidController::Buttons::Fire)) { + output |= 0x02; + } + } else if(addr == 0x4017) { + //Serial data is on port 2 + output |= ((~_stateBuffer) >> 6) & 0x02; + _stateBuffer <<= 1; + } + } else if(IsCurrentPort(addr)) { + output = ((~_stateBuffer) >> 3) & 0x10; + _stateBuffer <<= 1; + + if(IsPressed(ArkanoidController::Buttons::Fire)) { + output |= 0x08; + } + } + + return output; + } + + void WriteRam(uint16_t addr, uint8_t value) override + { + StrobeProcessWrite(value); + } +}; \ No newline at end of file diff --git a/Core/NES/Input/AsciiTurboFile.h b/Core/NES/Input/AsciiTurboFile.h new file mode 100644 index 00000000..b0aa78a1 --- /dev/null +++ b/Core/NES/Input/AsciiTurboFile.h @@ -0,0 +1,66 @@ +#pragma once +#include "stdafx.h" +#include "Shared/BaseControlDevice.h" +#include "Shared/Emulator.h" +#include "Shared/EmuSettings.h" +#include "Shared/BatteryManager.h" +#include "Shared/Interfaces/IBattery.h" +#include "Utilities/Serializer.h" + +class AsciiTurboFile : public BaseControlDevice, public IBattery +{ +private: + static constexpr int FileSize = 0x2000; + static constexpr int BitCount = FileSize * 8; + uint8_t _lastWrite = 0; + uint16_t _position = 0; + uint8_t _data[AsciiTurboFile::FileSize]; + +protected: + void Serialize(Serializer& s) override + { + BaseControlDevice::Serialize(s); + ArrayInfo data { _data, AsciiTurboFile::FileSize }; + s.Stream(_position, _lastWrite, data); + } + +public: + AsciiTurboFile(Emulator* emu) : BaseControlDevice(emu, ControllerType::AsciiTurboFile, BaseControlDevice::ExpDevicePort) + { + _emu->GetBatteryManager()->LoadBattery(".tf", _data, AsciiTurboFile::FileSize); + } + + ~AsciiTurboFile() + { + SaveBattery(); + } + + void SaveBattery() override + { + _emu->GetBatteryManager()->SaveBattery(".tf", _data, AsciiTurboFile::FileSize); + } + + uint8_t ReadRam(uint16_t addr) override + { + if(addr == 0x4017) { + return ((_data[_position / 8] >> (_position % 8)) & 0x01) << 2; + } + return 0; + } + + void WriteRam(uint16_t addr, uint8_t value) override + { + if(!(value & 0x02)) { + _position = 0; + } + + if(!(value & 0x04) && (_lastWrite & 0x04)) { + //Clock, perform write, increase position + _data[_position / 8] &= ~(1 << (_position % 8)); + _data[_position / 8] |= (value & 0x01) << (_position % 8); + _position = (_position + 1) & (AsciiTurboFile::BitCount - 1); + } + + _lastWrite = value; + } +}; \ No newline at end of file diff --git a/Core/NES/Input/BandaiHyperShot.h b/Core/NES/Input/BandaiHyperShot.h new file mode 100644 index 00000000..ac851440 --- /dev/null +++ b/Core/NES/Input/BandaiHyperShot.h @@ -0,0 +1,77 @@ +#pragma once +#include "stdafx.h" +#include "NES/Input/NesController.h" +#include "NES/Input/Zapper.h" +#include "NES/NesConsole.h" +#include "Shared/KeyManager.h" + +class BandaiHyperShot : public NesController +{ +private: + NesConsole* _console; + uint32_t _stateBuffer = 0; + +protected: + enum ZapperButtons { Fire = 9 }; + + bool HasCoordinates() override { return true; } + + string GetKeyNames() override + { + return NesController::GetKeyNames() + "F"; + } + + void InternalSetStateFromInput() override + { + NesController::InternalSetStateFromInput(); + + if(_emu->GetSettings()->IsInputEnabled()) { + SetPressedState(ZapperButtons::Fire, KeyManager::IsMouseButtonPressed(MouseButton::LeftButton)); + + MousePosition pos = KeyManager::GetMousePosition(); + if(KeyManager::IsMouseButtonPressed(MouseButton::RightButton)) { + pos.X = -1; + pos.Y = -1; + } + SetCoordinates(pos); + } + } + + bool IsLightFound() + { + return Zapper::StaticIsLightFound(GetCoordinates(), _console); + } + + void Serialize(Serializer& s) override + { + BaseControlDevice::Serialize(s); + s.Stream(_stateBuffer); + } + +public: + BandaiHyperShot(NesConsole* console, KeyMappingSet keyMappings) : NesController(console->GetEmulator(), ControllerType::BandaiHyperShot, BaseControlDevice::ExpDevicePort, keyMappings) + { + } + + void RefreshStateBuffer() override + { + _stateBuffer = (uint32_t)ToByte(); + } + + uint8_t ReadRam(uint16_t addr) override + { + if(addr == 0x4016) { + StrobeProcessRead(); + uint8_t output = (_stateBuffer & 0x01) << 1; + _stateBuffer >>= 1; + return output; + } else { + return (IsLightFound() ? 0 : 0x08) | (IsPressed(ZapperButtons::Fire) ? 0x10 : 0x00); + } + } + + void WriteRam(uint16_t addr, uint8_t value) override + { + StrobeProcessWrite(value); + } +}; \ No newline at end of file diff --git a/Core/NES/Input/BarcodeBattlerReader.h b/Core/NES/Input/BarcodeBattlerReader.h new file mode 100644 index 00000000..5f8a532b --- /dev/null +++ b/Core/NES/Input/BarcodeBattlerReader.h @@ -0,0 +1,106 @@ +#pragma once +#include "stdafx.h" +#include "Shared/BaseControlDevice.h" +#include "Shared/Emulator.h" +#include "Utilities/Serializer.h" +#include "NES/Input/IBarcodeReader.h" + +class BarcodeBattlerReader : public BaseControlDevice, public IBarcodeReader +{ +private: + static constexpr int StreamSize = 200; + uint64_t _newBarcode = 0; + uint32_t _newBarcodeDigitCount = 0; + + uint8_t _barcodeStream[BarcodeBattlerReader::StreamSize]; + uint64_t _insertCycle = 0; + +protected: + void Serialize(Serializer& s) override + { + BaseControlDevice::Serialize(s); + + ArrayInfo bitStream{ _barcodeStream, BarcodeBattlerReader::StreamSize }; + s.Stream(_newBarcode, _newBarcodeDigitCount, _insertCycle, bitStream); + } + + bool IsRawString() override + { + return true; + } + + void InitBarcodeStream() + { + vector state = GetRawState().State; + string barcodeText(state.begin(), state.end()); + + //Signature at the end, needed for code to be recognized + barcodeText += "EPOCH\xD\xA"; + //Pad to 20 characters with spaces + barcodeText.insert(0, 20 - barcodeText.size(), ' '); + + int pos = 0; + vector bits; + for(int i = 0; i < 20; i++) { + _barcodeStream[pos++] = 1; + for(int j = 0; j < 8; j++) { + _barcodeStream[pos++] = ~((barcodeText[i] >> j) & 0x01); + } + _barcodeStream[pos++] = 0; + } + } + +public: + BarcodeBattlerReader(Emulator* emu) : BaseControlDevice(emu, ControllerType::BarcodeBattler, BaseControlDevice::ExpDevicePort) + { + } + + void InternalSetStateFromInput() override + { + ClearState(); + + if(_newBarcodeDigitCount > 0) { + string barcodeText = std::to_string(_newBarcode); + //Pad 8 or 13 character barcode with 0s at start + barcodeText.insert(0, _newBarcodeDigitCount - barcodeText.size(), '0'); + SetTextState(barcodeText); + + _newBarcode = 0; + _newBarcodeDigitCount = 0; + } + } + + void OnAfterSetState() override + { + if(GetRawState().State.size() > 0) { + InitBarcodeStream(); + if(_emu) { + _insertCycle = _emu->GetMasterClock(); + } + } + } + + void InputBarcode(uint64_t barcode, uint32_t digitCount) override + { + _newBarcode = barcode; + _newBarcodeDigitCount = digitCount; + } + + uint8_t ReadRam(uint16_t addr) override + { + if(addr == 0x4017) { + uint64_t elapsedCycles = _emu->GetMasterClock() - _insertCycle; + uint32_t cyclesPerBit = _emu->GetMasterClockRate() / 1200; + + uint32_t streamPosition = (uint32_t)(elapsedCycles / cyclesPerBit); + if(streamPosition < BarcodeBattlerReader::StreamSize) { + return _barcodeStream[streamPosition] << 2; + } + } + return 0; + } + + void WriteRam(uint16_t addr, uint8_t value) override + { + } +}; \ No newline at end of file diff --git a/Core/NES/Input/BattleBox.h b/Core/NES/Input/BattleBox.h new file mode 100644 index 00000000..b334bfd6 --- /dev/null +++ b/Core/NES/Input/BattleBox.h @@ -0,0 +1,118 @@ +#pragma once +#include "stdafx.h" +#include "Shared/Emulator.h" +#include "Shared/BaseControlDevice.h" +#include "Shared/BatteryManager.h" +#include "Shared/Interfaces/IBattery.h" +#include "Utilities/Serializer.h" + +class BattleBox : public BaseControlDevice, public IBattery +{ +private: + static constexpr int FileSize = 0x200; + uint8_t _lastWrite = 0; + uint8_t _address = 0; + uint8_t _chipSelect = 0; + uint16_t _data[BattleBox::FileSize/2]; + uint8_t _output = 0; + bool _writeEnabled = false; + + uint8_t _inputBitPosition = 0; + uint16_t _inputData = 0; + bool _isWrite = false; + bool _isRead = false; + +protected: + void Serialize(Serializer& s) override + { + BaseControlDevice::Serialize(s); + ArrayInfo data{ (uint8_t*)_data, BattleBox::FileSize }; + s.Stream(_lastWrite, _address, _chipSelect, _output, _writeEnabled, _inputBitPosition, _isWrite, _isRead, _inputData, data); + } + +public: + BattleBox(Emulator* emu) : BaseControlDevice(emu, ControllerType::BattleBox, BaseControlDevice::ExpDevicePort) + { + _emu->GetBatteryManager()->LoadBattery(".bb", (uint8_t*)_data, BattleBox::FileSize); + } + + ~BattleBox() + { + SaveBattery(); + } + + void SaveBattery() override + { + _emu->GetBatteryManager()->SaveBattery(".bb", (uint8_t*)_data, BattleBox::FileSize); + } + + uint8_t ReadRam(uint16_t addr) override + { + if(addr == 0x4017) { + if(_lastWrite & 0x01) { + _chipSelect ^= 0x01; + _inputData = 0; + _inputBitPosition = 0; + } + _output ^= 0x01; + + uint8_t readBit = 0; + if(_isRead) { + readBit = ((_data[(_chipSelect ? 0x80 : 0) | _address] >> _inputBitPosition) & 0x01) << 3; + } + uint8_t writeBit = (_output << 4); + return readBit | writeBit; + } + return 0; + } + + void WriteRam(uint16_t addr, uint8_t value) override + { + if(value & 0x01 && !(_lastWrite & 0x01)) { + //Clock + _inputData &= ~(1 << _inputBitPosition); + _inputData |= (_output << _inputBitPosition); + _inputBitPosition++; + + if(_inputBitPosition > 15) { + if(_isWrite) { + _data[(_chipSelect ? 0x80 : 0) | _address] = _inputData; + _isWrite = false; + } else { + _isRead = false; + + //done reading addr/command or write data + uint8_t address = (_inputData & 0x7F); + + uint8_t cmd = ((_inputData & 0x7F00) >> 8) ^ 0x7F; + switch(cmd) { + case 0x01: + //read + _address = address; + _isRead = true; + break; + case 0x06: + //program + if(_writeEnabled) { + _address = address; + _isWrite = true; + } + break; + case 0x0C: + //chip erase + if(_writeEnabled) { + memset(_data, 0, BattleBox::FileSize); + } + break; + + case 0x0D: break; //busy monitor + case 0x09: _writeEnabled = true; break; //erase/write enable + case 0x0B: _writeEnabled = false; break; //erase/write disable + } + } + _inputBitPosition = 0; + } + } + _lastWrite = value; + } +}; \ No newline at end of file diff --git a/Core/NES/Input/ExcitingBoxingController.h b/Core/NES/Input/ExcitingBoxingController.h new file mode 100644 index 00000000..43fbfbf2 --- /dev/null +++ b/Core/NES/Input/ExcitingBoxingController.h @@ -0,0 +1,64 @@ +#pragma once +#include "stdafx.h" +#include "Shared/BaseControlDevice.h" +#include "Utilities/Serializer.h" + +class Emulator; + +class ExcitingBoxingController : public BaseControlDevice +{ +private: + uint8_t _selectedSensors = 0; + enum Buttons { LeftHook = 0, MoveRight, MoveLeft, RightHook, LeftJab, HitBody, RightJab, Straight }; + +protected: + void Serialize(Serializer& s) override + { + BaseControlDevice::Serialize(s); + s.Stream(_selectedSensors); + } + + string GetKeyNames() override + { + return "HRLhJBjS"; + } + + void InternalSetStateFromInput() override + { + for(KeyMapping keyMapping : _keyMappings) { + for(int i = 0; i < 8; i++) { + SetPressedState(i, keyMapping.CustomKeys[i]); + } + } + } + +public: + ExcitingBoxingController(Emulator* emu, KeyMappingSet keyMappings) : BaseControlDevice(emu, ControllerType::ExcitingBoxing, BaseControlDevice::ExpDevicePort, keyMappings) + { + } + + uint8_t ReadRam(uint16_t addr) override + { + if(addr == 0x4017) { + if(_selectedSensors == 0) { + return + (IsPressed(ExcitingBoxingController::Buttons::LeftHook) ? 0 : 0x02) | + (IsPressed(ExcitingBoxingController::Buttons::MoveRight) ? 0 : 0x04) | + (IsPressed(ExcitingBoxingController::Buttons::MoveLeft) ? 0 : 0x08) | + (IsPressed(ExcitingBoxingController::Buttons::RightHook) ? 0 : 0x10); + } else { + return + (IsPressed(ExcitingBoxingController::Buttons::LeftJab) ? 0 : 0x02) | + (IsPressed(ExcitingBoxingController::Buttons::HitBody) ? 0 : 0x04) | + (IsPressed(ExcitingBoxingController::Buttons::RightJab) ? 0 : 0x08) | + (IsPressed(ExcitingBoxingController::Buttons::Straight) ? 0 : 0x10); + } + } + return 0; + } + + void WriteRam(uint16_t addr, uint8_t value) override + { + _selectedSensors = (value & 0x02) >> 1; + } +}; \ No newline at end of file diff --git a/Core/NES/Input/FamilyBasicDataRecorder.h b/Core/NES/Input/FamilyBasicDataRecorder.h new file mode 100644 index 00000000..ae4d690f --- /dev/null +++ b/Core/NES/Input/FamilyBasicDataRecorder.h @@ -0,0 +1,141 @@ +#pragma once +#include "stdafx.h" +#include "../Utilities/Base64.h" +#include "Shared/BaseControlDevice.h" +#include "Shared/Emulator.h" +#include "Utilities/Serializer.h" + +class FamilyBasicDataRecorder : public BaseControlDevice +{ +private: + static constexpr int32_t SamplingRate = 88; + vector _data; + vector _fileData; + bool _enabled = false; + bool _isPlaying = false; + uint64_t _cycle = 0; + + bool _isRecording = false; + string _recordFilePath; + +protected: + void Serialize(Serializer& s) override + { + BaseControlDevice::Serialize(s); + VectorInfo data { &_data }; + s.Stream(_enabled, _isPlaying, _cycle, data); + + if(!s.IsSaving() && _isRecording) { + StopRecording(); + } + } + + bool IsRawString() override + { + return true; + } + + void InternalSetStateFromInput() override + { + if(_fileData.size() > 0) { + SetTextState(Base64::Encode(_fileData)); + _fileData.clear(); + } + } + +public: + FamilyBasicDataRecorder(Emulator* emu) : BaseControlDevice(emu, ControllerType::None, BaseControlDevice::ExpDevicePort2) + { + } + + ~FamilyBasicDataRecorder() + { + if(_isRecording) { + StopRecording(); + } + } + + void OnAfterSetState() override + { + if(GetRawState().State.size() > 0) { + _data = Base64::Decode(GetTextState()); + _cycle = _emu->GetMasterClock(); + _isPlaying = true; + _isRecording = false; + } + } + + void LoadFromFile(VirtualFile file) + { + if(file.IsValid()) { + vector fileData; + file.ReadFile(fileData); + _fileData = fileData; + } + } + + bool IsRecording() + { + return _isRecording; + } + + void StartRecording(string filePath) + { + _isPlaying = false; + _recordFilePath = filePath; + _data.clear(); + _cycle = _emu->GetMasterClock(); + _isRecording = true; + } + + void StopRecording() + { + _isRecording = false; + + vector fileData; + + int bitPos = 0; + uint8_t currentByte = 0; + for(uint8_t bitValue : _data) { + currentByte |= (bitValue & 0x01) << bitPos; + bitPos = (bitPos + 1) % 8; + if(bitPos == 0) { + fileData.push_back(currentByte); + currentByte = 0; + } + } + + ofstream out(_recordFilePath, ios::binary); + if(out) { + out.write((char*)fileData.data(), fileData.size()); + } + } + + uint8_t ReadRam(uint16_t addr) override + { + if(addr == 0x4016 && _isPlaying) { + uint32_t readPos = (uint32_t)((_emu->GetMasterClock() - _cycle) / FamilyBasicDataRecorder::SamplingRate); + + if((uint32_t)_data.size() > readPos / 8) { + uint8_t value = ((_data[readPos / 8] >> (readPos % 8)) & 0x01) << 1; + return _enabled ? value : 0; + } else { + _isPlaying = false; + } + } + + return 0; + } + + void WriteRam(uint16_t addr, uint8_t value) override + { + _enabled = (value & 0x04) != 0; + + if(_isRecording) { + while(_emu->GetMasterClock() - _cycle > FamilyBasicDataRecorder::SamplingRate) { + _data.push_back(value & 0x01); + _cycle += 88; + } + } + } +}; \ No newline at end of file diff --git a/Core/NES/Input/FamilyBasicKeyboard.h b/Core/NES/Input/FamilyBasicKeyboard.h new file mode 100644 index 00000000..d67024c4 --- /dev/null +++ b/Core/NES/Input/FamilyBasicKeyboard.h @@ -0,0 +1,123 @@ +#pragma once +#include "stdafx.h" +#include "Shared/BaseControlDevice.h" +#include "Utilities/Serializer.h" + +class FamilyBasicKeyboard : public BaseControlDevice +{ +private: + uint8_t _row = 0; + uint8_t _column = 0; + bool _enabled = false; + + const uint32_t _keyMatrix[72] = { + F8, Return, LeftBracket, RightBracket, + Kana, RightShift, Yen, Stop, + F7, AtSign, Colon, SemiColon, + Underscore, Slash, Minus, Caret, + F6, O, L, K, + Dot, Comma, P, Num0, + F5, I, U, J, + M, N, Num9, Num8, + F4, Y, G, H, + B, V, Num7, Num6, + F3, T, R, D, + F, C, Num5, Num4, + F2, W, S, A, + X, Z, E, Num3, + F1, Esc, Q, Ctrl, + LeftShift, Grph, Num1, Num2, + ClrHome, Up, Right, Left, + Down, Space, Del, Ins + }; + + enum Buttons + { + A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, + Num0, Num1, Num2, Num3, Num4, Num5, Num6, Num7, Num8, Num9, + Return, Space, Del, Ins, Esc, + Ctrl, RightShift, LeftShift, + RightBracket, LeftBracket, + Up, Down, Left, Right, + Dot, Comma, Colon, SemiColon, Underscore, Slash, Minus, Caret, + F1, F2, F3, F4, F5, F6, F7, F8, + Yen, Stop, AtSign, Grph, ClrHome, Kana + }; + + string GetKeyNames() override + { + return "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789rsdiecSs[]udlrd,:;_/-^12345678ysagck"; + } + + void InternalSetStateFromInput() override + { + //TODO + bool allowInput = false; // _emu->GetSettings()->IsKeyboardMode(); + + if(allowInput) { + for(KeyMapping keyMapping : _keyMappings) { + for(int i = 0; i < 72; i++) { + SetPressedState(i, keyMapping.CustomKeys[i]); + } + } + } + } + + uint8_t GetActiveKeys(uint8_t row, uint8_t column) + { + if(row == 9) { + //10th row has no keys + return 0; + } + + uint8_t result = 0; + uint32_t baseIndex = row * 8 + (column ? 4 : 0); + for(int i = 0; i < 4; i++) { + if(IsPressed(_keyMatrix[baseIndex + i])) { + result |= 0x10; + } + result >>= 1; + } + return result; + } + +protected: + void Serialize(Serializer& s) override + { + BaseControlDevice::Serialize(s); + s.Stream(_row, _column, _enabled); + } + +public: + FamilyBasicKeyboard(Emulator* emu, KeyMappingSet keyMappings) : BaseControlDevice(emu, ControllerType::FamilyBasicKeyboard, BaseControlDevice::ExpDevicePort, keyMappings) + { + } + + uint8_t ReadRam(uint16_t addr) override + { + if(addr == 0x4017) { + if(_enabled) { + return ((~GetActiveKeys(_row, _column)) << 1) & 0x1E; + } else { + return 0; + } + } + return 0; + } + + void WriteRam(uint16_t addr, uint8_t value) override + { + uint8_t prevColumn = _column; + _column = (value & 0x02) >> 1; + if(!_column && prevColumn) { + //"Incrementing the row from the (keyless) 10th row will cause it to wrap back to the first row." + _row = (_row + 1) % 10; + } + + if(value & 0x01) { + _row = 0; + } + + _enabled = (value & 0x04) != 0; + } +}; \ No newline at end of file diff --git a/Core/NES/Input/FamilyMatTrainer.h b/Core/NES/Input/FamilyMatTrainer.h new file mode 100644 index 00000000..7958d94d --- /dev/null +++ b/Core/NES/Input/FamilyMatTrainer.h @@ -0,0 +1,46 @@ +#pragma once +#include "stdafx.h" +#include "NES/Input/PowerPad.h" +#include "Utilities/Serializer.h" + +class FamilyMatTrainer : public PowerPad +{ +private: + uint8_t _ignoreRows = 0; + +protected: + void Serialize(Serializer& s) override + { + BaseControlDevice::Serialize(s); + s.Stream(_ignoreRows); + } + +public: + FamilyMatTrainer(Emulator* emu, KeyMappingSet keyMappings) : PowerPad(emu, ControllerType::FamilyTrainerMat, BaseControlDevice::ExpDevicePort, keyMappings) + { + } + + uint8_t ReadRam(uint16_t addr) override + { + uint8_t output = 0; + if(addr == 0x4017) { + uint8_t pressedKeys[4] = {}; + for(int j = 0; j < 3; j++) { + if((_ignoreRows >> (2 - j)) & 0x01) { + //Ignore this row + continue; + } + for(int i = 0; i < 4; i++) { + pressedKeys[i] |= IsPressed(j * 4 + i) ? 1 : 0; + } + } + output = ~((pressedKeys[0] << 4) | (pressedKeys[1] << 3) | (pressedKeys[2] << 2) | (pressedKeys[3] << 1)) & 0x1E; + } + return output; + } + + void WriteRam(uint16_t addr, uint8_t value) override + { + _ignoreRows = value & 0x07; + } +}; \ No newline at end of file diff --git a/Core/NES/Input/FourScore.h b/Core/NES/Input/FourScore.h new file mode 100644 index 00000000..b4bfb5d1 --- /dev/null +++ b/Core/NES/Input/FourScore.h @@ -0,0 +1,50 @@ +#pragma once +#include "stdafx.h" +#include "Shared/BaseControlDevice.h" +#include "Utilities/Serializer.h" + +class FourScore : public BaseControlDevice +{ +private: + uint32_t _signature4016 = 0; + uint32_t _signature4017 = 0; + +protected: + void Serialize(Serializer &s) override + { + BaseControlDevice::Serialize(s); + s.Stream(_signature4016, _signature4017); + } + + void RefreshStateBuffer() override + { + //Signature for port 0 = 0x10, reversed bit order => 0x08 + //Signature for port 1 = 0x20, reversed bit order => 0x04 + _signature4016 = (0x08 << 16); + _signature4017 = (0x04 << 16); + } + +public: + FourScore(Emulator* emu) : BaseControlDevice(emu, ControllerType::FourScore, BaseControlDevice::ExpDevicePort) + { + } + + uint8_t ReadRam(uint16_t addr) override + { + StrobeProcessRead(); + uint8_t output = 0; + if(addr == 0x4016) { + output = _signature4016 & 0x01; + _signature4016 >>= 1; + } else if(addr == 0x4017) { + output = _signature4017 & 0x01; + _signature4017 >>= 1; + } + return output; + } + + void WriteRam(uint16_t addr, uint8_t value) override + { + StrobeProcessWrite(value); + } +}; diff --git a/Core/NES/Input/HoriTrack.h b/Core/NES/Input/HoriTrack.h new file mode 100644 index 00000000..e99ea6dc --- /dev/null +++ b/Core/NES/Input/HoriTrack.h @@ -0,0 +1,54 @@ +#pragma once +#include "stdafx.h" +#include "NES/Input/NesController.h" +#include "Shared/KeyManager.h" + +class HoriTrack : public NesController +{ +protected: + bool HasCoordinates() override { return true; } + + void InternalSetStateFromInput() override + { + NesController::InternalSetStateFromInput(); + SetPressedState(NesController::Buttons::A, KeyManager::IsMouseButtonPressed(MouseButton::LeftButton)); + SetPressedState(NesController::Buttons::B, KeyManager::IsMouseButtonPressed(MouseButton::RightButton)); + SetMovement(KeyManager::GetMouseMovement( + _emu->GetSettings()->GetVideoConfig().VideoScale, + _emu->GetSettings()->GetInputConfig().MouseSensitivity + 1 + )); + } + +public: + HoriTrack(Emulator* emu, KeyMappingSet keyMappings) : NesController(emu, ControllerType::HoriTrack, BaseControlDevice::ExpDevicePort, keyMappings) + { + } + + uint8_t ReadRam(uint16_t addr) override + { + uint8_t output = 0; + if(addr == 0x4016) { + StrobeProcessRead(); + output = (_stateBuffer & 0x01) << 1; + _stateBuffer >>= 1; + } + return output; + } + + void RefreshStateBuffer() override + { + MouseMovement mov = GetMovement(); + + mov.dx = std::max(-8, std::min((int)mov.dx, 7)); + mov.dy = std::max(-8, std::min((int)mov.dy, 7)); + + mov.dx = ((mov.dx & 0x08) >> 3) | ((mov.dx & 0x04) >> 1) | ((mov.dx & 0x02) << 1) | ((mov.dx & 0x01) << 3); + mov.dy = ((mov.dy & 0x08) >> 3) | ((mov.dy & 0x04) >> 1) | ((mov.dy & 0x02) << 1) | ((mov.dy & 0x01) << 3); + + uint8_t byte1 = (~mov.dy & 0x0F) | ((~mov.dx & 0x0F) << 4); + uint8_t byte2 = 0x09; + + NesController::RefreshStateBuffer(); + _stateBuffer = (_stateBuffer & 0xFF) | (byte1 << 8) | (byte2 << 16); + } +}; \ No newline at end of file diff --git a/Core/NES/Input/IBarcodeReader.h b/Core/NES/Input/IBarcodeReader.h new file mode 100644 index 00000000..dededbf9 --- /dev/null +++ b/Core/NES/Input/IBarcodeReader.h @@ -0,0 +1,8 @@ +#pragma once +#include "stdafx.h" + +class IBarcodeReader +{ +public: + virtual void InputBarcode(uint64_t barcode, uint32_t digitCount) = 0; +}; diff --git a/Core/NES/Input/JissenMahjongController.h b/Core/NES/Input/JissenMahjongController.h new file mode 100644 index 00000000..bcec2f28 --- /dev/null +++ b/Core/NES/Input/JissenMahjongController.h @@ -0,0 +1,99 @@ +#pragma once +#include "stdafx.h" +#include "Shared/BaseControlDevice.h" +#include "Utilities/Serializer.h" + +class JissenMahjongController : public BaseControlDevice +{ +private: + uint8_t _row = 0; + uint32_t _stateBuffer = 0; + +protected: + enum Buttons { A = 0, B, C, D, E, F, G, H, I, J, K, L, M, N, Select, Start, Kan, Pon, Chii, Riichi, Ron }; + + string GetKeyNames() override + { + return "ABCDEFGHIJKLMNSTkpcir"; + } + + void InternalSetStateFromInput() override + { + for(KeyMapping keyMapping : _keyMappings) { + for(int i = 0; i < 21; i++) { + SetPressedState(i, keyMapping.CustomKeys[i]); + } + } + } + + void Serialize(Serializer& s) override + { + BaseControlDevice::Serialize(s); + s.Stream(_row, _stateBuffer); + } + +public: + JissenMahjongController(Emulator* emu, KeyMappingSet keyMappings) : BaseControlDevice(emu, ControllerType::JissenMahjong, BaseControlDevice::ExpDevicePort, keyMappings) + { + } + + uint8_t ReadRam(uint16_t addr) override + { + if(addr == 0x4017) { + StrobeProcessRead(); + uint8_t value = (_stateBuffer & 0x01) << 1; + _stateBuffer >>= 1; + return value; + } + return 0; + } + + void RefreshStateBuffer() override + { + switch(_row) { + default: + case 0: + _stateBuffer = 0; + break; + + case 1: + _stateBuffer = + (IsPressed(JissenMahjongController::Buttons::N) ? 0x04 : 0) | + (IsPressed(JissenMahjongController::Buttons::M) ? 0x08 : 0) | + (IsPressed(JissenMahjongController::Buttons::L) ? 0x10 : 0) | + (IsPressed(JissenMahjongController::Buttons::K) ? 0x20 : 0) | + (IsPressed(JissenMahjongController::Buttons::J) ? 0x40 : 0) | + (IsPressed(JissenMahjongController::Buttons::I) ? 0x80 : 0); + break; + + case 2: + _stateBuffer = + (IsPressed(JissenMahjongController::Buttons::H) ? 0x01 : 0) | + (IsPressed(JissenMahjongController::Buttons::G) ? 0x02 : 0) | + (IsPressed(JissenMahjongController::Buttons::F) ? 0x04 : 0) | + (IsPressed(JissenMahjongController::Buttons::E) ? 0x08 : 0) | + (IsPressed(JissenMahjongController::Buttons::D) ? 0x10 : 0) | + (IsPressed(JissenMahjongController::Buttons::C) ? 0x20 : 0) | + (IsPressed(JissenMahjongController::Buttons::B) ? 0x40 : 0) | + (IsPressed(JissenMahjongController::Buttons::A) ? 0x80 : 0); + break; + + case 3: + _stateBuffer = + (IsPressed(JissenMahjongController::Buttons::Ron) ? 0x02 : 0) | + (IsPressed(JissenMahjongController::Buttons::Riichi) ? 0x04 : 0) | + (IsPressed(JissenMahjongController::Buttons::Chii) ? 0x08 : 0) | + (IsPressed(JissenMahjongController::Buttons::Pon) ? 0x10 : 0) | + (IsPressed(JissenMahjongController::Buttons::Kan) ? 0x20 : 0) | + (IsPressed(JissenMahjongController::Buttons::Start) ? 0x40 : 0) | + (IsPressed(JissenMahjongController::Buttons::Select) ? 0x80 : 0); + break; + } + } + + void WriteRam(uint16_t addr, uint8_t value) override + { + _row = (value & 0x6) >> 1; + StrobeProcessWrite(value); + } +}; \ No newline at end of file diff --git a/Core/NES/Input/KonamiHyperShot.h b/Core/NES/Input/KonamiHyperShot.h new file mode 100644 index 00000000..16f84d0d --- /dev/null +++ b/Core/NES/Input/KonamiHyperShot.h @@ -0,0 +1,86 @@ +#pragma once +#include "stdafx.h" +#include "Shared/BaseControlDevice.h" +#include "Shared/Emulator.h" +#include "Utilities/Serializer.h" + +class KonamiHyperShot : public BaseControlDevice +{ +private: + bool _enableP1 = true; + bool _enableP2 = true; + uint32_t _p1TurboSpeed; + uint32_t _p2TurboSpeed; + vector _p2KeyMappings; + +protected: + enum Buttons { Player1Run = 0, Player1Jump, Player2Run, Player2Jump }; + + string GetKeyNames() override + { + return "RJrj"; + } + + void InternalSetStateFromInput() override + { + for(KeyMapping keyMapping : _keyMappings) { + SetPressedState(Buttons::Player1Jump, keyMapping.A); + SetPressedState(Buttons::Player1Run, keyMapping.B); + + uint8_t turboFreq = 1 << (4 - _p1TurboSpeed); + bool turboOn = (uint8_t)(_emu->GetFrameCount() % turboFreq) < turboFreq / 2; + if(turboOn) { + SetPressedState(Buttons::Player1Jump, keyMapping.TurboA); + SetPressedState(Buttons::Player1Run, keyMapping.TurboB); + } + } + + for(KeyMapping keyMapping : _p2KeyMappings) { + SetPressedState(Buttons::Player2Jump, keyMapping.A); + SetPressedState(Buttons::Player2Run, keyMapping.B); + + uint8_t turboFreq = 1 << (4 - _p2TurboSpeed); + bool turboOn = (uint8_t)(_emu->GetFrameCount() % turboFreq) < turboFreq / 2; + if(turboOn) { + SetPressedState(Buttons::Player2Jump, keyMapping.TurboA); + SetPressedState(Buttons::Player2Run, keyMapping.TurboB); + } + } + } + + void Serialize(Serializer& s) override + { + BaseControlDevice::Serialize(s); + s.Stream(_enableP1, _enableP2); + } + +public: + KonamiHyperShot(Emulator* emu, KeyMappingSet p1, KeyMappingSet p2) : BaseControlDevice(emu, ControllerType::KonamiHyperShot, BaseControlDevice::ExpDevicePort, p1) + { + _p1TurboSpeed = p1.TurboSpeed; + _p2TurboSpeed = p2.TurboSpeed; + _p2KeyMappings = p2.GetKeyMappingArray(); + } + + uint8_t ReadRam(uint16_t addr) override + { + uint8_t output = 0; + if(addr == 0x4017) { + if(_enableP1) { + output |= IsPressed(KonamiHyperShot::Buttons::Player1Jump) ? 0x02 : 0; + output |= IsPressed(KonamiHyperShot::Buttons::Player1Run) ? 0x04 : 0; + } + if(_enableP2) { + output |= IsPressed(KonamiHyperShot::Buttons::Player2Jump) ? 0x08 : 0; + output |= IsPressed(KonamiHyperShot::Buttons::Player2Run) ? 0x10 : 0; + } + } + return output; + } + + void WriteRam(uint16_t addr, uint8_t value) override + { + _enableP2 = (value & 0x02) == 0; + _enableP1 = (value & 0x04) == 0; + } +}; \ No newline at end of file diff --git a/Core/NES/Input/NesController.h b/Core/NES/Input/NesController.h index 46f7ba67..04d497bb 100644 --- a/Core/NES/Input/NesController.h +++ b/Core/NES/Input/NesController.h @@ -89,11 +89,10 @@ protected: public: enum Buttons { Up = 0, Down, Left, Right, Start, Select, B, A, Microphone }; - NesController(Emulator* emu, uint8_t port, KeyMappingSet keyMappings) : BaseControlDevice(emu, port, keyMappings) + NesController(Emulator* emu, ControllerType type, uint8_t port, KeyMappingSet keyMappings) : BaseControlDevice(emu, type, port, keyMappings) { _turboSpeed = keyMappings.TurboSpeed; - _microphoneEnabled = false; - //_microphoneEnabled = port == 1 && _console->GetSettings()->GetConsoleType() == ConsoleType::Famicom; + _microphoneEnabled = port == 1 && type == ControllerType::FamicomController; } uint8_t ToByte() diff --git a/Core/NES/Input/OekaKidsTablet.h b/Core/NES/Input/OekaKidsTablet.h new file mode 100644 index 00000000..fb219023 --- /dev/null +++ b/Core/NES/Input/OekaKidsTablet.h @@ -0,0 +1,82 @@ +#pragma once +#include "stdafx.h" +#include "Shared/BaseControlDevice.h" +#include "Shared/Emulator.h" +#include "Shared/EmuSettings.h" +#include "Shared/KeyManager.h" +#include "Utilities/Serializer.h" + +class OekaKidsTablet : public BaseControlDevice +{ +private: + bool _strobe = false; + bool _shift = false; + uint32_t _stateBuffer = 0; + +protected: + enum Buttons { Click, Touch }; + bool HasCoordinates() override { return true; } + + string GetKeyNames() override + { + return "CT"; + } + + void InternalSetStateFromInput() override + { + if(_emu->GetSettings()->IsInputEnabled()) { + MousePosition pos = KeyManager::GetMousePosition(); + SetPressedState(Buttons::Click, KeyManager::IsMouseButtonPressed(MouseButton::LeftButton)); + SetPressedState(Buttons::Touch, pos.Y >= 48 || KeyManager::IsMouseButtonPressed(MouseButton::LeftButton)); + SetCoordinates(pos); + } + } + + void Serialize(Serializer& s) override + { + BaseControlDevice::Serialize(s); + s.Stream(_strobe, _shift, _stateBuffer); + } + +public: + OekaKidsTablet(Emulator* emu) : BaseControlDevice(emu, ControllerType::OekaKidsTablet, BaseControlDevice::ExpDevicePort) + { + } + + uint8_t ReadRam(uint16_t addr) override + { + if(addr == 0x4017) { + if(_strobe) { + if(_shift) { + return (_stateBuffer & 0x40000) ? 0x00 : 0x08; + } else { + return 0x04; + } + } else { + return 0x00; + } + } + + return 0; + } + + void WriteRam(uint16_t addr, uint8_t value) override + { + _strobe = (value & 0x01) == 0x01; + bool shift = ((value >> 1) & 0x01) == 0x01; + + if(_strobe) { + if(!_shift && shift) { + _stateBuffer <<= 1; + } + _shift = shift; + } else { + MousePosition pos = GetCoordinates(); + + uint8_t xPosition = (uint8_t)((double)std::max(0, pos.X + 8) / 256.0 * 240); + uint8_t yPosition = (uint8_t)((double)std::max(0, pos.Y - 14) / 240.0 * 256); + + _stateBuffer = (xPosition << 10) | (yPosition << 2) | (IsPressed(OekaKidsTablet::Buttons::Touch) ? 0x02 : 0x00) | (IsPressed(OekaKidsTablet::Buttons::Click) ? 0x01 : 0x00); + } + } +}; \ No newline at end of file diff --git a/Core/NES/Input/PachinkoController.h b/Core/NES/Input/PachinkoController.h new file mode 100644 index 00000000..22f2b27a --- /dev/null +++ b/Core/NES/Input/PachinkoController.h @@ -0,0 +1,60 @@ +#pragma once +#include "stdafx.h" +#include "NES/Input/NesController.h" + +class PachinkoController : public NesController +{ +private: + uint8_t _analogData = 0; + +protected: + enum PachinkoButtons { Press = 8, Release = 9 }; + + void InternalSetStateFromInput() override + { + NesController::InternalSetStateFromInput(); + + for(KeyMapping keyMapping : _keyMappings) { + SetPressedState(PachinkoButtons::Press, keyMapping.CustomKeys[0]); + SetPressedState(PachinkoButtons::Release, keyMapping.CustomKeys[1]); + } + } + +public: + PachinkoController(Emulator* emu, KeyMappingSet keyMappings) : NesController(emu, ControllerType::Pachinko, BaseControlDevice::ExpDevicePort, keyMappings) + { + } + + uint8_t ReadRam(uint16_t addr) override + { + uint8_t output = 0; + if(addr == 0x4016) { + StrobeProcessRead(); + output = (_stateBuffer & 0x01) << 1; + _stateBuffer >>= 1; + } + return output; + } + + void RefreshStateBuffer() override + { + if(_analogData < 63 && IsPressed(PachinkoController::PachinkoButtons::Press)) { + _analogData++; + } else if(_analogData > 0 && IsPressed(PachinkoController::PachinkoButtons::Release)) { + _analogData--; + } + + uint8_t analogData = + ((_analogData & 0x01) << 7) | + ((_analogData & 0x02) << 5) | + ((_analogData & 0x04) << 3) | + ((_analogData & 0x08) << 1) | + ((_analogData & 0x10) >> 1) | + ((_analogData & 0x20) >> 3) | + ((_analogData & 0x40) >> 5) | + ((_analogData & 0x80) >> 7); + + NesController::RefreshStateBuffer(); + _stateBuffer = (_stateBuffer & 0xFF) | (~analogData << 8); + } +}; \ No newline at end of file diff --git a/Core/NES/Input/PartyTap.h b/Core/NES/Input/PartyTap.h new file mode 100644 index 00000000..2d223e9a --- /dev/null +++ b/Core/NES/Input/PartyTap.h @@ -0,0 +1,76 @@ +#pragma once +#include "stdafx.h" +#include "Shared/BaseControlDevice.h" +#include "Utilities/Serializer.h" + +class Emulator; + +class PartyTap : public BaseControlDevice +{ +private: + uint8_t _stateBuffer = 0; + uint8_t _readCount = 0; + bool _enabled = false; + +protected: + enum Buttons { B1 = 0, B2, B3, B4, B5, B6 }; + + string GetKeyNames() override + { + return "123456"; + } + + void InternalSetStateFromInput() override + { + for(KeyMapping keyMapping : _keyMappings) { + for(int i = 0; i < 6; i++) { + SetPressedState(i, keyMapping.CustomKeys[i]); + } + } + } + + void Serialize(Serializer& s) override + { + BaseControlDevice::Serialize(s); + s.Stream(_stateBuffer, _readCount, _enabled); + } + +public: + PartyTap(Emulator* emu, KeyMappingSet keyMappings) : BaseControlDevice(emu, ControllerType::PartyTap, BaseControlDevice::ExpDevicePort, keyMappings) + { + } + + uint8_t ReadRam(uint16_t addr) override + { + if(addr == 0x4017) { + StrobeProcessRead(); + if(_readCount < 2) { + uint8_t value = (_stateBuffer & 0x7) << 2; + _stateBuffer >>= 3; + _readCount++; + return value; + } else { + //"After 1st/2nd reads, a detection value can be read : $4017 & $1C == $14" + return 0x14; + } + } + return 0; + } + + void RefreshStateBuffer() override + { + _readCount = 0; + _stateBuffer = + IsPressed(PartyTap::Buttons::B1) ? 1 : 0 | + IsPressed(PartyTap::Buttons::B2) ? 2 : 0 | + IsPressed(PartyTap::Buttons::B3) ? 4 : 0 | + IsPressed(PartyTap::Buttons::B4) ? 8 : 0 | + IsPressed(PartyTap::Buttons::B5) ? 16 : 0 | + IsPressed(PartyTap::Buttons::B6) ? 32 : 0; + } + + void WriteRam(uint16_t addr, uint8_t value) override + { + StrobeProcessWrite(value); + } +}; \ No newline at end of file diff --git a/Core/NES/Input/PowerPad.h b/Core/NES/Input/PowerPad.h new file mode 100644 index 00000000..6cec30ae --- /dev/null +++ b/Core/NES/Input/PowerPad.h @@ -0,0 +1,81 @@ +#pragma once +#include "stdafx.h" +#include "Shared/BaseControlDevice.h" +#include "Utilities/Serializer.h" + +class Emulator; + +class PowerPad : public BaseControlDevice +{ +private: + uint8_t _stateBufferL = 0; + uint8_t _stateBufferH = 0; + +protected: + string GetKeyNames() override + { + return "123456789ABC"; + } + + void InternalSetStateFromInput() override + { + for(KeyMapping keyMapping : _keyMappings) { + for(int i = 0; i < 3; i++) { + for(int j = 0; j < 4; j++) { + if(false) { //TODO + //Invert the order of each row + SetPressedState(i*4+j, keyMapping.CustomKeys[i*4+(3-j)]); + } else { + SetPressedState(i*4+j, keyMapping.CustomKeys[i*4+j]); + } + } + } + } + } + + void RefreshStateBuffer() override + { + uint8_t pressedKeys[12] = {}; + for(int i = 0; i < 12; i++) { + pressedKeys[i] |= IsPressed(i) ? 1 : 0; + } + + //"Serial data from buttons 2, 1, 5, 9, 6, 10, 11, 7" + _stateBufferL = pressedKeys[1] | (pressedKeys[0] << 1) | (pressedKeys[4] << 2) | (pressedKeys[8] << 3) | (pressedKeys[5] << 4) | (pressedKeys[9] << 5) | (pressedKeys[10] << 6) | (pressedKeys[6] << 7); + + //"Serial data from buttons 4, 3, 12, 8 (following 4 bits read as H=1)" + _stateBufferH = pressedKeys[3] | (pressedKeys[2] << 1) | (pressedKeys[11] << 2) | (pressedKeys[7] << 3) | 0xF0; + } + + void Serialize(Serializer& s) override + { + BaseControlDevice::Serialize(s); + s.Stream(_stateBufferL, _stateBufferH); + } + +public: + PowerPad(Emulator* emu, ControllerType type, uint8_t port, KeyMappingSet keyMappings) : BaseControlDevice(emu, type, port, keyMappings) + { + } + + uint8_t ReadRam(uint16_t addr) override + { + uint8_t output = 0; + if(IsCurrentPort(addr)) { + StrobeProcessRead(); + + output = ((_stateBufferH & 0x01) << 4) | ((_stateBufferL & 0x01) << 3); + _stateBufferL >>= 1; + _stateBufferH >>= 1; + + _stateBufferL |= 0x80; + _stateBufferH |= 0x80; + } + return output; + } + + void WriteRam(uint16_t addr, uint8_t value) override + { + StrobeProcessWrite(value); + } +}; \ No newline at end of file diff --git a/Core/NES/Input/SuborKeyboard.h b/Core/NES/Input/SuborKeyboard.h new file mode 100644 index 00000000..6b0059ef --- /dev/null +++ b/Core/NES/Input/SuborKeyboard.h @@ -0,0 +1,126 @@ +#pragma once +#include "stdafx.h" +#include "Shared/BaseControlDevice.h" +#include "Utilities/Serializer.h" + +class SuborKeyboard : public BaseControlDevice +{ +private: + uint8_t _row = 0; + uint8_t _column = 0; + bool _enabled = false; + +protected: + string GetKeyNames() override + { + return "ABCDEFGHIJKLMNOPQRSTUVWXYZ01234567891234567890120123456789edpmdmncdsasbemglrcpcsasbteeehidududlr123"; + } + + enum Buttons + { + A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, + Num0, Num1, Num2, Num3, Num4, Num5, Num6, Num7, Num8, Num9, + F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12, + Numpad0, Numpad1, Numpad2, Numpad3, Numpad4, Numpad5, Numpad6, Numpad7, Numpad8, Numpad9, + NumpadEnter, NumpadDot, NumpadPlus, NumpadMultiply, NumpadDivide, NumpadMinus, NumLock, + Comma, Dot, SemiColon, Apostrophe, + Slash, Backslash, + Equal, Minus, Grave, + LeftBracket, RightBracket, + CapsLock, Pause, + Ctrl, Shift, Alt, + Space, Backspace, Tab, Esc, Enter, + End, Home, + Ins, Delete, + PageUp, PageDown, + Up, Down, Left, Right, + Unknown1, Unknown2, Unknown3, + }; + + Buttons _keyboardMatrix[104] = { + Num4, G, F, C, F2, E, Num5, V, + Num2, D, S, End, F1, W, Num3, X, + Ins, Backspace, PageDown, Right, F8, PageUp, Delete, Home, + Num9, I, L, Comma, F5, O, Num0, Dot, + RightBracket, Enter, Up, Left, F7, LeftBracket, Backslash, Down, + Q, CapsLock, Z, Tab, Esc, A, Num1, Ctrl, + Num7, Y, K, M, F4, U, Num8, J, + Minus, SemiColon, Apostrophe, Slash, F6, P, Equal, Shift, + T, H, N, Space, F3, R, Num6, B, + Numpad6, NumpadEnter, Numpad4, Numpad8, Numpad2, Unknown1, Unknown2, Unknown3, + Alt, Numpad4, Numpad7, F11, F12, Numpad1, Numpad2, Numpad8, + NumpadMinus, NumpadPlus, NumpadMultiply, Numpad9, F10, Numpad5, NumpadDivide, NumLock, + Grave, Numpad6, Pause, Space, F9, Numpad3, NumpadDot, Numpad0 + }; + + void InternalSetStateFromInput() override + { + //TODO + bool allowInput = false; // _emu->GetSettings()->IsKeyboardMode(); + if(allowInput) { + for(KeyMapping keyMapping : _keyMappings) { + for(int i = 0; i < 99; i++) { + SetPressedState(i, keyMapping.CustomKeys[i]); + } + } + } + } + + uint8_t GetActiveKeys(uint8_t row, uint8_t column) + { + uint8_t result = 0; + uint32_t baseIndex = row * 8 + (column ? 4 : 0); + for(int i = 0; i < 4; i++) { + if(IsPressed(_keyboardMatrix[baseIndex + i])) { + result |= 0x10; + } + result >>= 1; + } + return result; + } + + void Serialize(Serializer& s) override + { + BaseControlDevice::Serialize(s); + s.Stream(_row, _column, _enabled); + } + + void RefreshStateBuffer() override + { + _row = 0; + _column = 0; + } + +public: + SuborKeyboard(Emulator* emu, KeyMappingSet keyMappings) : BaseControlDevice(emu, ControllerType::SuborKeyboard, BaseControlDevice::ExpDevicePort, keyMappings) + { + } + + uint8_t ReadRam(uint16_t addr) override + { + if(addr == 0x4017) { + if(_enabled) { + uint8_t value = ((~GetActiveKeys(_row, _column)) << 1) & 0x1E; + return value; + } else { + return 0x1E; + } + } + return 0; + } + + void WriteRam(uint16_t addr, uint8_t value) override + { + StrobeProcessWrite(value); + + uint8_t prevColumn = _column; + _column = (value & 0x02) >> 1; + _enabled = (value & 0x04) != 0; + + if(_enabled) { + if(!_column && prevColumn) { + _row = (_row + 1) % 13; + } + } + } +}; \ No newline at end of file diff --git a/Core/NES/Input/SuborMouse.h b/Core/NES/Input/SuborMouse.h new file mode 100644 index 00000000..cac1fbaf --- /dev/null +++ b/Core/NES/Input/SuborMouse.h @@ -0,0 +1,110 @@ +#pragma once +#include "stdafx.h" +#include "Shared/BaseControlDevice.h" +#include "Shared/KeyManager.h" +#include "Shared/EmuSettings.h" +#include "Shared/Emulator.h" +#include "Utilities/Serializer.h" + +class SuborMouse : public BaseControlDevice +{ +private: + uint32_t _stateBuffer = 0; + uint8_t _packetBytes[3] = {}; + uint8_t _packetPos = 0; + uint8_t _packetSize = 1; + +protected: + bool HasCoordinates() override { return true; } + enum Buttons { Left = 0, Right }; + + void Serialize(Serializer& s) override + { + BaseControlDevice::Serialize(s); + ArrayInfo packetBytes { _packetBytes, 3 }; + s.Stream(_stateBuffer, _packetPos, _packetSize, packetBytes); + } + + void InternalSetStateFromInput() override + { + SetPressedState(Buttons::Left, KeyManager::IsMouseButtonPressed(MouseButton::LeftButton)); + SetPressedState(Buttons::Right, KeyManager::IsMouseButtonPressed(MouseButton::RightButton)); + SetMovement(KeyManager::GetMouseMovement( + _emu->GetSettings()->GetVideoConfig().VideoScale, + _emu->GetSettings()->GetInputConfig().MouseSensitivity + 1 + )); + } + +public: + SuborMouse(Emulator* emu, uint8_t port) : BaseControlDevice(emu, ControllerType::SuborMouse, port) + { + } + + void WriteRam(uint16_t addr, uint8_t value) override + { + StrobeProcessWrite(value); + } + + uint8_t ReadRam(uint16_t addr) override + { + uint8_t output = 0; + if((addr == 0x4016 && (_port & 0x01) == 0) || (addr == 0x4017 && (_port & 0x01) == 1)) { + StrobeProcessRead(); + output = (_stateBuffer & 0x80) >> 7; + if(_port >= 2) { + output <<= 1; + } + _stateBuffer <<= 1; + } + return output; + } + + void RefreshStateBuffer() override + { + if(_packetPos < _packetSize - 1) { + //3-byte packet is not done yet, move to next byte + _packetPos++; + _stateBuffer = _packetBytes[_packetPos]; + return; + } + + MouseMovement mov = GetMovement(); + + uint32_t upFlag = mov.dy < 0 ? 0x80 : 0; + uint32_t leftFlag = mov.dx < 0 ? 0x80 : 0; + + mov.dx = std::min(std::abs(mov.dx), 31); + mov.dy = std::min(std::abs(mov.dy), 31); + + if(mov.dx <= 1 && mov.dy <= 1) { + //1-byte packet + _packetBytes[0] = + (IsPressed(SuborMouse::Buttons::Left) ? 0x80 : 0) | + (IsPressed(SuborMouse::Buttons::Right) ? 0x40 : 0) | + (leftFlag && mov.dx ? 0x30 : (mov.dx ? 0x10 : 0)) | + (upFlag && mov.dy ? 0x0C : (mov.dy ? 0x04 : 0)); + _packetBytes[1] = 0; + _packetBytes[2] = 0; + + _packetSize = 1; + } else { + //3-byte packet + _packetBytes[0] = + (IsPressed(SuborMouse::Buttons::Left) ? 0x80 : 0) | + (IsPressed(SuborMouse::Buttons::Right) ? 0x40 : 0) | + (leftFlag ? 0x20 : 0) | + (mov.dx & 0x10) | + (upFlag ? 0x08 : 0) | + ((mov.dy & 0x10) >> 2) | + 0x01; + + _packetBytes[1] = ((mov.dx & 0x0F) << 2) | 0x02; + _packetBytes[2] = ((mov.dy & 0x0F) << 2) | 0x03; + + _packetSize = 3; + } + + _packetPos = 0; + _stateBuffer = _packetBytes[0]; + } +}; \ No newline at end of file diff --git a/Core/NES/Input/VirtualBoyController.h b/Core/NES/Input/VirtualBoyController.h new file mode 100644 index 00000000..d397b49e --- /dev/null +++ b/Core/NES/Input/VirtualBoyController.h @@ -0,0 +1,109 @@ +#pragma once +#include "stdafx.h" +#include "Shared/BaseControlDevice.h" +#include "Shared/Emulator.h" +#include "Shared/EmuSettings.h" +#include "Utilities/Serializer.h" + +class VirtualBoyController : public BaseControlDevice +{ +private: + uint32_t _stateBuffer = 0; + +protected: + enum Buttons { Down1 = 0, Left1, Select, Start, Up0, Down0, Left0, Right0, Right1, Up1, L, R, B, A }; + + string GetKeyNames() override + { + return "dlSTUDLRruLRBA"; + } + + void InternalSetStateFromInput() override + { + for(KeyMapping keyMapping : _keyMappings) { + for(int i = 0; i < 14; i++) { + SetPressedState(i, keyMapping.CustomKeys[i]); + } + + if(!_emu->GetSettings()->GetNesConfig().AllowInvalidInput) { + //If both U+D or L+R are pressed at the same time, act as if neither is pressed + if(IsPressed(Buttons::Up0) && IsPressed(Buttons::Down0)) { + ClearBit(Buttons::Down0); + ClearBit(Buttons::Up0); + } + if(IsPressed(Buttons::Left0) && IsPressed(Buttons::Right0)) { + ClearBit(Buttons::Left0); + ClearBit(Buttons::Right0); + } + if (IsPressed(Buttons::Up1) && IsPressed(Buttons::Down1)) { + ClearBit(Buttons::Down1); + ClearBit(Buttons::Up1); + } + if (IsPressed(Buttons::Left1) && IsPressed(Buttons::Right1)) { + ClearBit(Buttons::Left1); + ClearBit(Buttons::Right1); + } + } + } + } + + uint16_t ToByte() + { + //"A Virtual Boy controller returns a 16-bit report in a similar order as SNES, with two additional buttons." + + return + (uint8_t)IsPressed(Buttons::Down1) | + ((uint8_t)IsPressed(Buttons::Left1) << 1) | + ((uint8_t)IsPressed(Buttons::Select) << 2) | + ((uint8_t)IsPressed(Buttons::Start) << 3) | + ((uint8_t)IsPressed(Buttons::Up0) << 4) | + ((uint8_t)IsPressed(Buttons::Down0) << 5) | + ((uint8_t)IsPressed(Buttons::Left0) << 6) | + ((uint8_t)IsPressed(Buttons::Right0) << 7) | + ((uint8_t)IsPressed(Buttons::Right1) << 8) | + ((uint8_t)IsPressed(Buttons::Up1) << 9) | + ((uint8_t)IsPressed(Buttons::L) << 10) | + ((uint8_t)IsPressed(Buttons::R) << 11) | + ((uint8_t)IsPressed(Buttons::B) << 12) | + ((uint8_t)IsPressed(Buttons::A) << 13) | + (1 << 14); + } + + void Serialize(Serializer& s) override + { + BaseControlDevice::Serialize(s); + s.Stream(_stateBuffer); + } + + void RefreshStateBuffer() override + { + _stateBuffer = (uint32_t)ToByte(); + } + +public: + VirtualBoyController(Emulator* emu, uint8_t port, KeyMappingSet keyMappings) : BaseControlDevice(emu, ControllerType::VirtualBoyController, port, keyMappings) + { + } + + uint8_t ReadRam(uint16_t addr) override + { + uint8_t output = 0; + + if(IsCurrentPort(addr)) { + StrobeProcessRead(); + + 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; + } + + return output; + } + + void WriteRam(uint16_t addr, uint8_t value) override + { + StrobeProcessWrite(value); + } +}; \ No newline at end of file diff --git a/Core/NES/Input/VsZapper.h b/Core/NES/Input/VsZapper.h new file mode 100644 index 00000000..39855b4a --- /dev/null +++ b/Core/NES/Input/VsZapper.h @@ -0,0 +1,45 @@ +#pragma once +#include "stdafx.h" +#include "NES/Input/Zapper.h" + +class NesConsole; + +class VsZapper : public Zapper +{ +private: + uint32_t _stateBuffer = 0; + +protected: + void Serialize(Serializer& s) override + { + BaseControlDevice::Serialize(s); + s.Stream(_stateBuffer); + } + + void RefreshStateBuffer() override + { + _stateBuffer = 0x10 | (IsLightFound() ? 0x40 : 0x00) | (IsPressed(Zapper::Buttons::Fire) ? 0x80 : 0x00); + } + +public: + VsZapper(NesConsole* console, uint8_t port) : Zapper(console, ControllerType::VsZapper, port) + { + } + + uint8_t ReadRam(uint16_t addr) override + { + if(IsCurrentPort(addr)) { + StrobeProcessRead(); + uint8_t returnValue = _stateBuffer & 0x01; + _stateBuffer >>= 1; + return returnValue; + } + + return 0; + } + + void WriteRam(uint16_t addr, uint8_t value) override + { + StrobeProcessWrite(value); + } +}; \ No newline at end of file diff --git a/Core/NES/Input/Zapper.h b/Core/NES/Input/Zapper.h new file mode 100644 index 00000000..9e32d1b5 --- /dev/null +++ b/Core/NES/Input/Zapper.h @@ -0,0 +1,98 @@ +#pragma once +#include "stdafx.h" +#include "Shared/BaseControlDevice.h" +#include "NES/NesPpu.h" +#include "NES/NesConsole.h" +#include "Shared/Emulator.h" +#include "Shared/Emulator.h" +#include "Shared/EmuSettings.h" +#include "Shared/KeyManager.h" +#include "Utilities/Serializer.h" + +class Zapper : public BaseControlDevice +{ +private: + NesConsole* _console; + +protected: + bool HasCoordinates() override { return true; } + + string GetKeyNames() override + { + return "F"; + } + + ControllerType GetControllerType() + { + return ControllerType::NesController; + } + + enum Buttons { Fire }; + + void InternalSetStateFromInput() override + { + SetPressedState(Buttons::Fire, KeyManager::IsMouseButtonPressed(MouseButton::LeftButton)); + + MousePosition pos = KeyManager::GetMousePosition(); + if(KeyManager::IsMouseButtonPressed(MouseButton::RightButton)) { + pos.X = -1; + pos.Y = -1; + } + SetCoordinates(pos); + } + + bool IsLightFound() + { + return StaticIsLightFound(GetCoordinates(), _console); + } + +public: + Zapper(NesConsole* console, ControllerType type, uint8_t port) : BaseControlDevice(console->GetEmulator(), type, port) + { + _console = console; + } + + uint8_t ReadRam(uint16_t addr) override + { + uint8_t output = 0; + if((IsExpansionDevice() && addr == 0x4017) || IsCurrentPort(addr)) { + output = (IsLightFound() ? 0 : 0x08) | (IsPressed(Zapper::Buttons::Fire) ? 0x10 : 0x00); + } + return output; + } + + void WriteRam(uint16_t addr, uint8_t value) override + { + } + + static bool StaticIsLightFound(MousePosition pos, NesConsole* console) + { + NesPpu* ppu = console ? console->GetPpu() : nullptr; + if(!ppu) { + return false; + } + + int32_t scanline = ppu->GetCurrentScanline(); + int32_t cycle = ppu->GetCurrentCycle(); + int radius = (int)console->GetNesConfig().ZapperDetectionRadius; + + if(pos.X >= 0 && pos.Y >= 0) { + for(int yOffset = -radius; yOffset <= radius; yOffset++) { + int yPos = pos.Y + yOffset; + if(yPos >= 0 && yPos < NesPpu::ScreenHeight) { + for(int xOffset = -radius; xOffset <= radius; xOffset++) { + int xPos = pos.X + xOffset; + if(xPos >= 0 && xPos < NesPpu::ScreenWidth) { + if(scanline >= yPos && (scanline - yPos <= 20) && (scanline != yPos || cycle > xPos) && ppu->GetPixelBrightness(xPos, yPos) >= 85) { + //Light cannot be detected if the Y/X position is further ahead than the PPU, or if the PPU drew a dark color + return true; + } + } + } + } + } + } + + return false; + } +}; \ No newline at end of file diff --git a/Core/NES/NesConsole.cpp b/Core/NES/NesConsole.cpp index 75642e21..ec8fb409 100644 --- a/Core/NES/NesConsole.cpp +++ b/Core/NES/NesConsole.cpp @@ -249,3 +249,8 @@ uint64_t NesConsole::GetMasterClock() { return _cpu->GetCycleCount(); } + +uint32_t NesConsole::GetMasterClockRate() +{ + return NesCpu::ClockRateNtsc; +} diff --git a/Core/NES/NesConsole.h b/Core/NES/NesConsole.h index bfc5811d..8a29a469 100644 --- a/Core/NES/NesConsole.h +++ b/Core/NES/NesConsole.h @@ -77,5 +77,6 @@ public: virtual AddressInfo GetAbsoluteAddress(AddressInfo relAddress) override; virtual AddressInfo GetRelativeAddress(AddressInfo absAddress, CpuType cpuType) override; - virtual uint64_t GetMasterClock() override; + uint64_t GetMasterClock() override; + uint32_t GetMasterClockRate() override; }; diff --git a/Core/NES/NesControlManager.cpp b/Core/NES/NesControlManager.cpp index f65b52a2..3c32df36 100644 --- a/Core/NES/NesControlManager.cpp +++ b/Core/NES/NesControlManager.cpp @@ -3,8 +3,6 @@ #include "NES/BaseMapper.h" #include "NES/NesConsole.h" #include "NES/NesMemoryManager.h" -#include "NES/Input/NesController.h" -#include "SNES/Input/SnesController.h" #include "Shared/EmuSettings.h" #include "Shared/Interfaces/IKeyManager.h" #include "Shared/Interfaces/IInputProvider.h" @@ -13,30 +11,31 @@ #include "Shared/Emulator.h" #include "Shared/KeyManager.h" #include "Shared/SystemActionManager.h" -/*#include "Zapper.h" -#include "ArkanoidController.h" -#include "OekaKidsTablet.h" -#include "FourScore.h" -#include "SnesController.h" -#include "SnesMouse.h" -#include "PowerPad.h" -#include "FamilyMatTrainer.h" -#include "KonamiHyperShot.h" -#include "FamilyBasicKeyboard.h" -#include "FamilyBasicDataRecorder.h" -#include "PartyTap.h" -#include "PachinkoController.h" -#include "ExcitingBoxingController.h" -#include "SuborKeyboard.h" -#include "SuborMouse.h" -#include "JissenMahjongController.h" -#include "BarcodeBattlerReader.h" -#include "HoriTrack.h" -#include "BandaiHyperShot.h" -#include "VsZapper.h" -#include "AsciiTurboFile.h" -#include "BattleBox.h" -#include "VbController.h"*/ +#include "NES/Input/NesController.h" +#include "SNES/Input/SnesController.h" +#include "SNES/Input/SnesMouse.h" +#include "NES/Input/Zapper.h" +#include "NES/Input/ArkanoidController.h" +#include "NES/Input/OekaKidsTablet.h" +#include "NES/Input/FourScore.h" +#include "NES/Input/PowerPad.h" +#include "NES/Input/FamilyMatTrainer.h" +#include "NES/Input/KonamiHyperShot.h" +#include "NES/Input/FamilyBasicKeyboard.h" +#include "NES/Input/FamilyBasicDataRecorder.h" +#include "NES/Input/PartyTap.h" +#include "NES/Input/PachinkoController.h" +#include "NES/Input/ExcitingBoxingController.h" +#include "NES/Input/SuborKeyboard.h" +#include "NES/Input/SuborMouse.h" +#include "NES/Input/JissenMahjongController.h" +#include "NES/Input/BarcodeBattlerReader.h" +#include "NES/Input/HoriTrack.h" +#include "NES/Input/BandaiHyperShot.h" +#include "NES/Input/VsZapper.h" +#include "NES/Input/AsciiTurboFile.h" +#include "NES/Input/BattleBox.h" +#include "NES/Input/VirtualBoyController.h" NesControlManager::NesControlManager(shared_ptr console, shared_ptr mapperControlDevice) : BaseControlManager(console->GetEmulator()) { @@ -58,48 +57,43 @@ shared_ptr NesControlManager::CreateControllerDevice(Controll shared_ptr device; NesConfig cfg = _emu->GetSettings()->GetNesConfig(); + KeyMappingSet keys = cfg.Controllers[port].Keys; switch(type) { case ControllerType::None: break; - case ControllerType::NesController: device.reset(new NesController(_emu, port, cfg.Controllers[port].Keys)); break; - //case ControllerType::Zapper: device.reset(new Zapper(console, port)); break; - //case ControllerType::ArkanoidController: device.reset(new ArkanoidController(console, port)); break; - case ControllerType::SnesController: device.reset(new SnesController(_emu, port, cfg.Controllers[port].Keys)); break; - /*case ControllerType::PowerPad: device.reset(new PowerPad(console, port, console->GetSettings()->GetControllerKeys(port))); break; - case ControllerType::SnesMouse: device.reset(new SnesMouse(console, port)); break; - case ControllerType::SuborMouse: device.reset(new SuborMouse(console, port)); break; - case ControllerType::VsZapper: device.reset(new VsZapper(console, port)); break; - case ControllerType::VbController: device.reset(new VbController(console, port, console->GetSettings()->GetControllerKeys(port))); break;*/ - } - - return device; -} + case ControllerType::NesController: device.reset(new NesController(_emu, type, port, keys)); break; + case ControllerType::FamicomController: device.reset(new NesController(_emu, type, port, keys)); break; + case ControllerType::NesZapper: device.reset(new Zapper(_console.get(), type, port)); break; + case ControllerType::NesArkanoidController: device.reset(new ArkanoidController(_emu, type, port)); break; + case ControllerType::SnesController: device.reset(new SnesController(_emu, port, keys)); break; + case ControllerType::PowerPad: device.reset(new PowerPad(_emu, type, port, keys)); break; + case ControllerType::SnesMouse: device.reset(new SnesMouse(_emu, port)); break; + case ControllerType::SuborMouse: device.reset(new SuborMouse(_emu, port)); break; + case ControllerType::VsZapper: device.reset(new VsZapper(_console.get(), port)); break; + case ControllerType::VirtualBoyController: device.reset(new VirtualBoyController(_emu, port, keys)); break; -shared_ptr NesControlManager::CreateExpansionDevice(ExpansionPortDevice type) -{ - shared_ptr device; + //Exp port devices + case ControllerType::FamicomZapper: device.reset(new Zapper(_console.get(), type, BaseControlDevice::ExpDevicePort)); break; + case ControllerType::FamicomArkanoidController: device.reset(new ArkanoidController(_emu, type, BaseControlDevice::ExpDevicePort)); break; + case ControllerType::OekaKidsTablet: device.reset(new OekaKidsTablet(_emu)); break; + case ControllerType::FamilyTrainerMat: device.reset(new FamilyMatTrainer(_emu, keys)); break; + case ControllerType::KonamiHyperShot: device.reset(new KonamiHyperShot(_emu, keys, cfg.Controllers[1].Keys)); break; + case ControllerType::FamilyBasicKeyboard: device.reset(new FamilyBasicKeyboard(_emu, keys)); break; + case ControllerType::PartyTap: device.reset(new PartyTap(_emu, keys)); break; + case ControllerType::Pachinko: device.reset(new PachinkoController(_emu, keys)); break; + case ControllerType::ExcitingBoxing: device.reset(new ExcitingBoxingController(_emu, keys)); break; + case ControllerType::JissenMahjong: device.reset(new JissenMahjongController(_emu, keys)); break; + case ControllerType::SuborKeyboard: device.reset(new SuborKeyboard(_emu, keys)); break; + case ControllerType::BarcodeBattler: device.reset(new BarcodeBattlerReader(_emu)); break; + case ControllerType::HoriTrack: device.reset(new HoriTrack(_emu, keys)); break; + case ControllerType::BandaiHyperShot: device.reset(new BandaiHyperShot(_console.get(), keys)); break; + case ControllerType::AsciiTurboFile: device.reset(new AsciiTurboFile(_emu)); break; + case ControllerType::BattleBox: device.reset(new BattleBox(_emu)); break; - /*switch(type) { - case ExpansionPortDevice::Zapper: device.reset(new Zapper(console, BaseControlDevice::ExpDevicePort)); break; - case ExpansionPortDevice::ArkanoidController: device.reset(new ArkanoidController(console, BaseControlDevice::ExpDevicePort)); break; - case ExpansionPortDevice::OekaKidsTablet: device.reset(new OekaKidsTablet(console)); break; - case ExpansionPortDevice::FamilyTrainerMat: device.reset(new FamilyMatTrainer(console, console->GetSettings()->GetControllerKeys(0))); break; - case ExpansionPortDevice::KonamiHyperShot: device.reset(new KonamiHyperShot(console, console->GetSettings()->GetControllerKeys(0), console->GetSettings()->GetControllerKeys(1))); break; - case ExpansionPortDevice::FamilyBasicKeyboard: device.reset(new FamilyBasicKeyboard(console, console->GetSettings()->GetControllerKeys(0))); break; - case ExpansionPortDevice::PartyTap: device.reset(new PartyTap(console, console->GetSettings()->GetControllerKeys(0))); break; - case ExpansionPortDevice::Pachinko: device.reset(new PachinkoController(console, console->GetSettings()->GetControllerKeys(0))); break; - case ExpansionPortDevice::ExcitingBoxing: device.reset(new ExcitingBoxingController(console, console->GetSettings()->GetControllerKeys(0))); break; - case ExpansionPortDevice::JissenMahjong: device.reset(new JissenMahjongController(console, console->GetSettings()->GetControllerKeys(0))); break; - case ExpansionPortDevice::SuborKeyboard: device.reset(new SuborKeyboard(console, console->GetSettings()->GetControllerKeys(0))); break; - case ExpansionPortDevice::BarcodeBattler: device.reset(new BarcodeBattlerReader(console)); break; - case ExpansionPortDevice::HoriTrack: device.reset(new HoriTrack(console, console->GetSettings()->GetControllerKeys(0))); break; - case ExpansionPortDevice::BandaiHyperShot: device.reset(new BandaiHyperShot(console, console->GetSettings()->GetControllerKeys(0))); break; - case ExpansionPortDevice::AsciiTurboFile: device.reset(new AsciiTurboFile(console)); break; - case ExpansionPortDevice::BattleBox: device.reset(new BattleBox(console)); break; - - case ExpansionPortDevice::FourPlayerAdapter: + case ControllerType::FourScore: //TODO + case ControllerType::FourPlayerAdapter: //TODO default: break; - }*/ + } return device; } diff --git a/Core/SNES/ControlManager.cpp b/Core/SNES/ControlManager.cpp index 8c7e4623..4ef59963 100644 --- a/Core/SNES/ControlManager.cpp +++ b/Core/SNES/ControlManager.cpp @@ -41,7 +41,7 @@ shared_ptr ControlManager::CreateControllerDevice(ControllerT switch(type) { case ControllerType::None: break; case ControllerType::SnesController: device.reset(new SnesController(_emu, port, cfg.Controllers[port].Keys)); break; - case ControllerType::SnesMouse: device.reset(new SnesMouse(_console, port)); break; + case ControllerType::SnesMouse: device.reset(new SnesMouse(_emu, port)); break; case ControllerType::SuperScope: device.reset(new SuperScope(_console, port, cfg.Controllers[port].Keys)); break; case ControllerType::Multitap: device.reset(new Multitap(_console, port, cfg.Controllers[port].Keys, cfg.Controllers[2].Keys, cfg.Controllers[3].Keys, cfg.Controllers[4].Keys)); break; } diff --git a/Core/SNES/Input/Multitap.cpp b/Core/SNES/Input/Multitap.cpp index b99f001c..ea1f5c5c 100644 --- a/Core/SNES/Input/Multitap.cpp +++ b/Core/SNES/Input/Multitap.cpp @@ -94,7 +94,7 @@ void Multitap::RefreshStateBuffer() } } -Multitap::Multitap(Console* console, uint8_t port, KeyMappingSet keyMappings1, KeyMappingSet keyMappings2, KeyMappingSet keyMappings3, KeyMappingSet keyMappings4) : BaseControlDevice(console->GetEmulator(), port, keyMappings1) +Multitap::Multitap(Console* console, uint8_t port, KeyMappingSet keyMappings1, KeyMappingSet keyMappings2, KeyMappingSet keyMappings3, KeyMappingSet keyMappings4) : BaseControlDevice(console->GetEmulator(), ControllerType::Multitap, port, keyMappings1) { _turboSpeed[0] = keyMappings1.TurboSpeed; _turboSpeed[1] = keyMappings2.TurboSpeed; @@ -108,11 +108,6 @@ Multitap::Multitap(Console* console, uint8_t port, KeyMappingSet keyMappings1, K _internalRegs = console->GetInternalRegisters().get(); } -ControllerType Multitap::GetControllerType() -{ - return ControllerType::Multitap; -} - void Multitap::SetControllerState(uint8_t controllerNumber, ControlDeviceState state) { SnesController controller(_emu, 0, KeyMappingSet()); diff --git a/Core/SNES/Input/Multitap.h b/Core/SNES/Input/Multitap.h index 6038d652..599df24e 100644 --- a/Core/SNES/Input/Multitap.h +++ b/Core/SNES/Input/Multitap.h @@ -29,8 +29,6 @@ protected: public: Multitap(Console* console, uint8_t port, KeyMappingSet keyMappings1, KeyMappingSet keyMappings2, KeyMappingSet keyMappings3, KeyMappingSet keyMappings4); - ControllerType GetControllerType() override; - void SetControllerState(uint8_t controllerNumber, ControlDeviceState state); uint8_t ReadRam(uint16_t addr) override; diff --git a/Core/SNES/Input/SnesController.cpp b/Core/SNES/Input/SnesController.cpp index c267a79a..d616af01 100644 --- a/Core/SNES/Input/SnesController.cpp +++ b/Core/SNES/Input/SnesController.cpp @@ -2,7 +2,7 @@ #include "SNES/Input/SnesController.h" #include "Shared/Emulator.h" -SnesController::SnesController(Emulator* emu, uint8_t port, KeyMappingSet keyMappings) : BaseControlDevice(emu, port, keyMappings) +SnesController::SnesController(Emulator* emu, uint8_t port, KeyMappingSet keyMappings) : BaseControlDevice(emu, ControllerType::SnesController, port, keyMappings) { _turboSpeed = keyMappings.TurboSpeed; } @@ -71,11 +71,6 @@ void SnesController::RefreshStateBuffer() _stateBuffer = (uint32_t)ToByte(); } -ControllerType SnesController::GetControllerType() -{ - return ControllerType::SnesController; -} - uint8_t SnesController::ReadRam(uint16_t addr) { uint8_t output = 0; diff --git a/Core/SNES/Input/SnesController.h b/Core/SNES/Input/SnesController.h index fc4b712d..71e9250e 100644 --- a/Core/SNES/Input/SnesController.h +++ b/Core/SNES/Input/SnesController.h @@ -21,7 +21,6 @@ public: SnesController(Emulator* emu, uint8_t port, KeyMappingSet keyMappings); - ControllerType GetControllerType() override; uint8_t ReadRam(uint16_t addr) override; void WriteRam(uint16_t addr, uint8_t value) override; }; \ No newline at end of file diff --git a/Core/SNES/Input/SnesMouse.h b/Core/SNES/Input/SnesMouse.h index e7936576..2eeafd9d 100644 --- a/Core/SNES/Input/SnesMouse.h +++ b/Core/SNES/Input/SnesMouse.h @@ -1,6 +1,5 @@ #pragma once #include "stdafx.h" -#include "SNES/Console.h" #include "Shared/BaseControlDevice.h" #include "Shared/Interfaces/IKeyManager.h" #include "Shared/KeyManager.h" @@ -42,16 +41,11 @@ protected: public: enum Buttons { Left = 0, Right }; - SnesMouse(Console* console, uint8_t port) : BaseControlDevice(console->GetEmulator(), port) + SnesMouse(Emulator* emu, uint8_t port) : BaseControlDevice(emu, ControllerType::SnesMouse, port) { _settings = _emu->GetSettings(); } - ControllerType GetControllerType() override - { - return ControllerType::SnesMouse; - } - void WriteRam(uint16_t addr, uint8_t value) override { StrobeProcessWrite(value); diff --git a/Core/SNES/Input/SuperScope.h b/Core/SNES/Input/SuperScope.h index 7517661d..bfbbfa10 100644 --- a/Core/SNES/Input/SuperScope.h +++ b/Core/SNES/Input/SuperScope.h @@ -93,16 +93,11 @@ protected: } public: - SuperScope(Console* console, uint8_t port, KeyMappingSet keyMappings) : BaseControlDevice(console->GetEmulator(), port, keyMappings) + SuperScope(Console* console, uint8_t port, KeyMappingSet keyMappings) : BaseControlDevice(console->GetEmulator(), ControllerType::SuperScope, port, keyMappings) { _ppu = console->GetPpu().get(); } - ControllerType GetControllerType() override - { - return ControllerType::SuperScope; - } - uint8_t ReadRam(uint16_t addr) override { uint8_t output = 0; diff --git a/Core/Shared/BaseControlDevice.cpp b/Core/Shared/BaseControlDevice.cpp index 2cd283ce..0f56f794 100644 --- a/Core/Shared/BaseControlDevice.cpp +++ b/Core/Shared/BaseControlDevice.cpp @@ -5,9 +5,10 @@ #include "Utilities/StringUtilities.h" #include "Utilities/Serializer.h" -BaseControlDevice::BaseControlDevice(Emulator* emu, uint8_t port, KeyMappingSet keyMappingSet) +BaseControlDevice::BaseControlDevice(Emulator* emu, ControllerType type, uint8_t port, KeyMappingSet keyMappingSet) { _emu = emu; + _type = type; _port = port; _strobe = false; _keyMappings = keyMappingSet.GetKeyMappingArray(); @@ -22,6 +23,11 @@ uint8_t BaseControlDevice::GetPort() return _port; } +ControllerType BaseControlDevice::GetControllerType() +{ + return _type; +} + void BaseControlDevice::SetStateFromInput() { ClearState(); diff --git a/Core/Shared/BaseControlDevice.h b/Core/Shared/BaseControlDevice.h index 599f7030..86bfa708 100644 --- a/Core/Shared/BaseControlDevice.h +++ b/Core/Shared/BaseControlDevice.h @@ -17,6 +17,7 @@ protected: Emulator* _emu; vector _keyMappings; bool _strobe; + ControllerType _type; uint8_t _port; SimpleLock _stateLock; @@ -51,10 +52,11 @@ public: static constexpr uint8_t ExpDevicePort2 = 7; static constexpr uint8_t PortCount = ExpDevicePort2 + 1; - BaseControlDevice(Emulator* emu, uint8_t port, KeyMappingSet keyMappingSet = KeyMappingSet()); + BaseControlDevice(Emulator* emu, ControllerType type, uint8_t port, KeyMappingSet keyMappingSet = KeyMappingSet()); virtual ~BaseControlDevice(); uint8_t GetPort(); + ControllerType GetControllerType(); bool IsPressed(uint8_t bit); MousePosition GetCoordinates(); @@ -74,7 +76,6 @@ public: void SetRawState(ControlDeviceState state); ControlDeviceState GetRawState(); - virtual ControllerType GetControllerType() = 0; virtual uint8_t ReadRam(uint16_t addr) = 0; virtual void WriteRam(uint16_t addr, uint8_t value) = 0; diff --git a/Core/Shared/Emulator.cpp b/Core/Shared/Emulator.cpp index be7a9a9a..e14b2efe 100644 --- a/Core/Shared/Emulator.cpp +++ b/Core/Shared/Emulator.cpp @@ -331,8 +331,7 @@ bool Emulator::LoadRom(VirtualFile romFile, VirtualFile patchFile, bool stopRom, } if(patchFile.IsValid()) { - //TODO - //_patchPath = patchFile; + _patchFile = patchFile; if(romFile.ApplyPatch(patchFile)) { MessageManager::DisplayMessage("Patch", "ApplyingPatch", patchFile.GetFileName()); } @@ -478,6 +477,11 @@ uint64_t Emulator::GetMasterClock() return _console->GetMasterClock(); } +uint32_t Emulator::GetMasterClockRate() +{ + return _console->GetMasterClockRate(); +} + uint32_t Emulator::GetFrameCount() { return _console->GetPpuFrame().FrameCount; diff --git a/Core/Shared/Emulator.h b/Core/Shared/Emulator.h index ee429573..ec1e950a 100644 --- a/Core/Shared/Emulator.h +++ b/Core/Shared/Emulator.h @@ -129,6 +129,7 @@ public: ConsoleType GetConsoleType(); vector GetCpuTypes(); uint64_t GetMasterClock(); + uint32_t GetMasterClockRate(); EmulatorLock AcquireLock(); void Lock(); diff --git a/Core/Shared/Interfaces/IBattery.h b/Core/Shared/Interfaces/IBattery.h new file mode 100644 index 00000000..c79cb13e --- /dev/null +++ b/Core/Shared/Interfaces/IBattery.h @@ -0,0 +1,7 @@ +#pragma once + +class IBattery +{ +public: + virtual void SaveBattery() = 0; +}; \ No newline at end of file diff --git a/Core/Shared/Interfaces/IConsole.h b/Core/Shared/Interfaces/IConsole.h index 64ee0e00..66c77687 100644 --- a/Core/Shared/Interfaces/IConsole.h +++ b/Core/Shared/Interfaces/IConsole.h @@ -37,6 +37,7 @@ public: virtual vector GetCpuTypes() = 0; virtual uint64_t GetMasterClock() = 0; + virtual uint32_t GetMasterClockRate() = 0; virtual double GetFrameDelay() = 0; virtual double GetFps() = 0; diff --git a/Core/Shared/SettingTypes.h b/Core/Shared/SettingTypes.h index 1675cea1..7b4eefe9 100644 --- a/Core/Shared/SettingTypes.h +++ b/Core/Shared/SettingTypes.h @@ -144,12 +144,46 @@ struct AudioConfig //Update ControllerTypeNames when changing this enum class ControllerType { - None = 0, - SnesController = 1, - SnesMouse = 2, - SuperScope = 3, - Multitap = 4, - NesController = 5 + None, + + //SNES controllers + SnesController, + SnesMouse, + SuperScope, + Multitap, + + //NES controllers + NesController, + FamicomController, + NesZapper, + NesArkanoidController, + PowerPad, + SuborMouse, + VsZapper, + VirtualBoyController, + + //NES/Famicon expansion devices + FourScore, + FamicomZapper, + FourPlayerAdapter, + FamicomArkanoidController, + OekaKidsTablet, + FamilyTrainerMat, + KonamiHyperShot, + FamilyBasicKeyboard, + PartyTap, + Pachinko, + ExcitingBoxing, + JissenMahjong, + SuborKeyboard, + BarcodeBattler, + HoriTrack, + BandaiHyperShot, + AsciiTurboFile, + BattleBox, + + //Game Boy + GameboyController, }; struct KeyMapping @@ -354,6 +388,7 @@ enum class VsDualOutputOption struct NesConfig { ControllerConfig Controllers[5]; + uint32_t ZapperDetectionRadius = 0; bool EnableHdPacks = true; bool DisableGameDatabase = false; diff --git a/Core/Shared/SystemActionManager.h b/Core/Shared/SystemActionManager.h index 786630c8..80887d67 100644 --- a/Core/Shared/SystemActionManager.h +++ b/Core/Shared/SystemActionManager.h @@ -18,15 +18,10 @@ protected: public: enum Buttons { ResetButton = 0, PowerButton = 1 }; - SystemActionManager(Emulator* emu) : BaseControlDevice(emu, BaseControlDevice::ConsoleInputPort) + SystemActionManager(Emulator* emu) : BaseControlDevice(emu, ControllerType::None, BaseControlDevice::ConsoleInputPort) { } - ControllerType GetControllerType() override - { - return ControllerType::None; - } - uint8_t ReadRam(uint16_t addr) override { return 0; diff --git a/NewUI/Config/NesConfig.cs b/NewUI/Config/NesConfig.cs index d640c0d5..e30cf12f 100644 --- a/NewUI/Config/NesConfig.cs +++ b/NewUI/Config/NesConfig.cs @@ -12,6 +12,7 @@ namespace Mesen.GUI.Config { //Input [Reactive] public List Controllers { get; set; } = new List { new ControllerConfig(), new ControllerConfig(), new ControllerConfig(), new ControllerConfig(), new ControllerConfig() }; + [Reactive] public UInt32 ZapperDetectionRadius { get; set; } = 0; //Video [Reactive] public bool EnableHdPacks { get; set; } = true; @@ -117,6 +118,7 @@ namespace Mesen.GUI.Config this.Controllers[3].ToInterop(), this.Controllers[4].ToInterop() }, + ZapperDetectionRadius = ZapperDetectionRadius, EnableHdPacks = EnableHdPacks, DisableGameDatabase = DisableGameDatabase, @@ -246,6 +248,7 @@ namespace Mesen.GUI.Config { [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)] public InteropControllerConfig[] Controllers; + public UInt32 ZapperDetectionRadius; [MarshalAs(UnmanagedType.I1)] public bool EnableHdPacks; [MarshalAs(UnmanagedType.I1)] public bool DisableGameDatabase;