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_PLAYER
19#define TIA_PLAYER
20
21#include "bspf.hxx"
22#include "Serializable.hxx"
23#include "TIAConstants.hxx"
24
25class TIA;
26
27class Player : public Serializable
28{
29 public:
30 explicit Player(uInt32 collisionMask);
31
32 public:
33
34 void setTIA(TIA* tia) { myTIA = tia; }
35
36 void reset();
37
38 void grp(uInt8 value);
39
40 void hmp(uInt8 value);
41
42 void nusiz(uInt8 value, bool hblank);
43
44 void resp(uInt8 counter);
45
46 void refp(uInt8 value);
47
48 void vdelp(uInt8 value);
49
50 void toggleEnabled(bool enabled);
51
52 void toggleCollisions(bool enabled);
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 startMovement();
64
65 void nextLine();
66
67 uInt8 getClock() const { return myCounter; }
68
69 bool isOn() const { return (collision & 0x8000); }
70 uInt8 getColor() const { return myColor; }
71
72 void shufflePatterns();
73
74 uInt8 getRespClock() const;
75
76 uInt8 getPosition() const;
77 void setPosition(uInt8 newPosition);
78
79 uInt8 getGRPOld() const { return myPatternOld; }
80 uInt8 getGRPNew() const { return myPatternNew; }
81
82 void setGRPOld(uInt8 pattern);
83
84 /**
85 Serializable methods (see that class for more information).
86 */
87 bool save(Serializer& out) const override;
88 bool load(Serializer& in) override;
89
90 inline void movementTick(uInt32 clock, bool hblank);
91
92 inline void tick();
93
94 public:
95
96 uInt32 collision;
97 bool isMoving;
98
99 private:
100
101 void updatePattern();
102 void applyColors();
103 void setDivider(uInt8 divider);
104
105 private:
106
107 enum Count: Int8 {
108 renderCounterOffset = -5,
109 };
110
111 private:
112
113 uInt32 myCollisionMaskDisabled;
114 uInt32 myCollisionMaskEnabled;
115
116 uInt8 myColor;
117 uInt8 myObjectColor, myDebugColor;
118 bool myDebugEnabled;
119
120 bool myIsSuppressed;
121
122 uInt8 myHmmClocks;
123 uInt8 myCounter;
124
125 bool myIsRendering;
126 Int8 myRenderCounter;
127 Int8 myRenderCounterTripPoint;
128 uInt8 myDivider;
129 uInt8 myDividerPending;
130 uInt8 mySampleCounter;
131 Int8 myDividerChangeCounter;
132
133 const uInt8* myDecodes;
134 uInt8 myDecodesOffset; // needed for state saving
135
136 uInt8 myPatternOld;
137 uInt8 myPatternNew;
138 uInt8 myPattern;
139
140 bool myIsReflected;
141 bool myIsDelaying;
142 bool myInvertedPhaseClock;
143 bool myUseInvertedPhaseClock;
144
145 TIA* myTIA;
146
147 private:
148 Player(const Player&) = delete;
149 Player(Player&&) = delete;
150 Player& operator=(const Player&) = delete;
151 Player& operator=(Player&&) = delete;
152};
153
154// ############################################################################
155// Implementation
156// ############################################################################
157
158// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
159void Player::movementTick(uInt32 clock, bool hblank)
160{
161 if (clock == myHmmClocks)
162 isMoving = false;
163
164 if(isMoving)
165 {
166 if (hblank) tick();
167 myInvertedPhaseClock = !hblank;
168 }
169}
170
171// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
172void Player::tick()
173{
174 if(myUseInvertedPhaseClock && myInvertedPhaseClock)
175 {
176 myInvertedPhaseClock = false;
177 return;
178 }
179
180 if (!myIsRendering || myRenderCounter < myRenderCounterTripPoint)
181 collision = myCollisionMaskDisabled;
182 else
183 collision = (myPattern & (1 << mySampleCounter)) ? myCollisionMaskEnabled : myCollisionMaskDisabled;
184
185 if (myDecodes[myCounter]) {
186 myIsRendering = true;
187 mySampleCounter = 0;
188 myRenderCounter = renderCounterOffset;
189 } else if (myIsRendering) {
190 ++myRenderCounter;
191
192 switch (myDivider) {
193 case 1:
194 if (myRenderCounter > 0)
195 ++mySampleCounter;
196
197 if (myRenderCounter >= 0 && myDividerChangeCounter >= 0 && myDividerChangeCounter-- == 0)
198 setDivider(myDividerPending);
199
200 break;
201
202 default:
203 if (myRenderCounter > 1 && (((myRenderCounter - 1) % myDivider) == 0))
204 ++mySampleCounter;
205
206 if (myRenderCounter > 0 && myDividerChangeCounter >= 0 && myDividerChangeCounter-- == 0)
207 setDivider(myDividerPending);
208
209 break;
210 }
211
212 if (mySampleCounter > 7) myIsRendering = false;
213 }
214
215 if (++myCounter >= TIAConstants::H_PIXEL) myCounter = 0;
216}
217
218#endif // TIA_PLAYER
219