Merge remote-tracking branch 'remotes/origin/feature-lightgun'

This commit is contained in:
thrust26 2019-12-30 23:08:47 +01:00
commit 9a7dc74aea
22 changed files with 260 additions and 27 deletions

View File

@ -275,7 +275,7 @@
<li>Emulates <a href="http://en.wikipedia.org/wiki/CompuMate">Spectravideo CompuMate</a> system using your computer's keyboard,
including mapping of CompuMate 'Backspace', 'Space' and 'Enter' functionality to
to the actual keys on your keyboard</li>
<li>Emulates the Mindlink Controller using your computer's mouse</li>
<li>Emulates the Mindlink Controller and the Light Gun using your computer's mouse</li>
<li>Supports autodetection for most common controller types</li>
<li>Support for real Atari 2600 controllers using the
<a href="http://www.grandideastudio.com/portfolio/stelladaptor-2600">Stelladaptor</a> and
@ -1784,6 +1784,14 @@
<td> &#x2715;</td>
<td> &#x2715;</td>
</tr>
<tr>
<th> Light Gun</th>
<td> &#x2715;</td>
<td> &#x2715;</td>
<td> &#x2713;</td>
<td> &#x2715;</td>
<td> &#x2715;</td>
</tr>
<tr>
<th> Mindlink</th>
<td> &#x2715;</td>
@ -3817,6 +3825,7 @@ Ms Pac-Man (Stella extended codes):
<tr><td>SaveKey</td><td>A 32K EEPROM for saving high scores, etc. (the EEPROM portion of an AtariVox).</td></tr>
<tr><td>Genesis </td><td>Sega Genesis controller, which can be used similar to a BoosterGrip, giving an extra button.</td></tr>
<tr><td>CompuMate &#185</td><td>Spectravideo CompuMate (if either left or right is set, CompuMate is used for both).</td></tr>
<tr><td>Lightgun</td><td>Atari XG-1 compatible Light Gun</td></tr>
<tr><td>Mindlink &#185</td><td>Mindlink controller.</td></tr>
</table></td>
</tr>

View File

@ -99,8 +99,8 @@ void BoosterGrip::update()
{
// The following code was taken from z26
#define MJ_Threshold 2
int mousex = myEvent.get(Event::MouseAxisXValue),
mousey = myEvent.get(Event::MouseAxisYValue);
int mousex = myEvent.get(Event::MouseAxisXMove),
mousey = myEvent.get(Event::MouseAxisYMove);
if(mousex || mousey)
{
if((!(abs(mousey) > abs(mousex) << 1)) && (abs(mousex) >= MJ_Threshold))

View File

@ -47,6 +47,7 @@
#include "AmigaMouse.hxx"
#include "AtariMouse.hxx"
#include "TrakBall.hxx"
#include "Lightgun.hxx"
#include "FrameBuffer.hxx"
#include "TIASurface.hxx"
#include "OSystem.hxx"
@ -871,6 +872,10 @@ unique_ptr<Controller> Console::getControllerPort(const Controller::Type type,
controller = make_unique<MindLink>(port, myEvent, *mySystem);
break;
case Controller::Type::Lightgun:
controller = make_unique<Lightgun>(port, myEvent, *mySystem, myOSystem.frameBuffer());
break;
default:
// What else can we do?
// always create because it may have been changed by user dialog

View File

@ -109,7 +109,8 @@ string Controller::getName(const Type type)
"Unknown",
"AmigaMouse", "AtariMouse", "AtariVox", "BoosterGrip", "CompuMate",
"Driving", "Sega Genesis", "Joystick", "Keyboard", "KidVid", "MindLink",
"Paddles", "Paddles_IAxis", "Paddles_IAxDr", "SaveKey", "TrakBall"
"Paddles", "Paddles_IAxis", "Paddles_IAxDr", "SaveKey", "TrakBall",
"Lightgun"
};
return NAMES[int(type)];
@ -123,7 +124,8 @@ string Controller::getPropName(const Type type)
"AUTO",
"AMIGAMOUSE", "ATARIMOUSE", "ATARIVOX", "BOOSTERGRIP", "COMPUMATE",
"DRIVING", "GENESIS", "JOYSTICK", "KEYBOARD", "KIDVID", "MINDLINK",
"PADDLES", "PADDLES_IAXIS", "PADDLES_IAXDR", "SAVEKEY", "TRAKBALL"
"PADDLES", "PADDLES_IAXIS", "PADDLES_IAXDR", "SAVEKEY", "TRAKBALL",
"LIGHTGUN"
};
return PROP_NAMES[int(type)];

View File

@ -94,6 +94,7 @@ class Controller : public Serializable
AmigaMouse, AtariMouse, AtariVox, BoosterGrip, CompuMate,
Driving, Genesis, Joystick, Keyboard, KidVid, MindLink,
Paddles, PaddlesIAxis, PaddlesIAxDr, SaveKey, TrakBall,
Lightgun,
LastType
};

