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_PLAYFIELD
19#define TIA_PLAYFIELD
20
21#include "Serializable.hxx"
22#include "bspf.hxx"
23#include "TIAConstants.hxx"
24
25class TIA;
26
27class Playfield : public Serializable
28{
29 public:
30 /**
31 The collision mask is injected at construction
32 */
33 explicit Playfield(uInt32 collisionMask);
34
35 public:
36
37 /**
38 Set the TIA instance
39 */
40 void setTIA(TIA* tia) { myTIA = tia; }
41
42 /**
43 Reset to initial state.
44 */
45 void reset();
46
47 /**
48 PF0 write.
49 */
50 void pf0(uInt8 value);
51
52 /**
53 PF1 write.
54 */
55 void pf1(uInt8 value);
56
57 /**
58 PF2 write.
59 */
60 void pf2(uInt8 value);
61
62 /**
63 CTRLPF write.
64 */
65 void ctrlpf(uInt8 value);
66
67 /**
68 Enable / disable PF display (debugging only, not used during normal emulation).
69 */
70 void toggleEnabled(bool enabled);
71
72 /**
73 Enable / disable PF collisions (debugging only, not used during normal emulation).
74 */
75 void toggleCollisions(bool enabled);
76
77 /**
78 Set color PF.
79 */
80 void setColor(uInt8 color);
81
82 /**
83 Set color P0.
84 */
85 void setColorP0(uInt8 color);
86
87 /**
88 Set color P1.
89 */
90 void setColorP1(uInt8 color);
91
92 /**
93 Set the color used in "debug colors" mode.
94 */
95 void setDebugColor(uInt8 color);
96
97 /**
98 Enable "debug colors" mode.
99 */
100 void enableDebugColors(bool enabled);
101
102 /**
103 Update internal state to use the color loss palette.
104 */
105 void applyColorLoss();
106
107 /**
108 Notify playfield of line change,
109 */
110 void nextLine();
111
112 /**
113 Is the playfield visible? This is determined by looking at bit 15
114 of the collision mask.
115 */
116 bool isOn() const { return (collision & 0x8000); }
117
118 /**
119 Get the current color.
120 */
121 uInt8 getColor() const;
122
123 /**
124 Serializable methods (see that class for more information).
125 */
126 bool save(Serializer& out) const override;
127 bool load(Serializer& in) override;
128
129 /**
130 Tick one color clock. Inline for performance (implementation below).
131 */
132 inline void tick(uInt32 x);
133
134 public:
135
136 /**
137 16 bit Collision mask. Each sprite is represented by a single bit in the mask
138 (1 = active, 0 = inactive). All other bits are always 1. The highest bit is
139 abused to store visibility (as the actual collision bit will always be zero
140 if collisions are disabled).
141 */
142 uInt32 collision;
143
144 private:
145
146 /**
147 Playfield mode.
148 */
149 enum class ColorMode: uInt8 {normal, score};
150
151 private:
152
153 /**
154 Recalculate playfield color based on COLUPF, debug colors, color loss, etc.
155 */
156 void applyColors();
157
158 /**
159 Recalculate the playfield pattern from PF0, PF1 and PF2.
160 */
161 void updatePattern();
162
163 private:
164
165 /**
166 Collision mask values for active / inactive states. Disabling collisions
167 will change those.
168 */
169 uInt32 myCollisionMaskDisabled;
170 uInt32 myCollisionMaskEnabled;
171
172 /**
173 Enable / disable PF (debugging).
174 */
175 bool myIsSuppressed;
176
177 /**
178 Left / right PF colors. Derifed from P0 / P1 color, COLUPF and playfield mode.
179 */
180 uInt8 myColorLeft;
181 uInt8 myColorRight;
182
183 /**
184 P0 / P1 colors
185 */
186 uInt8 myColorP0;
187 uInt8 myColorP1;
188
189 /**
190 COLUPF and debug colors
191 */
192 uInt8 myObjectColor, myDebugColor;
193
194 /**
195 Debug colors enabled?
196 */
197 bool myDebugEnabled;
198
199 /**
200 * Plafield mode.
201 */
202 ColorMode myColorMode;
203
204 /**
205 Pattern derifed from PF0, PF1, PF2
206 */
207 uInt32 myPattern;
208
209 /**
210 "Effective pattern". Will be 0 if playfield is disabled (debug), otherwise the same as myPattern.
211 */
212 uInt32 myEffectivePattern;
213
214 /**
215 Reflected mode on / off.
216 */
217 bool myReflected;
218
219 /**
220 * Are we currently drawing the reflected PF?
221 */
222 bool myRefp;
223
224 /**
225 PF registers.
226 */
227 uInt8 myPf0;
228 uInt8 myPf1;
229 uInt8 myPf2;
230
231 /**
232 The current scanline position (0 .. 159).
233 */
234 uInt32 myX;
235
236 /**
237 TIA instance. Required for flushing the line cache.
238 */
239 TIA* myTIA;
240
241 private:
242 Playfield() = delete;
243 Playfield(const Playfield&) = delete;
244 Playfield(Playfield&&) = delete;
245 Playfield& operator=(const Playfield&) = delete;
246 Playfield& operator=(Playfield&&) = delete;
247};
248
249// ############################################################################
250// Implementation
251// ############################################################################
252
253// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
254void Playfield::tick(uInt32 x)
255{
256 myX = x;
257
258 // Reflected flag is updated only at x = 0 or x = 79
259 if (myX == TIAConstants::H_PIXEL / 2 || myX == 0) myRefp = myReflected;
260
261 if (x & 0x03) return;
262
263 uInt32 currentPixel;
264
265 if (myEffectivePattern == 0) {
266 currentPixel = 0;
267 } else if (x < TIAConstants::H_PIXEL / 2) {
268 currentPixel = myEffectivePattern & (1 << (x >> 2));
269 } else if (myRefp) {
270 currentPixel = myEffectivePattern & (1 << (39 - (x >> 2)));
271 } else {
272 currentPixel = myEffectivePattern & (1 << ((x >> 2) - 20));
273 }
274
275 collision = currentPixel ? myCollisionMaskEnabled : myCollisionMaskDisabled;
276}
277
278#endif // TIA_PLAYFIELD
279