1 //============================================================================
2 //
3 // SSSS tt lll lll
4 // SS SS tt ll ll
5 // SS tttttt eeee ll ll aaaa
6 // SSSS tt ee ee ll ll aa
7 // SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
8 // SS SS tt ee ll ll aa aa
9 // SSSS ttt eeeee llll llll aaaaa
10 //
11 // Copyright (c) 1995-2021 by Bradford W. Mott, Stephen Anthony
12 // and the Stella Team
13 //
14 // See the file "License.txt" for information on usage and redistribution of
15 // this file, and for a DISCLAIMER OF ALL WARRANTIES.
16 //============================================================================
17
18 #include "Event.hxx"
19 #include "TIA.hxx"
20 #include "FrameBuffer.hxx"
21
22 #include "Lightgun.hxx"
23
24 // | | Left port | Right port |
25 // | Fire button | SWCHA bit 4 | SWCHA bit 0 | DP:1
26 // | Detect light | INPT4 bit 7 | INPT5 bit 7 | DP:6
27
28 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Lightgun(Jack jack,const Event & event,const System & system,const string & romMd5,const FrameBuffer & frameBuffer)29 Lightgun::Lightgun(Jack jack, const Event& event, const System& system,
30 const string& romMd5, const FrameBuffer& frameBuffer)
31 : Controller(jack, event, system, Controller::Type::Lightgun),
32 myFrameBuffer{frameBuffer}
33 {
34 // Right now, there are only three games and a test ROM that use the light gun
35 if (romMd5 == "8da51e0c4b6b46f7619425119c7d018e" ||
36 romMd5 == "7e5ee26bc31ae8e4aa61388c935b9332")
37 {
38 // Sentinel
39 myOfsX = -24;
40 myOfsY = -5;
41 }
42 else if (romMd5 == "10c47acca2ecd212b900ad3cf6942dbb" ||
43 romMd5 == "15c11ab6e4502b2010b18366133fc322" ||
44 romMd5 == "557e893616648c37a27aab5a47acbf10" ||
45 romMd5 == "5d7293f1892b66c014e8d222e06f6165" ||
46 romMd5 == "b2ab209976354ad4a0e1676fc1fe5a82" ||
47 romMd5 == "b5a1a189601a785bdb2f02a424080412" ||
48 romMd5 == "c5bf03028b2e8f4950ec8835c6811d47" ||
49 romMd5 == "f0ef9a1e5d4027a157636d7f19952bb5")
50 {
51 // Shooting Arcade
52 myOfsX = -21;
53 myOfsY = 5;
54 }
55 else if (romMd5 == "2559948f39b91682934ea99d90ede631" ||
56 romMd5 == "e75ab446017448045b152eea78bf7910")
57 {
58 // Booby is Hungry
59 myOfsX = -21;
60 myOfsY = 5;
61 }
62 else if (romMd5 == "d65900fefa7dc18ac3ad99c213e2fa4e")
63 {
64 // Guntest
65 myOfsX = -25;
66 myOfsY = 1;
67 }
68 else
69 {
70 // unknown game, use average values
71 myOfsX = -23;
72 myOfsY = 1;
73 }
74 }
75
76 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
read(DigitalPin pin)77 bool Lightgun::read(DigitalPin pin)
78 {
79 // We need to override the Controller::read() method, since the lightgun
80 // checks this multiple times per frame
81 // (we can't just read 60 times per second in the ::update() method)
82 switch (pin)
83 {
84 case DigitalPin::Six: // INPT4/5
85 {
86 const Common::Rect& rect = myFrameBuffer.imageRect();
87
88 // abort when no valid framebuffer exists
89 if (rect.w() == 0 || rect.h() == 0)
90 return false;
91
92 TIA& tia = mySystem.tia();
93 // scale mouse coordinates into TIA coordinates
94 Int32 xMouse = (myEvent.get(Event::MouseAxisXValue) - rect.x())
95 * tia.width() / rect.w();
96 Int32 yMouse = (myEvent.get(Event::MouseAxisYValue) - rect.y())
97 * tia.height() / rect.h();
98
99 // get adjusted TIA coordinates
100 Int32 xTia = tia.clocksThisLine() - TIAConstants::H_BLANK_CLOCKS + myOfsX;
101 Int32 yTia = tia.scanlines() - tia.startLine() + myOfsY;
102
103 if (xTia < 0)
104 xTia += TIAConstants::H_CLOCKS;
105
106 bool enable = !((xTia - xMouse) >= 0 && (xTia - xMouse) < 15 && (yTia - yMouse) >= 0);
107
108 return enable;
109 }
110 default:
111 return Controller::read(pin);
112 }
113 }
114
115 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
update()116 void Lightgun::update()
117 {
118 // Digital events (from keyboard or joystick hats & buttons)
119 bool firePressed = myEvent.get(Event::LeftJoystickFire) != 0;
120
121 // We allow left and right mouse buttons for fire button
122 firePressed = firePressed
123 || myEvent.get(Event::MouseButtonLeftValue)
124 || myEvent.get(Event::MouseButtonRightValue);
125
126 setPin(DigitalPin::One, !getAutoFireState(firePressed));
127 }
128