View File

@ -70,8 +70,9 @@ Controller::Type ControllerDetector::autodetectPort(const uInt8* image, size_t s
else if(usesKeyboard(image, size, port))
type = Controller::Type::Keyboard;
else if(usesGenesisButton(image, size, port))
type = Controller::Type::Genesis;
else if(isProbablyLightGun(image, size, port))
type = Controller::Type::Lightgun;
}
else
{
@ -643,3 +644,41 @@ bool ControllerDetector::isProbablySaveKey(const uInt8* image, size_t size,
return false;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool ControllerDetector::isProbablyLightGun(const uInt8* image, size_t size,
Controller::Jack port)
{
if (port == Controller::Jack::Left)
{
// check for INPT4 after NOPs access
const int NUM_SIGS = 2;
const int SIG_SIZE = 6;
uInt8 signature[NUM_SIGS][SIG_SIZE] = {
{ 0xea, 0xea, 0xea, 0x24, 0x0c, 0x10 },
{ 0xea, 0xea, 0xea, 0x24, 0x3c, 0x10 }
}; // all pattern checked, only 'Sentinel' and 'Shooting Arcade' match
for (uInt32 i = 0; i < NUM_SIGS; ++i)
if (searchForBytes(image, size, signature[i], SIG_SIZE))
return true;
return false;
}
else if(port == Controller::Jack::Right)
{
// check for INPT5 after NOPs access
const int NUM_SIGS = 2;
const int SIG_SIZE = 6;
uInt8 signature[NUM_SIGS][SIG_SIZE] = {
{ 0xea, 0xea, 0xea, 0x24, 0x0d, 0x10 },
{ 0xea, 0xea, 0xea, 0x24, 0x3d, 0x10 }
}; // all pattern checked, only 'Bobby is Hungry' matches
for (uInt32 i = 0; i < NUM_SIGS; ++i)
if (searchForBytes(image, size, signature[i], SIG_SIZE))
return true;
}
return false;
}

View File

@ -111,6 +111,10 @@ class ControllerDetector
// Returns true if a SaveKey code pattern is found.
static bool isProbablySaveKey(const uInt8* image, size_t size, Controller::Jack port);
// Returns true if a Lightgun code pattern is found
static bool isProbablyLightGun(const uInt8* image, size_t size, Controller::Jack port);
private:
// Following constructors and assignment operators not supported
ControllerDetector() = delete;

View File

@ -62,7 +62,7 @@ void Driving::update()
// Mouse motion and button events
if(myControlID > -1)
{
int m_axis = myEvent.get(Event::MouseAxisXValue);
int m_axis = myEvent.get(Event::MouseAxisXMove);
if(m_axis < -2) --myCounter;
else if(m_axis > 2) ++myCounter;
if(myEvent.get(Event::MouseButtonLeftValue) ||
@ -75,7 +75,7 @@ void Driving::update()
// mapped to a separate driving controller
if(myControlIDX > -1)
{
int m_axis = myEvent.get(Event::MouseAxisXValue);
int m_axis = myEvent.get(Event::MouseAxisXMove);
if(m_axis < -2) --myCounter;
else if(m_axis > 2) ++myCounter;
if(myEvent.get(Event::MouseButtonLeftValue))
@ -83,7 +83,7 @@ void Driving::update()
}
if(myControlIDY > -1)
{
int m_axis = myEvent.get(Event::MouseAxisYValue);
int m_axis = myEvent.get(Event::MouseAxisYMove);
if(m_axis < -2) --myCounter;
else if(m_axis > 2) ++myCounter;
if(myEvent.get(Event::MouseButtonRightValue))

View File

@ -68,7 +68,7 @@ class Event
Combo1, Combo2, Combo3, Combo4, Combo5, Combo6, Combo7, Combo8,
Combo9, Combo10, Combo11, Combo12, Combo13, Combo14, Combo15, Combo16,
MouseAxisXValue, MouseAxisYValue,
MouseAxisXMove, MouseAxisYMove,
MouseButtonLeftValue, MouseButtonRightValue,
ChangeState, LoadState, SaveState, TakeSnapshot, Quit,
@ -121,6 +121,7 @@ class Event
CompuMateSlash,
ToggleInter,
MouseAxisXValue, MouseAxisYValue,
LastType
};

View File

@ -27,6 +27,7 @@
#include "OSystem.hxx"
#include "Joystick.hxx"
#include "Paddles.hxx"
#include "Lightgun.hxx"
#include "PointingDevice.hxx"
#include "PropsSet.hxx"
#include "Settings.hxx"
@ -247,8 +248,8 @@ void EventHandler::poll(uInt64 time)
// Turn off all mouse-related items; if they haven't been taken care of
// in the previous ::update() methods, they're now invalid
myEvent.set(Event::MouseAxisXValue, 0);
myEvent.set(Event::MouseAxisYValue, 0);
myEvent.set(Event::MouseAxisXMove, 0);
myEvent.set(Event::MouseAxisYMove, 0);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -269,8 +270,10 @@ void EventHandler::handleMouseMotionEvent(int x, int y, int xrel, int yrel)
{
if(!mySkipMouseMotion)
{
myEvent.set(Event::MouseAxisXValue, xrel);
myEvent.set(Event::MouseAxisYValue, yrel);
myEvent.set(Event::MouseAxisXValue, x); // required for Lightgun controller
myEvent.set(Event::MouseAxisYValue, y); // required for Lightgun controller
myEvent.set(Event::MouseAxisXMove, xrel);
myEvent.set(Event::MouseAxisYMove, yrel);
}
mySkipMouseMotion = false;
}
@ -1952,7 +1955,7 @@ const Event::EventSet EventHandler::MiscEvents = {
Event::Quit, Event::ReloadConsole, Event::Fry, Event::StartPauseMode,
Event::TogglePauseMode, Event::OptionsMenuMode, Event::CmdMenuMode, Event::ExitMode,
Event::TakeSnapshot, Event::ToggleContSnapshots, Event::ToggleContSnapshotsFrame,
// Event::MouseAxisXValue, Event::MouseAxisYValue,
// Event::MouseAxisXMove, Event::MouseAxisYMove,
// Event::MouseButtonLeftValue, Event::MouseButtonRightValue,
Event::HandleMouseControl, Event::ToggleGrabMouse,
Event::ToggleSAPortOrder,

View File

@ -841,10 +841,17 @@ void FrameBuffer::setCursorState()
bool analog = myOSystem.hasConsole() ?
(myOSystem.console().leftController().isAnalog() ||
myOSystem.console().rightController().isAnalog()) : false;
bool usesLightgun = emulation && myOSystem.hasConsole() ?
myOSystem.console().leftController().type() == Controller::Type::Lightgun ||
myOSystem.console().rightController().type() == Controller::Type::Lightgun : false;
bool alwaysUseMouse = BSPF::equalsIgnoreCase("always", myOSystem.settings().getString("usemouse"));
// Show/hide cursor in UI/emulation mode based on 'cursor' setting
int cursor = myOSystem.settings().getInt("cursor");
// always enable cursor in lightgun games
if (usesLightgun)
cursor |= 1;
switch(cursor)
{
case 0:

View File

@ -67,8 +67,8 @@ void Genesis::update()
{
// The following code was taken from z26
#define MJ_Threshold 2
int mousex = myEvent.get(Event::MouseAxisXValue),
mousey = myEvent.get(Event::MouseAxisYValue);
int mousex = myEvent.get(Event::MouseAxisXMove),
mousey = myEvent.get(Event::MouseAxisYMove);
if(mousex || mousey)
{
if((!(abs(mousey) > abs(mousex) << 1)) && (abs(mousex) >= MJ_Threshold))

View File

@ -81,8 +81,8 @@ void Joystick::update()
{
// The following code was taken from z26
#define MJ_Threshold 2
int mousex = myEvent.get(Event::MouseAxisXValue),
mousey = myEvent.get(Event::MouseAxisYValue);
int mousex = myEvent.get(Event::MouseAxisXMove),
mousey = myEvent.get(Event::MouseAxisYMove);
if(mousex || mousey)
{
if((!(abs(mousey) > abs(mousex) << 1)) && (abs(mousex) >= MJ_Threshold))

75
src/emucore/Lightgun.cxx Normal file
View File

@ -0,0 +1,75 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2019 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//============================================================================
#include "Event.hxx"
#include "TIA.hxx"
#include "FrameBuffer.hxx"
#include "Lightgun.hxx"
// | | Left port | Right port |
// | Fire button | SWCHA bit 4 | SWCHA bit 0 | DP:1
// | Detect light | INPT4 bit 7 | INPT5 bit 7 | DP:6
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Lightgun::Lightgun(Jack jack, const Event& event, const System& system, const FrameBuffer& frameBuffer)
: Controller(jack, event, system, Controller::Type::Lightgun),
myFrameBuffer(frameBuffer)
{
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool Lightgun::read(DigitalPin pin)
{
// We need to override the Controller::read() method, since the lightgun
// checks this multiple times per frame
// (we can't just read 60 times per second in the ::update() method)
switch (pin)
{
case DigitalPin::Six: // INPT4/5
{
const Common::Rect& rect = myFrameBuffer.imageRect();
// scale mouse coordinates into TIA coordinates
Int32 xMouse = (myEvent.get(Event::MouseAxisXValue) - rect.x())
* TIAConstants::H_PIXEL / rect.w();
Int32 yMouse = (myEvent.get(Event::MouseAxisYValue) - rect.y())
* 210 / rect.h(); // TODO: replace "magic number"
// get adjusted TIA coordinates
Int32 xTia = mySystem.tia().clocksThisLine() - TIAConstants::H_BLANK_CLOCKS + X_OFS;
Int32 yTia = mySystem.tia().scanlines() - mySystem.tia().startLine() + Y_OFS;
if (xTia < 0)
xTia += TIAConstants::H_CLOCKS;
bool enable = !((xTia - xMouse) >= 0 && (xTia - xMouse) < 15 && (yTia - yMouse) >= 0);
return enable;
}
default:
return Controller::read(pin);
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Lightgun::update()
{
// we allow left and right mouse buttons for fire button
setPin(DigitalPin::One, myEvent.get(Event::MouseButtonLeftValue)
|| myEvent.get(Event::MouseButtonRightValue));
}

77
src/emucore/Lightgun.hxx Normal file
View File

@ -0,0 +1,77 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2019 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//============================================================================
#ifndef LIGHTGUN_HXX
#define LIGHTGUN_HXX
/**
This class handles the lightgun controller
@author Thomas Jentzsch
*/
class Lightgun : public Controller
{
public:
/**
Create a new pair of paddle controllers plugged into the specified jack
@param jack The jack the controller is plugged into
@param event The event object to use for events
@param system The system using this controller
*/
Lightgun(Jack jack, const Event& event, const System& system, const FrameBuffer& frameBuffer);
virtual ~Lightgun() = default;
public:
using Controller::read;
/**
Read the value of the specified digital pin for this controller.
@param pin The pin of the controller jack to read
@return The state of the pin
*/
bool read(DigitalPin pin) override;
/**
Update the entire digital and analog pin state according to the
events currently set.
*/
void update() override;
/**
Returns the name of this controller.
*/
string name() const override { return "Lightgun"; }
private:
const FrameBuffer& myFrameBuffer;
static constexpr Int32 X_OFS = -21;
static constexpr Int32 Y_OFS = 5;
private:
// Following constructors and assignment operators not supported
Lightgun() = delete;
Lightgun(const Lightgun&) = delete;
Lightgun(Lightgun&&) = delete;
Lightgun& operator=(const Lightgun&) = delete;
Lightgun& operator=(Lightgun&&) = delete;
};
#endif

View File

@ -40,7 +40,7 @@ void MindLink::update()
return;
myMindlinkPos = (myMindlinkPos & 0x3fffffff) +
(myEvent.get(Event::MouseAxisXValue) << 3);
(myEvent.get(Event::MouseAxisXMove) << 3);
if(myMindlinkPos < 0x2800)
myMindlinkPos = 0x2800;
if(myMindlinkPos >= 0x3800)

View File

@ -162,13 +162,13 @@ Paddles::Paddles(Jack jack, const Event& event, const System& system,
abs(MOUSE_SENSITIVITY);
if(!swapaxis)
{
myAxisMouseMotion = Event::MouseAxisXValue;
myAxisMouseMotion = Event::MouseAxisXMove;
myAxisDigitalZero = 0;
myAxisDigitalOne = 1;
}
else
{
myAxisMouseMotion = Event::MouseAxisYValue;
myAxisMouseMotion = Event::MouseAxisYMove;
myAxisDigitalZero = 1;
myAxisDigitalOne = 0;
}
@ -277,7 +277,7 @@ void Paddles::update()
if(myMPaddleIDX > -1)
{
myCharge[myMPaddleIDX] = BSPF::clamp(myCharge[myMPaddleIDX] -
(myEvent.get(Event::MouseAxisXValue) * MOUSE_SENSITIVITY),
(myEvent.get(Event::MouseAxisXMove) * MOUSE_SENSITIVITY),
TRIGMIN, TRIGRANGE);
if(myEvent.get(Event::MouseButtonLeftValue))
setPin(ourButtonPin[myMPaddleIDX], false);
@ -285,7 +285,7 @@ void Paddles::update()
if(myMPaddleIDY > -1)
{
myCharge[myMPaddleIDY] = BSPF::clamp(myCharge[myMPaddleIDY] -
(myEvent.get(Event::MouseAxisYValue) * MOUSE_SENSITIVITY),
(myEvent.get(Event::MouseAxisYMove) * MOUSE_SENSITIVITY),
TRIGMIN, TRIGRANGE);
if(myEvent.get(Event::MouseButtonRightValue))
setPin(ourButtonPin[myMPaddleIDY], false);

View File

@ -79,11 +79,11 @@ void PointingDevice::update()
return;
// Update horizontal direction
updateDirection( myEvent.get(Event::MouseAxisXValue), myHCounterRemainder,
updateDirection( myEvent.get(Event::MouseAxisXMove), myHCounterRemainder,
myTrackBallLeft, myTrackBallLinesH, myScanCountH, myFirstScanOffsetH);
// Update vertical direction
updateDirection(-myEvent.get(Event::MouseAxisYValue), myVCounterRemainder,
updateDirection(-myEvent.get(Event::MouseAxisYMove), myVCounterRemainder,
myTrackBallDown, myTrackBallLinesV, myScanCountV, myFirstScanOffsetV);
// Get mouse button state

View File

@ -66,6 +66,7 @@ MODULE_OBJS := \
src/emucore/Joystick.o \
src/emucore/Keyboard.o \
src/emucore/KidVid.o \
src/emucore/Lightgun.o \
src/emucore/MindLink.o \
src/emucore/M6502.o \
src/emucore/M6532.o \

View File

@ -220,6 +220,7 @@ GameInfoDialog::GameInfoDialog(
VarList::push_back(ctrls, "SaveKey", "SAVEKEY");
VarList::push_back(ctrls, "Sega Genesis", "GENESIS");
VarList::push_back(ctrls, "KidVid", "KIDVID");
VarList::push_back(ctrls, "Lightgun", "LIGHTGUN");
VarList::push_back(ctrls, "MindLink", "MINDLINK");
ypos = VBORDER;

View File

@ -487,6 +487,7 @@
<ClCompile Include="..\emucore\EmulationTiming.cxx" />
<ClCompile Include="..\emucore\EmulationWorker.cxx" />
<ClCompile Include="..\emucore\FBSurface.cxx" />
<ClCompile Include="..\emucore\Lightgun.cxx" />
<ClCompile Include="..\emucore\MindLink.cxx" />
<ClCompile Include="..\emucore\PointingDevice.cxx" />
<ClCompile Include="..\emucore\ProfilingRunner.cxx" />
@ -1210,6 +1211,7 @@
<ClInclude Include="..\emucore\exception\FatalEmulationError.hxx" />
<ClInclude Include="..\emucore\FBSurface.hxx" />
<ClInclude Include="..\emucore\FrameBufferConstants.hxx" />
<ClInclude Include="..\emucore\Lightgun.hxx" />
<ClInclude Include="..\emucore\MindLink.hxx" />
<ClInclude Include="..\emucore\PointingDevice.hxx" />
<ClInclude Include="..\emucore\ProfilingRunner.hxx" />

View File

@ -996,6 +996,9 @@
<ClCompile Include="..\common\PhosphorHandler.cxx">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\emucore\Lightgun.cxx">
<Filter>Source Files\emucore</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\common\bspf.hxx">
@ -2036,6 +2039,9 @@
<ClInclude Include="..\common\PhosphorHandler.hxx">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\emucore\Lightgun.hxx">
<Filter>Header Files\emucore</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<None Include="stella.ico">