Files
MesenCE/Core/SNES/Input/SnesBlueRetroController.cpp
SourMesen 1489d34441 SNES: Added support for BlueRetro controller (#220)
This controller/adapter supports rumble as well as reading the state of both analog sticks and the L/R triggers.

Code by @RandalLinden
2026-07-16 05:10:26 -07:00

150 lines
4.2 KiB
C++

#include "pch.h"
#include "SNES/Input/SnesBlueRetroController.h"
#include "SNES/SnesConsole.h"
#include "Shared/Emulator.h"
#include "Shared/KeyManager.h"
SnesBlueRetroController::SnesBlueRetroController(Emulator* emu, SnesConsole* console, uint8_t port, KeyMappingSet keyMappings) : SnesRumbleController(emu, console, port, keyMappings)
{
_type = ControllerType::SnesBlueRetroController;
_lxAxisCodes = {
{ KeyManager::GetKeyCode("Pad1 LT X"), false, false },
{ KeyManager::GetKeyCode("Joy1 X"), false, false },
{ KeyManager::GetKeyCode("Pad1 X"), false, false },
};
_lyAxisCodes = {
{ KeyManager::GetKeyCode("Pad1 LT Y"), false, false },
{ KeyManager::GetKeyCode("Joy1 Y"), true, false },
{ KeyManager::GetKeyCode("Pad1 Y"), false, false },
};
_rxAxisCodes = {
{ KeyManager::GetKeyCode("Pad1 RT X"), false, false },
{ KeyManager::GetKeyCode("Joy1 Z"), false, false },
{ KeyManager::GetKeyCode("Pad1 X2"), false, false },
};
_ryAxisCodes = {
{ KeyManager::GetKeyCode("Pad1 RT Y"), false, false },
{ KeyManager::GetKeyCode("Joy1 Z2"), true, false },
{ KeyManager::GetKeyCode("Pad1 Y2"), false, false },
};
_alAxisCodes = {
{ KeyManager::GetKeyCode("Pad1 AL"), false, false },
{ KeyManager::GetKeyCode("Joy1 X2"), false, true },
{ KeyManager::GetKeyCode("Pad1 Z"), true, true },
};
_arAxisCodes = {
{ KeyManager::GetKeyCode("Pad1 AR"), false, false },
{ KeyManager::GetKeyCode("Joy1 Y2"), false, true },
{ KeyManager::GetKeyCode("Pad1 Z2"), false, true },
};
}
SnesBlueRetroController::~SnesBlueRetroController()
{
}
void SnesBlueRetroController::Serialize(Serializer& s)
{
SnesRumbleController::Serialize(s);
SV(_brState);
SV(_mode);
}
void SnesBlueRetroController::RefreshStateBuffer()
{
SnesRumbleController::RefreshStateBuffer();
_brState = ((uint64_t)IsPressed(Buttons::B) << 63) |
((uint64_t)IsPressed(Buttons::Y) << 62) |
((uint64_t)IsPressed(Buttons::Select) << 61) |
((uint64_t)IsPressed(Buttons::Start) << 60) |
((uint64_t)IsPressed(Buttons::Up) << 59) |
((uint64_t)IsPressed(Buttons::Down) << 58) |
((uint64_t)IsPressed(Buttons::Left) << 57) |
((uint64_t)IsPressed(Buttons::Right) << 56) |
((uint64_t)IsPressed(Buttons::A) << 55) |
((uint64_t)IsPressed(Buttons::X) << 54) |
((uint64_t)IsPressed(Buttons::L) << 53) |
((uint64_t)IsPressed(Buttons::R) << 52);
if(_mode) {
int16_t lx = GetAxisValue(_lxAxisCodes);
int16_t ly = GetAxisValue(_lyAxisCodes);
int16_t rx = GetAxisValue(_rxAxisCodes);
int16_t ry = GetAxisValue(_ryAxisCodes);
int16_t al = GetAxisValue(_alAxisCodes);
int16_t ar = GetAxisValue(_arAxisCodes);
if(_mode == 1) {
lx = (lx >> 8) & 0xFF;
ly = (ly >> 8) & 0xFF;
rx = (rx >> 8) & 0xFF;
ry = (ry >> 8) & 0xFF;
al = (al >> 0) & 0xFF;
ar = (ar >> 0) & 0xFF;
_brState |= (0x6ULL << 48) | ((uint64_t)lx << 40) | ((uint64_t)ly << 32) | ((uint64_t)rx << 24) | ((uint64_t)ry << 16) | ((uint64_t)al << 8) | ((uint64_t)ar << 0);
} else {
lx = (lx >> 12) & 0xF;
ly = (ly >> 12) & 0xF;
rx = (rx >> 12) & 0xF;
ry = (ry >> 12) & 0xF;
al = (al >> 4) & 0xF;
ar = (ar >> 4) & 0xF;
_brState |= (0x7ULL << 48) | ((uint64_t)lx << 44) | ((uint64_t)ly << 40) | ((uint64_t)rx << 36) | ((uint64_t)ry << 32) | ((uint64_t)al << 28) | ((uint64_t)ar << 24) | 0x0000000000FFFFFF;
}
} else {
_brState |= 0x0000FFFFFFFFFFFF;
}
}
int16_t SnesBlueRetroController::GetAxisValue(vector<AxisInfo>& axisInfos)
{
for(AxisInfo axisInfo : axisInfos) {
if(axisInfo.KeyCode != 0) {
optional<int16_t> axis = KeyManager::GetAxisPosition(axisInfo.KeyCode);
if(axis.has_value()) {
int16_t value = axis.value();
if(axisInfo.Invert) {
value = (value == INT16_MIN ? INT16_MAX : -value);
}
if(axisInfo.Scale) {
value = (value >> 8) + 0x80;
}
return value;
}
}
}
return 0;
}
uint8_t SnesBlueRetroController::ReadRam(uint16_t addr)
{
SnesRumbleController::ReadRam(addr);
if((_rumbleData & 0xFF00) == 0x6200) {
_mode = _rumbleData & 0x0003;
}
uint8_t output = 0;
if((addr == 0x4016 && (_port & 0x01) == 0) || (addr == 0x4017 && (_port & 0x01) == 1)) {
StrobeProcessRead();
output = ((_brState & 0x8000000000000000) >> 63) & 1;
if(_port >= 2) {
output <<= 1;
}
_brState <<= 1;
_brState |= 0x1;
}
return output;
}