mirror of
https://github.com/0ldsk00l/nestopia.git
synced 2025-04-02 10:31:51 -04:00
453 lines
14 KiB
C++
453 lines
14 KiB
C++
////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Nestopia - NES/Famicom emulator written in C++
|
|
//
|
|
// Copyright (C) 2003-2008 Martin Freij
|
|
//
|
|
// This file is part of Nestopia.
|
|
//
|
|
// Nestopia is free software; you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation; either version 2 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// Nestopia is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with Nestopia; if not, write to the Free Software
|
|
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
#include <cstring>
|
|
#include <new>
|
|
#include <algorithm>
|
|
#include "../NstMachine.hpp"
|
|
#include "../NstCartridge.hpp"
|
|
#include "../input/NstInpDevice.hpp"
|
|
#include "../input/NstInpAdapter.hpp"
|
|
#include "../input/NstInpPad.hpp"
|
|
#include "../input/NstInpZapper.hpp"
|
|
#include "../input/NstInpPaddle.hpp"
|
|
#include "../input/NstInpPowerPad.hpp"
|
|
#include "../input/NstInpPowerGlove.hpp"
|
|
#include "../input/NstInpMouse.hpp"
|
|
#include "../input/NstInpFamilyTrainer.hpp"
|
|
#include "../input/NstInpFamilyKeyboard.hpp"
|
|
#include "../input/NstInpSuborKeyboard.hpp"
|
|
#include "../input/NstInpDoremikkoKeyboard.hpp"
|
|
#include "../input/NstInpHoriTrack.hpp"
|
|
#include "../input/NstInpPachinko.hpp"
|
|
#include "../input/NstInpOekaKidsTablet.hpp"
|
|
#include "../input/NstInpKonamiHyperShot.hpp"
|
|
#include "../input/NstInpBandaiHyperShot.hpp"
|
|
#include "../input/NstInpCrazyClimber.hpp"
|
|
#include "../input/NstInpMahjong.hpp"
|
|
#include "../input/NstInpExcitingBoxing.hpp"
|
|
#include "../input/NstInpTopRider.hpp"
|
|
#include "../input/NstInpPokkunMoguraa.hpp"
|
|
#include "../input/NstInpPartyTap.hpp"
|
|
#include "../input/NstInpRob.hpp"
|
|
#include "../input/NstInpTurboFile.hpp"
|
|
#include "../input/NstInpBarcodeWorld.hpp"
|
|
|
|
namespace Nes
|
|
{
|
|
#ifdef NST_MSVC_OPTIMIZE
|
|
#pragma optimize("s", on)
|
|
#endif
|
|
|
|
namespace Api
|
|
{
|
|
Input::ControllerCaller Input::controllerCallback;
|
|
Input::AdapterCaller Input::adapterCallback;
|
|
}
|
|
|
|
namespace Core
|
|
{
|
|
namespace Input
|
|
{
|
|
Controllers::PollCaller2< Controllers::Pad > Controllers::Pad::callback;
|
|
Controllers::PollCaller1< Controllers::Zapper > Controllers::Zapper::callback;
|
|
Controllers::PollCaller1< Controllers::Paddle > Controllers::Paddle::callback;
|
|
Controllers::PollCaller1< Controllers::PowerPad > Controllers::PowerPad::callback;
|
|
Controllers::PollCaller1< Controllers::PowerGlove > Controllers::PowerGlove::callback;
|
|
Controllers::PollCaller1< Controllers::Mouse > Controllers::Mouse::callback;
|
|
Controllers::PollCaller1< Controllers::FamilyTrainer > Controllers::FamilyTrainer::callback;
|
|
Controllers::PollCaller3< Controllers::FamilyKeyboard > Controllers::FamilyKeyboard::callback;
|
|
Controllers::PollCaller3< Controllers::SuborKeyboard > Controllers::SuborKeyboard::callback;
|
|
Controllers::PollCaller3< Controllers::DoremikkoKeyboard > Controllers::DoremikkoKeyboard::callback;
|
|
Controllers::PollCaller1< Controllers::HoriTrack > Controllers::HoriTrack::callback;
|
|
Controllers::PollCaller1< Controllers::Pachinko > Controllers::Pachinko::callback;
|
|
Controllers::PollCaller1< Controllers::VsSystem > Controllers::VsSystem::callback;
|
|
Controllers::PollCaller1< Controllers::OekaKidsTablet > Controllers::OekaKidsTablet::callback;
|
|
Controllers::PollCaller1< Controllers::KonamiHyperShot > Controllers::KonamiHyperShot::callback;
|
|
Controllers::PollCaller1< Controllers::BandaiHyperShot > Controllers::BandaiHyperShot::callback;
|
|
Controllers::PollCaller1< Controllers::CrazyClimber > Controllers::CrazyClimber::callback;
|
|
Controllers::PollCaller2< Controllers::Mahjong > Controllers::Mahjong::callback;
|
|
Controllers::PollCaller2< Controllers::ExcitingBoxing > Controllers::ExcitingBoxing::callback;
|
|
Controllers::PollCaller1< Controllers::TopRider > Controllers::TopRider::callback;
|
|
Controllers::PollCaller2< Controllers::PokkunMoguraa > Controllers::PokkunMoguraa::callback;
|
|
Controllers::PollCaller1< Controllers::PartyTap > Controllers::PartyTap::callback;
|
|
Controllers::PollCaller1< Controllers::KaraokeStudio > Controllers::KaraokeStudio::callback;
|
|
|
|
Controllers::PowerPad::PowerPad() throw()
|
|
{
|
|
std::fill( sideA, sideA + NUM_SIDE_A_BUTTONS, false );
|
|
std::fill( sideB, sideB + NUM_SIDE_B_BUTTONS, false );
|
|
}
|
|
|
|
Controllers::PowerGlove::PowerGlove() throw()
|
|
{
|
|
x = 0;
|
|
y = 0;
|
|
distance = 0;
|
|
wrist = 0;
|
|
gesture = GESTURE_OPEN;
|
|
}
|
|
|
|
Controllers::FamilyTrainer::FamilyTrainer() throw()
|
|
{
|
|
std::fill( sideA, sideA + NUM_SIDE_A_BUTTONS, false );
|
|
std::fill( sideB, sideB + NUM_SIDE_B_BUTTONS, false );
|
|
}
|
|
|
|
Controllers::FamilyKeyboard::FamilyKeyboard() throw()
|
|
{
|
|
std::memset( parts, 0x00, sizeof(parts) );
|
|
}
|
|
|
|
Controllers::SuborKeyboard::SuborKeyboard() throw()
|
|
{
|
|
std::memset( parts, 0x00, sizeof(parts) );
|
|
}
|
|
|
|
Controllers::Controllers() throw()
|
|
{
|
|
}
|
|
}
|
|
}
|
|
|
|
namespace Api
|
|
{
|
|
Result Input::ConnectController(const uint port,const Type type) throw()
|
|
{
|
|
Core::Input::Device* old = NULL;
|
|
|
|
switch (port)
|
|
{
|
|
case PORT_1:
|
|
case PORT_2:
|
|
|
|
if (emulator.extPort->GetDevice( port ).GetType() == type)
|
|
{
|
|
return RESULT_NOP;
|
|
}
|
|
else switch (type)
|
|
{
|
|
case UNCONNECTED:
|
|
|
|
old = new (std::nothrow) Core::Input::Device( emulator.cpu );
|
|
break;
|
|
|
|
case PAD1:
|
|
case PAD2:
|
|
case PAD3:
|
|
case PAD4:
|
|
|
|
old = new (std::nothrow) Core::Input::Pad( emulator.cpu, uint(type) - PAD1 );
|
|
break;
|
|
|
|
case ZAPPER:
|
|
|
|
old = new (std::nothrow) Core::Input::Zapper( emulator.cpu, emulator.ppu );
|
|
break;
|
|
|
|
case PADDLE:
|
|
|
|
old = new (std::nothrow) Core::Input::Paddle( emulator.cpu, false );
|
|
break;
|
|
|
|
case POWERPAD:
|
|
|
|
old = new (std::nothrow) Core::Input::PowerPad( emulator.cpu );
|
|
break;
|
|
|
|
case POWERGLOVE:
|
|
|
|
old = new (std::nothrow) Core::Input::PowerGlove( emulator.cpu );
|
|
break;
|
|
|
|
case MOUSE:
|
|
|
|
old = new (std::nothrow) Core::Input::Mouse( emulator.cpu );
|
|
break;
|
|
|
|
case ROB:
|
|
|
|
old = new (std::nothrow) Core::Input::Rob( emulator.cpu, emulator.ppu );
|
|
break;
|
|
|
|
default: return RESULT_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
if (old)
|
|
old = &emulator.extPort->Connect( port, *old );
|
|
else
|
|
return RESULT_ERR_OUT_OF_MEMORY;
|
|
|
|
break;
|
|
|
|
case PORT_3:
|
|
case PORT_4:
|
|
|
|
if (emulator.extPort->NumPorts() > 2)
|
|
{
|
|
if (emulator.extPort->GetDevice( port ).GetType() == type)
|
|
{
|
|
return RESULT_NOP;
|
|
}
|
|
else switch (type)
|
|
{
|
|
case UNCONNECTED:
|
|
|
|
if (emulator.extPort->GetDevice( port == PORT_3 ? PORT_4 : PORT_3 ).GetType() == UNCONNECTED)
|
|
{
|
|
Core::Input::Adapter* const adapter = new (std::nothrow) Core::Input::AdapterTwo
|
|
(
|
|
emulator.extPort->GetDevice(0),
|
|
emulator.extPort->GetDevice(1),
|
|
emulator.extPort->GetType()
|
|
);
|
|
|
|
if (adapter == NULL)
|
|
return RESULT_ERR_OUT_OF_MEMORY;
|
|
|
|
for (uint i=2; i < 4; ++i)
|
|
delete &emulator.extPort->GetDevice(i);
|
|
|
|
delete emulator.extPort;
|
|
emulator.extPort = adapter;
|
|
}
|
|
else if (NULL != (old = new (std::nothrow) Core::Input::Device( emulator.cpu )))
|
|
{
|
|
old = &emulator.extPort->Connect( port, *old );
|
|
}
|
|
else
|
|
{
|
|
return RESULT_ERR_OUT_OF_MEMORY;
|
|
}
|
|
break;
|
|
|
|
case PAD1:
|
|
case PAD2:
|
|
case PAD3:
|
|
case PAD4:
|
|
|
|
if (NULL != (old = new (std::nothrow) Core::Input::Pad( emulator.cpu, uint(type) - PAD1 )))
|
|
{
|
|
old = &emulator.extPort->Connect( port, *old );
|
|
}
|
|
else
|
|
{
|
|
return RESULT_ERR_OUT_OF_MEMORY;
|
|
}
|
|
break;
|
|
|
|
default: return RESULT_ERR_INVALID_PARAM;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
switch (type)
|
|
{
|
|
case UNCONNECTED:
|
|
|
|
return RESULT_NOP;
|
|
|
|
case PAD1:
|
|
case PAD2:
|
|
case PAD3:
|
|
case PAD4:
|
|
{
|
|
Core::Input::Device* const devices[2] =
|
|
{
|
|
new (std::nothrow) Core::Input::Device( emulator.cpu ),
|
|
new (std::nothrow) Core::Input::Pad( emulator.cpu, uint(type) - PAD1 )
|
|
};
|
|
|
|
Core::Input::Adapter* adapter;
|
|
|
|
if
|
|
(
|
|
devices[0] && devices[1] && NULL !=
|
|
(
|
|
adapter = new (std::nothrow) Core::Input::AdapterFour
|
|
(
|
|
emulator.extPort->GetDevice(0),
|
|
emulator.extPort->GetDevice(1),
|
|
*devices[port == PORT_3],
|
|
*devices[port != PORT_3],
|
|
emulator.extPort->GetType()
|
|
)
|
|
)
|
|
)
|
|
{
|
|
delete emulator.extPort;
|
|
emulator.extPort = adapter;
|
|
}
|
|
else
|
|
{
|
|
delete devices[0];
|
|
delete devices[1];
|
|
|
|
return RESULT_ERR_OUT_OF_MEMORY;
|
|
}
|
|
break;
|
|
}
|
|
|
|
default: return RESULT_ERR_INVALID_PARAM;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case EXPANSION_PORT:
|
|
|
|
if (emulator.expPort->GetType() == type)
|
|
{
|
|
return RESULT_NOP;
|
|
}
|
|
else switch (type)
|
|
{
|
|
case UNCONNECTED: old = new (std::nothrow) Core::Input::Device( emulator.cpu ); break;
|
|
case PADDLE: old = new (std::nothrow) Core::Input::Paddle( emulator.cpu, true ); break;
|
|
case FAMILYTRAINER: old = new (std::nothrow) Core::Input::FamilyTrainer( emulator.cpu ); break;
|
|
case FAMILYKEYBOARD: old = new (std::nothrow) Core::Input::FamilyKeyboard( emulator.cpu, true ); break;
|
|
case SUBORKEYBOARD: old = new (std::nothrow) Core::Input::SuborKeyboard( emulator.cpu ); break;
|
|
case DOREMIKKOKEYBOARD: old = new (std::nothrow) Core::Input::DoremikkoKeyboard( emulator.cpu ); break;
|
|
case HORITRACK: old = new (std::nothrow) Core::Input::HoriTrack( emulator.cpu ); break;
|
|
case PACHINKO: old = new (std::nothrow) Core::Input::Pachinko( emulator.cpu ); break;
|
|
case OEKAKIDSTABLET: old = new (std::nothrow) Core::Input::OekaKidsTablet( emulator.cpu ); break;
|
|
case KONAMIHYPERSHOT: old = new (std::nothrow) Core::Input::KonamiHyperShot( emulator.cpu ); break;
|
|
case BANDAIHYPERSHOT: old = new (std::nothrow) Core::Input::BandaiHyperShot( emulator.cpu, emulator.ppu ); break;
|
|
case CRAZYCLIMBER: old = new (std::nothrow) Core::Input::CrazyClimber( emulator.cpu ); break;
|
|
case MAHJONG: old = new (std::nothrow) Core::Input::Mahjong( emulator.cpu ); break;
|
|
case EXCITINGBOXING: old = new (std::nothrow) Core::Input::ExcitingBoxing( emulator.cpu ); break;
|
|
case TOPRIDER: old = new (std::nothrow) Core::Input::TopRider( emulator.cpu ); break;
|
|
case POKKUNMOGURAA: old = new (std::nothrow) Core::Input::PokkunMoguraa( emulator.cpu ); break;
|
|
case PARTYTAP: old = new (std::nothrow) Core::Input::PartyTap( emulator.cpu ); break;
|
|
case TURBOFILE: old = new (std::nothrow) Core::Input::TurboFile( emulator.cpu ); break;
|
|
case BARCODEWORLD: old = new (std::nothrow) Core::Input::BarcodeWorld( emulator.cpu ); break;
|
|
|
|
default: return RESULT_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
if (old)
|
|
std::swap( old, emulator.expPort );
|
|
else
|
|
return RESULT_ERR_OUT_OF_MEMORY;
|
|
|
|
break;
|
|
|
|
default: return RESULT_ERR_INVALID_PARAM;
|
|
}
|
|
|
|
delete old;
|
|
emulator.InitializeInputDevices();
|
|
controllerCallback( port, type );
|
|
|
|
return RESULT_OK;
|
|
}
|
|
|
|
Result Input::ConnectAdapter(Adapter adapter) throw()
|
|
{
|
|
if (emulator.extPort->SetType( adapter ))
|
|
{
|
|
adapterCallback( adapter );
|
|
return RESULT_OK;
|
|
}
|
|
else
|
|
{
|
|
return RESULT_NOP;
|
|
}
|
|
}
|
|
|
|
Result Input::AutoSelectController(uint port) throw()
|
|
{
|
|
if (port >= NUM_PORTS)
|
|
return RESULT_ERR_INVALID_PARAM;
|
|
|
|
Type type;
|
|
|
|
if (emulator.image)
|
|
{
|
|
type = static_cast<Type>(emulator.image->GetDesiredController( port ));
|
|
}
|
|
else switch (port)
|
|
{
|
|
case PORT_1: type = PAD1; break;
|
|
case PORT_2: type = PAD2; break;
|
|
default: type = UNCONNECTED; break;
|
|
}
|
|
|
|
return ConnectController( port, type );
|
|
}
|
|
|
|
Result Input::AutoSelectControllers() throw()
|
|
{
|
|
Result result = RESULT_NOP;
|
|
|
|
for (uint i=0; i < NUM_PORTS; ++i)
|
|
{
|
|
const Result tmp = AutoSelectController( i );
|
|
|
|
if (result > tmp)
|
|
result = tmp;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
Result Input::AutoSelectAdapter() throw()
|
|
{
|
|
return ConnectAdapter( emulator.image ? static_cast<Adapter>(emulator.image->GetDesiredAdapter()) : ADAPTER_NES );
|
|
}
|
|
|
|
Input::Type Input::GetConnectedController(uint port) const throw()
|
|
{
|
|
if (port == EXPANSION_PORT)
|
|
return emulator.expPort->GetType();
|
|
|
|
if (port < emulator.extPort->NumPorts())
|
|
return emulator.extPort->GetDevice( port ).GetType();
|
|
|
|
return UNCONNECTED;
|
|
}
|
|
|
|
Input::Adapter Input::GetConnectedAdapter() const throw()
|
|
{
|
|
return emulator.extPort->GetType();
|
|
}
|
|
|
|
bool Input::IsControllerConnected(Type type) const throw()
|
|
{
|
|
if (emulator.expPort->GetType() == type)
|
|
return true;
|
|
|
|
for (uint ports=emulator.extPort->NumPorts(), i=0; i < ports; ++i)
|
|
{
|
|
if (emulator.extPort->GetDevice(i).GetType() == type)
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
#ifdef NST_MSVC_OPTIMIZE
|
|
#pragma optimize("", on)
|
|
#endif
|
|
}
|