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 | |
25 | class TIA; |
26 | |
27 | class 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
254 | void 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 | |