Input: Added Super Scope support
This commit is contained in:
parent
ed4a0e60e8
commit
da5e77d6e3
11 changed files with 177 additions and 7 deletions
|
@ -11,6 +11,7 @@
|
|||
#include "SnesController.h"
|
||||
#include "SnesMouse.h"
|
||||
#include "Multitap.h"
|
||||
#include "SuperScope.h"
|
||||
#include "EventType.h"
|
||||
#include "../Utilities/Serializer.h"
|
||||
|
||||
|
@ -110,7 +111,7 @@ shared_ptr<BaseControlDevice> ControlManager::CreateControllerDevice(ControllerT
|
|||
case ControllerType::None: break;
|
||||
case ControllerType::SnesController: device.reset(new SnesController(console, port, cfg.Controllers[port].Keys)); break;
|
||||
case ControllerType::SnesMouse: device.reset(new SnesMouse(console, port)); break;
|
||||
case ControllerType::SuperScope: 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;
|
||||
}
|
||||
|
||||
|
|
|
@ -171,6 +171,7 @@
|
|||
<ClInclude Include="SPC_DSP.h" />
|
||||
<ClInclude Include="SPC_Filter.h" />
|
||||
<ClInclude Include="stdafx.h" />
|
||||
<ClInclude Include="SuperScope.h" />
|
||||
<ClInclude Include="SystemActionManager.h" />
|
||||
<ClInclude Include="TraceLogger.h" />
|
||||
<ClInclude Include="VideoDecoder.h" />
|
||||
|
|
|
@ -398,6 +398,9 @@
|
|||
<ClInclude Include="FrameLimiter.h">
|
||||
<Filter>Misc</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="SuperScope.h">
|
||||
<Filter>SNES\Input</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="stdafx.cpp" />
|
||||
|
|
|
@ -102,9 +102,9 @@ void KeyManager::SetMousePosition(shared_ptr<Console> console, double x, double
|
|||
_mousePosition.Y = -1;
|
||||
} else {
|
||||
OverscanDimensions overscan = console->GetSettings()->GetOverscan();
|
||||
ScreenSize size = console->GetVideoDecoder()->GetScreenSize(false);
|
||||
_mousePosition.X = (int32_t)(x*size.Width/2 + overscan.Left);
|
||||
_mousePosition.Y = (int32_t)(y*size.Height/2 + overscan.Top);
|
||||
ScreenSize size = console->GetVideoDecoder()->GetScreenSize(true);
|
||||
_mousePosition.X = (int32_t)(x*size.Width + overscan.Left);
|
||||
_mousePosition.Y = (int32_t)(y*size.Height + overscan.Top);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
32
Core/Ppu.cpp
32
Core/Ppu.cpp
|
@ -441,6 +441,9 @@ bool Ppu::ProcessEndOfScanline(uint16_t hClock)
|
|||
_scanline++;
|
||||
|
||||
if(_scanline == _nmiScanline) {
|
||||
ProcessLocationLatchRequest();
|
||||
_latchRequest = false;
|
||||
|
||||
//Reset OAM address at the start of vblank?
|
||||
if(!_forcedVblank) {
|
||||
//TODO, the timing of this may be slightly off? should happen at H=10 based on anomie's docs
|
||||
|
@ -1566,6 +1569,29 @@ bool Ppu::IsDoubleWidth()
|
|||
return _hiResMode || _bgMode == 5 || _bgMode == 6;
|
||||
}
|
||||
|
||||
void Ppu::SetLocationLatchRequest(uint16_t x, uint16_t y)
|
||||
{
|
||||
//Used by super scope
|
||||
_latchRequest = true;
|
||||
_latchRequestX = x;
|
||||
_latchRequestY = y;
|
||||
}
|
||||
|
||||
void Ppu::ProcessLocationLatchRequest()
|
||||
{
|
||||
//Used by super scope
|
||||
if(_latchRequest) {
|
||||
uint16_t cycle = GetCycle();
|
||||
uint16_t scanline = GetRealScanline();
|
||||
if(scanline > _latchRequestY || (_latchRequestY == scanline && cycle >= _latchRequestX)) {
|
||||
_latchRequest = false;
|
||||
_horizontalLocation = _latchRequestX;
|
||||
_verticalLocation = _latchRequestY;
|
||||
_locationLatched = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Ppu::LatchLocationValues()
|
||||
{
|
||||
_horizontalLocation = GetCycle();
|
||||
|
@ -1699,6 +1725,8 @@ uint8_t Ppu::Read(uint16_t addr)
|
|||
|
||||
case 0x213C: {
|
||||
//OPHCT - Horizontal Scanline Location
|
||||
ProcessLocationLatchRequest();
|
||||
|
||||
uint8_t value;
|
||||
if(_horizontalLocToggle) {
|
||||
//"Note that the value read is only 9 bits: bits 1-7 of the high byte are PPU2 Open Bus."
|
||||
|
@ -1713,6 +1741,8 @@ uint8_t Ppu::Read(uint16_t addr)
|
|||
|
||||
case 0x213D: {
|
||||
//OPVCT - Vertical Scanline Location
|
||||
ProcessLocationLatchRequest();
|
||||
|
||||
uint8_t value;
|
||||
if(_verticalLocationToggle) {
|
||||
//"Note that the value read is only 9 bits: bits 1-7 of the high byte are PPU2 Open Bus."
|
||||
|
@ -1739,6 +1769,8 @@ uint8_t Ppu::Read(uint16_t addr)
|
|||
|
||||
case 0x213F: {
|
||||
//STAT78 - PPU Status Flag and Version
|
||||
ProcessLocationLatchRequest();
|
||||
|
||||
uint8_t value = (
|
||||
(_oddFrame ? 0x80 : 0) |
|
||||
(_locationLatched ? 0x40 : 0) |
|
||||
|
|
|
@ -149,6 +149,9 @@ private:
|
|||
uint16_t _verticalLocation = 0;
|
||||
bool _verticalLocationToggle = false;
|
||||
bool _locationLatched = false;
|
||||
bool _latchRequest = false;
|
||||
uint16_t _latchRequestX = 0;
|
||||
uint16_t _latchRequestY = 0;
|
||||
|
||||
Timer _frameSkipTimer;
|
||||
bool _skipRender = false;
|
||||
|
@ -278,6 +281,8 @@ public:
|
|||
uint8_t* GetCgRam();
|
||||
uint8_t* GetSpriteRam();
|
||||
|
||||
void SetLocationLatchRequest(uint16_t x, uint16_t y);
|
||||
void ProcessLocationLatchRequest();
|
||||
void LatchLocationValues();
|
||||
|
||||
uint8_t Read(uint16_t addr);
|
||||
|
|
121
Core/SuperScope.h
Normal file
121
Core/SuperScope.h
Normal file
|
@ -0,0 +1,121 @@
|
|||
#pragma once
|
||||
#include "stdafx.h"
|
||||
#include "BaseControlDevice.h"
|
||||
#include "KeyManager.h"
|
||||
#include "Ppu.h"
|
||||
#include "../Utilities/Serializer.h"
|
||||
|
||||
class SuperScope : public BaseControlDevice
|
||||
{
|
||||
private:
|
||||
enum Buttons { Fire = 0, Cursor = 1, Turbo = 2, Pause = 3 };
|
||||
uint32_t _stateBuffer = 0;
|
||||
bool _prevFireButton = false;
|
||||
bool _prevTurboButton = false;
|
||||
bool _prevPauseButton = false;
|
||||
bool _turbo = false;
|
||||
Ppu *_ppu;
|
||||
|
||||
protected:
|
||||
bool HasCoordinates() override { return true; }
|
||||
|
||||
string GetKeyNames() override
|
||||
{
|
||||
return "FCTP";
|
||||
}
|
||||
|
||||
void InternalSetStateFromInput() override
|
||||
{
|
||||
SetPressedState(Buttons::Fire, KeyManager::IsMouseButtonPressed(MouseButton::LeftButton));
|
||||
SetPressedState(Buttons::Cursor, KeyManager::IsMouseButtonPressed(MouseButton::RightButton));
|
||||
SetPressedState(Buttons::Turbo, KeyManager::IsMouseButtonPressed(MouseButton::MiddleButton));
|
||||
for(KeyMapping keyMapping : _keyMappings) {
|
||||
SetPressedState(Buttons::Pause, KeyManager::IsKeyPressed(keyMapping.Start));
|
||||
}
|
||||
|
||||
MousePosition pos = KeyManager::GetMousePosition();
|
||||
SetCoordinates(pos);
|
||||
}
|
||||
|
||||
void OnAfterSetState() override
|
||||
{
|
||||
MousePosition pos = GetCoordinates();
|
||||
|
||||
//Make the PPU latch the H/V counters at the mouse's position (offset slightly to make target in the center of the mouse cursor)
|
||||
if(pos.X >= 0 && pos.Y >= 0) {
|
||||
_ppu->SetLocationLatchRequest(pos.X + 10, std::max(0, pos.Y - 3));
|
||||
}
|
||||
}
|
||||
|
||||
void Serialize(Serializer &s) override
|
||||
{
|
||||
BaseControlDevice::Serialize(s);
|
||||
s.Stream(_stateBuffer, _prevTurboButton, _prevFireButton, _prevPauseButton, _turbo);
|
||||
}
|
||||
|
||||
void RefreshStateBuffer() override
|
||||
{
|
||||
_stateBuffer = (uint32_t)ToByte();
|
||||
_prevFireButton = IsPressed(Buttons::Fire);
|
||||
_prevTurboButton = IsPressed(Buttons::Turbo);
|
||||
_prevPauseButton = IsPressed(Buttons::Pause);
|
||||
}
|
||||
|
||||
uint16_t ToByte()
|
||||
{
|
||||
uint16_t output = 0xFF00; //signature bits
|
||||
|
||||
if(!_prevTurboButton && IsPressed(Buttons::Turbo)) {
|
||||
_turbo = !_turbo;
|
||||
}
|
||||
|
||||
if((_turbo || !_prevFireButton) && IsPressed(Buttons::Fire)) {
|
||||
output |= 0x01;
|
||||
}
|
||||
|
||||
if(IsPressed(Buttons::Cursor)) {
|
||||
output |= 0x02;
|
||||
}
|
||||
|
||||
if(_turbo) {
|
||||
output |= 0x04;
|
||||
}
|
||||
|
||||
if(!_prevPauseButton && IsPressed(Buttons::Pause)) {
|
||||
output |= 0x08;
|
||||
}
|
||||
|
||||
if(GetCoordinates().X < 0 || GetCoordinates().Y < 0) {
|
||||
output |= 0x40; //offscreen flag
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
public:
|
||||
SuperScope(Console* console, uint8_t port, KeyMappingSet keyMappings) : BaseControlDevice(console, port, keyMappings)
|
||||
{
|
||||
_ppu = console->GetPpu().get();
|
||||
}
|
||||
|
||||
uint8_t ReadRam(uint16_t addr) override
|
||||
{
|
||||
uint8_t output = 0;
|
||||
|
||||
if(IsCurrentPort(addr)) {
|
||||
StrobeProcessRead();
|
||||
|
||||
output = (_stateBuffer & 0x01);
|
||||
|
||||
_stateBuffer >>= 1;
|
||||
_stateBuffer |= 0x8000;
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
void WriteRam(uint16_t addr, uint8_t value) override
|
||||
{
|
||||
StrobeProcessWrite(value);
|
||||
}
|
||||
};
|
|
@ -21,6 +21,7 @@
|
|||
#define DEVICE_AUTO RETRO_DEVICE_JOYPAD
|
||||
#define DEVICE_GAMEPAD RETRO_DEVICE_SUBCLASS(RETRO_DEVICE_JOYPAD, 0)
|
||||
#define DEVICE_MULTITAP RETRO_DEVICE_SUBCLASS(RETRO_DEVICE_JOYPAD, 1)
|
||||
#define DEVICE_SUPERSCOPE RETRO_DEVICE_SUBCLASS(RETRO_DEVICE_POINTER, 0)
|
||||
#define DEVICE_SNESMOUSE RETRO_DEVICE_SUBCLASS(RETRO_DEVICE_MOUSE, 2)
|
||||
|
||||
static retro_log_printf_t logCallback = nullptr;
|
||||
|
@ -124,6 +125,7 @@ extern "C" {
|
|||
{ "None", DEVICE_NONE },
|
||||
{ "SNES Controller", DEVICE_GAMEPAD },
|
||||
{ "SNES Mouse", DEVICE_SNESMOUSE },
|
||||
{ "Super Scope", DEVICE_SUPERSCOPE },
|
||||
{ "Multitap", DEVICE_MULTITAP },
|
||||
{ NULL, 0 },
|
||||
};
|
||||
|
@ -145,7 +147,7 @@ extern "C" {
|
|||
|
||||
static constexpr struct retro_controller_info ports[] = {
|
||||
{ pads1, 3 },
|
||||
{ pads2, 4 },
|
||||
{ pads2, 5 },
|
||||
{ pads3, 1 },
|
||||
{ pads4, 1 },
|
||||
{ pads5, 1 },
|
||||
|
@ -468,7 +470,10 @@ extern "C" {
|
|||
if(device == DEVICE_AUTO) {
|
||||
if(port <= 4) {
|
||||
switch(_console->GetSettings()->GetInputConfig().Controllers[port].Type) {
|
||||
case ControllerType::Multitap:
|
||||
case ControllerType::SnesController: device = DEVICE_GAMEPAD; break;
|
||||
|
||||
case ControllerType::SuperScope: device = DEVICE_SUPERSCOPE; break;
|
||||
case ControllerType::SnesMouse: device = DEVICE_SNESMOUSE; break;
|
||||
default: return;
|
||||
}
|
||||
|
@ -513,6 +518,7 @@ extern "C" {
|
|||
case DEVICE_GAMEPAD: type = ControllerType::SnesController; break;
|
||||
case DEVICE_MULTITAP: type = ControllerType::Multitap; break;
|
||||
case DEVICE_SNESMOUSE: type = ControllerType::SnesMouse; break;
|
||||
case DEVICE_SUPERSCOPE: type = ControllerType::SuperScope; break;
|
||||
}
|
||||
input.Controllers[port].Type = type;
|
||||
}
|
||||
|
|
|
@ -851,6 +851,7 @@
|
|||
<Value ID="SnesController">SNES Controller</Value>
|
||||
<Value ID="SnesMouse">SNES Mouse</Value>
|
||||
<Value ID="SuperScope">Super Scope</Value>
|
||||
<Value ID="Multitap">Super Multitap</Value>
|
||||
</Enum>
|
||||
<Enum ID="VideoAspectRatio">
|
||||
<Value ID="NoStretching">Default (No Stretching)</Value>
|
||||
|
|
|
@ -98,7 +98,7 @@ namespace Mesen.GUI.Emulation
|
|||
|
||||
public static bool NeedMouseIcon
|
||||
{
|
||||
get { return false; }
|
||||
get { return IsLightGun; }
|
||||
}
|
||||
|
||||
public static void OnMouseMove(Control ctrl)
|
||||
|
|
|
@ -24,7 +24,7 @@ namespace Mesen.GUI.Forms.Config
|
|||
Entity = cfg;
|
||||
|
||||
BaseConfigForm.InitializeComboBox((ComboBox)cboPlayer1, typeof(ControllerType), ControllerType.SuperScope);
|
||||
BaseConfigForm.InitializeComboBox((ComboBox)cboPlayer2, typeof(ControllerType), ControllerType.SuperScope);
|
||||
BaseConfigForm.InitializeComboBox((ComboBox)cboPlayer2, typeof(ControllerType));
|
||||
|
||||
BaseConfigForm.InitializeComboBox((ComboBox)cboMultitap1, typeof(ControllerType), ControllerType.None, ControllerType.Multitap, ControllerType.SnesMouse, ControllerType.SuperScope);
|
||||
BaseConfigForm.InitializeComboBox((ComboBox)cboMultitap2, typeof(ControllerType), ControllerType.None, ControllerType.Multitap, ControllerType.SnesMouse, ControllerType.SuperScope);
|
||||
|
|
Loading…
Add table
Reference in a new issue