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#include "Playfield.hxx"
19#include "TIA.hxx"
20
21// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
22Playfield::Playfield(uInt32 collisionMask)
23 : myCollisionMaskDisabled(collisionMask),
24 myCollisionMaskEnabled(0xFFFF),
25 myIsSuppressed(false),
26 myTIA(nullptr)
27{
28 reset();
29}
30
31// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
32void Playfield::reset()
33{
34 myPattern = 0;
35 myReflected = false;
36 myRefp = false;
37
38 myPf0 = 0;
39 myPf1 = 0;
40 myPf2 = 0;
41
42 myX = 0;
43
44 myObjectColor = myDebugColor = 0;
45 myColorLeft = myColorRight = 0;
46 myColorP0 = myColorP1 = 0;
47 myColorMode = ColorMode::normal;
48 myDebugEnabled = false;
49
50 collision = 0;
51
52 updatePattern();
53}
54
55// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
56void Playfield::pf0(uInt8 value)
57{
58 if (myPf0 == value >> 4) return;
59
60 myTIA->flushLineCache();
61
62 myPattern = (myPattern & 0x000FFFF0) | (value >> 4);
63 myPf0 = value >> 4;
64
65 updatePattern();
66}
67
68// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
69void Playfield::pf1(uInt8 value)
70{
71 if (myPf1 == value) return;
72
73 myTIA->flushLineCache();
74
75 myPattern = (myPattern & 0x000FF00F)
76 | ((value & 0x80) >> 3)
77 | ((value & 0x40) >> 1)
78 | ((value & 0x20) << 1)
79 | ((value & 0x10) << 3)
80 | ((value & 0x08) << 5)
81 | ((value & 0x04) << 7)
82 | ((value & 0x02) << 9)
83 | ((value & 0x01) << 11);
84
85 myPf1 = value;
86 updatePattern();
87}
88
89// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
90void Playfield::pf2(uInt8 value)
91{
92 if (myPf2 == value) return;
93
94 myTIA->flushLineCache();
95
96 myPattern = (myPattern & 0x00000FFF) | (value << 12);
97 myPf2 = value;
98
99 updatePattern();
100}
101
102// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
103void Playfield::ctrlpf(uInt8 value)
104{
105 const bool reflected = (value & 0x01) > 0;
106 const ColorMode colorMode = (value & 0x06) == 0x02 ? ColorMode::score : ColorMode::normal;
107
108 if (myReflected == reflected && myColorMode == colorMode) return;
109
110 myTIA->flushLineCache();
111
112 myReflected = reflected;
113 myColorMode = colorMode;
114 applyColors();
115}
116
117// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
118void Playfield::toggleEnabled(bool enabled)
119{
120 myIsSuppressed = !enabled;
121
122 updatePattern();
123}
124
125// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
126void Playfield::toggleCollisions(bool enabled)
127{
128 // Only keep bit 15 active if collisions are disabled.
129 myCollisionMaskEnabled = enabled ? 0xFFFF : (0x8000 | myCollisionMaskDisabled);
130}
131
132// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
133void Playfield::setColor(uInt8 color)
134{
135 if (color != myObjectColor && myColorMode == ColorMode::normal) myTIA->flushLineCache();
136
137 myObjectColor = color;
138 applyColors();
139}
140
141// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
142void Playfield::setColorP0(uInt8 color)
143{
144 if (color != myColorP0 && myColorMode == ColorMode::score) myTIA->flushLineCache();
145
146 myColorP0 = color;
147 applyColors();
148}
149
150// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
151void Playfield::setColorP1(uInt8 color)
152{
153 if (color != myColorP1 && myColorMode == ColorMode::score) myTIA->flushLineCache();
154
155 myColorP1 = color;
156 applyColors();
157}
158
159// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
160void Playfield::setDebugColor(uInt8 color)
161{
162 myTIA->flushLineCache();
163 // allow slight luminance variations without changing color
164 if((color & 0xe) == 0xe)
165 color -= 2;
166 if((color & 0xe) == 0x0)
167 color += 2;
168 myDebugColor = color;
169 applyColors();
170}
171
172// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
173void Playfield::enableDebugColors(bool enabled)
174{
175 myTIA->flushLineCache();
176 myDebugEnabled = enabled;
177 applyColors();
178}
179
180// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
181void Playfield::applyColorLoss()
182{
183 myTIA->flushLineCache();
184 applyColors();
185}
186
187// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
188void Playfield::nextLine()
189{
190 collision = myCollisionMaskDisabled;
191}
192
193// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
194void Playfield::applyColors()
195{
196 if (myDebugEnabled)
197 myColorLeft = myColorRight = myDebugColor;
198 else
199 {
200 switch (myColorMode)
201 {
202 case ColorMode::normal:
203 if (myTIA->colorLossActive())
204 myColorLeft = myColorRight = myObjectColor |= 0x01;
205 else
206 myColorLeft = myColorRight = myObjectColor &= 0xfe;
207 break;
208
209 case ColorMode::score:
210 if (myTIA->colorLossActive())
211 {
212 myColorLeft = myColorP0 |= 0x01;
213 myColorRight = myColorP1 |= 0x01;
214 }
215 else
216 {
217 myColorLeft = myColorP0 &= 0xfe;
218 myColorRight = myColorP1 &= 0xfe;
219 }
220 break;
221 }
222 }
223}
224
225// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
226uInt8 Playfield::getColor() const
227{
228 if (!myDebugEnabled)
229 return myX < TIAConstants::H_PIXEL / 2 ? myColorLeft : myColorRight;
230 else
231 {
232 if (myX < TIAConstants::H_PIXEL / 2)
233 {
234 // left side:
235 if(myX < 16)
236 return myDebugColor - 2; // PF0
237 if(myX < 48)
238 return myDebugColor; // PF1
239 }
240 else
241 {
242 // right side:
243 if(!myReflected)
244 {
245 if(myX < TIAConstants::H_PIXEL / 2 + 16)
246 return myDebugColor - 2; // PF0
247 if(myX < TIAConstants::H_PIXEL / 2 + 48)
248 return myDebugColor; // PF1
249 }
250 else
251 {
252 if(myX >= TIAConstants::H_PIXEL - 16)
253 return myDebugColor - 2; // PF0
254 if(myX >= TIAConstants::H_PIXEL - 48)
255 return myDebugColor; // PF1
256 }
257 }
258 return myDebugColor + 2; // PF2
259 }
260}
261
262// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
263void Playfield::updatePattern()
264{
265 myEffectivePattern = myIsSuppressed ? 0 : myPattern;
266}
267
268// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
269bool Playfield::save(Serializer& out) const
270{
271 try
272 {
273 out.putInt(collision);
274 out.putInt(myCollisionMaskDisabled);
275 out.putInt(myCollisionMaskEnabled);
276
277 out.putBool(myIsSuppressed);
278
279 out.putByte(myColorLeft);
280 out.putByte(myColorRight);
281 out.putByte(myColorP0);
282 out.putByte(myColorP1);
283 out.putByte(myObjectColor);
284 out.putByte(myDebugColor);
285 out.putBool(myDebugEnabled);
286
287 out.putByte(uInt8(myColorMode));
288
289 out.putInt(myPattern);
290 out.putInt(myEffectivePattern);
291 out.putBool(myRefp);
292 out.putBool(myReflected);
293
294 out.putByte(myPf0);
295 out.putByte(myPf1);
296 out.putByte(myPf2);
297
298 out.putInt(myX);
299 }
300 catch(...)
301 {
302 cerr << "ERROR: TIA_Playfield::save" << endl;
303 return false;
304 }
305
306 return true;
307}
308
309// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
310bool Playfield::load(Serializer& in)
311{
312 try
313 {
314 collision = in.getInt();
315 myCollisionMaskDisabled = in.getInt();
316 myCollisionMaskEnabled = in.getInt();
317
318 myIsSuppressed = in.getBool();
319
320 myColorLeft = in.getByte();
321 myColorRight = in.getByte();
322 myColorP0 = in.getByte();
323 myColorP1 = in.getByte();
324 myObjectColor = in.getByte();
325 myDebugColor = in.getByte();
326 myDebugEnabled = in.getBool();
327
328 myColorMode = ColorMode(in.getByte());
329
330 myPattern = in.getInt();
331 myEffectivePattern = in.getInt();
332 myRefp = in.getBool();
333 myReflected = in.getBool();
334
335 myPf0 = in.getByte();
336 myPf1 = in.getByte();
337 myPf2 = in.getByte();
338
339 myX = in.getInt();
340
341 applyColors();
342 updatePattern();
343 }
344 catch(...)
345 {
346 cerr << "ERROR: TIA_Playfield::load" << endl;
347 return false;
348 }
349
350 return true;
351}
352