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-2019 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#ifndef TIA_MISSILE
19#define TIA_MISSILE
20
21#include "Serializable.hxx"
22#include "bspf.hxx"
23#include "Player.hxx"
24#include "TIAConstants.hxx"
25
26class TIA;
27
28class Missile : public Serializable
29{
30 public:
31
32 explicit Missile(uInt32 collisionMask);
33
34 public:
35
36 void setTIA(TIA* tia) { myTIA = tia; }
37
38 void reset();
39
40 void enam(uInt8 value);
41
42 void hmm(uInt8 value);
43
44 void resm(uInt8 counter, bool hblank);
45
46 void resmp(uInt8 value, const Player& player);
47
48 void nusiz(uInt8 value);
49
50 void startMovement();
51
52 void nextLine();
53
54 void setColor(uInt8 color);
55
56 void setDebugColor(uInt8 color);
57 void enableDebugColors(bool enabled);
58
59 void applyColorLoss();
60
61 void setInvertedPhaseClock(bool enable);
62
63 void toggleCollisions(bool enabled);
64
65 void toggleEnabled(bool enabled);
66
67 bool isOn() const { return (collision & 0x8000); }
68 uInt8 getColor() const { return myColor; }
69
70 uInt8 getPosition() const;
71 void setPosition(uInt8 newPosition);
72
73 /**
74 Serializable methods (see that class for more information).
75 */
76 bool save(Serializer& out) const override;
77 bool load(Serializer& in) override;
78
79 inline void movementTick(uInt8 clock, uInt8 hclock, bool hblank);
80
81 inline void tick(uInt8 hclock, bool isReceivingMclock = true);
82
83 public:
84
85 uInt32 collision;
86 bool isMoving;
87
88 private:
89
90 void updateEnabled();
91 void applyColors();
92
93 private:
94
95 enum Count: Int8 {
96 renderCounterOffset = -4
97 };
98
99 private:
100
101 uInt32 myCollisionMaskDisabled;
102 uInt32 myCollisionMaskEnabled;
103
104 bool myIsEnabled;
105 bool myIsSuppressed;
106 bool myEnam;
107 uInt8 myResmp;
108
109 uInt8 myHmmClocks;
110 uInt8 myCounter;
111
112 uInt8 myWidth;
113 uInt8 myEffectiveWidth;
114
115 bool myIsRendering;
116 bool myIsVisible;
117 Int8 myRenderCounter;
118
119 const uInt8* myDecodes;
120 uInt8 myDecodesOffset; // needed for state saving
121
122 uInt8 myColor;
123 uInt8 myObjectColor, myDebugColor;
124 bool myDebugEnabled;
125
126 bool myInvertedPhaseClock;
127 bool myUseInvertedPhaseClock;
128
129 TIA *myTIA;
130
131 private:
132 Missile(const Missile&) = delete;
133 Missile(Missile&&) = delete;
134 Missile& operator=(const Missile&) = delete;
135 Missile& operator=(Missile&&) = delete;
136};
137
138// ############################################################################
139// Implementation
140// ############################################################################
141
142// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
143void Missile::movementTick(uInt8 clock, uInt8 hclock, bool hblank)
144{
145 if(clock == myHmmClocks) isMoving = false;
146
147 if (isMoving)
148 {
149 if (hblank) tick(hclock, false);
150 myInvertedPhaseClock = !hblank;
151 }
152}
153
154// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
155void Missile::tick(uInt8 hclock, bool isReceivingMclock)
156{
157 if(myUseInvertedPhaseClock && myInvertedPhaseClock)
158 {
159 myInvertedPhaseClock = false;
160 return;
161 }
162
163 myIsVisible =
164 myIsRendering &&
165 (myRenderCounter >= 0 || (isMoving && isReceivingMclock && myRenderCounter == -1 && myWidth < 4 && ((hclock + 1) % 4 == 3)));
166
167 collision = (myIsVisible && myIsEnabled) ? myCollisionMaskEnabled : myCollisionMaskDisabled;
168
169 if (myDecodes[myCounter] && !myResmp) {
170 myIsRendering = true;
171 myRenderCounter = renderCounterOffset;
172 } else if (myIsRendering) {
173
174 if (myRenderCounter == -1) {
175 if (isMoving && isReceivingMclock) {
176 switch ((hclock + 1) % 4) {
177 case 3:
178 myEffectiveWidth = myWidth == 1 ? 2 : myWidth;
179 if (myWidth < 4) ++myRenderCounter;
180 break;
181
182 case 2:
183 myEffectiveWidth = 0;
184 break;
185
186 default:
187 myEffectiveWidth = myWidth;
188 break;
189 }
190 } else {
191 myEffectiveWidth = myWidth;
192 }
193 }
194
195 if (++myRenderCounter >= (isMoving ? myEffectiveWidth : myWidth)) myIsRendering = false;
196 }
197
198 if (++myCounter >= TIAConstants::H_PIXEL) myCounter = 0;
199}
200
201#endif // TIA_MISSILE
202