mirror of
https://github.com/libretro/Mesen.git
synced 2024-12-14 21:08:44 +00:00
151 lines
4.8 KiB
C++
151 lines
4.8 KiB
C++
#include "stdafx.h"
|
|
#include "StandardController.h"
|
|
#include "ControlManager.h"
|
|
#include "PPU.h"
|
|
#include "EmulationSettings.h"
|
|
#include "ArkanoidController.h"
|
|
#include "OekaKidsTablet.h"
|
|
|
|
StandardController::StandardController(uint8_t port, bool emptyPort) : BaseControlDevice(port)
|
|
{
|
|
_isEmptyPort = emptyPort;
|
|
}
|
|
|
|
void StandardController::StreamState(bool saving)
|
|
{
|
|
BaseControlDevice::StreamState(saving);
|
|
|
|
SnapshotInfo additionalController{ _additionalController.get() };
|
|
Stream(_stateBuffer, _stateBufferFamicom, additionalController);
|
|
}
|
|
|
|
uint8_t StandardController::GetButtonState()
|
|
{
|
|
ButtonState state;
|
|
|
|
if(!EmulationSettings::CheckFlag(EmulationFlags::InBackground) || EmulationSettings::CheckFlag(EmulationFlags::AllowBackgroundInput)) {
|
|
for(size_t i = 0, len = _keyMappings.size(); i < len; i++) {
|
|
KeyMapping keyMapping = _keyMappings[i];
|
|
|
|
state.A |= ControlManager::IsKeyPressed(keyMapping.A);
|
|
state.B |= ControlManager::IsKeyPressed(keyMapping.B);
|
|
state.Select |= ControlManager::IsKeyPressed(keyMapping.Select);
|
|
state.Start |= ControlManager::IsKeyPressed(keyMapping.Start);
|
|
state.Up |= ControlManager::IsKeyPressed(keyMapping.Up);
|
|
state.Down |= ControlManager::IsKeyPressed(keyMapping.Down);
|
|
state.Left |= ControlManager::IsKeyPressed(keyMapping.Left);
|
|
state.Right |= ControlManager::IsKeyPressed(keyMapping.Right);
|
|
|
|
//Turbo buttons - need to be applied for at least 2 reads in a row (some games require this)
|
|
uint8_t turboFreq = 1 << (4 - _turboSpeed);
|
|
bool turboOn = (uint8_t)(PPU::GetFrameCount() % turboFreq) < turboFreq / 2;
|
|
if(turboOn) {
|
|
state.A |= ControlManager::IsKeyPressed(keyMapping.TurboA);
|
|
state.B |= ControlManager::IsKeyPressed(keyMapping.TurboB);
|
|
state.Start |= ControlManager::IsKeyPressed(keyMapping.TurboStart);
|
|
state.Select |= ControlManager::IsKeyPressed(keyMapping.TurboSelect);
|
|
}
|
|
}
|
|
|
|
if(!EmulationSettings::CheckFlag(EmulationFlags::AllowInvalidInput)) {
|
|
if(state.Up && state.Down) {
|
|
state.Down = false;
|
|
}
|
|
if(state.Left && state.Right) {
|
|
state.Right = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return state.ToByte();
|
|
}
|
|
|
|
uint32_t StandardController::GetNetPlayState()
|
|
{
|
|
return GetButtonState();
|
|
}
|
|
|
|
uint8_t StandardController::GetPortOutput()
|
|
{
|
|
uint8_t returnValue = _stateBuffer & 0x01;
|
|
_stateBuffer >>= 1;
|
|
|
|
if(_famiconDevice && (_additionalController || EmulationSettings::CheckFlag(EmulationFlags::HasFourScore))) {
|
|
if(std::dynamic_pointer_cast<Zapper>(_additionalController) || std::dynamic_pointer_cast<OekaKidsTablet>(_additionalController)) {
|
|
returnValue |= _additionalController->GetPortOutput();
|
|
} else if(std::dynamic_pointer_cast<ArkanoidController>(_additionalController)) {
|
|
returnValue |= std::dynamic_pointer_cast<ArkanoidController>(_additionalController)->GetExpansionPortOutput(_port);
|
|
} else {
|
|
returnValue |= (_stateBufferFamicom & 0x01) << 1;
|
|
_stateBufferFamicom >>= 1;
|
|
_stateBufferFamicom |= 0x800000;
|
|
}
|
|
}
|
|
|
|
//"All subsequent reads will return D=1 on an authentic controller but may return D=0 on third party controllers."
|
|
_stateBuffer |= _isEmptyPort ? 0 : 0x800000;
|
|
|
|
return returnValue;
|
|
}
|
|
|
|
void StandardController::RefreshStateBuffer()
|
|
{
|
|
_stateBuffer = GetControlState();
|
|
_lastButtonState = _stateBuffer;
|
|
if((_additionalController && !std::dynamic_pointer_cast<Zapper>(_additionalController)) || EmulationSettings::CheckFlag(EmulationFlags::HasFourScore)) {
|
|
//Next 8 bits = Gamepad 3/4
|
|
if(_famiconDevice) {
|
|
if(std::dynamic_pointer_cast<ArkanoidController>(_additionalController) || std::dynamic_pointer_cast<OekaKidsTablet>(_additionalController)) {
|
|
_additionalController->RefreshStateBuffer();
|
|
} else {
|
|
//Four player adapter (Famicom)
|
|
_stateBufferFamicom = _additionalController ? _additionalController->GetControlState() : 0;
|
|
_stateBufferFamicom |= 0xFFFF00;
|
|
}
|
|
} else {
|
|
//Four-score adapter (NES)
|
|
_stateBuffer |= _additionalController ? (_additionalController->GetControlState() << 8) : 0;
|
|
|
|
//Last 8 bits = signature
|
|
//Signature for port 0 = 0x10, reversed bit order => 0x08
|
|
//Signature for port 1 = 0x20, reversed bit order => 0x04
|
|
_stateBuffer |= (GetPort() == 0 ? 0x08 : 0x04) << 16;
|
|
}
|
|
} else {
|
|
//"All subsequent reads will return D=1 on an authentic controller but may return D=0 on third party controllers."
|
|
_stateBuffer |= _isEmptyPort ? 0 : 0xFFFF00;
|
|
}
|
|
}
|
|
|
|
uint8_t StandardController::RefreshState()
|
|
{
|
|
return GetButtonState();
|
|
}
|
|
|
|
void StandardController::AddAdditionalController(shared_ptr<BaseControlDevice> controller)
|
|
{
|
|
_additionalController = controller;
|
|
}
|
|
|
|
shared_ptr<BaseControlDevice> StandardController::GetAdditionalController()
|
|
{
|
|
return _additionalController;
|
|
}
|
|
|
|
uint32_t StandardController::GetInternalState()
|
|
{
|
|
return _stateBuffer;
|
|
}
|
|
|
|
void StandardController::SetInternalState(uint32_t state)
|
|
{
|
|
_stateBuffer = state;
|
|
}
|
|
|
|
uint8_t StandardController::GetLastButtonState()
|
|
{
|
|
return _lastButtonState;
|
|
}
|
|
|
|
|