~ubuntu-branches/debian/squeeze/stella/squeeze

« back to all changes in this revision

Viewing changes to src/emucore/TIA.cxx

  • Committer: Bazaar Package Importer
  • Author(s): Stephen Kitt
  • Date: 2010-07-12 23:49:36 UTC
  • mfrom: (1.1.4 upstream)
  • Revision ID: james.westby@ubuntu.com-20100712234936-juawrr3etzhr2qpv
Tags: 3.1.2-1
* New maintainer (closes: #532039).
* New upstream version (closes: #461121):
  - includes launcher (closes: #396058).
* Fix the reference to the X Window System in the description (closes:
  #411815).
* Move to main, DFSG-free ROMs are available (see README.Debian).
* Enhance the package description.
* Drop the libslang2-dev dependency (closes: #560274).
* Remove the Encoding entry from stella.desktop.
* Avoid ignoring errors when cleaning.
* Add ${misc:Depends} to the package dependencies.
* Provide a doc-base file to install the documentation using doc-base.
* Switch to debhelper 7 with a simplified rules file.
* Use autotools-dev to provide updated configuration files.
* Update to Standards-Version 3.9.0:
  - Move to menu section Applications/Emulators.
  - Move the homepage declaration.
* Re-write the manpage.

Show diffs side-by-side

added added

removed removed

Lines of Context:
8
8
//  SS  SS   tt   ee      ll   ll  aa  aa
9
9
//   SSSS     ttt  eeeee llll llll  aaaaa
10
10
//
11
 
// Copyright (c) 1995-2008 by Bradford W. Mott and the Stella team
 
11
// Copyright (c) 1995-2010 by Bradford W. Mott, Stephen Anthony
 
12
// and the Stella Team
12
13
//
13
 
// See the file "license" for information on usage and redistribution of
 
14
// See the file "License.txt" for information on usage and redistribution of
14
15
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
15
16
//
16
 
// $Id: TIA.cxx,v 1.94 2008/05/22 17:54:54 stephena Exp $
 
17
// $Id: TIA.cxx 2012 2010-04-14 22:35:46Z stephena $
17
18
//============================================================================
18
19
 
19
 
//#define DEBUG_HMOVE
20
 
 
21
20
#include <cassert>
22
21
#include <cstdlib>
23
22
#include <cstring>
26
25
 
27
26
#include "Console.hxx"
28
27
#include "Control.hxx"
29
 
#include "Deserializer.hxx"
 
28
#include "Device.hxx"
30
29
#include "M6502.hxx"
31
 
#include "Serializer.hxx"
32
30
#include "Settings.hxx"
33
31
#include "Sound.hxx"
34
32
#include "System.hxx"
 
33
#include "TIATables.hxx"
35
34
 
36
35
#include "TIA.hxx"
37
36
 
38
37
#define HBLANK 68
 
38
#define USE_MMR_LATCHES
39
39
 
40
40
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
41
 
TIA::TIA(Console& console, Settings& settings)
42
 
    : myConsole(console),
43
 
      mySettings(settings),
44
 
      mySound(NULL),
45
 
      myColorLossEnabled(false),
46
 
      myMaximumNumberOfScanlines(262),
47
 
      myCOLUBK(myColor[0]),
48
 
      myCOLUPF(myColor[1]),
49
 
      myCOLUP0(myColor[2]),
50
 
      myCOLUP1(myColor[3])
 
41
TIA::TIA(Console& console, Sound& sound, Settings& settings)
 
42
  : myConsole(console),
 
43
    mySound(sound),
 
44
    mySettings(settings),
 
45
    myMaximumNumberOfScanlines(262),
 
46
    myColorLossEnabled(false),
 
47
    myPartialFrameFlag(false),
 
48
    myAutoFrameEnabled(false),
 
49
    myFrameCounter(0)
51
50
{
52
 
  uInt32 i;
53
 
 
54
51
  // Allocate buffers for two frame buffers
55
 
  myCurrentFrameBuffer = new uInt8[160 * 300];
56
 
  myPreviousFrameBuffer = new uInt8[160 * 300];
57
 
 
58
 
  myFrameGreyed = false;
59
 
  myAutoFrameEnabled = false;
60
 
 
61
 
  for(i = 0; i < 6; ++i)
62
 
    myBitEnabled[i] = true;
 
52
  myCurrentFrameBuffer = new uInt8[160 * 320];
 
53
  myPreviousFrameBuffer = new uInt8[160 * 320];
 
54
 
 
55
  // Make sure all TIA bits are enabled
 
56
  enableBits(true);
63
57
 
64
58
  for(uInt16 x = 0; x < 2; ++x)
65
59
  {
67
61
    {
68
62
      if(enabled & PriorityBit)
69
63
      {
70
 
        uInt8 color = 0;
 
64
        // Priority from highest to lowest:
 
65
        //   PF/BL => P0/M0 => P1/M1 => BK
 
66
        uInt8 color = _BK;
71
67
 
72
 
        if((enabled & (myP1Bit | myM1Bit)) != 0)
73
 
          color = 3;
74
 
        if((enabled & (myP0Bit | myM0Bit)) != 0)
75
 
          color = 2;
76
 
        if((enabled & myBLBit) != 0)
77
 
          color = 1;
78
 
        if((enabled & myPFBit) != 0)
79
 
          color = 1;  // NOTE: Playfield has priority so ScoreBit isn't used
 
68
        if((enabled & M1Bit) != 0)
 
69
          color = _M1;
 
70
        if((enabled & P1Bit) != 0)
 
71
          color = _P1;
 
72
        if((enabled & M0Bit) != 0)
 
73
          color = _M0;
 
74
        if((enabled & P0Bit) != 0)
 
75
          color = _P0;
 
76
        if((enabled & BLBit) != 0)
 
77
          color = _BL;
 
78
        if((enabled & PFBit) != 0)
 
79
          color = _PF;  // NOTE: Playfield has priority so ScoreBit isn't used
80
80
 
81
81
        myPriorityEncoder[x][enabled] = color;
82
82
      }
83
83
      else
84
84
      {
85
 
        uInt8 color = 0;
 
85
        // Priority from highest to lowest:
 
86
        //   P0/M0 => P1/M1 => PF/BL => BK
 
87
        uInt8 color = _BK;
86
88
 
87
 
        if((enabled & myBLBit) != 0)
88
 
          color = 1;
89
 
        if((enabled & myPFBit) != 0)
90
 
          color = (enabled & ScoreBit) ? ((x == 0) ? 2 : 3) : 1;
91
 
        if((enabled & (myP1Bit | myM1Bit)) != 0)
92
 
          color = (color != 2) ? 3 : 2;
93
 
        if((enabled & (myP0Bit | myM0Bit)) != 0)
94
 
          color = 2;
 
89
        if((enabled & BLBit) != 0)
 
90
          color = _BL;
 
91
        if((enabled & PFBit) != 0)
 
92
          color = (enabled & ScoreBit) ? ((x == 0) ? _P0 : _P1) : _PF;
 
93
        if((enabled & M1Bit) != 0)
 
94
          color = _M1;
 
95
        if((enabled & P1Bit) != 0)
 
96
          color = _P1;
 
97
        if((enabled & M0Bit) != 0)
 
98
          color = _M0;
 
99
        if((enabled & P0Bit) != 0)
 
100
          color = _P0;
95
101
 
96
102
        myPriorityEncoder[x][enabled] = color;
97
103
      }
98
104
    }
99
105
  }
100
106
 
101
 
  for(i = 0; i < 640; ++i)
102
 
    ourDisabledMaskTable[i] = 0;
103
 
 
104
107
  // Compute all of the mask tables
105
 
  computeBallMaskTable();
106
 
  computeCollisionTable();
107
 
  computeMissleMaskTable();
108
 
  computePlayerMaskTable();
109
 
  computePlayerPositionResetWhenTable();
110
 
  computePlayerReflectTable();
111
 
  computePlayfieldMaskTable();
112
 
 
113
 
  // Init stats counters
114
 
  myFrameCounter = 0;
115
 
 
 
108
  TIATables::computeAllTables();
 
109
 
 
110
  // Zero audio registers
116
111
  myAUDV0 = myAUDV1 = myAUDF0 = myAUDF1 = myAUDC0 = myAUDC1 = 0;
117
112
}
118
113
 
127
122
void TIA::reset()
128
123
{
129
124
  // Reset the sound device
130
 
  mySound->reset();
 
125
  mySound.reset();
131
126
 
132
 
  // Currently no objects are enabled
 
127
  // Currently no objects are enabled or selectively disabled
133
128
  myEnabledObjects = 0;
 
129
  myDisabledObjects = 0xFF;
 
130
  myAllowHMOVEBlanks = true;
134
131
 
135
132
  // Some default values for the registers
136
 
  myVSYNC = 0;
137
 
  myVBLANK = 0;
138
 
  myNUSIZ0 = 0;
139
 
  myNUSIZ1 = 0;
140
 
  myCOLUP0 = 0;
141
 
  myCOLUP1 = 0;
142
 
  myCOLUPF = 0;
 
133
  myColorPtr = myColor;
 
134
  myVSYNC = myVBLANK = 0;
 
135
  myNUSIZ0 = myNUSIZ1 = 0;
 
136
  myColor[_P0] = myColor[_P1] = myColor[_PF] = myColor[_BK] = 0;
 
137
  myColor[_M0] = myColor[_M1] = myColor[_BL] = myColor[_HBLANK] = 0;
 
138
 
143
139
  myPlayfieldPriorityAndScore = 0;
144
 
  myCOLUBK = 0;
145
140
  myCTRLPF = 0;
146
 
  myREFP0 = false;
147
 
  myREFP1 = false;
 
141
  myREFP0 = myREFP1 = false;
148
142
  myPF = 0;
149
 
  myGRP0 = 0;
150
 
  myGRP1 = 0;
151
 
  myDGRP0 = 0;
152
 
  myDGRP1 = 0;
153
 
  myENAM0 = false;
154
 
  myENAM1 = false;
155
 
  myENABL = false;
156
 
  myDENABL = false;
157
 
  myHMP0 = 0;
158
 
  myHMP1 = 0;
159
 
  myHMM0 = 0;
160
 
  myHMM1 = 0;
161
 
  myHMBL = 0;
162
 
  myVDELP0 = false;
163
 
  myVDELP1 = false;
164
 
  myVDELBL = false;
165
 
  myRESMP0 = false;
166
 
  myRESMP1 = false;
 
143
  myGRP0 = myGRP1 = myDGRP0 = myDGRP1 = 0;
 
144
  myENAM0 = myENAM1 = myENABL = myDENABL = false;
 
145
  myHMP0 = myHMP1 = myHMM0 = myHMM1 = myHMBL = 0;
 
146
  myVDELP0 = myVDELP1 = myVDELBL = myRESMP0 = myRESMP1 = false;
167
147
  myCollision = 0;
168
 
  myPOSP0 = 0;
169
 
  myPOSP1 = 0;
170
 
  myPOSM0 = 0;
171
 
  myPOSM1 = 0;
172
 
  myPOSBL = 0;
 
148
  myCollisionEnabledMask = 0xFFFFFFFF;
 
149
  myPOSP0 = myPOSP1 = myPOSM0 = myPOSM1 = myPOSBL = 0;
173
150
 
174
151
  // Some default values for the "current" variables
175
152
  myCurrentGRP0 = 0;
176
153
  myCurrentGRP1 = 0;
177
 
  myCurrentBLMask = ourBallMaskTable[0][0];
178
 
  myCurrentM0Mask = ourMissleMaskTable[0][0][0];
179
 
  myCurrentM1Mask = ourMissleMaskTable[0][0][0];
180
 
  myCurrentP0Mask = ourPlayerMaskTable[0][0][0];
181
 
  myCurrentP1Mask = ourPlayerMaskTable[0][0][0];
182
 
  myCurrentPFMask = ourPlayfieldTable[0];
183
 
 
184
 
  myLastHMOVEClock = 0;
 
154
 
 
155
  myMotionClockP0 = 0;
 
156
  myMotionClockP1 = 0;
 
157
  myMotionClockM0 = 0;
 
158
  myMotionClockM1 = 0;
 
159
  myMotionClockBL = 0;
 
160
 
 
161
  mySuppressP0 = mySuppressP1 = 0;
 
162
 
 
163
  myHMP0mmr = myHMP1mmr = myHMM0mmr = myHMM1mmr = myHMBLmmr = false;
 
164
 
 
165
  myCurrentHMOVEPos = myPreviousHMOVEPos = 0x7FFFFFFF;
185
166
  myHMOVEBlankEnabled = false;
186
 
  myM0CosmicArkMotionEnabled = false;
187
 
  myM0CosmicArkCounter = 0;
188
167
 
189
168
  enableBits(true);
190
169
 
191
170
  myDumpEnabled = false;
192
171
  myDumpDisabledCycle = 0;
193
172
 
194
 
  myAllowHMOVEBlanks = 
195
 
      (myConsole.properties().get(Emulation_HmoveBlanks) == "YES");
196
 
 
197
 
  myFloatTIAOutputPins = mySettings.getBool("tiafloat");
198
 
 
 
173
  // Should undriven pins be randomly driven high or low?
 
174
  myTIAPinsDriven = mySettings.getBool("tiadriven");
 
175
 
 
176
  myFrameCounter = 0;
 
177
  myScanlineCountForLastFrame = 0;
199
178
  myAutoFrameEnabled = (mySettings.getInt("framerate") <= 0);
 
179
  myFramerate = myConsole.getFramerate();
200
180
 
201
 
  if(myConsole.getFramerate() > 55.0)  // NTSC
 
181
  if(myFramerate > 55.0)  // NTSC
202
182
  {
 
183
    myFixedColor[_P0] = 0x30303030;
 
184
    myFixedColor[_P1] = 0x16161616;
 
185
    myFixedColor[_M0] = 0x38383838;
 
186
    myFixedColor[_M1] = 0x12121212;
 
187
    myFixedColor[_BL] = 0x7e7e7e7e;
 
188
    myFixedColor[_PF] = 0x76767676;
 
189
    myFixedColor[_BK] = 0x0a0a0a0a;
 
190
    myFixedColor[_HBLANK] = 0x0e0e0e0e;
203
191
    myColorLossEnabled = false;
204
192
    myMaximumNumberOfScanlines = 290;
205
193
  }
206
194
  else
207
195
  {
208
 
    myColorLossEnabled = true;
 
196
    myFixedColor[_P0] = 0x62626262;
 
197
    myFixedColor[_P1] = 0x26262626;
 
198
    myFixedColor[_M0] = 0x68686868;
 
199
    myFixedColor[_M1] = 0x2e2e2e2e;
 
200
    myFixedColor[_BL] = 0xdededede;
 
201
    myFixedColor[_PF] = 0xd8d8d8d8;
 
202
    myFixedColor[_BK] = 0x1c1c1c1c;
 
203
    myFixedColor[_HBLANK] = 0x0e0e0e0e;
 
204
    myColorLossEnabled = mySettings.getBool("colorloss");
209
205
    myMaximumNumberOfScanlines = 342;
210
206
  }
211
207
 
212
 
  myFrameCounter = 0;
213
 
 
214
208
  // Recalculate the size of the display
215
209
  frameReset();
216
210
}
224
218
  // Reset pixel pointer and drawing flag
225
219
  myFramePointer = myCurrentFrameBuffer;
226
220
 
227
 
  myYStart = atoi(myConsole.properties().get(Display_YStart).c_str());
228
 
  myHeight = atoi(myConsole.properties().get(Display_Height).c_str());
229
 
 
230
 
  // Calculate color clock offsets for starting and stoping frame drawing
231
 
  myStartDisplayOffset = 228 * myYStart;
232
 
  myStopDisplayOffset = myStartDisplayOffset + 228 * myHeight;
 
221
  // Make sure all these are within bounds
 
222
  myFrameWidth  = 160;
 
223
  myFrameYStart = atoi(myConsole.properties().get(Display_YStart).c_str());
 
224
  if(myFrameYStart < 0)       myFrameYStart = 0;
 
225
  else if(myFrameYStart > 64) myFrameYStart = 64;
 
226
  myFrameHeight = atoi(myConsole.properties().get(Display_Height).c_str());
 
227
  if(myFrameHeight < 210)      myFrameHeight = 210;
 
228
  else if(myFrameHeight > 256) myFrameHeight = 256;
 
229
 
 
230
  // Calculate color clock offsets for starting and stopping frame drawing
 
231
  // Note that although we always start drawing at scanline zero, the
 
232
  // framebuffer that is exposed outside the class actually starts at 'ystart'
 
233
  myFramePointerOffset = myFrameWidth * myFrameYStart;
 
234
  myStartDisplayOffset = 0;
 
235
 
 
236
  // NTSC screens will process at least 262 scanlines,
 
237
  // while PAL will have at least 312
 
238
  // In any event, at most 320 lines can be processed
 
239
  uInt32 scanlines = myFrameYStart + myFrameHeight;
 
240
  if(myMaximumNumberOfScanlines == 290)
 
241
    scanlines = BSPF_max(scanlines, 262u);  // NTSC
 
242
  else
 
243
    scanlines = BSPF_max(scanlines, 312u);  // PAL
 
244
  myStopDisplayOffset = 228 * BSPF_min(scanlines, 320u);
233
245
 
234
246
  // Reasonable values to start and stop the current frame drawing
235
247
  myClockWhenFrameStarted = mySystem->cycles() * 3;
238
250
  myClockAtLastUpdate = myClockWhenFrameStarted;
239
251
  myClocksToEndOfScanLine = 228;
240
252
  myVSYNCFinishClock = 0x7FFFFFFF;
241
 
  myScanlineCountForLastFrame = 0;
242
 
  myCurrentScanline = 0;
243
 
 
244
 
  myFrameXStart = 0;    // Hardcoded in preparation for new TIA class
245
 
  myFrameWidth  = 160;  // Hardcoded in preparation for new TIA class
246
 
  myFrameYStart = atoi(myConsole.properties().get(Display_YStart).c_str());
247
 
  myFrameHeight = atoi(myConsole.properties().get(Display_Height).c_str());
248
 
 
249
 
  // Make sure the height value is reasonable, because we need a certain
250
 
  // minimum amount of space for the onscreen GUI
251
 
  if(myFrameHeight < 210)
252
 
  {
253
 
    // Values are illegal so reset to default values
254
 
    myFrameHeight = 210;
255
 
  }
256
253
}
257
254
 
258
255
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
262
259
  uInt32 cycles = mySystem->cycles();
263
260
 
264
261
  // Adjust the sound cycle indicator
265
 
  mySound->adjustCycleCounter(-1 * cycles);
 
262
  mySound.adjustCycleCounter(-1 * cycles);
266
263
 
267
264
  // Adjust the dump cycle
268
265
  myDumpDisabledCycle -= cycles;
276
273
  myClockStopDisplay -= clocks;
277
274
  myClockAtLastUpdate -= clocks;
278
275
  myVSYNCFinishClock -= clocks;
279
 
  myLastHMOVEClock -= clocks;
280
276
}
281
277
 
282
278
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
299
295
  access.directPeekBase = 0;
300
296
  access.directPokeBase = 0;
301
297
  access.device = &device;
 
298
  access.type = System::PA_READWRITE;
302
299
 
303
300
  // We're installing in a 2600 system
304
301
  for(uInt32 i = 0; i < 8192; i += (1 << shift))
305
 
  {
306
302
    if((i & 0x1080) == 0x0000)
307
 
    {
308
303
      mySystem->setPageAccess(i >> shift, access);
309
 
    }
310
 
  }
311
 
 
312
304
}
313
305
 
314
306
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
315
307
bool TIA::save(Serializer& out) const
316
308
{
317
 
  string device = name();
 
309
  const string& device = name();
318
310
 
319
311
  try
320
312
  {
326
318
    out.putInt(myClockAtLastUpdate);
327
319
    out.putInt(myClocksToEndOfScanLine);
328
320
    out.putInt(myScanlineCountForLastFrame);
329
 
    out.putInt(myCurrentScanline);
330
321
    out.putInt(myVSYNCFinishClock);
331
322
 
332
323
    out.putByte((char)myEnabledObjects);
 
324
    out.putByte((char)myDisabledObjects);
333
325
 
334
326
    out.putByte((char)myVSYNC);
335
327
    out.putByte((char)myVBLANK);
336
328
    out.putByte((char)myNUSIZ0);
337
329
    out.putByte((char)myNUSIZ1);
338
330
 
339
 
    out.putInt(myCOLUP0);
340
 
    out.putInt(myCOLUP1);
341
 
    out.putInt(myCOLUPF);
342
 
    out.putInt(myCOLUBK);
 
331
    out.putInt(myColor[_P0]);
 
332
    out.putInt(myColor[_P1]);
 
333
    out.putInt(myColor[_PF]);
 
334
    out.putInt(myColor[_BK]);
 
335
    out.putInt(myColor[_M0]);
 
336
    out.putInt(myColor[_M1]);
 
337
    out.putInt(myColor[_BL]);
343
338
 
344
339
    out.putByte((char)myCTRLPF);
345
340
    out.putByte((char)myPlayfieldPriorityAndScore);
365
360
    out.putBool(myRESMP0);
366
361
    out.putBool(myRESMP1);
367
362
    out.putInt(myCollision);
 
363
    out.putInt(myCollisionEnabledMask);
 
364
    out.putByte((char)myCurrentGRP0);
 
365
    out.putByte((char)myCurrentGRP1);
 
366
 
 
367
    out.putBool(myDumpEnabled);
 
368
    out.putInt(myDumpDisabledCycle);
 
369
 
368
370
    out.putInt(myPOSP0);
369
371
    out.putInt(myPOSP1);
370
372
    out.putInt(myPOSM0);
371
373
    out.putInt(myPOSM1);
372
374
    out.putInt(myPOSBL);
373
375
 
374
 
    out.putByte((char)myCurrentGRP0);
375
 
    out.putByte((char)myCurrentGRP1);
376
 
 
377
 
// pointers
378
 
//  myCurrentBLMask = ourBallMaskTable[0][0];
379
 
//  myCurrentM0Mask = ourMissleMaskTable[0][0][0];
380
 
//  myCurrentM1Mask = ourMissleMaskTable[0][0][0];
381
 
//  myCurrentP0Mask = ourPlayerMaskTable[0][0][0];
382
 
//  myCurrentP1Mask = ourPlayerMaskTable[0][0][0];
383
 
//  myCurrentPFMask = ourPlayfieldTable[0];
384
 
 
385
 
    out.putInt(myLastHMOVEClock);
 
376
    out.putInt(myMotionClockP0);
 
377
    out.putInt(myMotionClockP1);
 
378
    out.putInt(myMotionClockM0);
 
379
    out.putInt(myMotionClockM1);
 
380
    out.putInt(myMotionClockBL);
 
381
 
 
382
    out.putInt(myStartP0);
 
383
    out.putInt(myStartP1);
 
384
    out.putInt(myStartM0);
 
385
    out.putInt(myStartM1);
 
386
 
 
387
    out.putByte(mySuppressP0);
 
388
    out.putByte(mySuppressP1);
 
389
 
 
390
    out.putBool(myHMP0mmr);
 
391
    out.putBool(myHMP1mmr);
 
392
    out.putBool(myHMM0mmr);
 
393
    out.putBool(myHMM1mmr);
 
394
    out.putBool(myHMBLmmr);
 
395
 
 
396
    out.putInt(myCurrentHMOVEPos);
 
397
    out.putInt(myPreviousHMOVEPos);
386
398
    out.putBool(myHMOVEBlankEnabled);
387
 
    out.putBool(myM0CosmicArkMotionEnabled);
388
 
    out.putInt(myM0CosmicArkCounter);
389
399
 
390
 
    out.putBool(myDumpEnabled);
391
 
    out.putInt(myDumpDisabledCycle);
 
400
    out.putInt(myFrameCounter);
392
401
 
393
402
    // Save the sound sample stuff ...
394
 
    mySound->save(out);
395
 
  }
396
 
  catch(char *msg)
397
 
  {
398
 
    cerr << msg << endl;
399
 
    return false;
400
 
  }
401
 
  catch(...)
402
 
  {
403
 
    cerr << "Unknown error in save state for " << device << endl;
 
403
    mySound.save(out);
 
404
  }
 
405
  catch(const char* msg)
 
406
  {
 
407
    cerr << "ERROR: TIA::save" << endl << "  " << msg << endl;
404
408
    return false;
405
409
  }
406
410
 
408
412
}
409
413
 
410
414
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
411
 
bool TIA::load(Deserializer& in)
 
415
bool TIA::load(Serializer& in)
412
416
{
413
 
  string device = name();
 
417
  const string& device = name();
414
418
 
415
419
  try
416
420
  {
422
426
    myClockStopDisplay = (Int32) in.getInt();
423
427
    myClockAtLastUpdate = (Int32) in.getInt();
424
428
    myClocksToEndOfScanLine = (Int32) in.getInt();
425
 
    myScanlineCountForLastFrame = (Int32) in.getInt();
426
 
    myCurrentScanline = (Int32) in.getInt();
 
429
    myScanlineCountForLastFrame = (uInt32) in.getInt();
427
430
    myVSYNCFinishClock = (Int32) in.getInt();
428
431
 
429
432
    myEnabledObjects = (uInt8) in.getByte();
 
433
    myDisabledObjects = (uInt8) in.getByte();
430
434
 
431
435
    myVSYNC = (uInt8) in.getByte();
432
436
    myVBLANK = (uInt8) in.getByte();
433
437
    myNUSIZ0 = (uInt8) in.getByte();
434
438
    myNUSIZ1 = (uInt8) in.getByte();
435
439
 
436
 
    myCOLUP0 = (uInt32) in.getInt();
437
 
    myCOLUP1 = (uInt32) in.getInt();
438
 
    myCOLUPF = (uInt32) in.getInt();
439
 
    myCOLUBK = (uInt32) in.getInt();
 
440
    myColor[_P0] = (uInt32) in.getInt();
 
441
    myColor[_P1] = (uInt32) in.getInt();
 
442
    myColor[_PF] = (uInt32) in.getInt();
 
443
    myColor[_BK] = (uInt32) in.getInt();
 
444
    myColor[_M0] = (uInt32) in.getInt();
 
445
    myColor[_M1] = (uInt32) in.getInt();
 
446
    myColor[_BL] = (uInt32) in.getInt();
440
447
 
441
448
    myCTRLPF = (uInt8) in.getByte();
442
449
    myPlayfieldPriorityAndScore = (uInt8) in.getByte();
451
458
    myENAM1 = in.getBool();
452
459
    myENABL = in.getBool();
453
460
    myDENABL = in.getBool();
454
 
    myHMP0 = (Int8) in.getByte();
455
 
    myHMP1 = (Int8) in.getByte();
456
 
    myHMM0 = (Int8) in.getByte();
457
 
    myHMM1 = (Int8) in.getByte();
458
 
    myHMBL = (Int8) in.getByte();
 
461
    myHMP0 = (uInt8) in.getByte();
 
462
    myHMP1 = (uInt8) in.getByte();
 
463
    myHMM0 = (uInt8) in.getByte();
 
464
    myHMM1 = (uInt8) in.getByte();
 
465
    myHMBL = (uInt8) in.getByte();
459
466
    myVDELP0 = in.getBool();
460
467
    myVDELP1 = in.getBool();
461
468
    myVDELBL = in.getBool();
462
469
    myRESMP0 = in.getBool();
463
470
    myRESMP1 = in.getBool();
464
471
    myCollision = (uInt16) in.getInt();
 
472
    myCollisionEnabledMask = in.getInt();
 
473
    myCurrentGRP0 = (uInt8) in.getByte();
 
474
    myCurrentGRP1 = (uInt8) in.getByte();
 
475
 
 
476
    myDumpEnabled = in.getBool();
 
477
    myDumpDisabledCycle = (Int32) in.getInt();
 
478
 
465
479
    myPOSP0 = (Int16) in.getInt();
466
480
    myPOSP1 = (Int16) in.getInt();
467
481
    myPOSM0 = (Int16) in.getInt();
468
482
    myPOSM1 = (Int16) in.getInt();
469
483
    myPOSBL = (Int16) in.getInt();
470
484
 
471
 
    myCurrentGRP0 = (uInt8) in.getByte();
472
 
    myCurrentGRP1 = (uInt8) in.getByte();
473
 
 
474
 
// pointers
475
 
//  myCurrentBLMask = ourBallMaskTable[0][0];
476
 
//  myCurrentM0Mask = ourMissleMaskTable[0][0][0];
477
 
//  myCurrentM1Mask = ourMissleMaskTable[0][0][0];
478
 
//  myCurrentP0Mask = ourPlayerMaskTable[0][0][0];
479
 
//  myCurrentP1Mask = ourPlayerMaskTable[0][0][0];
480
 
//  myCurrentPFMask = ourPlayfieldTable[0];
481
 
 
482
 
    myLastHMOVEClock = (Int32) in.getInt();
 
485
    myMotionClockP0 = (Int32) in.getInt();
 
486
    myMotionClockP1 = (Int32) in.getInt();
 
487
    myMotionClockM0 = (Int32) in.getInt();
 
488
    myMotionClockM1 = (Int32) in.getInt();
 
489
    myMotionClockBL = (Int32) in.getInt();
 
490
 
 
491
    myStartP0 = (Int32) in.getInt();
 
492
    myStartP1 = (Int32) in.getInt();
 
493
    myStartM0 = (Int32) in.getInt();
 
494
    myStartM1 = (Int32) in.getInt();
 
495
 
 
496
    mySuppressP0 = (uInt8) in.getByte();
 
497
    mySuppressP1 = (uInt8) in.getByte();
 
498
 
 
499
    myHMP0mmr = in.getBool();
 
500
    myHMP1mmr = in.getBool();
 
501
    myHMM0mmr = in.getBool();
 
502
    myHMM1mmr = in.getBool();
 
503
    myHMBLmmr = in.getBool();
 
504
 
 
505
    myCurrentHMOVEPos = (Int32) in.getInt();
 
506
    myPreviousHMOVEPos = (Int32) in.getInt();
483
507
    myHMOVEBlankEnabled = in.getBool();
484
 
    myM0CosmicArkMotionEnabled = in.getBool();
485
 
    myM0CosmicArkCounter = (uInt32) in.getInt();
486
508
 
487
 
    myDumpEnabled = in.getBool();
488
 
    myDumpDisabledCycle = (Int32) in.getInt();
 
509
    myFrameCounter = (Int32) in.getInt();
489
510
 
490
511
    // Load the sound sample stuff ...
491
 
    mySound->load(in);
 
512
    mySound.load(in);
492
513
 
493
514
    // Reset TIA bits to be on
494
515
    enableBits(true);
495
 
  }
496
 
  catch(char *msg)
497
 
  {
498
 
    cerr << msg << endl;
499
 
    return false;
500
 
  }
501
 
  catch(...)
502
 
  {
503
 
    cerr << "Unknown error in load state for " << device << endl;
 
516
    myAllowHMOVEBlanks = true;
 
517
    myColorPtr = myColor;
 
518
  }
 
519
  catch(const char* msg)
 
520
  {
 
521
    cerr << "ERROR: TIA::load" << endl << "  " << msg << endl;
 
522
    return false;
 
523
  }
 
524
 
 
525
  return true;
 
526
}
 
527
 
 
528
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
529
bool TIA::saveDisplay(Serializer& out) const
 
530
{
 
531
  try
 
532
  {
 
533
    out.putBool(myPartialFrameFlag);
 
534
    out.putInt(myFramePointerClocks);
 
535
 
 
536
    for(int i = 0; i < 160*320; ++i)
 
537
      out.putByte(myCurrentFrameBuffer[i]);
 
538
  }
 
539
  catch(const char* msg)
 
540
  {
 
541
    cerr << "ERROR: TIA::saveDisplay" << endl << "  " << msg << endl;
 
542
    return false;
 
543
  }
 
544
 
 
545
  return true;
 
546
}
 
547
 
 
548
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
549
bool TIA::loadDisplay(Serializer& in)
 
550
{
 
551
  try
 
552
  {
 
553
    myPartialFrameFlag = in.getBool();
 
554
    myFramePointerClocks = (uInt32) in.getInt();
 
555
 
 
556
    // Reset frame buffer pointer and data
 
557
    clearBuffers();
 
558
    myFramePointer = myCurrentFrameBuffer;
 
559
    for(int i = 0; i < 160*320; ++i)
 
560
      myCurrentFrameBuffer[i] = myPreviousFrameBuffer[i] = (uInt8) in.getByte();
 
561
 
 
562
    // If we're in partial frame mode, make sure to re-create the screen
 
563
    // as it existed when the state was saved
 
564
    if(myPartialFrameFlag)
 
565
      myFramePointer += myFramePointerClocks;
 
566
  }
 
567
  catch(const char* msg)
 
568
  {
 
569
    cerr << "ERROR: TIA::loadDisplay" << endl << "  " << msg << endl;
504
570
    return false;
505
571
  }
506
572
 
524
590
 
525
591
  // TODO: have code here that handles errors....
526
592
 
527
 
  uInt32 totalClocks = (mySystem->cycles() * 3) - myClockWhenFrameStarted;
528
 
  myCurrentScanline = totalClocks / 228;
529
 
 
530
 
  if(myPartialFrameFlag) {
531
 
    // grey out old frame contents
532
 
    if(!myFrameGreyed) greyOutFrame();
533
 
    myFrameGreyed = true;
534
 
  } else {
535
 
    endFrame();
536
 
  }
 
593
  endFrame();
537
594
}
538
595
 
539
596
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
562
619
 
563
620
  // Reset frame buffer pointer
564
621
  myFramePointer = myCurrentFrameBuffer;
 
622
  myFramePointerClocks = 0;
565
623
 
566
624
  // If color loss is enabled then update the color registers based on
567
625
  // the number of scanlines in the last frame that was generated
569
627
  {
570
628
    if(myScanlineCountForLastFrame & 0x01)
571
629
    {
572
 
      myCOLUP0 |= 0x01010101;
573
 
      myCOLUP1 |= 0x01010101;
574
 
      myCOLUPF |= 0x01010101;
575
 
      myCOLUBK |= 0x01010101;
 
630
      myColor[_P0] |= 0x01010101;
 
631
      myColor[_P1] |= 0x01010101;
 
632
      myColor[_PF] |= 0x01010101;
 
633
      myColor[_BK] |= 0x01010101;
 
634
      myColor[_M0] |= 0x01010101;
 
635
      myColor[_M1] |= 0x01010101;
 
636
      myColor[_BL] |= 0x01010101;
576
637
    }
577
638
    else
578
639
    {
579
 
      myCOLUP0 &= 0xfefefefe;
580
 
      myCOLUP1 &= 0xfefefefe;
581
 
      myCOLUPF &= 0xfefefefe;
582
 
      myCOLUBK &= 0xfefefefe;
 
640
      myColor[_P0] &= 0xfefefefe;
 
641
      myColor[_P1] &= 0xfefefefe;
 
642
      myColor[_PF] &= 0xfefefefe;
 
643
      myColor[_BK] &= 0xfefefefe;
 
644
      myColor[_M0] &= 0xfefefefe;
 
645
      myColor[_M1] &= 0xfefefefe;
 
646
      myColor[_BL] &= 0xfefefefe;
583
647
    }
584
648
  }   
585
 
 
586
 
  myFrameGreyed = false;
 
649
  myStartScanline = 0x7FFFFFFF;
587
650
}
588
651
 
589
652
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
591
654
{
592
655
  // This stuff should only happen at the end of a frame
593
656
  // Compute the number of scanlines in the frame
594
 
  myScanlineCountForLastFrame = myCurrentScanline;
 
657
  uInt32 previousCount = myScanlineCountForLastFrame;
 
658
  myScanlineCountForLastFrame = scanlines();
595
659
 
596
660
  // Stats counters
597
661
  myFrameCounter++;
598
662
 
599
663
  // Recalculate framerate. attempting to auto-correct for scanline 'jumps'
600
 
  if(myFrameCounter % 32 == 0 && myAutoFrameEnabled)
601
 
  {
602
 
    float framerate =
603
 
      (myScanlineCountForLastFrame > 285 ? 15600.0 : 15720.0) /
604
 
       myScanlineCountForLastFrame;
605
 
    myConsole.setFramerate(framerate);
606
 
  }
607
 
 
608
 
  myFrameGreyed = false;
 
664
  if(myFrameCounter % 8 == 0 && myAutoFrameEnabled &&
 
665
     myScanlineCountForLastFrame < myMaximumNumberOfScanlines)
 
666
  {
 
667
    myFramerate = (myScanlineCountForLastFrame > 285 ? 15600.0 : 15720.0) /
 
668
                   myScanlineCountForLastFrame;
 
669
    myConsole.setFramerate(myFramerate);
 
670
 
 
671
    // Adjust end-of-frame pointer
 
672
    // We always accommodate the highest # of scanlines, up to the maximum
 
673
    // size of the buffer (currently, 320 lines)
 
674
    uInt32 offset = 228 * myScanlineCountForLastFrame;
 
675
    if(offset > myStopDisplayOffset && offset < 228 * 320)
 
676
      myStopDisplayOffset = offset;
 
677
  }
 
678
 
 
679
  // This is a bit of a hack for those ROMs which generate too many
 
680
  // scanlines each frame, usually caused by VBLANK taking too long
 
681
  // When this happens, the frame pointers sometimes get 'confused',
 
682
  // and the framebuffer class doesn't properly overwrite data from
 
683
  // the previous frame, causing graphical garbage
 
684
  //
 
685
  // We basically erase the entire contents of both buffers, making
 
686
  // sure that they're also different from one another
 
687
  // This will force the framebuffer class to completely re-render
 
688
  // the screen
 
689
  if(previousCount > myMaximumNumberOfScanlines &&
 
690
     myScanlineCountForLastFrame <= myMaximumNumberOfScanlines)
 
691
  {
 
692
    memset(myCurrentFrameBuffer, 0, 160 * 320);
 
693
    memset(myPreviousFrameBuffer, 1, 160 * 320);
 
694
  }
 
695
}
 
696
 
 
697
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
698
bool TIA::scanlinePos(uInt16& x, uInt16& y) const
 
699
{
 
700
  if(myPartialFrameFlag)
 
701
  {
 
702
    // We only care about the scanline position when it's in the viewable area
 
703
    if(myFramePointerClocks >= myFramePointerOffset)
 
704
    {
 
705
      x = (myFramePointerClocks - myFramePointerOffset) % 160;
 
706
      y = (myFramePointerClocks - myFramePointerOffset) / 160;
 
707
      return true;
 
708
    }
 
709
    else
 
710
    {
 
711
      x = 0;
 
712
      y = 0;
 
713
      return false;
 
714
    }
 
715
  }
 
716
  else
 
717
  {
 
718
    x = width();
 
719
    y = height();
 
720
    return false;
 
721
  }
 
722
}
 
723
 
 
724
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
725
void TIA::enableBits(bool mode)
 
726
{
 
727
  toggleBit(P0Bit, mode ? 1 : 0);
 
728
  toggleBit(P1Bit, mode ? 1 : 0);
 
729
  toggleBit(M0Bit, mode ? 1 : 0);
 
730
  toggleBit(M1Bit, mode ? 1 : 0);
 
731
  toggleBit(BLBit, mode ? 1 : 0);
 
732
  toggleBit(PFBit, mode ? 1 : 0);
 
733
}
 
734
 
 
735
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
736
bool TIA::toggleBit(TIABit b, uInt8 mode)
 
737
{
 
738
  // If mode is 0 or 1, use it as a boolean (off or on)
 
739
  // Otherwise, flip the state
 
740
  bool on = (mode == 0 || mode == 1) ? bool(mode) : !(myDisabledObjects & b);
 
741
  if(on)  myDisabledObjects |= b;
 
742
  else    myDisabledObjects &= ~b;
 
743
 
 
744
  return on;
 
745
}
 
746
 
 
747
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
748
void TIA::enableCollisions(bool mode)
 
749
{
 
750
  toggleCollision(P0Bit, mode ? 1 : 0);
 
751
  toggleCollision(P1Bit, mode ? 1 : 0);
 
752
  toggleCollision(M0Bit, mode ? 1 : 0);
 
753
  toggleCollision(M1Bit, mode ? 1 : 0);
 
754
  toggleCollision(BLBit, mode ? 1 : 0);
 
755
  toggleCollision(PFBit, mode ? 1 : 0);
 
756
}
 
757
 
 
758
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
759
bool TIA::toggleCollision(TIABit b, uInt8 mode)
 
760
{
 
761
  uInt16 enabled = myCollisionEnabledMask >> 16;
 
762
 
 
763
  // If mode is 0 or 1, use it as a boolean (off or on)
 
764
  // Otherwise, flip the state
 
765
  bool on = (mode == 0 || mode == 1) ? bool(mode) : !(enabled & b);
 
766
  if(on)  enabled |= b;
 
767
  else    enabled &= ~b;
 
768
 
 
769
  // Assume all collisions are on, then selectively turn the desired ones off
 
770
  uInt16 mask = 0xffff;
 
771
  if(!(enabled & P0Bit))
 
772
    mask &= ~(Cx_M0P0 | Cx_M1P0 | Cx_P0PF | Cx_P0BL | Cx_P0P1);
 
773
  if(!(enabled & P1Bit))
 
774
    mask &= ~(Cx_M0P1 | Cx_M1P1 | Cx_P1PF | Cx_P1BL | Cx_P0P1);
 
775
  if(!(enabled & M0Bit))
 
776
    mask &= ~(Cx_M0P0 | Cx_M0P1 | Cx_M0PF | Cx_M0BL | Cx_M0M1);
 
777
  if(!(enabled & M1Bit))
 
778
    mask &= ~(Cx_M1P0 | Cx_M1P1 | Cx_M1PF | Cx_M1BL | Cx_M0M1);
 
779
  if(!(enabled & BLBit))
 
780
    mask &= ~(Cx_P0BL | Cx_P1BL | Cx_M0BL | Cx_M1BL | Cx_BLPF);
 
781
  if(!(enabled & PFBit))
 
782
    mask &= ~(Cx_P0PF | Cx_P1PF | Cx_M0PF | Cx_M1PF | Cx_BLPF);
 
783
 
 
784
  // Now combine the masks
 
785
  myCollisionEnabledMask = (enabled << 16) | mask;
 
786
 
 
787
  return on;
 
788
}
 
789
 
 
790
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
791
bool TIA::toggleHMOVEBlank()
 
792
{
 
793
  myAllowHMOVEBlanks = myAllowHMOVEBlanks ? false : true;
 
794
  return myAllowHMOVEBlanks;
 
795
}
 
796
 
 
797
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
798
bool TIA::toggleFixedColors(uInt8 mode)
 
799
{
 
800
  // If mode is 0 or 1, use it as a boolean (off or on)
 
801
  // Otherwise, flip the state
 
802
  bool on = (mode == 0 || mode == 1) ? bool(mode) :
 
803
            (myColorPtr == myColor ? true : false);
 
804
  if(on)  myColorPtr = myFixedColor;
 
805
  else    myColorPtr = myColor;
 
806
 
 
807
  return on;
609
808
}
610
809
 
611
810
#ifdef DEBUGGER_SUPPORT
613
812
void TIA::updateScanline()
614
813
{
615
814
  // Start a new frame if the old one was finished
616
 
  if(!myPartialFrameFlag) {
 
815
  if(!myPartialFrameFlag)
617
816
    startFrame();
618
 
  }
619
 
 
620
 
  // grey out old frame contents
621
 
  if(!myFrameGreyed) greyOutFrame();
622
 
  myFrameGreyed = true;
623
817
 
624
818
  // true either way:
625
819
  myPartialFrameFlag = true;
634
828
          updateFrame(clock);
635
829
  } while(clock < endClock);
636
830
 
637
 
  totalClocks = (mySystem->cycles() * 3) - myClockWhenFrameStarted;
638
 
  myCurrentScanline = totalClocks / 228;
639
 
 
640
831
  // if we finished the frame, get ready for the next one
641
832
  if(!myPartialFrameFlag)
642
833
    endFrame();
646
837
void TIA::updateScanlineByStep()
647
838
{
648
839
  // Start a new frame if the old one was finished
649
 
  if(!myPartialFrameFlag) {
 
840
  if(!myPartialFrameFlag)
650
841
    startFrame();
651
 
  }
652
 
 
653
 
  // grey out old frame contents
654
 
  if(!myFrameGreyed) greyOutFrame();
655
 
  myFrameGreyed = true;
656
842
 
657
843
  // true either way:
658
844
  myPartialFrameFlag = true;
659
845
 
660
 
  int totalClocks = (mySystem->cycles() * 3) - myClockWhenFrameStarted;
661
 
 
662
846
  // Update frame by one CPU instruction/color clock
663
847
  mySystem->m6502().execute(1);
664
848
  updateFrame(mySystem->cycles() * 3);
665
849
 
666
 
  totalClocks = (mySystem->cycles() * 3) - myClockWhenFrameStarted;
667
 
  myCurrentScanline = totalClocks / 228;
668
 
 
669
850
  // if we finished the frame, get ready for the next one
670
851
  if(!myPartialFrameFlag)
671
852
    endFrame();
675
856
void TIA::updateScanlineByTrace(int target)
676
857
{
677
858
  // Start a new frame if the old one was finished
678
 
  if(!myPartialFrameFlag) {
 
859
  if(!myPartialFrameFlag)
679
860
    startFrame();
680
 
  }
681
 
 
682
 
  // grey out old frame contents
683
 
  if(!myFrameGreyed) greyOutFrame();
684
 
  myFrameGreyed = true;
685
861
 
686
862
  // true either way:
687
863
  myPartialFrameFlag = true;
688
864
 
689
 
  int totalClocks = (mySystem->cycles() * 3) - myClockWhenFrameStarted;
690
 
 
691
865
  while(mySystem->m6502().getPC() != target)
692
866
  {
693
867
    mySystem->m6502().execute(1);
694
868
    updateFrame(mySystem->cycles() * 3);
695
869
  }
696
870
 
697
 
  totalClocks = (mySystem->cycles() * 3) - myClockWhenFrameStarted;
698
 
  myCurrentScanline = totalClocks / 228;
699
 
 
700
871
  // if we finished the frame, get ready for the next one
701
872
  if(!myPartialFrameFlag)
702
873
    endFrame();
704
875
#endif
705
876
 
706
877
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
707
 
uInt32 TIA::width() const 
708
 
{
709
 
  return myFrameWidth; 
710
 
}
711
 
 
712
 
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
713
 
uInt32 TIA::height() const 
714
 
{
715
 
  return myFrameHeight; 
716
 
}
717
 
 
718
 
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
719
 
uInt32 TIA::scanlines() const
720
 
{
721
 
  // calculate the current scanline
722
 
  uInt32 totalClocks = (mySystem->cycles() * 3) - myClockWhenFrameStarted;
723
 
  return totalClocks/228;
724
 
}
725
 
 
726
 
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
727
 
uInt32 TIA::clocksThisLine() const
728
 
{
729
 
  // calculate the current scanline
730
 
  uInt32 totalClocks = (mySystem->cycles() * 3) - myClockWhenFrameStarted;
731
 
  return totalClocks%228;
732
 
}
733
 
 
734
 
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
735
 
void TIA::setSound(Sound& sound)
736
 
{
737
 
  mySound = &sound;
738
 
}
739
 
 
740
 
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
741
 
void TIA::computeBallMaskTable()
742
 
{
743
 
  // First, calculate masks for alignment 0
744
 
  for(Int32 size = 0; size < 4; ++size)
745
 
  {
746
 
    Int32 x;
747
 
 
748
 
    // Set all of the masks to false to start with
749
 
    for(x = 0; x < 160; ++x)
750
 
    {
751
 
      ourBallMaskTable[0][size][x] = false;
752
 
    }
753
 
 
754
 
    // Set the necessary fields true
755
 
    for(x = 0; x < 160 + 8; ++x)
756
 
    {
757
 
      if((x >= 0) && (x < (1 << size)))
758
 
      {
759
 
        ourBallMaskTable[0][size][x % 160] = true;
760
 
      }
761
 
    }
762
 
 
763
 
    // Copy fields into the wrap-around area of the mask
764
 
    for(x = 0; x < 160; ++x)
765
 
    {
766
 
      ourBallMaskTable[0][size][x + 160] = ourBallMaskTable[0][size][x];
767
 
    }
768
 
  }
769
 
 
770
 
  // Now, copy data for alignments of 1, 2 and 3
771
 
  for(uInt32 align = 1; align < 4; ++align)
772
 
  {
773
 
    for(uInt32 size = 0; size < 4; ++size)
774
 
    {
775
 
      for(uInt32 x = 0; x < 320; ++x)
776
 
      {
777
 
        ourBallMaskTable[align][size][x] = 
778
 
            ourBallMaskTable[0][size][(x + 320 - align) % 320];
779
 
      }
780
 
    }
781
 
  }
782
 
}
783
 
 
784
 
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
785
 
void TIA::computeCollisionTable()
786
 
{
787
 
  for(uInt8 i = 0; i < 64; ++i)
788
 
  { 
789
 
    ourCollisionTable[i] = 0;
790
 
 
791
 
    if((i & myM0Bit) && (i & myP1Bit))    // M0-P1
792
 
      ourCollisionTable[i] |= 0x0001;
793
 
 
794
 
    if((i & myM0Bit) && (i & myP0Bit))    // M0-P0
795
 
      ourCollisionTable[i] |= 0x0002;
796
 
 
797
 
    if((i & myM1Bit) && (i & myP0Bit))    // M1-P0
798
 
      ourCollisionTable[i] |= 0x0004;
799
 
 
800
 
    if((i & myM1Bit) && (i & myP1Bit))    // M1-P1
801
 
      ourCollisionTable[i] |= 0x0008;
802
 
 
803
 
    if((i & myP0Bit) && (i & myPFBit))    // P0-PF
804
 
      ourCollisionTable[i] |= 0x0010;
805
 
 
806
 
    if((i & myP0Bit) && (i & myBLBit))    // P0-BL
807
 
      ourCollisionTable[i] |= 0x0020;
808
 
 
809
 
    if((i & myP1Bit) && (i & myPFBit))    // P1-PF
810
 
      ourCollisionTable[i] |= 0x0040;
811
 
 
812
 
    if((i & myP1Bit) && (i & myBLBit))    // P1-BL
813
 
      ourCollisionTable[i] |= 0x0080;
814
 
 
815
 
    if((i & myM0Bit) && (i & myPFBit))    // M0-PF
816
 
      ourCollisionTable[i] |= 0x0100;
817
 
 
818
 
    if((i & myM0Bit) && (i & myBLBit))    // M0-BL
819
 
      ourCollisionTable[i] |= 0x0200;
820
 
 
821
 
    if((i & myM1Bit) && (i & myPFBit))    // M1-PF
822
 
      ourCollisionTable[i] |= 0x0400;
823
 
 
824
 
    if((i & myM1Bit) && (i & myBLBit))    // M1-BL
825
 
      ourCollisionTable[i] |= 0x0800;
826
 
 
827
 
    if((i & myBLBit) && (i & myPFBit))    // BL-PF
828
 
      ourCollisionTable[i] |= 0x1000;
829
 
 
830
 
    if((i & myP0Bit) && (i & myP1Bit))    // P0-P1
831
 
      ourCollisionTable[i] |= 0x2000;
832
 
 
833
 
    if((i & myM0Bit) && (i & myM1Bit))    // M0-M1
834
 
      ourCollisionTable[i] |= 0x4000;
835
 
  }
836
 
}
837
 
 
838
 
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
839
 
void TIA::computeMissleMaskTable()
840
 
{
841
 
  // First, calculate masks for alignment 0
842
 
  Int32 x, size, number;
843
 
 
844
 
  // Clear the missle table to start with
845
 
  for(number = 0; number < 8; ++number)
846
 
    for(size = 0; size < 4; ++size)
847
 
      for(x = 0; x < 160; ++x)
848
 
        ourMissleMaskTable[0][number][size][x] = false;
849
 
 
850
 
  for(number = 0; number < 8; ++number)
851
 
  {
852
 
    for(size = 0; size < 4; ++size)
853
 
    {
854
 
      for(x = 0; x < 160 + 72; ++x)
855
 
      {
856
 
        // Only one copy of the missle
857
 
        if((number == 0x00) || (number == 0x05) || (number == 0x07))
858
 
        {
859
 
          if((x >= 0) && (x < (1 << size)))
860
 
            ourMissleMaskTable[0][number][size][x % 160] = true;
861
 
        }
862
 
        // Two copies - close
863
 
        else if(number == 0x01)
864
 
        {
865
 
          if((x >= 0) && (x < (1 << size)))
866
 
            ourMissleMaskTable[0][number][size][x % 160] = true;
867
 
          else if(((x - 16) >= 0) && ((x - 16) < (1 << size)))
868
 
            ourMissleMaskTable[0][number][size][x % 160] = true;
869
 
        }
870
 
        // Two copies - medium
871
 
        else if(number == 0x02)
872
 
        {
873
 
          if((x >= 0) && (x < (1 << size)))
874
 
            ourMissleMaskTable[0][number][size][x % 160] = true;
875
 
          else if(((x - 32) >= 0) && ((x - 32) < (1 << size)))
876
 
            ourMissleMaskTable[0][number][size][x % 160] = true;
877
 
        }
878
 
        // Three copies - close
879
 
        else if(number == 0x03)
880
 
        {
881
 
          if((x >= 0) && (x < (1 << size)))
882
 
            ourMissleMaskTable[0][number][size][x % 160] = true;
883
 
          else if(((x - 16) >= 0) && ((x - 16) < (1 << size)))
884
 
            ourMissleMaskTable[0][number][size][x % 160] = true;
885
 
          else if(((x - 32) >= 0) && ((x - 32) < (1 << size)))
886
 
            ourMissleMaskTable[0][number][size][x % 160] = true;
887
 
        }
888
 
        // Two copies - wide
889
 
        else if(number == 0x04)
890
 
        {
891
 
          if((x >= 0) && (x < (1 << size)))
892
 
            ourMissleMaskTable[0][number][size][x % 160] = true;
893
 
          else if(((x - 64) >= 0) && ((x - 64) < (1 << size)))
894
 
            ourMissleMaskTable[0][number][size][x % 160] = true;
895
 
        }
896
 
        // Three copies - medium
897
 
        else if(number == 0x06)
898
 
        {
899
 
          if((x >= 0) && (x < (1 << size)))
900
 
            ourMissleMaskTable[0][number][size][x % 160] = true;
901
 
          else if(((x - 32) >= 0) && ((x - 32) < (1 << size)))
902
 
            ourMissleMaskTable[0][number][size][x % 160] = true;
903
 
          else if(((x - 64) >= 0) && ((x - 64) < (1 << size)))
904
 
            ourMissleMaskTable[0][number][size][x % 160] = true;
905
 
        }
906
 
      }
907
 
 
908
 
      // Copy data into wrap-around area
909
 
      for(x = 0; x < 160; ++x)
910
 
        ourMissleMaskTable[0][number][size][x + 160] = 
911
 
          ourMissleMaskTable[0][number][size][x];
912
 
    }
913
 
  }
914
 
 
915
 
  // Now, copy data for alignments of 1, 2 and 3
916
 
  for(uInt32 align = 1; align < 4; ++align)
917
 
  {
918
 
    for(number = 0; number < 8; ++number)
919
 
    {
920
 
      for(size = 0; size < 4; ++size)
921
 
      {
922
 
        for(x = 0; x < 320; ++x)
923
 
        {
924
 
          ourMissleMaskTable[align][number][size][x] = 
925
 
            ourMissleMaskTable[0][number][size][(x + 320 - align) % 320];
926
 
        }
927
 
      }
928
 
    }
929
 
  }
930
 
}
931
 
 
932
 
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
933
 
void TIA::computePlayerMaskTable()
934
 
{
935
 
  // First, calculate masks for alignment 0
936
 
  Int32 x, enable, mode;
937
 
 
938
 
  // Set the player mask table to all zeros
939
 
  for(enable = 0; enable < 2; ++enable)
940
 
    for(mode = 0; mode < 8; ++mode)
941
 
      for(x = 0; x < 160; ++x)
942
 
        ourPlayerMaskTable[0][enable][mode][x] = 0x00;
943
 
 
944
 
  // Now, compute the player mask table
945
 
  for(enable = 0; enable < 2; ++enable)
946
 
  {
947
 
    for(mode = 0; mode < 8; ++mode)
948
 
    {
949
 
      for(x = 0; x < 160 + 72; ++x)
950
 
      {
951
 
        if(mode == 0x00)
952
 
        {
953
 
          if((enable == 0) && (x >= 0) && (x < 8))
954
 
            ourPlayerMaskTable[0][enable][mode][x % 160] = 0x80 >> x;
955
 
        }
956
 
        else if(mode == 0x01)
957
 
        {
958
 
          if((enable == 0) && (x >= 0) && (x < 8))
959
 
            ourPlayerMaskTable[0][enable][mode][x % 160] = 0x80 >> x;
960
 
          else if(((x - 16) >= 0) && ((x - 16) < 8))
961
 
            ourPlayerMaskTable[0][enable][mode][x % 160] = 0x80 >> (x - 16);
962
 
        }
963
 
        else if(mode == 0x02)
964
 
        {
965
 
          if((enable == 0) && (x >= 0) && (x < 8))
966
 
            ourPlayerMaskTable[0][enable][mode][x % 160] = 0x80 >> x;
967
 
          else if(((x - 32) >= 0) && ((x - 32) < 8))
968
 
            ourPlayerMaskTable[0][enable][mode][x % 160] = 0x80 >> (x - 32);
969
 
        }
970
 
        else if(mode == 0x03)
971
 
        {
972
 
          if((enable == 0) && (x >= 0) && (x < 8))
973
 
            ourPlayerMaskTable[0][enable][mode][x % 160] = 0x80 >> x;
974
 
          else if(((x - 16) >= 0) && ((x - 16) < 8))
975
 
            ourPlayerMaskTable[0][enable][mode][x % 160] = 0x80 >> (x - 16);
976
 
          else if(((x - 32) >= 0) && ((x - 32) < 8))
977
 
            ourPlayerMaskTable[0][enable][mode][x % 160] = 0x80 >> (x - 32);
978
 
        }
979
 
        else if(mode == 0x04)
980
 
        {
981
 
          if((enable == 0) && (x >= 0) && (x < 8))
982
 
            ourPlayerMaskTable[0][enable][mode][x % 160] = 0x80 >> x;
983
 
          else if(((x - 64) >= 0) && ((x - 64) < 8))
984
 
            ourPlayerMaskTable[0][enable][mode][x % 160] = 0x80 >> (x - 64);
985
 
        }
986
 
        else if(mode == 0x05)
987
 
        {
988
 
          // For some reason in double size mode the player's output
989
 
          // is delayed by one pixel thus we use > instead of >=
990
 
          if((enable == 0) && (x > 0) && (x <= 16))
991
 
            ourPlayerMaskTable[0][enable][mode][x % 160] = 0x80 >> ((x - 1)/2);
992
 
        }
993
 
        else if(mode == 0x06)
994
 
        {
995
 
          if((enable == 0) && (x >= 0) && (x < 8))
996
 
            ourPlayerMaskTable[0][enable][mode][x % 160] = 0x80 >> x;
997
 
          else if(((x - 32) >= 0) && ((x - 32) < 8))
998
 
            ourPlayerMaskTable[0][enable][mode][x % 160] = 0x80 >> (x - 32);
999
 
          else if(((x - 64) >= 0) && ((x - 64) < 8))
1000
 
            ourPlayerMaskTable[0][enable][mode][x % 160] = 0x80 >> (x - 64);
1001
 
        }
1002
 
        else if(mode == 0x07)
1003
 
        {
1004
 
          // For some reason in quad size mode the player's output
1005
 
          // is delayed by one pixel thus we use > instead of >=
1006
 
          if((enable == 0) && (x > 0) && (x <= 32))
1007
 
            ourPlayerMaskTable[0][enable][mode][x % 160] = 0x80 >> ((x - 1)/4);
1008
 
        }
1009
 
      }
1010
 
  
1011
 
      // Copy data into wrap-around area
1012
 
      for(x = 0; x < 160; ++x)
1013
 
      {
1014
 
        ourPlayerMaskTable[0][enable][mode][x + 160] = 
1015
 
            ourPlayerMaskTable[0][enable][mode][x];
1016
 
      }
1017
 
    }
1018
 
  }
1019
 
 
1020
 
  // Now, copy data for alignments of 1, 2 and 3
1021
 
  for(uInt32 align = 1; align < 4; ++align)
1022
 
  {
1023
 
    for(enable = 0; enable < 2; ++enable)
1024
 
    {
1025
 
      for(mode = 0; mode < 8; ++mode)
1026
 
      {
1027
 
        for(x = 0; x < 320; ++x)
1028
 
        {
1029
 
          ourPlayerMaskTable[align][enable][mode][x] =
1030
 
              ourPlayerMaskTable[0][enable][mode][(x + 320 - align) % 320];
1031
 
        }
1032
 
      }
1033
 
    }
1034
 
  }
1035
 
}
1036
 
 
1037
 
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1038
 
void TIA::computePlayerPositionResetWhenTable()
1039
 
{
1040
 
  uInt32 mode, oldx, newx;
1041
 
 
1042
 
  // Loop through all player modes, all old player positions, and all new
1043
 
  // player positions and determine where the new position is located:
1044
 
  // 1 means the new position is within the display of an old copy of the
1045
 
  // player, -1 means the new position is within the delay portion of an
1046
 
  // old copy of the player, and 0 means it's neither of these two
1047
 
  for(mode = 0; mode < 8; ++mode)
1048
 
  {
1049
 
    for(oldx = 0; oldx < 160; ++oldx)
1050
 
    {
1051
 
      // Set everything to 0 for non-delay/non-display section
1052
 
      for(newx = 0; newx < 160; ++newx)
1053
 
      {
1054
 
        ourPlayerPositionResetWhenTable[mode][oldx][newx] = 0;
1055
 
      }
1056
 
 
1057
 
      // Now, we'll set the entries for non-delay/non-display section
1058
 
      for(newx = 0; newx < 160 + 72 + 5; ++newx)
1059
 
      {
1060
 
        if(mode == 0x00)
1061
 
        {
1062
 
          if((newx >= oldx) && (newx < (oldx + 4)))
1063
 
            ourPlayerPositionResetWhenTable[mode][oldx][newx % 160] = -1;
1064
 
 
1065
 
          if((newx >= oldx + 4) && (newx < (oldx + 4 + 8)))
1066
 
            ourPlayerPositionResetWhenTable[mode][oldx][newx % 160] = 1;
1067
 
        }
1068
 
        else if(mode == 0x01)
1069
 
        {
1070
 
          if((newx >= oldx) && (newx < (oldx + 4)))
1071
 
            ourPlayerPositionResetWhenTable[mode][oldx][newx % 160] = -1;
1072
 
          else if((newx >= (oldx + 16)) && (newx < (oldx + 16 + 4)))
1073
 
            ourPlayerPositionResetWhenTable[mode][oldx][newx % 160] = -1;
1074
 
 
1075
 
          if((newx >= oldx + 4) && (newx < (oldx + 4 + 8)))
1076
 
            ourPlayerPositionResetWhenTable[mode][oldx][newx % 160] = 1;
1077
 
          else if((newx >= oldx + 16 + 4) && (newx < (oldx + 16 + 4 + 8)))
1078
 
            ourPlayerPositionResetWhenTable[mode][oldx][newx % 160] = 1;
1079
 
        }
1080
 
        else if(mode == 0x02)
1081
 
        {
1082
 
          if((newx >= oldx) && (newx < (oldx + 4)))
1083
 
            ourPlayerPositionResetWhenTable[mode][oldx][newx % 160] = -1;
1084
 
          else if((newx >= (oldx + 32)) && (newx < (oldx + 32 + 4)))
1085
 
            ourPlayerPositionResetWhenTable[mode][oldx][newx % 160] = -1;
1086
 
 
1087
 
          if((newx >= oldx + 4) && (newx < (oldx + 4 + 8)))
1088
 
            ourPlayerPositionResetWhenTable[mode][oldx][newx % 160] = 1;
1089
 
          else if((newx >= oldx + 32 + 4) && (newx < (oldx + 32 + 4 + 8)))
1090
 
            ourPlayerPositionResetWhenTable[mode][oldx][newx % 160] = 1;
1091
 
        }
1092
 
        else if(mode == 0x03)
1093
 
        {
1094
 
          if((newx >= oldx) && (newx < (oldx + 4)))
1095
 
            ourPlayerPositionResetWhenTable[mode][oldx][newx % 160] = -1;
1096
 
          else if((newx >= (oldx + 16)) && (newx < (oldx + 16 + 4)))
1097
 
            ourPlayerPositionResetWhenTable[mode][oldx][newx % 160] = -1;
1098
 
          else if((newx >= (oldx + 32)) && (newx < (oldx + 32 + 4)))
1099
 
            ourPlayerPositionResetWhenTable[mode][oldx][newx % 160] = -1;
1100
 
 
1101
 
          if((newx >= oldx + 4) && (newx < (oldx + 4 + 8)))
1102
 
            ourPlayerPositionResetWhenTable[mode][oldx][newx % 160] = 1;
1103
 
          else if((newx >= oldx + 16 + 4) && (newx < (oldx + 16 + 4 + 8)))
1104
 
            ourPlayerPositionResetWhenTable[mode][oldx][newx % 160] = 1;
1105
 
          else if((newx >= oldx + 32 + 4) && (newx < (oldx + 32 + 4 + 8)))
1106
 
            ourPlayerPositionResetWhenTable[mode][oldx][newx % 160] = 1;
1107
 
        }
1108
 
        else if(mode == 0x04)
1109
 
        {
1110
 
          if((newx >= oldx) && (newx < (oldx + 4)))
1111
 
            ourPlayerPositionResetWhenTable[mode][oldx][newx % 160] = -1;
1112
 
          else if((newx >= (oldx + 64)) && (newx < (oldx + 64 + 4)))
1113
 
            ourPlayerPositionResetWhenTable[mode][oldx][newx % 160] = -1;
1114
 
 
1115
 
          if((newx >= oldx + 4) && (newx < (oldx + 4 + 8)))
1116
 
            ourPlayerPositionResetWhenTable[mode][oldx][newx % 160] = 1;
1117
 
          else if((newx >= oldx + 64 + 4) && (newx < (oldx + 64 + 4 + 8)))
1118
 
            ourPlayerPositionResetWhenTable[mode][oldx][newx % 160] = 1;
1119
 
        }
1120
 
        else if(mode == 0x05)
1121
 
        {
1122
 
          if((newx >= oldx) && (newx < (oldx + 4)))
1123
 
            ourPlayerPositionResetWhenTable[mode][oldx][newx % 160] = -1;
1124
 
 
1125
 
          if((newx >= oldx + 4) && (newx < (oldx + 4 + 16)))
1126
 
            ourPlayerPositionResetWhenTable[mode][oldx][newx % 160] = 1;
1127
 
        }
1128
 
        else if(mode == 0x06)
1129
 
        {
1130
 
          if((newx >= oldx) && (newx < (oldx + 4)))
1131
 
            ourPlayerPositionResetWhenTable[mode][oldx][newx % 160] = -1;
1132
 
          else if((newx >= (oldx + 32)) && (newx < (oldx + 32 + 4)))
1133
 
            ourPlayerPositionResetWhenTable[mode][oldx][newx % 160] = -1;
1134
 
          else if((newx >= (oldx + 64)) && (newx < (oldx + 64 + 4)))
1135
 
            ourPlayerPositionResetWhenTable[mode][oldx][newx % 160] = -1;
1136
 
 
1137
 
          if((newx >= oldx + 4) && (newx < (oldx + 4 + 8)))
1138
 
            ourPlayerPositionResetWhenTable[mode][oldx][newx % 160] = 1;
1139
 
          else if((newx >= oldx + 32 + 4) && (newx < (oldx + 32 + 4 + 8)))
1140
 
            ourPlayerPositionResetWhenTable[mode][oldx][newx % 160] = 1;
1141
 
          else if((newx >= oldx + 64 + 4) && (newx < (oldx + 64 + 4 + 8)))
1142
 
            ourPlayerPositionResetWhenTable[mode][oldx][newx % 160] = 1;
1143
 
        }
1144
 
        else if(mode == 0x07)
1145
 
        {
1146
 
          if((newx >= oldx) && (newx < (oldx + 4)))
1147
 
            ourPlayerPositionResetWhenTable[mode][oldx][newx % 160] = -1;
1148
 
 
1149
 
          if((newx >= oldx + 4) && (newx < (oldx + 4 + 32)))
1150
 
            ourPlayerPositionResetWhenTable[mode][oldx][newx % 160] = 1;
1151
 
        }
1152
 
      }
1153
 
 
1154
 
      // Let's do a sanity check on our table entries
1155
 
      uInt32 s1 = 0, s2 = 0;
1156
 
      for(newx = 0; newx < 160; ++newx)
1157
 
      {
1158
 
        if(ourPlayerPositionResetWhenTable[mode][oldx][newx] == -1)
1159
 
          ++s1;
1160
 
        if(ourPlayerPositionResetWhenTable[mode][oldx][newx] == 1)
1161
 
          ++s2;
1162
 
      }
1163
 
      assert((s1 % 4 == 0) && (s2 % 8 == 0));
1164
 
    }
1165
 
  }
1166
 
}
1167
 
 
1168
 
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1169
 
void TIA::computePlayerReflectTable()
1170
 
{
1171
 
  for(uInt16 i = 0; i < 256; ++i)
1172
 
  {
1173
 
    uInt8 r = 0;
1174
 
 
1175
 
    for(uInt16 t = 1; t <= 128; t *= 2)
1176
 
    {
1177
 
      r = (r << 1) | ((i & t) ? 0x01 : 0x00);
1178
 
    }
1179
 
 
1180
 
    ourPlayerReflectTable[i] = r;
1181
 
  } 
1182
 
}
1183
 
 
1184
 
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1185
 
void TIA::computePlayfieldMaskTable()
1186
 
{
1187
 
  Int32 x;
1188
 
 
1189
 
  // Compute playfield mask table for non-reflected mode
1190
 
  for(x = 0; x < 160; ++x)
1191
 
  {
1192
 
    if(x < 16)
1193
 
      ourPlayfieldTable[0][x] = 0x00001 << (x / 4);
1194
 
    else if(x < 48)
1195
 
      ourPlayfieldTable[0][x] = 0x00800 >> ((x - 16) / 4);
1196
 
    else if(x < 80) 
1197
 
      ourPlayfieldTable[0][x] = 0x01000 << ((x - 48) / 4);
1198
 
    else if(x < 96) 
1199
 
      ourPlayfieldTable[0][x] = 0x00001 << ((x - 80) / 4);
1200
 
    else if(x < 128)
1201
 
      ourPlayfieldTable[0][x] = 0x00800 >> ((x - 96) / 4);
1202
 
    else if(x < 160) 
1203
 
      ourPlayfieldTable[0][x] = 0x01000 << ((x - 128) / 4);
1204
 
  }
1205
 
 
1206
 
  // Compute playfield mask table for reflected mode
1207
 
  for(x = 0; x < 160; ++x)
1208
 
  {
1209
 
    if(x < 16)
1210
 
      ourPlayfieldTable[1][x] = 0x00001 << (x / 4);
1211
 
    else if(x < 48)
1212
 
      ourPlayfieldTable[1][x] = 0x00800 >> ((x - 16) / 4);
1213
 
    else if(x < 80) 
1214
 
      ourPlayfieldTable[1][x] = 0x01000 << ((x - 48) / 4);
1215
 
    else if(x < 112) 
1216
 
      ourPlayfieldTable[1][x] = 0x80000 >> ((x - 80) / 4);
1217
 
    else if(x < 144) 
1218
 
      ourPlayfieldTable[1][x] = 0x00010 << ((x - 112) / 4);
1219
 
    else if(x < 160) 
1220
 
      ourPlayfieldTable[1][x] = 0x00008 >> ((x - 144) / 4);
1221
 
  }
1222
 
}
1223
 
 
1224
 
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1225
 
inline void TIA::updateFrameScanline(uInt32 clocksToUpdate, uInt32 hpos)
1226
 
{
1227
 
  // Calculate the ending frame pointer value
1228
 
  uInt8* ending = myFramePointer + clocksToUpdate;
1229
 
 
1230
 
  // See if we're in the vertical blank region
1231
 
  if(myVBLANK & 0x02)
1232
 
  {
1233
 
    memset(myFramePointer, 0, clocksToUpdate);
1234
 
  }
1235
 
  // Handle all other possible combinations
1236
 
  else
1237
 
  {
1238
 
    switch(myEnabledObjects | myPlayfieldPriorityAndScore)
1239
 
    {
1240
 
      // Background 
1241
 
      case 0x00:
1242
 
      case 0x00 | ScoreBit:
1243
 
      case 0x00 | PriorityBit:
1244
 
      case 0x00 | PriorityBit | ScoreBit:
1245
 
      {
1246
 
        memset(myFramePointer, myCOLUBK, clocksToUpdate);
1247
 
        break;
1248
 
      }
1249
 
 
1250
 
      // Playfield is enabled and the score bit is not set
1251
 
      case myPFBit: 
1252
 
      case myPFBit | PriorityBit:
1253
 
      {
1254
 
        uInt32* mask = &myCurrentPFMask[hpos];
1255
 
 
1256
 
        // Update a uInt8 at a time until reaching a uInt32 boundary
1257
 
        for(; ((uintptr_t)myFramePointer & 0x03) && (myFramePointer < ending);
1258
 
            ++myFramePointer, ++mask)
1259
 
        {
1260
 
          *myFramePointer = (myPF & *mask) ? myCOLUPF : myCOLUBK;
1261
 
        }
1262
 
 
1263
 
        // Now, update a uInt32 at a time
1264
 
        for(; myFramePointer < ending; myFramePointer += 4, mask += 4)
1265
 
        {
1266
 
          *((uInt32*)myFramePointer) = (myPF & *mask) ? myCOLUPF : myCOLUBK;
1267
 
        }
1268
 
        break;
1269
 
      }
1270
 
 
1271
 
      // Playfield is enabled and the score bit is set
1272
 
      case myPFBit | ScoreBit:
1273
 
      case myPFBit | ScoreBit | PriorityBit:
1274
 
      {
1275
 
        uInt32* mask = &myCurrentPFMask[hpos];
1276
 
 
1277
 
        // Update a uInt8 at a time until reaching a uInt32 boundary
1278
 
        for(; ((uintptr_t)myFramePointer & 0x03) && (myFramePointer < ending); 
1279
 
            ++myFramePointer, ++mask, ++hpos)
1280
 
        {
1281
 
          *myFramePointer = (myPF & *mask) ? 
1282
 
              (hpos < 80 ? myCOLUP0 : myCOLUP1) : myCOLUBK;
1283
 
        }
1284
 
 
1285
 
        // Now, update a uInt32 at a time
1286
 
        for(; myFramePointer < ending; 
1287
 
            myFramePointer += 4, mask += 4, hpos += 4)
1288
 
        {
1289
 
          *((uInt32*)myFramePointer) = (myPF & *mask) ?
1290
 
              (hpos < 80 ? myCOLUP0 : myCOLUP1) : myCOLUBK;
1291
 
        }
1292
 
        break;
1293
 
      }
1294
 
 
1295
 
      // Player 0 is enabled
1296
 
      case myP0Bit:
1297
 
      case myP0Bit | ScoreBit:
1298
 
      case myP0Bit | PriorityBit:
1299
 
      case myP0Bit | ScoreBit | PriorityBit:
1300
 
      {
1301
 
        uInt8* mP0 = &myCurrentP0Mask[hpos];
1302
 
 
1303
 
        while(myFramePointer < ending)
1304
 
        {
1305
 
          if(!((uintptr_t)myFramePointer & 0x03) && !*(uInt32*)mP0)
1306
 
          {
1307
 
            *(uInt32*)myFramePointer = myCOLUBK;
1308
 
            mP0 += 4; myFramePointer += 4;
1309
 
          }
1310
 
          else
1311
 
          {
1312
 
            *myFramePointer = (myCurrentGRP0 & *mP0) ? myCOLUP0 : myCOLUBK;
1313
 
            ++mP0; ++myFramePointer;
1314
 
          }
1315
 
        }
1316
 
        break;
1317
 
      }
1318
 
 
1319
 
      // Player 1 is enabled
1320
 
      case myP1Bit:
1321
 
      case myP1Bit | ScoreBit:
1322
 
      case myP1Bit | PriorityBit:
1323
 
      case myP1Bit | ScoreBit | PriorityBit:
1324
 
      {
1325
 
        uInt8* mP1 = &myCurrentP1Mask[hpos];
1326
 
 
1327
 
        while(myFramePointer < ending)
1328
 
        {
1329
 
          if(!((uintptr_t)myFramePointer & 0x03) && !*(uInt32*)mP1)
1330
 
          {
1331
 
            *(uInt32*)myFramePointer = myCOLUBK;
1332
 
            mP1 += 4; myFramePointer += 4;
1333
 
          }
1334
 
          else
1335
 
          {
1336
 
            *myFramePointer = (myCurrentGRP1 & *mP1) ? myCOLUP1 : myCOLUBK;
1337
 
            ++mP1; ++myFramePointer;
1338
 
          }
1339
 
        }
1340
 
        break;
1341
 
      }
1342
 
 
1343
 
      // Player 0 and 1 are enabled
1344
 
      case myP0Bit | myP1Bit:
1345
 
      case myP0Bit | myP1Bit | ScoreBit:
1346
 
      case myP0Bit | myP1Bit | PriorityBit:
1347
 
      case myP0Bit | myP1Bit | ScoreBit | PriorityBit:
1348
 
      {
1349
 
        uInt8* mP0 = &myCurrentP0Mask[hpos];
1350
 
        uInt8* mP1 = &myCurrentP1Mask[hpos];
1351
 
 
1352
 
        while(myFramePointer < ending)
1353
 
        {
1354
 
          if(!((uintptr_t)myFramePointer & 0x03) && !*(uInt32*)mP0 &&
1355
 
              !*(uInt32*)mP1)
1356
 
          {
1357
 
            *(uInt32*)myFramePointer = myCOLUBK;
1358
 
            mP0 += 4; mP1 += 4; myFramePointer += 4;
1359
 
          }
1360
 
          else
1361
 
          {
1362
 
            *myFramePointer = (myCurrentGRP0 & *mP0) ? 
1363
 
                myCOLUP0 : ((myCurrentGRP1 & *mP1) ? myCOLUP1 : myCOLUBK);
1364
 
 
1365
 
            if((myCurrentGRP0 & *mP0) && (myCurrentGRP1 & *mP1))
1366
 
              myCollision |= ourCollisionTable[myP0Bit | myP1Bit];
1367
 
 
1368
 
            ++mP0; ++mP1; ++myFramePointer;
1369
 
          }
1370
 
        }
1371
 
        break;
1372
 
      }
1373
 
 
1374
 
      // Missle 0 is enabled
1375
 
      case myM0Bit:
1376
 
      case myM0Bit | ScoreBit:
1377
 
      case myM0Bit | PriorityBit:
1378
 
      case myM0Bit | ScoreBit | PriorityBit:
1379
 
      {
1380
 
        uInt8* mM0 = &myCurrentM0Mask[hpos];
1381
 
 
1382
 
        while(myFramePointer < ending)
1383
 
        {
1384
 
          if(!((uintptr_t)myFramePointer & 0x03) && !*(uInt32*)mM0)
1385
 
          {
1386
 
            *(uInt32*)myFramePointer = myCOLUBK;
1387
 
            mM0 += 4; myFramePointer += 4;
1388
 
          }
1389
 
          else
1390
 
          {
1391
 
            *myFramePointer = *mM0 ? myCOLUP0 : myCOLUBK;
1392
 
            ++mM0; ++myFramePointer;
1393
 
          }
1394
 
        }
1395
 
        break;
1396
 
      }
1397
 
 
1398
 
      // Missle 1 is enabled
1399
 
      case myM1Bit:
1400
 
      case myM1Bit | ScoreBit:
1401
 
      case myM1Bit | PriorityBit:
1402
 
      case myM1Bit | ScoreBit | PriorityBit:
1403
 
      {
1404
 
        uInt8* mM1 = &myCurrentM1Mask[hpos];
1405
 
 
1406
 
        while(myFramePointer < ending)
1407
 
        {
1408
 
          if(!((uintptr_t)myFramePointer & 0x03) && !*(uInt32*)mM1)
1409
 
          {
1410
 
            *(uInt32*)myFramePointer = myCOLUBK;
1411
 
            mM1 += 4; myFramePointer += 4;
1412
 
          }
1413
 
          else
1414
 
          {
1415
 
            *myFramePointer = *mM1 ? myCOLUP1 : myCOLUBK;
1416
 
            ++mM1; ++myFramePointer;
1417
 
          }
1418
 
        }
1419
 
        break;
1420
 
      }
1421
 
 
1422
 
      // Ball is enabled
1423
 
      case myBLBit:
1424
 
      case myBLBit | ScoreBit:
1425
 
      case myBLBit | PriorityBit:
1426
 
      case myBLBit | ScoreBit | PriorityBit:
1427
 
      {
1428
 
        uInt8* mBL = &myCurrentBLMask[hpos];
1429
 
 
1430
 
        while(myFramePointer < ending)
1431
 
        {
1432
 
          if(!((uintptr_t)myFramePointer & 0x03) && !*(uInt32*)mBL)
1433
 
          {
1434
 
            *(uInt32*)myFramePointer = myCOLUBK;
1435
 
            mBL += 4; myFramePointer += 4;
1436
 
          }
1437
 
          else
1438
 
          {
1439
 
            *myFramePointer = *mBL ? myCOLUPF : myCOLUBK;
1440
 
            ++mBL; ++myFramePointer;
1441
 
          }
1442
 
        }
1443
 
        break;
1444
 
      }
1445
 
 
1446
 
      // Missle 0 and 1 are enabled
1447
 
      case myM0Bit | myM1Bit:
1448
 
      case myM0Bit | myM1Bit | ScoreBit:
1449
 
      case myM0Bit | myM1Bit | PriorityBit:
1450
 
      case myM0Bit | myM1Bit | ScoreBit | PriorityBit:
1451
 
      {
1452
 
        uInt8* mM0 = &myCurrentM0Mask[hpos];
1453
 
        uInt8* mM1 = &myCurrentM1Mask[hpos];
1454
 
 
1455
 
        while(myFramePointer < ending)
1456
 
        {
1457
 
          if(!((uintptr_t)myFramePointer & 0x03) && !*(uInt32*)mM0 && !*(uInt32*)mM1)
1458
 
          {
1459
 
            *(uInt32*)myFramePointer = myCOLUBK;
1460
 
            mM0 += 4; mM1 += 4; myFramePointer += 4;
1461
 
          }
1462
 
          else
1463
 
          {
1464
 
            *myFramePointer = *mM0 ? myCOLUP0 : (*mM1 ? myCOLUP1 : myCOLUBK);
1465
 
 
1466
 
            if(*mM0 && *mM1)
1467
 
              myCollision |= ourCollisionTable[myM0Bit | myM1Bit];
1468
 
 
1469
 
            ++mM0; ++mM1; ++myFramePointer;
1470
 
          }
1471
 
        }
1472
 
        break;
1473
 
      }
1474
 
 
1475
 
      // Ball and Missle 0 are enabled and playfield priority is not set
1476
 
      case myBLBit | myM0Bit:
1477
 
      case myBLBit | myM0Bit | ScoreBit:
1478
 
      {
1479
 
        uInt8* mBL = &myCurrentBLMask[hpos];
1480
 
        uInt8* mM0 = &myCurrentM0Mask[hpos];
1481
 
 
1482
 
        while(myFramePointer < ending)
1483
 
        {
1484
 
          if(!((uintptr_t)myFramePointer & 0x03) && !*(uInt32*)mBL && !*(uInt32*)mM0)
1485
 
          {
1486
 
            *(uInt32*)myFramePointer = myCOLUBK;
1487
 
            mBL += 4; mM0 += 4; myFramePointer += 4;
1488
 
          }
1489
 
          else
1490
 
          {
1491
 
            *myFramePointer = (*mM0 ? myCOLUP0 : (*mBL ? myCOLUPF : myCOLUBK));
1492
 
 
1493
 
            if(*mBL && *mM0)
1494
 
              myCollision |= ourCollisionTable[myBLBit | myM0Bit];
1495
 
 
1496
 
            ++mBL; ++mM0; ++myFramePointer;
1497
 
          }
1498
 
        }
1499
 
        break;
1500
 
      }
1501
 
 
1502
 
      // Ball and Missle 0 are enabled and playfield priority is set
1503
 
      case myBLBit | myM0Bit | PriorityBit:
1504
 
      case myBLBit | myM0Bit | ScoreBit | PriorityBit:
1505
 
      {
1506
 
        uInt8* mBL = &myCurrentBLMask[hpos];
1507
 
        uInt8* mM0 = &myCurrentM0Mask[hpos];
1508
 
 
1509
 
        while(myFramePointer < ending)
1510
 
        {
1511
 
          if(!((uintptr_t)myFramePointer & 0x03) && !*(uInt32*)mBL && !*(uInt32*)mM0)
1512
 
          {
1513
 
            *(uInt32*)myFramePointer = myCOLUBK;
1514
 
            mBL += 4; mM0 += 4; myFramePointer += 4;
1515
 
          }
1516
 
          else
1517
 
          {
1518
 
            *myFramePointer = (*mBL ? myCOLUPF : (*mM0 ? myCOLUP0 : myCOLUBK));
1519
 
 
1520
 
            if(*mBL && *mM0)
1521
 
              myCollision |= ourCollisionTable[myBLBit | myM0Bit];
1522
 
 
1523
 
            ++mBL; ++mM0; ++myFramePointer;
1524
 
          }
1525
 
        }
1526
 
        break;
1527
 
      }
1528
 
 
1529
 
      // Ball and Missle 1 are enabled and playfield priority is not set
1530
 
      case myBLBit | myM1Bit:
1531
 
      case myBLBit | myM1Bit | ScoreBit:
1532
 
      {
1533
 
        uInt8* mBL = &myCurrentBLMask[hpos];
1534
 
        uInt8* mM1 = &myCurrentM1Mask[hpos];
1535
 
 
1536
 
        while(myFramePointer < ending)
1537
 
        {
1538
 
          if(!((uintptr_t)myFramePointer & 0x03) && !*(uInt32*)mBL && 
1539
 
              !*(uInt32*)mM1)
1540
 
          {
1541
 
            *(uInt32*)myFramePointer = myCOLUBK;
1542
 
            mBL += 4; mM1 += 4; myFramePointer += 4;
1543
 
          }
1544
 
          else
1545
 
          {
1546
 
            *myFramePointer = (*mM1 ? myCOLUP1 : (*mBL ? myCOLUPF : myCOLUBK));
1547
 
 
1548
 
            if(*mBL && *mM1)
1549
 
              myCollision |= ourCollisionTable[myBLBit | myM1Bit];
1550
 
 
1551
 
            ++mBL; ++mM1; ++myFramePointer;
1552
 
          }
1553
 
        }
1554
 
        break;
1555
 
      }
1556
 
 
1557
 
      // Ball and Missle 1 are enabled and playfield priority is set
1558
 
      case myBLBit | myM1Bit | PriorityBit:
1559
 
      case myBLBit | myM1Bit | ScoreBit | PriorityBit:
1560
 
      {
1561
 
        uInt8* mBL = &myCurrentBLMask[hpos];
1562
 
        uInt8* mM1 = &myCurrentM1Mask[hpos];
1563
 
 
1564
 
        while(myFramePointer < ending)
1565
 
        {
1566
 
          if(!((uintptr_t)myFramePointer & 0x03) && !*(uInt32*)mBL && 
1567
 
              !*(uInt32*)mM1)
1568
 
          {
1569
 
            *(uInt32*)myFramePointer = myCOLUBK;
1570
 
            mBL += 4; mM1 += 4; myFramePointer += 4;
1571
 
          }
1572
 
          else
1573
 
          {
1574
 
            *myFramePointer = (*mBL ? myCOLUPF : (*mM1 ? myCOLUP1 : myCOLUBK));
1575
 
 
1576
 
            if(*mBL && *mM1)
1577
 
              myCollision |= ourCollisionTable[myBLBit | myM1Bit];
1578
 
 
1579
 
            ++mBL; ++mM1; ++myFramePointer;
1580
 
          }
1581
 
        }
1582
 
        break;
1583
 
      }
1584
 
 
1585
 
      // Ball and Player 1 are enabled and playfield priority is not set
1586
 
      case myBLBit | myP1Bit:
1587
 
      case myBLBit | myP1Bit | ScoreBit:
1588
 
      {
1589
 
        uInt8* mBL = &myCurrentBLMask[hpos];
1590
 
        uInt8* mP1 = &myCurrentP1Mask[hpos];
1591
 
 
1592
 
        while(myFramePointer < ending)
1593
 
        {
1594
 
          if(!((uintptr_t)myFramePointer & 0x03) && !*(uInt32*)mP1 && !*(uInt32*)mBL)
1595
 
          {
1596
 
            *(uInt32*)myFramePointer = myCOLUBK;
1597
 
            mBL += 4; mP1 += 4; myFramePointer += 4;
1598
 
          }
1599
 
          else
1600
 
          {
1601
 
            *myFramePointer = (myCurrentGRP1 & *mP1) ? myCOLUP1 : 
1602
 
                (*mBL ? myCOLUPF : myCOLUBK);
1603
 
 
1604
 
            if(*mBL && (myCurrentGRP1 & *mP1))
1605
 
              myCollision |= ourCollisionTable[myBLBit | myP1Bit];
1606
 
 
1607
 
            ++mBL; ++mP1; ++myFramePointer;
1608
 
          }
1609
 
        }
1610
 
        break;
1611
 
      }
1612
 
 
1613
 
      // Ball and Player 1 are enabled and playfield priority is set
1614
 
      case myBLBit | myP1Bit | PriorityBit:
1615
 
      case myBLBit | myP1Bit | PriorityBit | ScoreBit:
1616
 
      {
1617
 
        uInt8* mBL = &myCurrentBLMask[hpos];
1618
 
        uInt8* mP1 = &myCurrentP1Mask[hpos];
1619
 
 
1620
 
        while(myFramePointer < ending)
1621
 
        {
1622
 
          if(!((uintptr_t)myFramePointer & 0x03) && !*(uInt32*)mP1 && !*(uInt32*)mBL)
1623
 
          {
1624
 
            *(uInt32*)myFramePointer = myCOLUBK;
1625
 
            mBL += 4; mP1 += 4; myFramePointer += 4;
1626
 
          }
1627
 
          else
1628
 
          {
1629
 
            *myFramePointer = *mBL ? myCOLUPF : 
1630
 
                ((myCurrentGRP1 & *mP1) ? myCOLUP1 : myCOLUBK);
1631
 
 
1632
 
            if(*mBL && (myCurrentGRP1 & *mP1))
1633
 
              myCollision |= ourCollisionTable[myBLBit | myP1Bit];
1634
 
 
1635
 
            ++mBL; ++mP1; ++myFramePointer;
1636
 
          }
1637
 
        }
1638
 
        break;
1639
 
      }
1640
 
 
1641
 
      // Playfield and Player 0 are enabled and playfield priority is not set
1642
 
      case myPFBit | myP0Bit:
1643
 
      {
1644
 
        uInt32* mPF = &myCurrentPFMask[hpos];
1645
 
        uInt8* mP0 = &myCurrentP0Mask[hpos];
1646
 
 
1647
 
        while(myFramePointer < ending)
1648
 
        {
1649
 
          if(!((uintptr_t)myFramePointer & 0x03) && !*(uInt32*)mP0)
1650
 
          {
1651
 
            *(uInt32*)myFramePointer = (myPF & *mPF) ? myCOLUPF : myCOLUBK;
1652
 
            mPF += 4; mP0 += 4; myFramePointer += 4;
1653
 
          }
1654
 
          else
1655
 
          {
1656
 
            *myFramePointer = (myCurrentGRP0 & *mP0) ? 
1657
 
                  myCOLUP0 : ((myPF & *mPF) ? myCOLUPF : myCOLUBK);
1658
 
 
1659
 
            if((myPF & *mPF) && (myCurrentGRP0 & *mP0))
1660
 
              myCollision |= ourCollisionTable[myPFBit | myP0Bit];
1661
 
 
1662
 
            ++mPF; ++mP0; ++myFramePointer;
1663
 
          }
1664
 
        }
1665
 
 
1666
 
        break;
1667
 
      }
1668
 
 
1669
 
      // Playfield and Player 0 are enabled and playfield priority is set
1670
 
      case myPFBit | myP0Bit | PriorityBit:
1671
 
      {
1672
 
        uInt32* mPF = &myCurrentPFMask[hpos];
1673
 
        uInt8* mP0 = &myCurrentP0Mask[hpos];
1674
 
 
1675
 
        while(myFramePointer < ending)
1676
 
        {
1677
 
          if(!((uintptr_t)myFramePointer & 0x03) && !*(uInt32*)mP0)
1678
 
          {
1679
 
            *(uInt32*)myFramePointer = (myPF & *mPF) ? myCOLUPF : myCOLUBK;
1680
 
            mPF += 4; mP0 += 4; myFramePointer += 4;
1681
 
          }
1682
 
          else
1683
 
          {
1684
 
            *myFramePointer = (myPF & *mPF) ? myCOLUPF : 
1685
 
                ((myCurrentGRP0 & *mP0) ? myCOLUP0 : myCOLUBK);
1686
 
 
1687
 
            if((myPF & *mPF) && (myCurrentGRP0 & *mP0))
1688
 
              myCollision |= ourCollisionTable[myPFBit | myP0Bit];
1689
 
 
1690
 
            ++mPF; ++mP0; ++myFramePointer;
1691
 
          }
1692
 
        }
1693
 
 
1694
 
        break;
1695
 
      }
1696
 
 
1697
 
      // Playfield and Player 1 are enabled and playfield priority is not set
1698
 
      case myPFBit | myP1Bit:
1699
 
      {
1700
 
        uInt32* mPF = &myCurrentPFMask[hpos];
1701
 
        uInt8* mP1 = &myCurrentP1Mask[hpos];
1702
 
 
1703
 
        while(myFramePointer < ending)
1704
 
        {
1705
 
          if(!((uintptr_t)myFramePointer & 0x03) && !*(uInt32*)mP1)
1706
 
          {
1707
 
            *(uInt32*)myFramePointer = (myPF & *mPF) ? myCOLUPF : myCOLUBK;
1708
 
            mPF += 4; mP1 += 4; myFramePointer += 4;
1709
 
          }
1710
 
          else
1711
 
          {
1712
 
            *myFramePointer = (myCurrentGRP1 & *mP1) ? 
1713
 
                  myCOLUP1 : ((myPF & *mPF) ? myCOLUPF : myCOLUBK);
1714
 
 
1715
 
            if((myPF & *mPF) && (myCurrentGRP1 & *mP1))
1716
 
              myCollision |= ourCollisionTable[myPFBit | myP1Bit];
1717
 
 
1718
 
            ++mPF; ++mP1; ++myFramePointer;
1719
 
          }
1720
 
        }
1721
 
 
1722
 
        break;
1723
 
      }
1724
 
 
1725
 
      // Playfield and Player 1 are enabled and playfield priority is set
1726
 
      case myPFBit | myP1Bit | PriorityBit:
1727
 
      {
1728
 
        uInt32* mPF = &myCurrentPFMask[hpos];
1729
 
        uInt8* mP1 = &myCurrentP1Mask[hpos];
1730
 
 
1731
 
        while(myFramePointer < ending)
1732
 
        {
1733
 
          if(!((uintptr_t)myFramePointer & 0x03) && !*(uInt32*)mP1)
1734
 
          {
1735
 
            *(uInt32*)myFramePointer = (myPF & *mPF) ? myCOLUPF : myCOLUBK;
1736
 
            mPF += 4; mP1 += 4; myFramePointer += 4;
1737
 
          }
1738
 
          else
1739
 
          {
1740
 
            *myFramePointer = (myPF & *mPF) ? myCOLUPF : 
1741
 
                ((myCurrentGRP1 & *mP1) ? myCOLUP1 : myCOLUBK);
1742
 
 
1743
 
            if((myPF & *mPF) && (myCurrentGRP1 & *mP1))
1744
 
              myCollision |= ourCollisionTable[myPFBit | myP1Bit];
1745
 
 
1746
 
            ++mPF; ++mP1; ++myFramePointer;
1747
 
          }
1748
 
        }
1749
 
 
1750
 
        break;
1751
 
      }
1752
 
 
1753
 
      // Playfield and Ball are enabled
1754
 
      case myPFBit | myBLBit:
1755
 
      case myPFBit | myBLBit | PriorityBit:
1756
 
      {
1757
 
        uInt32* mPF = &myCurrentPFMask[hpos];
1758
 
        uInt8* mBL = &myCurrentBLMask[hpos];
1759
 
 
1760
 
        while(myFramePointer < ending)
1761
 
        {
1762
 
          if(!((uintptr_t)myFramePointer & 0x03) && !*(uInt32*)mBL)
1763
 
          {
1764
 
            *(uInt32*)myFramePointer = (myPF & *mPF) ? myCOLUPF : myCOLUBK;
1765
 
            mPF += 4; mBL += 4; myFramePointer += 4;
1766
 
          }
1767
 
          else
1768
 
          {
1769
 
            *myFramePointer = ((myPF & *mPF) || *mBL) ? myCOLUPF : myCOLUBK;
1770
 
 
1771
 
            if((myPF & *mPF) && *mBL)
1772
 
              myCollision |= ourCollisionTable[myPFBit | myBLBit];
1773
 
 
1774
 
            ++mPF; ++mBL; ++myFramePointer;
1775
 
          }
1776
 
        }
1777
 
        break;
1778
 
      }
1779
 
 
1780
 
      // Handle all of the other cases
1781
 
      default:
1782
 
      {
1783
 
        for(; myFramePointer < ending; ++myFramePointer, ++hpos)
1784
 
        {
1785
 
          uInt8 enabled = (myPF & myCurrentPFMask[hpos]) ? myPFBit : 0;
1786
 
 
1787
 
          if((myEnabledObjects & myBLBit) && myCurrentBLMask[hpos])
1788
 
            enabled |= myBLBit;
1789
 
 
1790
 
          if(myCurrentGRP1 & myCurrentP1Mask[hpos])
1791
 
            enabled |= myP1Bit;
1792
 
 
1793
 
          if((myEnabledObjects & myM1Bit) && myCurrentM1Mask[hpos])
1794
 
            enabled |= myM1Bit;
1795
 
 
1796
 
          if(myCurrentGRP0 & myCurrentP0Mask[hpos])
1797
 
            enabled |= myP0Bit;
1798
 
 
1799
 
          if((myEnabledObjects & myM0Bit) && myCurrentM0Mask[hpos])
1800
 
            enabled |= myM0Bit;
1801
 
 
1802
 
          myCollision |= ourCollisionTable[enabled];
1803
 
          *myFramePointer = myColor[myPriorityEncoder[hpos < 80 ? 0 : 1]
1804
 
              [enabled | myPlayfieldPriorityAndScore]];
1805
 
        }
1806
 
        break;  
1807
 
      }
1808
 
    }
1809
 
  }
1810
 
  myFramePointer = ending;
1811
 
}
1812
 
 
1813
 
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1814
 
inline void TIA::updateFrame(Int32 clock)
1815
 
{
1816
 
  // See if we're in the nondisplayable portion of the screen or if
1817
 
  // we've already updated this portion of the screen
1818
 
  if((clock < myClockStartDisplay) || 
1819
 
      (myClockAtLastUpdate >= myClockStopDisplay) ||  
1820
 
      (myClockAtLastUpdate >= clock))
1821
 
  {
 
878
void TIA::updateFrame(Int32 clock)
 
879
{
 
880
  // See if we've already updated this portion of the screen
 
881
  if((clock < myClockStartDisplay) ||
 
882
     (myClockAtLastUpdate >= myClockStopDisplay) ||
 
883
     (myClockAtLastUpdate >= clock))
1822
884
    return;
1823
 
  }
1824
885
 
1825
886
  // Truncate the number of cycles to update to the stop display point
1826
887
  if(clock > myClockStopDisplay)
1827
 
  {
1828
888
    clock = myClockStopDisplay;
1829
 
  }
 
889
 
 
890
  // Determine how many scanlines to process
 
891
  // It's easier to think about this in scanlines rather than color clocks
 
892
  uInt32 startLine = (myClockAtLastUpdate - myClockWhenFrameStarted) / 228;
 
893
  uInt32 endLine = (clock - myClockWhenFrameStarted) / 228;
1830
894
 
1831
895
  // Update frame one scanline at a time
1832
 
  do
 
896
  for(uInt32 line = startLine; line <= endLine; ++line)
1833
897
  {
 
898
    // Only check for inter-line changes after the current scanline
 
899
    // The ideas for much of the following code was inspired by MESS
 
900
    // (used with permission from Wilbert Pol)
 
901
    if(line != startLine)
 
902
    {
 
903
      // We're no longer concerned with previously issued HMOVE's
 
904
      myPreviousHMOVEPos = 0x7FFFFFFF;
 
905
      bool posChanged = false;
 
906
 
 
907
      // Apply pending motion clocks from a HMOVE initiated during the scanline
 
908
      if(myCurrentHMOVEPos != 0x7FFFFFFF)
 
909
      {
 
910
        if(myCurrentHMOVEPos >= 97 && myCurrentHMOVEPos < 157)
 
911
        {
 
912
          myPOSP0 -= myMotionClockP0;
 
913
          myPOSP1 -= myMotionClockP1;
 
914
          myPOSM0 -= myMotionClockM0;
 
915
          myPOSM1 -= myMotionClockM1;
 
916
          myPOSBL -= myMotionClockBL;
 
917
          myPreviousHMOVEPos = myCurrentHMOVEPos;
 
918
          posChanged = true;
 
919
        }
 
920
        // Indicate that the HMOVE has been completed
 
921
        myCurrentHMOVEPos = 0x7FFFFFFF;
 
922
      }
 
923
#ifdef USE_MMR_LATCHES
 
924
      // Apply extra clocks for 'more motion required/mmr'
 
925
      if(myHMP0mmr) { myPOSP0 -= 17; posChanged = true; }
 
926
      if(myHMP1mmr) { myPOSP1 -= 17; posChanged = true; }
 
927
      if(myHMM0mmr) { myPOSM0 -= 17; posChanged = true; }
 
928
      if(myHMM1mmr) { myPOSM1 -= 17; posChanged = true; }
 
929
      if(myHMBLmmr) { myPOSBL -= 17; posChanged = true; }
 
930
#endif
 
931
      // Make sure positions are in range
 
932
      if(posChanged)
 
933
      {
 
934
        if(myPOSP0 < 0) { myPOSP0 += 160; }  myPOSP0 %= 160;
 
935
        if(myPOSP1 < 0) { myPOSP1 += 160; }  myPOSP1 %= 160;
 
936
        if(myPOSM0 < 0) { myPOSM0 += 160; }  myPOSM0 %= 160;
 
937
        if(myPOSM1 < 0) { myPOSM1 += 160; }  myPOSM1 %= 160;
 
938
        if(myPOSBL < 0) { myPOSBL += 160; }  myPOSBL %= 160;
 
939
      }
 
940
    }
 
941
 
1834
942
    // Compute the number of clocks we're going to update
1835
943
    Int32 clocksToUpdate = 0;
1836
944
 
1853
961
      myClockAtLastUpdate = clock;
1854
962
    }
1855
963
 
1856
 
    Int32 startOfScanLine = HBLANK + myFrameXStart;
 
964
    Int32 startOfScanLine = HBLANK;
1857
965
 
1858
966
    // Skip over as many horizontal blank clocks as we can
1859
967
    if(clocksFromStartOfScanLine < startOfScanLine)
1875
983
    // Update as much of the scanline as we can
1876
984
    if(clocksToUpdate != 0)
1877
985
    {
1878
 
      updateFrameScanline(clocksToUpdate, clocksFromStartOfScanLine - HBLANK);
 
986
      // Calculate the ending frame pointer value
 
987
      uInt8* ending = myFramePointer + clocksToUpdate;
 
988
      myFramePointerClocks += clocksToUpdate;
 
989
 
 
990
      // See if we're in the vertical blank region
 
991
      if(myVBLANK & 0x02)
 
992
      {
 
993
        memset(myFramePointer, 0, clocksToUpdate);
 
994
      }
 
995
      // Handle all other possible combinations
 
996
      else
 
997
      {
 
998
        // Update masks
 
999
        myCurrentBLMask = &TIATables::BLMask[myPOSBL & 0x03]
 
1000
            [(myCTRLPF & 0x30) >> 4][160 - (myPOSBL & 0xFC)];
 
1001
        myCurrentP0Mask = &TIATables::PxMask[myPOSP0 & 0x03]
 
1002
            [mySuppressP0][myNUSIZ0 & 0x07][160 - (myPOSP0 & 0xFC)];
 
1003
        myCurrentP1Mask = &TIATables::PxMask[myPOSP1 & 0x03]
 
1004
            [mySuppressP1][myNUSIZ1 & 0x07][160 - (myPOSP1 & 0xFC)];
 
1005
 
 
1006
        // TODO - 08-27-2009: Simulate the weird effects of Cosmic Ark and
 
1007
        // Stay Frosty.  The movement itself is well understood, but there
 
1008
        // also seems to be some widening and blanking occurring as well.
 
1009
        // This doesn't properly emulate the effect at a low level; it only
 
1010
        // simulates the behaviour as visually seen in the aforementioned
 
1011
        // ROMs.  Other ROMs may break this simulation; more testing is
 
1012
        // required to figure out what's really going on here.
 
1013
        if(myHMM0mmr)
 
1014
        {
 
1015
          switch(myPOSM0 % 4)
 
1016
          {
 
1017
            case 3:
 
1018
              // Stretch this missle so it's 2 pixels wide and shifted one
 
1019
              // pixel to the left
 
1020
              myCurrentM0Mask = &TIATables::MxMask[(myPOSM0-1) & 0x03]
 
1021
                  [myNUSIZ0 & 0x07][((myNUSIZ0 & 0x30) >> 4)|1][160 - ((myPOSM0-1) & 0xFC)];
 
1022
              break;
 
1023
            case 2:
 
1024
              // Missle is disabled on this line
 
1025
              myCurrentM0Mask = &TIATables::DisabledMask[0];
 
1026
              break;
 
1027
            default:
 
1028
              myCurrentM0Mask = &TIATables::MxMask[myPOSM0 & 0x03]
 
1029
                  [myNUSIZ0 & 0x07][(myNUSIZ0 & 0x30) >> 4][160 - (myPOSM0 & 0xFC)];
 
1030
              break;
 
1031
          }
 
1032
        }
 
1033
        else
 
1034
          myCurrentM0Mask = &TIATables::MxMask[myPOSM0 & 0x03]
 
1035
              [myNUSIZ0 & 0x07][(myNUSIZ0 & 0x30) >> 4][160 - (myPOSM0 & 0xFC)];
 
1036
        if(myHMM1mmr)
 
1037
        {
 
1038
          switch(myPOSM1 % 4)
 
1039
          {
 
1040
            case 3:
 
1041
              // Stretch this missle so it's 2 pixels wide and shifted one
 
1042
              // pixel to the left
 
1043
              myCurrentM1Mask = &TIATables::MxMask[(myPOSM1-1) & 0x03]
 
1044
                  [myNUSIZ1 & 0x07][((myNUSIZ1 & 0x30) >> 4)|1][160 - ((myPOSM1-1) & 0xFC)];
 
1045
              break;
 
1046
            case 2:
 
1047
              // Missle is disabled on this line
 
1048
              myCurrentM1Mask = &TIATables::DisabledMask[0];
 
1049
              break;
 
1050
            default:
 
1051
              myCurrentM1Mask = &TIATables::MxMask[myPOSM1 & 0x03]
 
1052
                  [myNUSIZ1 & 0x07][(myNUSIZ1 & 0x30) >> 4][160 - (myPOSM1 & 0xFC)];
 
1053
              break;
 
1054
          }
 
1055
        }
 
1056
        else
 
1057
          myCurrentM1Mask = &TIATables::MxMask[myPOSM1 & 0x03]
 
1058
              [myNUSIZ1 & 0x07][(myNUSIZ1 & 0x30) >> 4][160 - (myPOSM1 & 0xFC)];
 
1059
 
 
1060
        uInt8 enabledObjects = myEnabledObjects & myDisabledObjects;
 
1061
        uInt32 hpos = clocksFromStartOfScanLine - HBLANK;
 
1062
        for(; myFramePointer < ending; ++myFramePointer, ++hpos)
 
1063
        {
 
1064
          uInt8 enabled = ((enabledObjects & PFBit) &&
 
1065
                           (myPF & myCurrentPFMask[hpos])) ? PFBit : 0;
 
1066
 
 
1067
          if((enabledObjects & BLBit) && myCurrentBLMask[hpos])
 
1068
            enabled |= BLBit;
 
1069
 
 
1070
          if((enabledObjects & P1Bit) && (myCurrentGRP1 & myCurrentP1Mask[hpos]))
 
1071
            enabled |= P1Bit;
 
1072
 
 
1073
          if((enabledObjects & M1Bit) && myCurrentM1Mask[hpos])
 
1074
            enabled |= M1Bit;
 
1075
 
 
1076
          if((enabledObjects & P0Bit) && (myCurrentGRP0 & myCurrentP0Mask[hpos]))
 
1077
            enabled |= P0Bit;
 
1078
 
 
1079
          if((enabledObjects & M0Bit) && myCurrentM0Mask[hpos])
 
1080
            enabled |= M0Bit;
 
1081
 
 
1082
          myCollision |= TIATables::CollisionMask[enabled];
 
1083
          *myFramePointer = myColorPtr[myPriorityEncoder[hpos < 80 ? 0 : 1]
 
1084
              [enabled | myPlayfieldPriorityAndScore]];
 
1085
        }
 
1086
      }
 
1087
      myFramePointer = ending;
1879
1088
    }
1880
1089
 
1881
1090
    // Handle HMOVE blanks if they are enabled
1883
1092
        (clocksFromStartOfScanLine < (HBLANK + 8)))
1884
1093
    {
1885
1094
      Int32 blanks = (HBLANK + 8) - clocksFromStartOfScanLine;
1886
 
      memset(oldFramePointer, 0, blanks);
 
1095
      memset(oldFramePointer, myColorPtr[_HBLANK], blanks);
1887
1096
 
1888
1097
      if((clocksToUpdate + clocksFromStartOfScanLine) >= (HBLANK + 8))
1889
 
      {
1890
1098
        myHMOVEBlankEnabled = false;
1891
 
      }
1892
1099
    }
1893
1100
 
1894
1101
    // See if we're at the end of a scanline
1895
1102
    if(myClocksToEndOfScanLine == 228)
1896
1103
    {
1897
 
      myFramePointer -= (160 - myFrameWidth - myFrameXStart);
1898
 
 
1899
1104
      // Yes, so set PF mask based on current CTRLPF reflection state 
1900
 
      myCurrentPFMask = ourPlayfieldTable[myCTRLPF & 0x01];
1901
 
 
1902
 
      // TODO: These should be reset right after the first copy of the player
1903
 
      // has passed.  However, for now we'll just reset at the end of the 
1904
 
      // scanline since the other way would be to slow (01/21/99).
1905
 
      myCurrentP0Mask = &ourPlayerMaskTable[myPOSP0 & 0x03]
1906
 
          [0][myNUSIZ0 & 0x07][160 - (myPOSP0 & 0xFC)];
1907
 
      myCurrentP1Mask = &ourPlayerMaskTable[myPOSP1 & 0x03]
1908
 
          [0][myNUSIZ1 & 0x07][160 - (myPOSP1 & 0xFC)];
1909
 
 
1910
 
      // Handle the "Cosmic Ark" TIA bug if it's enabled
1911
 
      if(myM0CosmicArkMotionEnabled)
1912
 
      {
1913
 
        // Movement table associated with the bug
1914
 
        static uInt32 m[4] = {18, 33, 0, 17};
1915
 
 
1916
 
        myM0CosmicArkCounter = (myM0CosmicArkCounter + 1) & 3;
1917
 
        myPOSM0 -= m[myM0CosmicArkCounter];
1918
 
 
1919
 
        if(myPOSM0 >= 160)
1920
 
          myPOSM0 -= 160;
1921
 
        else if(myPOSM0 < 0)
1922
 
          myPOSM0 += 160;
1923
 
 
1924
 
        if(myM0CosmicArkCounter == 1)
1925
 
        {
1926
 
          // Stretch this missle so it's at least 2 pixels wide
1927
 
          myCurrentM0Mask = &ourMissleMaskTable[myPOSM0 & 0x03]
1928
 
              [myNUSIZ0 & 0x07][((myNUSIZ0 & 0x30) >> 4) | 0x01]
1929
 
              [160 - (myPOSM0 & 0xFC)];
1930
 
        }
1931
 
        else if(myM0CosmicArkCounter == 2)
1932
 
        {
1933
 
          // Missle is disabled on this line 
1934
 
          myCurrentM0Mask = &ourDisabledMaskTable[0];
1935
 
        }
1936
 
        else
1937
 
        {
1938
 
          myCurrentM0Mask = &ourMissleMaskTable[myPOSM0 & 0x03]
1939
 
              [myNUSIZ0 & 0x07][(myNUSIZ0 & 0x30) >> 4][160 - (myPOSM0 & 0xFC)];
1940
 
        }
1941
 
      } 
 
1105
      myCurrentPFMask = TIATables::PFMask[myCTRLPF & 0x01];
 
1106
 
 
1107
      // TODO - 01-21-99: These should be reset right after the first copy
 
1108
      // of the player has passed.  However, for now we'll just reset at the
 
1109
      // end of the scanline since the other way would be too slow.
 
1110
      mySuppressP0 = mySuppressP1 = 0;
1942
1111
    }
1943
 
  } 
1944
 
  while(myClockAtLastUpdate < clock);
 
1112
  }
1945
1113
}
1946
1114
 
1947
1115
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1951
1119
      (myClockWhenFrameStarted / 3)) % 76);
1952
1120
 
1953
1121
  if(cyclesToEndOfLine < 76)
1954
 
  {
1955
1122
    mySystem->incrementCycles(cyclesToEndOfLine);
1956
 
  }
1957
 
}
1958
 
 
1959
 
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1960
 
void TIA::greyOutFrame()
1961
 
{
1962
 
  unsigned int c = scanlines();
1963
 
  if(c < myYStart) c = myYStart;
1964
 
 
1965
 
  for(unsigned int s = c; s < (myHeight + myYStart); s++)
1966
 
          for(unsigned int i = 0; i < 160; i++) {
1967
 
                  uInt8 tmp = myCurrentFrameBuffer[ (s - myYStart) * 160 + i] & 0x0f;
1968
 
                  tmp >>= 1;
1969
 
                  myCurrentFrameBuffer[ (s - myYStart) * 160 + i] = tmp;
1970
 
          }
1971
 
 
1972
1123
}
1973
1124
 
1974
1125
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1975
1126
void TIA::clearBuffers()
1976
1127
{
1977
 
  for(uInt32 i = 0; i < 160 * 300; ++i)
1978
 
  {
1979
 
    myCurrentFrameBuffer[i] = myPreviousFrameBuffer[i] = 0;
1980
 
  }
 
1128
  memset(myCurrentFrameBuffer, 0, 160 * 320);
 
1129
  memset(myPreviousFrameBuffer, 0, 160 * 320);
 
1130
}
 
1131
 
 
1132
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
1133
inline uInt8 TIA::dumpedInputPort(int resistance)
 
1134
{
 
1135
  if(resistance == Controller::minimumResistance)
 
1136
  {
 
1137
    return 0x80;
 
1138
  }
 
1139
  else if((resistance == Controller::maximumResistance) || myDumpEnabled)
 
1140
  {
 
1141
    return 0x00;
 
1142
  }
 
1143
  else
 
1144
  {
 
1145
    // Constant here is derived from '1.6 * 0.01e-6 * 228 / 3'
 
1146
    uInt32 needed = (uInt32)
 
1147
      (1.216e-6 * resistance * myScanlineCountForLastFrame * myFramerate);
 
1148
    if((mySystem->cycles() - myDumpDisabledCycle) > needed)
 
1149
      return 0x80;
 
1150
    else
 
1151
      return 0x00;
 
1152
  }
 
1153
  return 0x00;
1981
1154
}
1982
1155
 
1983
1156
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1986
1159
  // Update frame to current color clock before we look at anything!
1987
1160
  updateFrame(mySystem->cycles() * 3);
1988
1161
 
1989
 
  uInt8 noise = mySystem->getDataBusState() & 0x3F;
1990
 
  
1991
 
  // On certain CMOS EPROM chips the unused TIA pins on a read are not
1992
 
  // floating but pulled high. Programmers might want to check their
1993
 
  // games for compatibility, so we make this optional. 
1994
 
  if(!myFloatTIAOutputPins) noise = 0x3F;
 
1162
  // If pins are undriven, we start with the last databus value
 
1163
  // Otherwise, there is some randomness injected into the mix
 
1164
  uInt8 value = myTIAPinsDriven ? mySystem->getDataBusState(0xFF) :
 
1165
                                  mySystem->getDataBusState();
 
1166
  uInt16 collision = myCollision & (uInt16)myCollisionEnabledMask;
1995
1167
 
1996
1168
  switch(addr & 0x000f)
1997
1169
  {
1998
 
    case 0x00:    // CXM0P
1999
 
      return ((myCollision & 0x0001) ? 0x80 : 0x00) | 
2000
 
          ((myCollision & 0x0002) ? 0x40 : 0x00) | noise;
2001
 
 
2002
 
    case 0x01:    // CXM1P
2003
 
      return ((myCollision & 0x0004) ? 0x80 : 0x00) | 
2004
 
          ((myCollision & 0x0008) ? 0x40 : 0x00) | noise;
2005
 
 
2006
 
    case 0x02:    // CXP0FB
2007
 
      return ((myCollision & 0x0010) ? 0x80 : 0x00) | 
2008
 
          ((myCollision & 0x0020) ? 0x40 : 0x00) | noise;
2009
 
 
2010
 
    case 0x03:    // CXP1FB
2011
 
      return ((myCollision & 0x0040) ? 0x80 : 0x00) | 
2012
 
          ((myCollision & 0x0080) ? 0x40 : 0x00) | noise;
2013
 
 
2014
 
    case 0x04:    // CXM0FB
2015
 
      return ((myCollision & 0x0100) ? 0x80 : 0x00) | 
2016
 
          ((myCollision & 0x0200) ? 0x40 : 0x00) | noise;
2017
 
 
2018
 
    case 0x05:    // CXM1FB
2019
 
      return ((myCollision & 0x0400) ? 0x80 : 0x00) | 
2020
 
          ((myCollision & 0x0800) ? 0x40 : 0x00) | noise;
2021
 
 
2022
 
    case 0x06:    // CXBLPF
2023
 
      return ((myCollision & 0x1000) ? 0x80 : 0x00) | noise;
2024
 
 
2025
 
    case 0x07:    // CXPPMM
2026
 
      return ((myCollision & 0x2000) ? 0x80 : 0x00) | 
2027
 
          ((myCollision & 0x4000) ? 0x40 : 0x00) | noise;
2028
 
 
2029
 
    case 0x08:    // INPT0
2030
 
    {
2031
 
      Int32 r = myConsole.controller(Controller::Left).read(Controller::Nine);
2032
 
      if(r == Controller::minimumResistance)
2033
 
      {
2034
 
        return 0x80 | noise;
2035
 
      }
2036
 
      else if((r == Controller::maximumResistance) || myDumpEnabled)
2037
 
      {
2038
 
        return noise;
2039
 
      }
2040
 
      else
2041
 
      {
2042
 
        double t = (1.5327 * r * 0.01E-6);
2043
 
        uInt32 needed = (uInt32)(t * 1.19E6);
2044
 
        if((mySystem->cycles() - myDumpDisabledCycle) > needed)
2045
 
        {
2046
 
          return 0x80 | noise;
2047
 
        }
2048
 
        else
2049
 
        {
2050
 
          return noise;
2051
 
        }
2052
 
      }
2053
 
    }
2054
 
 
2055
 
    case 0x09:    // INPT1
2056
 
    {
2057
 
      Int32 r = myConsole.controller(Controller::Left).read(Controller::Five);
2058
 
      if(r == Controller::minimumResistance)
2059
 
      {
2060
 
        return 0x80 | noise;
2061
 
      }
2062
 
      else if((r == Controller::maximumResistance) || myDumpEnabled)
2063
 
      {
2064
 
        return noise;
2065
 
      }
2066
 
      else
2067
 
      {
2068
 
        double t = (1.5327 * r * 0.01E-6);
2069
 
        uInt32 needed = (uInt32)(t * 1.19E6);
2070
 
        if((mySystem->cycles() - myDumpDisabledCycle) > needed)
2071
 
        {
2072
 
          return 0x80 | noise;
2073
 
        }
2074
 
        else
2075
 
        {
2076
 
          return noise;
2077
 
        }
2078
 
      }
2079
 
    }
2080
 
 
2081
 
    case 0x0A:    // INPT2
2082
 
    {
2083
 
      Int32 r = myConsole.controller(Controller::Right).read(Controller::Nine);
2084
 
      if(r == Controller::minimumResistance)
2085
 
      {
2086
 
        return 0x80 | noise;
2087
 
      }
2088
 
      else if((r == Controller::maximumResistance) || myDumpEnabled)
2089
 
      {
2090
 
        return noise;
2091
 
      }
2092
 
      else
2093
 
      {
2094
 
        double t = (1.5327 * r * 0.01E-6);
2095
 
        uInt32 needed = (uInt32)(t * 1.19E6);
2096
 
        if((mySystem->cycles() - myDumpDisabledCycle) > needed)
2097
 
        {
2098
 
          return 0x80 | noise;
2099
 
        }
2100
 
        else
2101
 
        {
2102
 
          return noise;
2103
 
        }
2104
 
      }
2105
 
    }
2106
 
 
2107
 
    case 0x0B:    // INPT3
2108
 
    {
2109
 
      Int32 r = myConsole.controller(Controller::Right).read(Controller::Five);
2110
 
      if(r == Controller::minimumResistance)
2111
 
      {
2112
 
        return 0x80 | noise;
2113
 
      }
2114
 
      else if((r == Controller::maximumResistance) || myDumpEnabled)
2115
 
      {
2116
 
        return noise;
2117
 
      }
2118
 
      else
2119
 
      {
2120
 
        double t = (1.5327 * r * 0.01E-6);
2121
 
        uInt32 needed = (uInt32)(t * 1.19E6);
2122
 
        if((mySystem->cycles() - myDumpDisabledCycle) > needed)
2123
 
        {
2124
 
          return 0x80 | noise;
2125
 
        }
2126
 
        else
2127
 
        {
2128
 
          return noise;
2129
 
        }
2130
 
      }
2131
 
    }
2132
 
 
2133
 
    case 0x0C:    // INPT4
2134
 
      return myConsole.controller(Controller::Left).read(Controller::Six) ?
2135
 
          (0x80 | noise) : noise;
2136
 
 
2137
 
    case 0x0D:    // INPT5
2138
 
      return myConsole.controller(Controller::Right).read(Controller::Six) ?
2139
 
          (0x80 | noise) : noise;
2140
 
 
2141
 
    case 0x0e:
2142
 
      return noise;
 
1170
    case CXM0P:
 
1171
      value = (value & 0x3F) |
 
1172
              ((collision & Cx_M0P1) ? 0x80 : 0x00) |
 
1173
              ((collision & Cx_M0P0) ? 0x40 : 0x00);
 
1174
      break;
 
1175
 
 
1176
    case CXM1P:
 
1177
      value = (value & 0x3F) |
 
1178
              ((collision & Cx_M1P0) ? 0x80 : 0x00) |
 
1179
              ((collision & Cx_M1P1) ? 0x40 : 0x00);
 
1180
      break;
 
1181
 
 
1182
    case CXP0FB:
 
1183
      value = (value & 0x3F) |
 
1184
              ((collision & Cx_P0PF) ? 0x80 : 0x00) |
 
1185
              ((collision & Cx_P0BL) ? 0x40 : 0x00);
 
1186
      break;
 
1187
 
 
1188
    case CXP1FB:
 
1189
      value = (value & 0x3F) |
 
1190
              ((collision & Cx_P1PF) ? 0x80 : 0x00) |
 
1191
              ((collision & Cx_P1BL) ? 0x40 : 0x00);
 
1192
      break;
 
1193
 
 
1194
    case CXM0FB:
 
1195
      value = (value & 0x3F) |
 
1196
              ((collision & Cx_M0PF) ? 0x80 : 0x00) |
 
1197
              ((collision & Cx_M0BL) ? 0x40 : 0x00);
 
1198
      break;
 
1199
 
 
1200
    case CXM1FB:
 
1201
      value = (value & 0x3F) |
 
1202
              ((collision & Cx_M1PF) ? 0x80 : 0x00) |
 
1203
              ((collision & Cx_M1BL) ? 0x40 : 0x00);
 
1204
      break;
 
1205
 
 
1206
    case CXBLPF:
 
1207
      value = (value & 0x7F) | ((collision & Cx_BLPF) ? 0x80 : 0x00);
 
1208
      break;
 
1209
 
 
1210
    case CXPPMM:
 
1211
      value = (value & 0x3F) |
 
1212
              ((collision & Cx_P0P1) ? 0x80 : 0x00) |
 
1213
              ((collision & Cx_M0M1) ? 0x40 : 0x00);
 
1214
      break;
 
1215
 
 
1216
    case INPT0:
 
1217
      value = (value & 0x7F) |
 
1218
        dumpedInputPort(myConsole.controller(Controller::Left).read(Controller::Nine));
 
1219
      break;
 
1220
 
 
1221
    case INPT1:
 
1222
      value = (value & 0x7F) |
 
1223
        dumpedInputPort(myConsole.controller(Controller::Left).read(Controller::Five));
 
1224
      break;
 
1225
 
 
1226
    case INPT2:
 
1227
      value = (value & 0x7F) |
 
1228
        dumpedInputPort(myConsole.controller(Controller::Right).read(Controller::Nine));
 
1229
      break;
 
1230
 
 
1231
    case INPT3:
 
1232
      value = (value & 0x7F) |
 
1233
        dumpedInputPort(myConsole.controller(Controller::Right).read(Controller::Five));
 
1234
      break;
 
1235
 
 
1236
    case INPT4:
 
1237
      value = (value & 0x7F) |
 
1238
        (myConsole.controller(Controller::Left).read(Controller::Six) ? 0x80 : 0x00);
 
1239
      break;
 
1240
 
 
1241
    case INPT5:
 
1242
      value = (value & 0x7F) |
 
1243
        (myConsole.controller(Controller::Right).read(Controller::Six) ? 0x80 : 0x00);
 
1244
      break;
2143
1245
 
2144
1246
    default:
2145
 
      return noise;
 
1247
      break;
2146
1248
  }
 
1249
  return value;
2147
1250
}
2148
1251
 
2149
1252
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2150
 
void TIA::poke(uInt16 addr, uInt8 value)
 
1253
bool TIA::poke(uInt16 addr, uInt8 value)
2151
1254
{
2152
1255
  addr = addr & 0x003f;
2153
1256
 
2154
1257
  Int32 clock = mySystem->cycles() * 3;
2155
 
  Int16 delay = ourPokeDelayTable[addr];
 
1258
  Int16 delay = TIATables::PokeDelay[addr];
2156
1259
 
2157
1260
  // See if this is a poke to a PF register
2158
1261
  if(delay == -1)
2166
1269
  updateFrame(clock + delay);
2167
1270
 
2168
1271
  // If a VSYNC hasn't been generated in time go ahead and end the frame
2169
 
  if(((clock - myClockWhenFrameStarted) / 228) > myMaximumNumberOfScanlines)
 
1272
  if(((clock - myClockWhenFrameStarted) / 228) > (Int32)myMaximumNumberOfScanlines)
2170
1273
  {
2171
1274
    mySystem->m6502().stop();
2172
1275
    myPartialFrameFlag = false;
2174
1277
 
2175
1278
  switch(addr)
2176
1279
  {
2177
 
    case 0x00:    // Vertical sync set-clear
 
1280
    case VSYNC:    // Vertical sync set-clear
2178
1281
    {
2179
1282
      myVSYNC = value;
2180
1283
 
2197
1300
      break;
2198
1301
    }
2199
1302
 
2200
 
    case 0x01:    // Vertical blank set-clear
 
1303
    case VBLANK:  // Vertical blank set-clear
2201
1304
    {
2202
1305
      // Is the dump to ground path being set for I0, I1, I2, and I3?
2203
1306
      if(!(myVBLANK & 0x80) && (value & 0x80))
2204
1307
      {
2205
1308
        myDumpEnabled = true;
2206
1309
      }
2207
 
 
2208
1310
      // Is the dump to ground path being removed from I0, I1, I2, and I3?
2209
 
      if((myVBLANK & 0x80) && !(value & 0x80))
 
1311
      else if((myVBLANK & 0x80) && !(value & 0x80))
2210
1312
      {
2211
1313
        myDumpEnabled = false;
2212
1314
        myDumpDisabledCycle = mySystem->cycles();
2213
1315
      }
2214
1316
 
 
1317
#if 0 // TODO - this isn't yet complete
 
1318
      // Check for the first scanline at which VBLANK is disabled.
 
1319
      // Usually, this will be the first scanline to start drawing.
 
1320
      if(myStartScanline == 0x7FFFFFFF && !(value & 0x10))
 
1321
        myStartScanline = scanlines();
 
1322
#endif
 
1323
 
2215
1324
      myVBLANK = value;
2216
1325
      break;
2217
1326
    }
2218
1327
 
2219
 
    case 0x02:    // Wait for leading edge of HBLANK
 
1328
    case WSYNC:   // Wait for leading edge of HBLANK
2220
1329
    {
2221
1330
      // It appears that the 6507 only halts during a read cycle so
2222
1331
      // we test here for follow-on writes which should be ignored as
2233
1342
      break;
2234
1343
    }
2235
1344
 
2236
 
    case 0x03:    // Reset horizontal sync counter
 
1345
    case RSYNC:   // Reset horizontal sync counter
2237
1346
    {
2238
1347
//      cerr << "TIA Poke: " << hex << addr << endl;
2239
1348
      break;
2240
1349
    }
2241
1350
 
2242
 
    case 0x04:    // Number-size of player-missle 0
 
1351
    case NUSIZ0:  // Number-size of player-missle 0
2243
1352
    {
 
1353
      // TODO - 08-11-2009: determine correct delay instead of always
 
1354
      // using '8'.
2244
1355
      myNUSIZ0 = value;
2245
 
 
2246
 
      // TODO: Technically the "enable" part, [0], should depend on the current
2247
 
      // enabled or disabled state.  This mean we probably need a data member
2248
 
      // to maintain that state (01/21/99).
2249
 
      myCurrentP0Mask = &ourPlayerMaskTable[myPOSP0 & 0x03]
2250
 
          [0][myNUSIZ0 & 0x07][160 - (myPOSP0 & 0xFC)];
2251
 
 
2252
 
      myCurrentM0Mask = &ourMissleMaskTable[myPOSM0 & 0x03]
2253
 
          [myNUSIZ0 & 0x07][(myNUSIZ0 & 0x30) >> 4][160 - (myPOSM0 & 0xFC)];
2254
 
 
 
1356
      mySuppressP0 = 0;
2255
1357
      break;
2256
1358
    }
2257
1359
 
2258
 
    case 0x05:    // Number-size of player-missle 1
 
1360
    case NUSIZ1:  // Number-size of player-missle 1
2259
1361
    {
 
1362
      // TODO - 08-11-2009: determine correct delay instead of always
 
1363
      // using '8'.
2260
1364
      myNUSIZ1 = value;
2261
 
 
2262
 
      // TODO: Technically the "enable" part, [0], should depend on the current
2263
 
      // enabled or disabled state.  This mean we probably need a data member
2264
 
      // to maintain that state (01/21/99).
2265
 
      myCurrentP1Mask = &ourPlayerMaskTable[myPOSP1 & 0x03]
2266
 
          [0][myNUSIZ1 & 0x07][160 - (myPOSP1 & 0xFC)];
2267
 
 
2268
 
      myCurrentM1Mask = &ourMissleMaskTable[myPOSM1 & 0x03]
2269
 
          [myNUSIZ1 & 0x07][(myNUSIZ1 & 0x30) >> 4][160 - (myPOSM1 & 0xFC)];
2270
 
 
2271
 
      break;
2272
 
    }
2273
 
 
2274
 
    case 0x06:    // Color-Luminance Player 0
2275
 
    {
2276
 
      uInt32 color = (uInt32)(value & 0xfe);
2277
 
      if(myColorLossEnabled && (myScanlineCountForLastFrame & 0x01))
2278
 
      {
2279
 
        color |= 0x01;
2280
 
      }
2281
 
      myCOLUP0 = (((((color << 8) | color) << 8) | color) << 8) | color;
2282
 
      break;
2283
 
    }
2284
 
 
2285
 
    case 0x07:    // Color-Luminance Player 1
2286
 
    {
2287
 
      uInt32 color = (uInt32)(value & 0xfe);
2288
 
      if(myColorLossEnabled && (myScanlineCountForLastFrame & 0x01))
2289
 
      {
2290
 
        color |= 0x01;
2291
 
      }
2292
 
      myCOLUP1 = (((((color << 8) | color) << 8) | color) << 8) | color;
2293
 
      break;
2294
 
    }
2295
 
 
2296
 
    case 0x08:    // Color-Luminance Playfield
2297
 
    {
2298
 
      uInt32 color = (uInt32)(value & 0xfe);
2299
 
      if(myColorLossEnabled && (myScanlineCountForLastFrame & 0x01))
2300
 
      {
2301
 
        color |= 0x01;
2302
 
      }
2303
 
      myCOLUPF = (((((color << 8) | color) << 8) | color) << 8) | color;
2304
 
      break;
2305
 
    }
2306
 
 
2307
 
    case 0x09:    // Color-Luminance Background
2308
 
    {
2309
 
      uInt32 color = (uInt32)(value & 0xfe);
2310
 
      if(myColorLossEnabled && (myScanlineCountForLastFrame & 0x01))
2311
 
      {
2312
 
        color |= 0x01;
2313
 
      }
2314
 
      myCOLUBK = (((((color << 8) | color) << 8) | color) << 8) | color;
2315
 
      break;
2316
 
    }
2317
 
 
2318
 
    case 0x0A:    // Control Playfield, Ball size, Collisions
 
1365
      mySuppressP1 = 0;
 
1366
      break;
 
1367
    }
 
1368
 
 
1369
    case COLUP0:  // Color-Luminance Player 0
 
1370
    {
 
1371
      uInt32 color = (uInt32)(value & 0xfe);
 
1372
      if(myColorLossEnabled && (myScanlineCountForLastFrame & 0x01))
 
1373
      {
 
1374
        color |= 0x01;
 
1375
      }
 
1376
      myColor[_P0] = myColor[_M0] =
 
1377
          (((((color << 8) | color) << 8) | color) << 8) | color;
 
1378
      break;
 
1379
    }
 
1380
 
 
1381
    case COLUP1:  // Color-Luminance Player 1
 
1382
    {
 
1383
      uInt32 color = (uInt32)(value & 0xfe);
 
1384
      if(myColorLossEnabled && (myScanlineCountForLastFrame & 0x01))
 
1385
      {
 
1386
        color |= 0x01;
 
1387
      }
 
1388
      myColor[_P1] = myColor[_M1] =
 
1389
          (((((color << 8) | color) << 8) | color) << 8) | color;
 
1390
      break;
 
1391
    }
 
1392
 
 
1393
    case COLUPF:  // Color-Luminance Playfield
 
1394
    {
 
1395
      uInt32 color = (uInt32)(value & 0xfe);
 
1396
      if(myColorLossEnabled && (myScanlineCountForLastFrame & 0x01))
 
1397
      {
 
1398
        color |= 0x01;
 
1399
      }
 
1400
      myColor[_PF] = myColor[_BL] =
 
1401
          (((((color << 8) | color) << 8) | color) << 8) | color;
 
1402
      break;
 
1403
    }
 
1404
 
 
1405
    case COLUBK:  // Color-Luminance Background
 
1406
    {
 
1407
      uInt32 color = (uInt32)(value & 0xfe);
 
1408
      if(myColorLossEnabled && (myScanlineCountForLastFrame & 0x01))
 
1409
      {
 
1410
        color |= 0x01;
 
1411
      }
 
1412
      myColor[_BK] = (((((color << 8) | color) << 8) | color) << 8) | color;
 
1413
      break;
 
1414
    }
 
1415
 
 
1416
    case CTRLPF:  // Control Playfield, Ball size, Collisions
2319
1417
    {
2320
1418
      myCTRLPF = value;
2321
1419
 
2327
1425
      // Update the playfield mask based on reflection state if 
2328
1426
      // we're still on the left hand side of the playfield
2329
1427
      if(((clock - myClockWhenFrameStarted) % 228) < (68 + 79))
2330
 
      {
2331
 
        myCurrentPFMask = ourPlayfieldTable[myCTRLPF & 0x01];
2332
 
      }
2333
 
 
2334
 
      myCurrentBLMask = &ourBallMaskTable[myPOSBL & 0x03]
2335
 
          [(myCTRLPF & 0x30) >> 4][160 - (myPOSBL & 0xFC)];
 
1428
        myCurrentPFMask = TIATables::PFMask[myCTRLPF & 0x01];
2336
1429
 
2337
1430
      break;
2338
1431
    }
2339
1432
 
2340
 
    case 0x0B:    // Reflect Player 0
 
1433
    case REFP0:   // Reflect Player 0
2341
1434
    {
2342
1435
      // See if the reflection state of the player is being changed
2343
1436
      if(((value & 0x08) && !myREFP0) || (!(value & 0x08) && myREFP0))
2344
1437
      {
2345
1438
        myREFP0 = (value & 0x08);
2346
 
        myCurrentGRP0 = ourPlayerReflectTable[myCurrentGRP0];
 
1439
        myCurrentGRP0 = TIATables::GRPReflect[myCurrentGRP0];
2347
1440
      }
2348
1441
      break;
2349
1442
    }
2350
1443
 
2351
 
    case 0x0C:    // Reflect Player 1
 
1444
    case REFP1:   // Reflect Player 1
2352
1445
    {
2353
1446
      // See if the reflection state of the player is being changed
2354
1447
      if(((value & 0x08) && !myREFP1) || (!(value & 0x08) && myREFP1))
2355
1448
      {
2356
1449
        myREFP1 = (value & 0x08);
2357
 
        myCurrentGRP1 = ourPlayerReflectTable[myCurrentGRP1];
 
1450
        myCurrentGRP1 = TIATables::GRPReflect[myCurrentGRP1];
2358
1451
      }
2359
1452
      break;
2360
1453
    }
2361
1454
 
2362
 
    case 0x0D:    // Playfield register byte 0
 
1455
    case PF0:     // Playfield register byte 0
2363
1456
    {
2364
1457
      myPF = (myPF & 0x000FFFF0) | ((value >> 4) & 0x0F);
2365
1458
 
2366
 
      if(!myBitEnabled[TIA::PF] || myPF == 0)
2367
 
        myEnabledObjects &= ~myPFBit;
 
1459
      if(myPF == 0)
 
1460
        myEnabledObjects &= ~PFBit;
2368
1461
      else
2369
 
        myEnabledObjects |= myPFBit;
 
1462
        myEnabledObjects |= PFBit;
2370
1463
 
2371
1464
      break;
2372
1465
    }
2373
1466
 
2374
 
    case 0x0E:    // Playfield register byte 1
 
1467
    case PF1:     // Playfield register byte 1
2375
1468
    {
2376
1469
      myPF = (myPF & 0x000FF00F) | ((uInt32)value << 4);
2377
1470
 
2378
 
      if(!myBitEnabled[TIA::PF] || myPF == 0)
2379
 
        myEnabledObjects &= ~myPFBit;
 
1471
      if(myPF == 0)
 
1472
        myEnabledObjects &= ~PFBit;
2380
1473
      else
2381
 
        myEnabledObjects |= myPFBit;
 
1474
        myEnabledObjects |= PFBit;
2382
1475
 
2383
1476
      break;
2384
1477
    }
2385
1478
 
2386
 
    case 0x0F:    // Playfield register byte 2
 
1479
    case PF2:     // Playfield register byte 2
2387
1480
    {
2388
1481
      myPF = (myPF & 0x00000FFF) | ((uInt32)value << 12);
2389
1482
 
2390
 
      if(!myBitEnabled[TIA::PF] || myPF == 0)
2391
 
        myEnabledObjects &= ~myPFBit;
2392
 
      else
2393
 
        myEnabledObjects |= myPFBit;
2394
 
 
2395
 
      break;
2396
 
    }
2397
 
 
2398
 
    case 0x10:    // Reset Player 0
2399
 
    {
2400
 
      Int32 hpos = (clock - myClockWhenFrameStarted) % 228;
2401
 
      Int32 newx = hpos < HBLANK ? 3 : (((hpos - HBLANK) + 5) % 160);
2402
 
 
2403
 
      // Find out under what condition the player is being reset
2404
 
      Int8 when = ourPlayerPositionResetWhenTable[myNUSIZ0 & 7][myPOSP0][newx];
2405
 
 
2406
 
#ifdef DEBUG_HMOVE
2407
 
      if((clock - myLastHMOVEClock) < (24 * 3))
2408
 
        cerr << "Reset Player 0 within 24 cycles of HMOVE: "
2409
 
             << ((clock - myLastHMOVEClock)/3)
2410
 
             << "  hpos: " << hpos << ", newx = " << newx << endl;
2411
 
#endif
2412
 
 
2413
 
      // Player is being reset during the display of one of its copies
2414
 
      if(when == 1)
2415
 
      {
2416
 
        // So we go ahead and update the display before moving the player
2417
 
        // TODO: The 11 should depend on how much of the player has already
2418
 
        // been displayed.  Probably change table to return the amount to
2419
 
        // delay by instead of just 1 (01/21/99).
2420
 
        updateFrame(clock + 11);
2421
 
 
2422
 
        myPOSP0 = newx;
2423
 
 
2424
 
        // Setup the mask to skip the first copy of the player
2425
 
        myCurrentP0Mask = &ourPlayerMaskTable[myPOSP0 & 0x03]
2426
 
            [1][myNUSIZ0 & 0x07][160 - (myPOSP0 & 0xFC)];
2427
 
      }
2428
 
      // Player is being reset in neither the delay nor display section
2429
 
      else if(when == 0)
2430
 
      {
2431
 
        myPOSP0 = newx;
2432
 
 
2433
 
        // So we setup the mask to skip the first copy of the player
2434
 
        myCurrentP0Mask = &ourPlayerMaskTable[myPOSP0 & 0x03]
2435
 
            [1][myNUSIZ0 & 0x07][160 - (myPOSP0 & 0xFC)];
2436
 
      }
2437
 
      // Player is being reset during the delay section of one of its copies
2438
 
      else if(when == -1)
2439
 
      {
2440
 
        myPOSP0 = newx;
2441
 
 
2442
 
        // So we setup the mask to display all copies of the player
2443
 
        myCurrentP0Mask = &ourPlayerMaskTable[myPOSP0 & 0x03]
2444
 
            [0][myNUSIZ0 & 0x07][160 - (myPOSP0 & 0xFC)];
2445
 
      }
2446
 
      break;
2447
 
    }
2448
 
 
2449
 
    case 0x11:    // Reset Player 1
2450
 
    {
2451
 
      Int32 hpos = (clock - myClockWhenFrameStarted) % 228;
2452
 
      Int32 newx = hpos < HBLANK ? 3 : (((hpos - HBLANK) + 5) % 160);
2453
 
 
2454
 
      // Find out under what condition the player is being reset
2455
 
      Int8 when = ourPlayerPositionResetWhenTable[myNUSIZ1 & 7][myPOSP1][newx];
2456
 
 
2457
 
#ifdef DEBUG_HMOVE
2458
 
      if((clock - myLastHMOVEClock) < (24 * 3))
2459
 
        cerr << "Reset Player 1 within 24 cycles of HMOVE: "
2460
 
             << ((clock - myLastHMOVEClock)/3)
2461
 
             << "  hpos: " << hpos << ", newx = " << newx << endl;
2462
 
#endif
2463
 
 
2464
 
      // Player is being reset during the display of one of its copies
2465
 
      if(when == 1)
2466
 
      {
2467
 
        // So we go ahead and update the display before moving the player
2468
 
        // TODO: The 11 should depend on how much of the player has already
2469
 
        // been displayed.  Probably change table to return the amount to
2470
 
        // delay by instead of just 1 (01/21/99).
2471
 
        updateFrame(clock + 11);
2472
 
 
2473
 
        myPOSP1 = newx;
2474
 
 
2475
 
        // Setup the mask to skip the first copy of the player
2476
 
        myCurrentP1Mask = &ourPlayerMaskTable[myPOSP1 & 0x03]
2477
 
            [1][myNUSIZ1 & 0x07][160 - (myPOSP1 & 0xFC)];
2478
 
      }
2479
 
      // Player is being reset in neither the delay nor display section
2480
 
      else if(when == 0)
2481
 
      {
2482
 
        myPOSP1 = newx;
2483
 
 
2484
 
        // So we setup the mask to skip the first copy of the player
2485
 
        myCurrentP1Mask = &ourPlayerMaskTable[myPOSP1 & 0x03]
2486
 
            [1][myNUSIZ1 & 0x07][160 - (myPOSP1 & 0xFC)];
2487
 
      }
2488
 
      // Player is being reset during the delay section of one of its copies
2489
 
      else if(when == -1)
2490
 
      {
2491
 
        myPOSP1 = newx;
2492
 
 
2493
 
        // So we setup the mask to display all copies of the player
2494
 
        myCurrentP1Mask = &ourPlayerMaskTable[myPOSP1 & 0x03]
2495
 
            [0][myNUSIZ1 & 0x07][160 - (myPOSP1 & 0xFC)];
2496
 
      }
2497
 
      break;
2498
 
    }
2499
 
 
2500
 
    case 0x12:    // Reset Missle 0
2501
 
    {
2502
 
      int hpos = (clock - myClockWhenFrameStarted) % 228;
2503
 
      myPOSM0 = hpos < HBLANK ? 2 : (((hpos - HBLANK) + 4) % 160);
2504
 
 
2505
 
#ifdef DEBUG_HMOVE
2506
 
      if((clock - myLastHMOVEClock) < (24 * 3))
2507
 
        cerr << "Reset Missle 0 within 24 cycles of HMOVE: "
2508
 
             << ((clock - myLastHMOVEClock)/3)
2509
 
             << "  hpos: " << hpos << ", myPOSM0 = " << myPOSM0 << endl;
2510
 
#endif
2511
 
 
2512
 
      // TODO: Remove the following special hack for Dolphin by
2513
 
      // figuring out what really happens when Reset Missle 
2514
 
      // occurs 20 cycles after an HMOVE (04/13/02).
2515
 
      if(((clock - myLastHMOVEClock) == (20 * 3)) && (hpos == 69))
2516
 
      {
2517
 
        myPOSM0 = 8;
2518
 
      }
2519
 
      // TODO: Remove the following special hack for Solaris by
2520
 
      // figuring out what really happens when Reset Missle 
2521
 
      // occurs 9 cycles after an HMOVE (04/11/08).
2522
 
      else if(((clock - myLastHMOVEClock) == (9 * 3)) && (hpos == 36))
2523
 
      {
2524
 
        myPOSM0 = 8;
2525
 
      }
2526
 
 
2527
 
      myCurrentM0Mask = &ourMissleMaskTable[myPOSM0 & 0x03]
2528
 
          [myNUSIZ0 & 0x07][(myNUSIZ0 & 0x30) >> 4][160 - (myPOSM0 & 0xFC)];
2529
 
      break;
2530
 
    }
2531
 
 
2532
 
    case 0x13:    // Reset Missle 1
2533
 
    {
2534
 
      int hpos = (clock - myClockWhenFrameStarted) % 228;
2535
 
      myPOSM1 = hpos < HBLANK ? 2 : (((hpos - HBLANK) + 4) % 160);
2536
 
 
2537
 
#ifdef DEBUG_HMOVE
2538
 
      if((clock - myLastHMOVEClock) < (24 * 3))
2539
 
        cerr << "Reset Missle 1 within 24 cycles of HMOVE: "
2540
 
             << ((clock - myLastHMOVEClock)/3)
2541
 
             << "  hpos: " << hpos << ", myPOSM1 = " << myPOSM1 << endl;
2542
 
#endif
2543
 
 
2544
 
      // TODO: Remove the following special hack for Pitfall II by
2545
 
      // figuring out what really happens when Reset Missle 
2546
 
      // occurs 3 cycles after an HMOVE (04/13/02).
2547
 
      if(((clock - myLastHMOVEClock) == (3 * 3)) && (hpos == 18))
2548
 
      {
2549
 
        myPOSM1 = 3;
2550
 
      }
2551
 
 
2552
 
      myCurrentM1Mask = &ourMissleMaskTable[myPOSM1 & 0x03]
2553
 
          [myNUSIZ1 & 0x07][(myNUSIZ1 & 0x30) >> 4][160 - (myPOSM1 & 0xFC)];
2554
 
      break;
2555
 
    }
2556
 
 
2557
 
    case 0x14:    // Reset Ball
2558
 
    {
2559
 
      int hpos = (clock - myClockWhenFrameStarted) % 228 ;
2560
 
      myPOSBL = hpos < HBLANK ? 2 : (((hpos - HBLANK) + 4) % 160);
2561
 
 
2562
 
#ifdef DEBUG_HMOVE
2563
 
      if((clock - myLastHMOVEClock) < (24 * 3))
2564
 
        cerr << "Reset Ball within 24 cycles of HMOVE: "
2565
 
             << ((clock - myLastHMOVEClock)/3)
2566
 
             << "  hpos: " << hpos << ", myPOSBL = " << myPOSBL << endl;
2567
 
#endif
2568
 
 
2569
 
      // TODO: Remove the following special hack by figuring out what
2570
 
      // really happens when Reset Ball occurs 18 cycles after an HMOVE.
2571
 
      if((clock - myLastHMOVEClock) == (18 * 3))
2572
 
      {
2573
 
        // Escape from the Mindmaster (01/09/99)
2574
 
        if((hpos == 60) || (hpos == 69))
2575
 
          myPOSBL = 10;
2576
 
        // Mission Survive (04/11/08)
2577
 
        else if(hpos == 63)
2578
 
          myPOSBL = 7;
2579
 
      }
2580
 
      // TODO: Remove the following special hack for Escape from the
2581
 
      // Mindmaster by figuring out what really happens when Reset Ball 
2582
 
      // occurs 15 cycles after an HMOVE (04/11/08).
2583
 
      else if(((clock - myLastHMOVEClock) == (15 * 3)) && (hpos == 60))
2584
 
      {
2585
 
        myPOSBL = 10;
2586
 
      } 
2587
 
      // TODO: Remove the following special hack for Decathlon by
2588
 
      // figuring out what really happens when Reset Ball 
2589
 
      // occurs 3 cycles after an HMOVE (04/13/02).
2590
 
      else if(((clock - myLastHMOVEClock) == (3 * 3)) && (hpos == 18))
2591
 
      {
2592
 
        myPOSBL = 3;
2593
 
      } 
2594
 
      // TODO: Remove the following special hack for Robot Tank by
2595
 
      // figuring out what really happens when Reset Ball 
2596
 
      // occurs 7 cycles after an HMOVE (04/13/02).
2597
 
      else if(((clock - myLastHMOVEClock) == (7 * 3)) && (hpos == 30))
2598
 
      {
2599
 
        myPOSBL = 6;
2600
 
      } 
2601
 
      // TODO: Remove the following special hack for Hole Hunter by
2602
 
      // figuring out what really happens when Reset Ball 
2603
 
      // occurs 6 cycles after an HMOVE (04/13/02).
2604
 
      else if(((clock - myLastHMOVEClock) == (6 * 3)) && (hpos == 27))
2605
 
      {
2606
 
        myPOSBL = 5;
2607
 
      }
2608
 
      // TODO: Remove the following special hack for Swoops! by
2609
 
      // figuring out what really happens when Reset Ball 
2610
 
      // occurs 9 cycles after an HMOVE (04/11/08).
2611
 
      else if(((clock - myLastHMOVEClock) == (9 * 3)) && (hpos == 36))
2612
 
      {
2613
 
        myPOSBL = 7;
2614
 
      }
2615
 
      // TODO: Remove the following special hack for Solaris by
2616
 
      // figuring out what really happens when Reset Ball 
2617
 
      // occurs 12 cycles after an HMOVE (04/11/08).
2618
 
      else if(((clock - myLastHMOVEClock) == (12 * 3)) && (hpos == 45))
2619
 
      {
2620
 
        myPOSBL = 8;
2621
 
      }
2622
 
 
2623
 
      myCurrentBLMask = &ourBallMaskTable[myPOSBL & 0x03]
2624
 
          [(myCTRLPF & 0x30) >> 4][160 - (myPOSBL & 0xFC)];
2625
 
      break;
2626
 
    }
2627
 
 
2628
 
    case 0x15:    // Audio control 0
 
1483
      if(myPF == 0)
 
1484
        myEnabledObjects &= ~PFBit;
 
1485
      else
 
1486
        myEnabledObjects |= PFBit;
 
1487
 
 
1488
      break;
 
1489
    }
 
1490
 
 
1491
    case RESP0:   // Reset Player 0
 
1492
    {
 
1493
      Int32 hpos = (clock - myClockWhenFrameStarted) % 228 - HBLANK;
 
1494
      Int16 newx;
 
1495
 
 
1496
      // Check if HMOVE is currently active
 
1497
      if(myCurrentHMOVEPos != 0x7FFFFFFF)
 
1498
      {
 
1499
        newx = hpos < 7 ? 3 : ((hpos + 5) % 160);
 
1500
        // If HMOVE is active, adjust for any remaining horizontal move clocks
 
1501
        applyActiveHMOVEMotion(hpos, newx, myMotionClockP0);
 
1502
      }
 
1503
      else
 
1504
      {
 
1505
        newx = hpos < -2 ? 3 : ((hpos + 5) % 160);
 
1506
        applyPreviousHMOVEMotion(hpos, newx, myHMP0);
 
1507
      }
 
1508
      if(myPOSP0 != newx)
 
1509
      {
 
1510
        // Find out under what condition the player is being reset
 
1511
        delay = TIATables::PxPosResetWhen[myNUSIZ0 & 7][myPOSP0][newx];
 
1512
 
 
1513
        switch(delay)
 
1514
        {
 
1515
          // Player is being reset during the display of one of its copies
 
1516
          case 1:
 
1517
            // TODO - 08-20-2009: determine whether we really need to update
 
1518
            // the frame here, and also come up with a way to eliminate the
 
1519
            // 200KB PxPosResetWhen table.
 
1520
            updateFrame(clock + 11);
 
1521
            mySuppressP0 = 1;
 
1522
            break;
 
1523
 
 
1524
          // Player is being reset in neither the delay nor display section
 
1525
          case 0:
 
1526
            mySuppressP0 = 1;
 
1527
            break;
 
1528
 
 
1529
          // Player is being reset during the delay section of one of its copies
 
1530
          case -1:
 
1531
            mySuppressP0 = 0;
 
1532
            break;
 
1533
        }
 
1534
        myPOSP0 = newx;
 
1535
      }
 
1536
      break;
 
1537
    }
 
1538
 
 
1539
    case RESP1:   // Reset Player 1
 
1540
    {
 
1541
      Int32 hpos = (clock - myClockWhenFrameStarted) % 228 - HBLANK;
 
1542
      Int16 newx;
 
1543
 
 
1544
      // Check if HMOVE is currently active
 
1545
      if(myCurrentHMOVEPos != 0x7FFFFFFF)
 
1546
      {
 
1547
        newx = hpos < 7 ? 3 : ((hpos + 5) % 160);
 
1548
        // If HMOVE is active, adjust for any remaining horizontal move clocks
 
1549
        applyActiveHMOVEMotion(hpos, newx, myMotionClockP1);
 
1550
      }
 
1551
      else
 
1552
      {
 
1553
        newx = hpos < -2 ? 3 : ((hpos + 5) % 160);
 
1554
        applyPreviousHMOVEMotion(hpos, newx, myHMP1);
 
1555
      }
 
1556
      if(myPOSP1 != newx)
 
1557
      {
 
1558
        // Find out under what condition the player is being reset
 
1559
        delay = TIATables::PxPosResetWhen[myNUSIZ1 & 7][myPOSP1][newx];
 
1560
 
 
1561
        switch(delay)
 
1562
        {
 
1563
          // Player is being reset during the display of one of its copies
 
1564
          case 1:
 
1565
            // TODO - 08-20-2009: determine whether we really need to update
 
1566
            // the frame here, and also come up with a way to eliminate the
 
1567
            // 200KB PxPosResetWhen table.
 
1568
            updateFrame(clock + 11);
 
1569
            mySuppressP1 = 1;
 
1570
            break;
 
1571
 
 
1572
          // Player is being reset in neither the delay nor display section
 
1573
          case 0:
 
1574
            mySuppressP1 = 1;
 
1575
            break;
 
1576
 
 
1577
          // Player is being reset during the delay section of one of its copies
 
1578
          case -1:
 
1579
            mySuppressP1 = 0;
 
1580
            break;
 
1581
        }
 
1582
        myPOSP1 = newx;
 
1583
      }
 
1584
      break;
 
1585
    }
 
1586
 
 
1587
    case RESM0:   // Reset Missle 0
 
1588
    {
 
1589
      Int32 hpos = (clock - myClockWhenFrameStarted) % 228 - HBLANK;
 
1590
      Int16 newx;
 
1591
 
 
1592
      // Check if HMOVE is currently active
 
1593
      if(myCurrentHMOVEPos != 0x7FFFFFFF)
 
1594
      {
 
1595
        newx = hpos < 7 ? 2 : ((hpos + 4) % 160);
 
1596
        // If HMOVE is active, adjust for any remaining horizontal move clocks
 
1597
        applyActiveHMOVEMotion(hpos, newx, myMotionClockM0);
 
1598
      }
 
1599
      else
 
1600
      {
 
1601
        newx = hpos < -1 ? 2 : ((hpos + 4) % 160);
 
1602
        applyPreviousHMOVEMotion(hpos, newx, myHMM0);
 
1603
      }
 
1604
      if(newx != myPOSM0)
 
1605
      {
 
1606
        myPOSM0 = newx;
 
1607
      }
 
1608
      break;
 
1609
    }
 
1610
 
 
1611
    case RESM1:   // Reset Missle 1
 
1612
    {
 
1613
      Int32 hpos = (clock - myClockWhenFrameStarted) % 228 - HBLANK;
 
1614
      Int16 newx;
 
1615
 
 
1616
      // Check if HMOVE is currently active
 
1617
      if(myCurrentHMOVEPos != 0x7FFFFFFF)
 
1618
      {
 
1619
        newx = hpos < 7 ? 2 : ((hpos + 4) % 160);
 
1620
        // If HMOVE is active, adjust for any remaining horizontal move clocks
 
1621
        applyActiveHMOVEMotion(hpos, newx, myMotionClockM1);
 
1622
      }
 
1623
      else
 
1624
      {
 
1625
        newx = hpos < -1 ? 2 : ((hpos + 4) % 160);
 
1626
        applyPreviousHMOVEMotion(hpos, newx, myHMM1);
 
1627
      }
 
1628
      if(newx != myPOSM1)
 
1629
      {
 
1630
        myPOSM1 = newx;
 
1631
      }
 
1632
      break;
 
1633
    }
 
1634
 
 
1635
    case RESBL:   // Reset Ball
 
1636
    {
 
1637
      Int32 hpos = (clock - myClockWhenFrameStarted) % 228 - HBLANK;
 
1638
 
 
1639
      // Check if HMOVE is currently active
 
1640
      if(myCurrentHMOVEPos != 0x7FFFFFFF)
 
1641
      {
 
1642
        myPOSBL = hpos < 7 ? 2 : ((hpos + 4) % 160);
 
1643
        // If HMOVE is active, adjust for any remaining horizontal move clocks
 
1644
        applyActiveHMOVEMotion(hpos, myPOSBL, myMotionClockBL);
 
1645
      }
 
1646
      else
 
1647
      {
 
1648
        myPOSBL = hpos < 0 ? 2 : ((hpos + 4) % 160);
 
1649
        applyPreviousHMOVEMotion(hpos, myPOSBL, myHMBL);
 
1650
      }
 
1651
      break;
 
1652
    }
 
1653
 
 
1654
    case AUDC0:   // Audio control 0
2629
1655
    {
2630
1656
      myAUDC0 = value & 0x0f;
2631
 
      mySound->set(addr, value, mySystem->cycles());
 
1657
      mySound.set(addr, value, mySystem->cycles());
2632
1658
      break;
2633
1659
    }
2634
1660
  
2635
 
    case 0x16:    // Audio control 1
 
1661
    case AUDC1:   // Audio control 1
2636
1662
    {
2637
1663
      myAUDC1 = value & 0x0f;
2638
 
      mySound->set(addr, value, mySystem->cycles());
 
1664
      mySound.set(addr, value, mySystem->cycles());
2639
1665
      break;
2640
1666
    }
2641
1667
  
2642
 
    case 0x17:    // Audio frequency 0
 
1668
    case AUDF0:   // Audio frequency 0
2643
1669
    {
2644
1670
      myAUDF0 = value & 0x1f;
2645
 
      mySound->set(addr, value, mySystem->cycles());
 
1671
      mySound.set(addr, value, mySystem->cycles());
2646
1672
      break;
2647
1673
    }
2648
1674
  
2649
 
    case 0x18:    // Audio frequency 1
 
1675
    case AUDF1:   // Audio frequency 1
2650
1676
    {
2651
1677
      myAUDF1 = value & 0x1f;
2652
 
      mySound->set(addr, value, mySystem->cycles());
 
1678
      mySound.set(addr, value, mySystem->cycles());
2653
1679
      break;
2654
1680
    }
2655
1681
  
2656
 
    case 0x19:    // Audio volume 0
 
1682
    case AUDV0:   // Audio volume 0
2657
1683
    {
2658
1684
      myAUDV0 = value & 0x0f;
2659
 
      mySound->set(addr, value, mySystem->cycles());
 
1685
      mySound.set(addr, value, mySystem->cycles());
2660
1686
      break;
2661
1687
    }
2662
1688
  
2663
 
    case 0x1A:    // Audio volume 1
 
1689
    case AUDV1:   // Audio volume 1
2664
1690
    {
2665
1691
      myAUDV1 = value & 0x0f;
2666
 
      mySound->set(addr, value, mySystem->cycles());
 
1692
      mySound.set(addr, value, mySystem->cycles());
2667
1693
      break;
2668
1694
    }
2669
1695
 
2670
 
    case 0x1B:    // Graphics Player 0
 
1696
    case GRP0:    // Graphics Player 0
2671
1697
    {
2672
1698
      // Set player 0 graphics
2673
 
      myGRP0 = (myBitEnabled[TIA::P0] ? value : 0);
 
1699
      myGRP0 = value;
2674
1700
 
2675
1701
      // Copy player 1 graphics into its delayed register
2676
1702
      myDGRP1 = myGRP1;
2677
1703
 
2678
1704
      // Get the "current" data for GRP0 base on delay register and reflect
2679
1705
      uInt8 grp0 = myVDELP0 ? myDGRP0 : myGRP0;
2680
 
      myCurrentGRP0 = myREFP0 ? ourPlayerReflectTable[grp0] : grp0; 
 
1706
      myCurrentGRP0 = myREFP0 ? TIATables::GRPReflect[grp0] : grp0; 
2681
1707
 
2682
1708
      // Get the "current" data for GRP1 base on delay register and reflect
2683
1709
      uInt8 grp1 = myVDELP1 ? myDGRP1 : myGRP1;
2684
 
      myCurrentGRP1 = myREFP1 ? ourPlayerReflectTable[grp1] : grp1; 
 
1710
      myCurrentGRP1 = myREFP1 ? TIATables::GRPReflect[grp1] : grp1; 
2685
1711
 
2686
1712
      // Set enabled object bits
2687
1713
      if(myCurrentGRP0 != 0)
2688
 
        myEnabledObjects |= myP0Bit;
 
1714
        myEnabledObjects |= P0Bit;
2689
1715
      else
2690
 
        myEnabledObjects &= ~myP0Bit;
 
1716
        myEnabledObjects &= ~P0Bit;
2691
1717
 
2692
1718
      if(myCurrentGRP1 != 0)
2693
 
        myEnabledObjects |= myP1Bit;
 
1719
        myEnabledObjects |= P1Bit;
2694
1720
      else
2695
 
        myEnabledObjects &= ~myP1Bit;
 
1721
        myEnabledObjects &= ~P1Bit;
2696
1722
 
2697
1723
      break;
2698
1724
    }
2699
1725
 
2700
 
    case 0x1C:    // Graphics Player 1
 
1726
    case GRP1:    // Graphics Player 1
2701
1727
    {
2702
1728
      // Set player 1 graphics
2703
 
      myGRP1 = (myBitEnabled[TIA::P1] ? value : 0);
 
1729
      myGRP1 = value;
2704
1730
 
2705
1731
      // Copy player 0 graphics into its delayed register
2706
1732
      myDGRP0 = myGRP0;
2710
1736
 
2711
1737
      // Get the "current" data for GRP0 base on delay register
2712
1738
      uInt8 grp0 = myVDELP0 ? myDGRP0 : myGRP0;
2713
 
      myCurrentGRP0 = myREFP0 ? ourPlayerReflectTable[grp0] : grp0; 
 
1739
      myCurrentGRP0 = myREFP0 ? TIATables::GRPReflect[grp0] : grp0; 
2714
1740
 
2715
1741
      // Get the "current" data for GRP1 base on delay register
2716
1742
      uInt8 grp1 = myVDELP1 ? myDGRP1 : myGRP1;
2717
 
      myCurrentGRP1 = myREFP1 ? ourPlayerReflectTable[grp1] : grp1; 
 
1743
      myCurrentGRP1 = myREFP1 ? TIATables::GRPReflect[grp1] : grp1; 
2718
1744
 
2719
1745
      // Set enabled object bits
2720
1746
      if(myCurrentGRP0 != 0)
2721
 
        myEnabledObjects |= myP0Bit;
 
1747
        myEnabledObjects |= P0Bit;
2722
1748
      else
2723
 
        myEnabledObjects &= ~myP0Bit;
 
1749
        myEnabledObjects &= ~P0Bit;
2724
1750
 
2725
1751
      if(myCurrentGRP1 != 0)
2726
 
        myEnabledObjects |= myP1Bit;
 
1752
        myEnabledObjects |= P1Bit;
2727
1753
      else
2728
 
        myEnabledObjects &= ~myP1Bit;
 
1754
        myEnabledObjects &= ~P1Bit;
2729
1755
 
2730
1756
      if(myVDELBL ? myDENABL : myENABL)
2731
 
        myEnabledObjects |= myBLBit;
 
1757
        myEnabledObjects |= BLBit;
2732
1758
      else
2733
 
        myEnabledObjects &= ~myBLBit;
 
1759
        myEnabledObjects &= ~BLBit;
2734
1760
 
2735
1761
      break;
2736
1762
    }
2737
1763
 
2738
 
    case 0x1D:    // Enable Missile 0 graphics
 
1764
    case ENAM0:   // Enable Missile 0 graphics
2739
1765
    {
2740
 
      myENAM0 = (myBitEnabled[TIA::M0] ? value & 0x02 : 0);
 
1766
      myENAM0 = value & 0x02;
2741
1767
 
2742
1768
      if(myENAM0 && !myRESMP0)
2743
 
        myEnabledObjects |= myM0Bit;
 
1769
        myEnabledObjects |= M0Bit;
2744
1770
      else
2745
 
        myEnabledObjects &= ~myM0Bit;
 
1771
        myEnabledObjects &= ~M0Bit;
2746
1772
      break;
2747
1773
    }
2748
1774
 
2749
 
    case 0x1E:    // Enable Missile 1 graphics
 
1775
    case ENAM1:   // Enable Missile 1 graphics
2750
1776
    {
2751
 
      myENAM1 = (myBitEnabled[TIA::M1] ? value & 0x02 : 0);
 
1777
      myENAM1 = value & 0x02;
2752
1778
 
2753
1779
      if(myENAM1 && !myRESMP1)
2754
 
        myEnabledObjects |= myM1Bit;
 
1780
        myEnabledObjects |= M1Bit;
2755
1781
      else
2756
 
        myEnabledObjects &= ~myM1Bit;
 
1782
        myEnabledObjects &= ~M1Bit;
2757
1783
      break;
2758
1784
    }
2759
1785
 
2760
 
    case 0x1F:    // Enable Ball graphics
 
1786
    case ENABL:   // Enable Ball graphics
2761
1787
    {
2762
 
      myENABL = (myBitEnabled[TIA::BL] ? value & 0x02 : 0);
 
1788
      myENABL = value & 0x02;
2763
1789
 
2764
1790
      if(myVDELBL ? myDENABL : myENABL)
2765
 
        myEnabledObjects |= myBLBit;
 
1791
        myEnabledObjects |= BLBit;
2766
1792
      else
2767
 
        myEnabledObjects &= ~myBLBit;
2768
 
 
2769
 
      break;
2770
 
    }
2771
 
 
2772
 
    case 0x20:    // Horizontal Motion Player 0
2773
 
    {
2774
 
      myHMP0 = value >> 4;
2775
 
      break;
2776
 
    }
2777
 
 
2778
 
    case 0x21:    // Horizontal Motion Player 1
2779
 
    {
2780
 
      myHMP1 = value >> 4;
2781
 
      break;
2782
 
    }
2783
 
 
2784
 
    case 0x22:    // Horizontal Motion Missle 0
2785
 
    {
2786
 
      Int8 tmp = value >> 4;
2787
 
 
2788
 
      // Should we enabled TIA M0 "bug" used for stars in Cosmic Ark?
2789
 
      if((clock == (myLastHMOVEClock + 21 * 3)) && (myHMM0 == 7) && (tmp == 6))
2790
 
      {
2791
 
        myM0CosmicArkMotionEnabled = true;
2792
 
        myM0CosmicArkCounter = 0;
2793
 
      }
2794
 
 
2795
 
      myHMM0 = tmp;
2796
 
      break;
2797
 
    }
2798
 
 
2799
 
    case 0x23:    // Horizontal Motion Missle 1
2800
 
    {
2801
 
      myHMM1 = value >> 4;
2802
 
      break;
2803
 
    }
2804
 
 
2805
 
    case 0x24:    // Horizontal Motion Ball
2806
 
    {
2807
 
      myHMBL = value >> 4;
2808
 
      break;
2809
 
    }
2810
 
 
2811
 
    case 0x25:    // Vertial Delay Player 0
 
1793
        myEnabledObjects &= ~BLBit;
 
1794
 
 
1795
      break;
 
1796
    }
 
1797
 
 
1798
    case HMP0:    // Horizontal Motion Player 0
 
1799
    {
 
1800
      pokeHMP0(value, clock);
 
1801
      break;
 
1802
    }
 
1803
 
 
1804
    case HMP1:    // Horizontal Motion Player 1
 
1805
    {
 
1806
      pokeHMP1(value, clock);
 
1807
      break;
 
1808
    }
 
1809
 
 
1810
    case HMM0:    // Horizontal Motion Missle 0
 
1811
    {
 
1812
      pokeHMM0(value, clock);
 
1813
      break;
 
1814
    }
 
1815
 
 
1816
    case HMM1:    // Horizontal Motion Missle 1
 
1817
    {
 
1818
      pokeHMM1(value, clock);
 
1819
      break;
 
1820
    }
 
1821
 
 
1822
    case HMBL:    // Horizontal Motion Ball
 
1823
    {
 
1824
      pokeHMBL(value, clock);
 
1825
      break;
 
1826
    }
 
1827
 
 
1828
    case VDELP0:  // Vertical Delay Player 0
2812
1829
    {
2813
1830
      myVDELP0 = value & 0x01;
2814
1831
 
2815
1832
      uInt8 grp0 = myVDELP0 ? myDGRP0 : myGRP0;
2816
 
      myCurrentGRP0 = myREFP0 ? ourPlayerReflectTable[grp0] : grp0; 
 
1833
      myCurrentGRP0 = myREFP0 ? TIATables::GRPReflect[grp0] : grp0; 
2817
1834
 
2818
1835
      if(myCurrentGRP0 != 0)
2819
 
        myEnabledObjects |= myP0Bit;
 
1836
        myEnabledObjects |= P0Bit;
2820
1837
      else
2821
 
        myEnabledObjects &= ~myP0Bit;
 
1838
        myEnabledObjects &= ~P0Bit;
2822
1839
      break;
2823
1840
    }
2824
1841
 
2825
 
    case 0x26:    // Vertial Delay Player 1
 
1842
    case VDELP1:  // Vertical Delay Player 1
2826
1843
    {
2827
1844
      myVDELP1 = value & 0x01;
2828
1845
 
2829
1846
      uInt8 grp1 = myVDELP1 ? myDGRP1 : myGRP1;
2830
 
      myCurrentGRP1 = myREFP1 ? ourPlayerReflectTable[grp1] : grp1; 
 
1847
      myCurrentGRP1 = myREFP1 ? TIATables::GRPReflect[grp1] : grp1; 
2831
1848
 
2832
1849
      if(myCurrentGRP1 != 0)
2833
 
        myEnabledObjects |= myP1Bit;
 
1850
        myEnabledObjects |= P1Bit;
2834
1851
      else
2835
 
        myEnabledObjects &= ~myP1Bit;
 
1852
        myEnabledObjects &= ~P1Bit;
2836
1853
      break;
2837
1854
    }
2838
1855
 
2839
 
    case 0x27:    // Vertial Delay Ball
 
1856
    case VDELBL:  // Vertical Delay Ball
2840
1857
    {
2841
1858
      myVDELBL = value & 0x01;
2842
1859
 
2843
1860
      if(myVDELBL ? myDENABL : myENABL)
2844
 
        myEnabledObjects |= myBLBit;
 
1861
        myEnabledObjects |= BLBit;
2845
1862
      else
2846
 
        myEnabledObjects &= ~myBLBit;
 
1863
        myEnabledObjects &= ~BLBit;
2847
1864
      break;
2848
1865
    }
2849
1866
 
2850
 
    case 0x28:    // Reset missle 0 to player 0
 
1867
    case RESMP0:  // Reset missle 0 to player 0
2851
1868
    {
2852
1869
      if(myRESMP0 && !(value & 0x02))
2853
1870
      {
2854
 
        uInt16 middle;
2855
 
 
2856
 
        if((myNUSIZ0 & 0x07) == 0x05)
2857
 
          middle = 8;
2858
 
        else if((myNUSIZ0 & 0x07) == 0x07)
2859
 
          middle = 16;
2860
 
        else
2861
 
          middle = 4;
2862
 
 
2863
 
        myPOSM0 = (myPOSP0 + middle) % 160;
2864
 
        myCurrentM0Mask = &ourMissleMaskTable[myPOSM0 & 0x03]
2865
 
            [myNUSIZ0 & 0x07][(myNUSIZ0 & 0x30) >> 4][160 - (myPOSM0 & 0xFC)];
 
1871
        uInt16 middle = 4;
 
1872
        switch(myNUSIZ0 & 0x07)
 
1873
        {
 
1874
          case 0x05: middle = 8;  break;  // double size
 
1875
          case 0x07: middle = 16; break;  // quad size
 
1876
        }
 
1877
        myPOSM0 = myPOSP0 + middle;
 
1878
        if(myCurrentHMOVEPos != 0x7FFFFFFF)
 
1879
        {
 
1880
          myPOSM0 -= (8 - myMotionClockP0);
 
1881
          myPOSM0 += (8 - myMotionClockM0);
 
1882
          if(myPOSM0 < 0)  myPOSM0 += 160;
 
1883
        }
 
1884
        myPOSM0 %= 160;
2866
1885
      }
2867
 
 
2868
1886
      myRESMP0 = value & 0x02;
2869
1887
 
2870
1888
      if(myENAM0 && !myRESMP0)
2871
 
        myEnabledObjects |= myM0Bit;
 
1889
        myEnabledObjects |= M0Bit;
2872
1890
      else
2873
 
        myEnabledObjects &= ~myM0Bit;
 
1891
        myEnabledObjects &= ~M0Bit;
2874
1892
 
2875
1893
      break;
2876
1894
    }
2877
1895
 
2878
 
    case 0x29:    // Reset missle 1 to player 1
 
1896
    case RESMP1:  // Reset missle 1 to player 1
2879
1897
    {
2880
1898
      if(myRESMP1 && !(value & 0x02))
2881
1899
      {
2882
 
        uInt16 middle;
2883
 
 
2884
 
        if((myNUSIZ1 & 0x07) == 0x05)
2885
 
          middle = 8;
2886
 
        else if((myNUSIZ1 & 0x07) == 0x07)
2887
 
          middle = 16;
2888
 
        else
2889
 
          middle = 4;
2890
 
 
2891
 
        myPOSM1 = (myPOSP1 + middle) % 160;
2892
 
        myCurrentM1Mask = &ourMissleMaskTable[myPOSM1 & 0x03]
2893
 
            [myNUSIZ1 & 0x07][(myNUSIZ1 & 0x30) >> 4][160 - (myPOSM1 & 0xFC)];
 
1900
        uInt16 middle = 4;
 
1901
        switch(myNUSIZ1 & 0x07)
 
1902
        {
 
1903
          case 0x05: middle = 8;  break;  // double size
 
1904
          case 0x07: middle = 16; break;  // quad size
 
1905
        }
 
1906
        myPOSM1 = myPOSP1 + middle;
 
1907
        if(myCurrentHMOVEPos != 0x7FFFFFFF)
 
1908
        {
 
1909
          myPOSM1 -= (8 - myMotionClockP1);
 
1910
          myPOSM1 += (8 - myMotionClockM1);
 
1911
          if(myPOSM1 < 0)  myPOSM1 += 160;
 
1912
        }
 
1913
        myPOSM1 %= 160;
2894
1914
      }
2895
 
 
2896
1915
      myRESMP1 = value & 0x02;
2897
1916
 
2898
1917
      if(myENAM1 && !myRESMP1)
2899
 
        myEnabledObjects |= myM1Bit;
 
1918
        myEnabledObjects |= M1Bit;
2900
1919
      else
2901
 
        myEnabledObjects &= ~myM1Bit;
 
1920
        myEnabledObjects &= ~M1Bit;
2902
1921
      break;
2903
1922
    }
2904
1923
 
2905
 
    case 0x2A:    // Apply horizontal motion
 
1924
    case HMOVE:   // Apply horizontal motion
2906
1925
    {
2907
 
      // Figure out what cycle we're at
2908
 
      Int32 x = ((clock - myClockWhenFrameStarted) % 228) / 3;
 
1926
      int hpos = (clock - myClockWhenFrameStarted) % 228 - HBLANK;
 
1927
      myCurrentHMOVEPos = hpos;
2909
1928
 
2910
1929
      // See if we need to enable the HMOVE blank bug
2911
 
      if(myAllowHMOVEBlanks && ourHMOVEBlankEnableCycles[x])
2912
 
      {
2913
 
        // TODO: Allow this to be turned off using properties...
2914
 
        myHMOVEBlankEnabled = true;
2915
 
      }
2916
 
 
2917
 
      myPOSP0 += ourCompleteMotionTable[x][myHMP0];
2918
 
      myPOSP1 += ourCompleteMotionTable[x][myHMP1];
2919
 
      myPOSM0 += ourCompleteMotionTable[x][myHMM0];
2920
 
      myPOSM1 += ourCompleteMotionTable[x][myHMM1];
2921
 
      myPOSBL += ourCompleteMotionTable[x][myHMBL];
2922
 
 
2923
 
      if(myPOSP0 >= 160)
2924
 
        myPOSP0 -= 160;
2925
 
      else if(myPOSP0 < 0)
2926
 
        myPOSP0 += 160;
2927
 
 
2928
 
      if(myPOSP1 >= 160)
2929
 
        myPOSP1 -= 160;
2930
 
      else if(myPOSP1 < 0)
2931
 
        myPOSP1 += 160;
2932
 
 
2933
 
      if(myPOSM0 >= 160)
2934
 
        myPOSM0 -= 160;
2935
 
      else if(myPOSM0 < 0)
2936
 
        myPOSM0 += 160;
2937
 
 
2938
 
      if(myPOSM1 >= 160)
2939
 
        myPOSM1 -= 160;
2940
 
      else if(myPOSM1 < 0)
2941
 
        myPOSM1 += 160;
2942
 
 
2943
 
      if(myPOSBL >= 160)
2944
 
        myPOSBL -= 160;
2945
 
      else if(myPOSBL < 0)
2946
 
        myPOSBL += 160;
2947
 
 
2948
 
      myCurrentBLMask = &ourBallMaskTable[myPOSBL & 0x03]
2949
 
          [(myCTRLPF & 0x30) >> 4][160 - (myPOSBL & 0xFC)];
2950
 
 
2951
 
      myCurrentP0Mask = &ourPlayerMaskTable[myPOSP0 & 0x03]
2952
 
          [0][myNUSIZ0 & 0x07][160 - (myPOSP0 & 0xFC)];
2953
 
      myCurrentP1Mask = &ourPlayerMaskTable[myPOSP1 & 0x03]
2954
 
          [0][myNUSIZ1 & 0x07][160 - (myPOSP1 & 0xFC)];
2955
 
 
2956
 
      myCurrentM0Mask = &ourMissleMaskTable[myPOSM0 & 0x03]
2957
 
          [myNUSIZ0 & 0x07][(myNUSIZ0 & 0x30) >> 4][160 - (myPOSM0 & 0xFC)];
2958
 
      myCurrentM1Mask = &ourMissleMaskTable[myPOSM1 & 0x03]
2959
 
          [myNUSIZ1 & 0x07][(myNUSIZ1 & 0x30) >> 4][160 - (myPOSM1 & 0xFC)];
2960
 
 
2961
 
      // Remember what clock HMOVE occured at
2962
 
      myLastHMOVEClock = clock;
2963
 
 
2964
 
      // Disable TIA M0 "bug" used for stars in Cosmic ark
2965
 
      myM0CosmicArkMotionEnabled = false;
 
1930
      myHMOVEBlankEnabled = myAllowHMOVEBlanks ? 
 
1931
        TIATables::HMOVEBlankEnableCycles[((clock - myClockWhenFrameStarted) % 228) / 3] : false;
 
1932
 
 
1933
#ifdef USE_MMR_LATCHES
 
1934
      // Do we have to undo some of the already applied cycles from an
 
1935
      // active graphics latch?
 
1936
      if(hpos + HBLANK < 17 * 4)
 
1937
      {
 
1938
        Int16 cycle_fix = 17 - ((hpos + VBLANK + 7) / 4);
 
1939
        if(myHMP0mmr)  myPOSP0 = (myPOSP0 + cycle_fix) % 160;
 
1940
        if(myHMP1mmr)  myPOSP1 = (myPOSP1 + cycle_fix) % 160;
 
1941
        if(myHMM0mmr)  myPOSM0 = (myPOSM0 + cycle_fix) % 160;
 
1942
        if(myHMM1mmr)  myPOSM1 = (myPOSM1 + cycle_fix) % 160;
 
1943
        if(myHMBLmmr)  myPOSBL = (myPOSBL + cycle_fix) % 160;
 
1944
      }
 
1945
      myHMP0mmr = myHMP1mmr = myHMM0mmr = myHMM1mmr = myHMBLmmr = false;
 
1946
#endif
 
1947
      // Can HMOVE activities be ignored?
 
1948
      if(hpos >= -5 && hpos < 97 )
 
1949
      {
 
1950
        myMotionClockP0 = 0;
 
1951
        myMotionClockP1 = 0;
 
1952
        myMotionClockM0 = 0;
 
1953
        myMotionClockM1 = 0;
 
1954
        myMotionClockBL = 0;
 
1955
        myHMOVEBlankEnabled = false;
 
1956
        myCurrentHMOVEPos = 0x7FFFFFFF;
 
1957
        break;
 
1958
      }
 
1959
 
 
1960
      myMotionClockP0 = (myHMP0 ^ 0x80) >> 4;
 
1961
      myMotionClockP1 = (myHMP1 ^ 0x80) >> 4;
 
1962
      myMotionClockM0 = (myHMM0 ^ 0x80) >> 4;
 
1963
      myMotionClockM1 = (myHMM1 ^ 0x80) >> 4;
 
1964
      myMotionClockBL = (myHMBL ^ 0x80) >> 4;
 
1965
 
 
1966
      // Adjust number of graphics motion clocks for active display
 
1967
      if(hpos >= 97 && hpos < 151)
 
1968
      {
 
1969
        Int16 skip_motclks = (160 - myCurrentHMOVEPos - 6) >> 2;
 
1970
        myMotionClockP0 -= skip_motclks;
 
1971
        myMotionClockP1 -= skip_motclks;
 
1972
        myMotionClockM0 -= skip_motclks;
 
1973
        myMotionClockM1 -= skip_motclks;
 
1974
        myMotionClockBL -= skip_motclks;
 
1975
        if(myMotionClockP0 < 0)  myMotionClockP0 = 0;
 
1976
        if(myMotionClockP1 < 0)  myMotionClockP1 = 0;
 
1977
        if(myMotionClockM0 < 0)  myMotionClockM0 = 0;
 
1978
        if(myMotionClockM1 < 0)  myMotionClockM1 = 0;
 
1979
        if(myMotionClockBL < 0)  myMotionClockBL = 0;
 
1980
      }
 
1981
 
 
1982
      if(hpos >= -56 && hpos < -5)
 
1983
      {
 
1984
        Int16 max_motclks = (7 - (myCurrentHMOVEPos + 5)) >> 2;
 
1985
        if(myMotionClockP0 > max_motclks)  myMotionClockP0 = max_motclks;
 
1986
        if(myMotionClockP1 > max_motclks)  myMotionClockP1 = max_motclks;
 
1987
        if(myMotionClockM0 > max_motclks)  myMotionClockM0 = max_motclks;
 
1988
        if(myMotionClockM1 > max_motclks)  myMotionClockM1 = max_motclks;
 
1989
        if(myMotionClockBL > max_motclks)  myMotionClockBL = max_motclks;
 
1990
      }
 
1991
 
 
1992
      // Apply horizontal motion
 
1993
      if(hpos < -5 || hpos >= 157)
 
1994
      {
 
1995
        myPOSP0 += 8 - myMotionClockP0;
 
1996
        myPOSP1 += 8 - myMotionClockP1;
 
1997
        myPOSM0 += 8 - myMotionClockM0;
 
1998
        myPOSM1 += 8 - myMotionClockM1;
 
1999
        myPOSBL += 8 - myMotionClockBL;
 
2000
      }
 
2001
 
 
2002
      // Make sure positions are in range
 
2003
      if(myPOSP0 < 0) { myPOSP0 += 160; }  myPOSP0 %= 160;
 
2004
      if(myPOSP1 < 0) { myPOSP1 += 160; }  myPOSP1 %= 160;
 
2005
      if(myPOSM0 < 0) { myPOSM0 += 160; }  myPOSM0 %= 160;
 
2006
      if(myPOSM1 < 0) { myPOSM1 += 160; }  myPOSM1 %= 160;
 
2007
      if(myPOSBL < 0) { myPOSBL += 160; }  myPOSBL %= 160;
 
2008
 
 
2009
      mySuppressP0 = mySuppressP1 = 0;
2966
2010
      break;
2967
2011
    }
2968
2012
 
2969
 
    case 0x2b:    // Clear horizontal motion registers
 
2013
    case HMCLR:   // Clear horizontal motion registers
2970
2014
    {
2971
 
      myHMP0 = 0;
2972
 
      myHMP1 = 0;
2973
 
      myHMM0 = 0;
2974
 
      myHMM1 = 0;
2975
 
      myHMBL = 0;
 
2015
      pokeHMP0(0, clock);
 
2016
      pokeHMP1(0, clock);
 
2017
      pokeHMM0(0, clock);
 
2018
      pokeHMM1(0, clock);
 
2019
      pokeHMBL(0, clock);
2976
2020
      break;
2977
2021
    }
2978
2022
 
2979
 
    case 0x2c:    // Clear collision latches
 
2023
    case CXCLR:   // Clear collision latches
2980
2024
    {
2981
2025
      myCollision = 0;
2982
2026
      break;
2990
2034
      break;
2991
2035
    }
2992
2036
  }
2993
 
}
2994
 
 
2995
 
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2996
 
uInt8 TIA::ourBallMaskTable[4][4][320];
2997
 
 
2998
 
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2999
 
uInt16 TIA::ourCollisionTable[64];
3000
 
 
3001
 
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
3002
 
uInt8 TIA::ourDisabledMaskTable[640];
3003
 
 
3004
 
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
3005
 
const Int16 TIA::ourPokeDelayTable[64] = {
3006
 
   0,  1,  0,  0,  8,  8,  0,  0,  0,  0,  0,  1,  1, -1, -1, -1,
3007
 
   0,  0,  8,  8,  0,  0,  0,  0,  0,  0,  0,  1,  1,  0,  0,  0,
3008
 
   0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
3009
 
   0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
3010
 
};
3011
 
 
3012
 
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
3013
 
uInt8 TIA::ourMissleMaskTable[4][8][4][320];
3014
 
 
3015
 
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
3016
 
const bool TIA::ourHMOVEBlankEnableCycles[76] = {
3017
 
  true,  true,  true,  true,  true,  true,  true,  true,  true,  true,   // 00
3018
 
  true,  true,  true,  true,  true,  true,  true,  true,  true,  true,   // 10
3019
 
  true,  false, false, false, false, false, false, false, false, false,  // 20
3020
 
  false, false, false, false, false, false, false, false, false, false,  // 30
3021
 
  false, false, false, false, false, false, false, false, false, false,  // 40
3022
 
  false, false, false, false, false, false, false, false, false, false,  // 50
3023
 
  false, false, false, false, false, false, false, false, false, false,  // 60
3024
 
  false, false, false, false, false, true                                // 70
3025
 
};
3026
 
 
3027
 
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
3028
 
const Int32 TIA::ourCompleteMotionTable[76][16] = {
3029
 
  { 0, -1, -2, -3, -4, -5, -6, -7,  8,  7,  6,  5,  4,  3,  2,  1}, // HBLANK
3030
 
  { 0, -1, -2, -3, -4, -5, -6, -7,  8,  7,  6,  5,  4,  3,  2,  1}, // HBLANK
3031
 
  { 0, -1, -2, -3, -4, -5, -6, -7,  8,  7,  6,  5,  4,  3,  2,  1}, // HBLANK
3032
 
  { 0, -1, -2, -3, -4, -5, -6, -7,  8,  7,  6,  5,  4,  3,  2,  1}, // HBLANK
3033
 
  { 0, -1, -2, -3, -4, -5, -6, -6,  8,  7,  6,  5,  4,  3,  2,  1}, // HBLANK
3034
 
  { 0, -1, -2, -3, -4, -5, -5, -5,  8,  7,  6,  5,  4,  3,  2,  1}, // HBLANK
3035
 
  { 0, -1, -2, -3, -4, -5, -5, -5,  8,  7,  6,  5,  4,  3,  2,  1}, // HBLANK
3036
 
  { 0, -1, -2, -3, -4, -4, -4, -4,  8,  7,  6,  5,  4,  3,  2,  1}, // HBLANK
3037
 
  { 0, -1, -2, -3, -3, -3, -3, -3,  8,  7,  6,  5,  4,  3,  2,  1}, // HBLANK
3038
 
  { 0, -1, -2, -2, -2, -2, -2, -2,  8,  7,  6,  5,  4,  3,  2,  1}, // HBLANK
3039
 
  { 0, -1, -2, -2, -2, -2, -2, -2,  8,  7,  6,  5,  4,  3,  2,  1}, // HBLANK
3040
 
  { 0, -1, -1, -1, -1, -1, -1, -1,  8,  7,  6,  5,  4,  3,  2,  1}, // HBLANK
3041
 
  { 0,  0,  0,  0,  0,  0,  0,  0,  8,  7,  6,  5,  4,  3,  2,  1}, // HBLANK
3042
 
  { 1,  1,  1,  1,  1,  1,  1,  1,  8,  7,  6,  5,  4,  3,  2,  1}, // HBLANK
3043
 
  { 1,  1,  1,  1,  1,  1,  1,  1,  8,  7,  6,  5,  4,  3,  2,  1}, // HBLANK
3044
 
  { 2,  2,  2,  2,  2,  2,  2,  2,  8,  7,  6,  5,  4,  3,  2,  2}, // HBLANK
3045
 
  { 3,  3,  3,  3,  3,  3,  3,  3,  8,  7,  6,  5,  4,  3,  3,  3}, // HBLANK
3046
 
  { 4,  4,  4,  4,  4,  4,  4,  4,  8,  7,  6,  5,  4,  4,  4,  4}, // HBLANK
3047
 
  { 4,  4,  4,  4,  4,  4,  4,  4,  8,  7,  6,  5,  4,  4,  4,  4}, // HBLANK
3048
 
  { 5,  5,  5,  5,  5,  5,  5,  5,  8,  7,  6,  5,  5,  5,  5,  5}, // HBLANK
3049
 
  { 6,  6,  6,  6,  6,  6,  6,  6,  8,  7,  6,  6,  6,  6,  6,  6}, // HBLANK
3050
 
  { 0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0},    
3051
 
  { 0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0},    
3052
 
  { 0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0},    
3053
 
  { 0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0},    
3054
 
  { 0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0},    
3055
 
  { 0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0},    
3056
 
  { 0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0},    
3057
 
  { 0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0},    
3058
 
  { 0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0},    
3059
 
  { 0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0},    
3060
 
  { 0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0},    
3061
 
  { 0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0},    
3062
 
  { 0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0},    
3063
 
  { 0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0},    
3064
 
  { 0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0},    
3065
 
  { 0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0},    
3066
 
  { 0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0},    
3067
 
  { 0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0},    
3068
 
  { 0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0},    
3069
 
  { 0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0},    
3070
 
  { 0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0},    
3071
 
  { 0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0},    
3072
 
  { 0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0},    
3073
 
  { 0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0},    
3074
 
  { 0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0},    
3075
 
  { 0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0},    
3076
 
  { 0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0},    
3077
 
  { 0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0},    
3078
 
  { 0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0},    
3079
 
  { 0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0},    
3080
 
  { 0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0},    
3081
 
  { 0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0},    
3082
 
  { 0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0},    
3083
 
  { 0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0},    
3084
 
  { 0,  0,  0,  0,  0,  0,  0, -1,  0,  0,  0,  0,  0,  0,  0,  0},    
3085
 
  { 0,  0,  0,  0,  0,  0, -1, -2,  0,  0,  0,  0,  0,  0,  0,  0},    
3086
 
  { 0,  0,  0,  0,  0, -1, -2, -3,  0,  0,  0,  0,  0,  0,  0,  0},    
3087
 
  { 0,  0,  0,  0,  0, -1, -2, -3,  0,  0,  0,  0,  0,  0,  0,  0},
3088
 
  { 0,  0,  0,  0, -1, -2, -3, -4,  0,  0,  0,  0,  0,  0,  0,  0}, 
3089
 
  { 0,  0,  0, -1, -2, -3, -4, -5,  0,  0,  0,  0,  0,  0,  0,  0},
3090
 
  { 0,  0, -1, -2, -3, -4, -5, -6,  0,  0,  0,  0,  0,  0,  0,  0},
3091
 
  { 0,  0, -1, -2, -3, -4, -5, -6,  0,  0,  0,  0,  0,  0,  0,  0},
3092
 
  { 0, -1, -2, -3, -4, -5, -6, -7,  0,  0,  0,  0,  0,  0,  0,  0},
3093
 
  {-1, -2, -3, -4, -5, -6, -7, -8,  0,  0,  0,  0,  0,  0,  0,  0},
3094
 
  {-2, -3, -4, -5, -6, -7, -8, -9,  0,  0,  0,  0,  0,  0,  0, -1},
3095
 
  {-2, -3, -4, -5, -6, -7, -8, -9,  0,  0,  0,  0,  0,  0,  0, -1},
3096
 
  {-3, -4, -5, -6, -7, -8, -9,-10,  0,  0,  0,  0,  0,  0, -1, -2}, 
3097
 
  {-4, -5, -6, -7, -8, -9,-10,-11,  0,  0,  0,  0,  0, -1, -2, -3},
3098
 
  {-5, -6, -7, -8, -9,-10,-11,-12,  0,  0,  0,  0, -1, -2, -3, -4},
3099
 
  {-5, -6, -7, -8, -9,-10,-11,-12,  0,  0,  0,  0, -1, -2, -3, -4},
3100
 
  {-6, -7, -8, -9,-10,-11,-12,-13,  0,  0,  0, -1, -2, -3, -4, -5},
3101
 
  {-7, -8, -9,-10,-11,-12,-13,-14,  0,  0, -1, -2, -3, -4, -5, -6},
3102
 
  {-8, -9,-10,-11,-12,-13,-14,-15,  0, -1, -2, -3, -4, -5, -6, -7},
3103
 
  {-8, -9,-10,-11,-12,-13,-14,-15,  0, -1, -2, -3, -4, -5, -6, -7},
3104
 
  { 0, -1, -2, -3, -4, -5, -6, -7,  8,  7,  6,  5,  4,  3,  2,  1}  // HBLANK
3105
 
};
3106
 
 
3107
 
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
3108
 
uInt8 TIA::ourPlayerMaskTable[4][2][8][320];
3109
 
 
3110
 
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
3111
 
Int8 TIA::ourPlayerPositionResetWhenTable[8][160][160];
3112
 
 
3113
 
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
3114
 
uInt8 TIA::ourPlayerReflectTable[256];
3115
 
 
3116
 
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
3117
 
uInt32 TIA::ourPlayfieldTable[2][160];
 
2037
  return true;
 
2038
}
 
2039
 
 
2040
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
2041
// Note that the following methods to change the horizontal motion registers
 
2042
// are not completely accurate.  We should be taking care of the following
 
2043
// explanation from A. Towers Hardware Notes:
 
2044
//
 
2045
//   Much more interesting is this: if the counter has not yet
 
2046
//   reached the value in HMxx (or has reached it but not yet
 
2047
//   commited the comparison) and a value with at least one bit
 
2048
//   in common with all remaining internal counter states is
 
2049
//   written (zeros or ones), the stopping condition will never be
 
2050
//   reached and the object will be moved a full 15 pixels left.
 
2051
//   In addition to this, the HMOVE will complete without clearing
 
2052
//   the "more movement required" latch, and so will continue to send
 
2053
//   an additional clock signal every 4 CLK (during visible and
 
2054
//   non-visible parts of the scanline) until another HMOVE operation
 
2055
//   clears the latch. The HMCLR command does not reset these latches.
 
2056
//
 
2057
// This condition is what causes the 'starfield effect' in Cosmic Ark,
 
2058
// and the 'snow' in Stay Frosty.  Ideally, we'd trace the counter and
 
2059
// do a compare every colour clock, updating the horizontal positions
 
2060
// when applicable.  We can save time by cheating, and noting that the
 
2061
// effect only occurs for 'magic numbers' 0x70 and 0x80.
 
2062
//
 
2063
// Most of the ideas in these methods come from MESS.
 
2064
// (used with permission from Wilbert Pol)
 
2065
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
2066
void TIA::pokeHMP0(uInt8 value, Int32 clock)
 
2067
{
 
2068
  value &= 0xF0;
 
2069
  if(myHMP0 == value)
 
2070
    return;
 
2071
 
 
2072
  int hpos  = (clock - myClockWhenFrameStarted) % 228 - HBLANK;
 
2073
 
 
2074
  // Check if HMOVE is currently active
 
2075
  if(myCurrentHMOVEPos != 0x7FFFFFFF &&
 
2076
     hpos < BSPF_min(myCurrentHMOVEPos + 6 + myMotionClockP0 * 4, 7))
 
2077
  {
 
2078
    Int32 newMotion = (value ^ 0x80) >> 4;
 
2079
    // Check if new horizontal move can still be applied normally
 
2080
    if(newMotion > myMotionClockP0 ||
 
2081
       hpos <= BSPF_min(myCurrentHMOVEPos + 6 + newMotion * 4, 7))
 
2082
    {
 
2083
      myPOSP0 -= (newMotion - myMotionClockP0);
 
2084
      myMotionClockP0 = newMotion;
 
2085
    }
 
2086
    else
 
2087
    {
 
2088
      myPOSP0 -= (15 - myMotionClockP0);
 
2089
      myMotionClockP0 = 15;
 
2090
      if(value != 0x70 && value != 0x80)
 
2091
        myHMP0mmr = true;
 
2092
    }
 
2093
    if(myPOSP0 < 0) { myPOSP0 += 160; }  myPOSP0 %= 160;
 
2094
  }
 
2095
  myHMP0 = value;
 
2096
}
 
2097
 
 
2098
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
2099
void TIA::pokeHMP1(uInt8 value, Int32 clock)
 
2100
{
 
2101
  value &= 0xF0;
 
2102
  if(myHMP1 == value)
 
2103
    return;
 
2104
 
 
2105
  int hpos  = (clock - myClockWhenFrameStarted) % 228 - HBLANK;
 
2106
 
 
2107
  // Check if HMOVE is currently active
 
2108
  if(myCurrentHMOVEPos != 0x7FFFFFFF &&
 
2109
     hpos < BSPF_min(myCurrentHMOVEPos + 6 + myMotionClockP1 * 4, 7))
 
2110
  {
 
2111
    Int32 newMotion = (value ^ 0x80) >> 4;
 
2112
    // Check if new horizontal move can still be applied normally
 
2113
    if(newMotion > myMotionClockP1 ||
 
2114
       hpos <= BSPF_min(myCurrentHMOVEPos + 6 + newMotion * 4, 7))
 
2115
    {
 
2116
      myPOSP1 -= (newMotion - myMotionClockP1);
 
2117
      myMotionClockP1 = newMotion;
 
2118
    }
 
2119
    else
 
2120
    {
 
2121
      myPOSP1 -= (15 - myMotionClockP1);
 
2122
      myMotionClockP1 = 15;
 
2123
      if(value != 0x70 && value != 0x80)
 
2124
        myHMP1mmr = true;
 
2125
    }
 
2126
    if(myPOSP1 < 0) { myPOSP1 += 160; }  myPOSP1 %= 160;
 
2127
  }
 
2128
  myHMP1 = value;
 
2129
}
 
2130
 
 
2131
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
2132
void TIA::pokeHMM0(uInt8 value, Int32 clock)
 
2133
{
 
2134
  value &= 0xF0;
 
2135
  if(myHMM0 == value)
 
2136
    return;
 
2137
 
 
2138
  int hpos  = (clock - myClockWhenFrameStarted) % 228 - HBLANK;
 
2139
 
 
2140
  // Check if HMOVE is currently active
 
2141
  if(myCurrentHMOVEPos != 0x7FFFFFFF &&
 
2142
     hpos < BSPF_min(myCurrentHMOVEPos + 6 + myMotionClockM0 * 4, 7))
 
2143
  {
 
2144
    Int32 newMotion = (value ^ 0x80) >> 4;
 
2145
    // Check if new horizontal move can still be applied normally
 
2146
    if(newMotion > myMotionClockM0 ||
 
2147
       hpos <= BSPF_min(myCurrentHMOVEPos + 6 + newMotion * 4, 7))
 
2148
    {
 
2149
      myPOSM0 -= (newMotion - myMotionClockM0);
 
2150
      myMotionClockM0 = newMotion;
 
2151
    }
 
2152
    else
 
2153
    {
 
2154
      myPOSM0 -= (15 - myMotionClockM0);
 
2155
      myMotionClockM0 = 15;
 
2156
      if(value != 0x70 && value != 0x80)
 
2157
        myHMM0mmr = true;
 
2158
    }
 
2159
    if(myPOSM0 < 0) { myPOSM0 += 160; }  myPOSM0 %= 160;
 
2160
  }
 
2161
  myHMM0 = value;
 
2162
}
 
2163
 
 
2164
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
2165
void TIA::pokeHMM1(uInt8 value, Int32 clock)
 
2166
{
 
2167
  value &= 0xF0;
 
2168
  if(myHMM1 == value)
 
2169
    return;
 
2170
 
 
2171
  int hpos  = (clock - myClockWhenFrameStarted) % 228 - HBLANK;
 
2172
 
 
2173
  // Check if HMOVE is currently active
 
2174
  if(myCurrentHMOVEPos != 0x7FFFFFFF &&
 
2175
     hpos < BSPF_min(myCurrentHMOVEPos + 6 + myMotionClockM1 * 4, 7))
 
2176
  {
 
2177
    Int32 newMotion = (value ^ 0x80) >> 4;
 
2178
    // Check if new horizontal move can still be applied normally
 
2179
    if(newMotion > myMotionClockM1 ||
 
2180
       hpos <= BSPF_min(myCurrentHMOVEPos + 6 + newMotion * 4, 7))
 
2181
    {
 
2182
      myPOSM1 -= (newMotion - myMotionClockM1);
 
2183
      myMotionClockM1 = newMotion;
 
2184
    }
 
2185
    else
 
2186
    {
 
2187
      myPOSM1 -= (15 - myMotionClockM1);
 
2188
      myMotionClockM1 = 15;
 
2189
      if(value != 0x70 && value != 0x80)
 
2190
        myHMM1mmr = true;
 
2191
    }
 
2192
    if(myPOSM1 < 0) { myPOSM1 += 160; }  myPOSM1 %= 160;
 
2193
  }
 
2194
  myHMM1 = value;
 
2195
}
 
2196
 
 
2197
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
2198
void TIA::pokeHMBL(uInt8 value, Int32 clock)
 
2199
{
 
2200
  value &= 0xF0;
 
2201
  if(myHMBL == value)
 
2202
    return;
 
2203
 
 
2204
  int hpos  = (clock - myClockWhenFrameStarted) % 228 - HBLANK;
 
2205
 
 
2206
  // Check if HMOVE is currently active
 
2207
  if(myCurrentHMOVEPos != 0x7FFFFFFF &&
 
2208
     hpos < BSPF_min(myCurrentHMOVEPos + 6 + myMotionClockBL * 4, 7))
 
2209
  {
 
2210
    Int32 newMotion = (value ^ 0x80) >> 4;
 
2211
    // Check if new horizontal move can still be applied normally
 
2212
    if(newMotion > myMotionClockBL ||
 
2213
       hpos <= BSPF_min(myCurrentHMOVEPos + 6 + newMotion * 4, 7))
 
2214
    {
 
2215
      myPOSBL -= (newMotion - myMotionClockBL);
 
2216
      myMotionClockBL = newMotion;
 
2217
    }
 
2218
    else
 
2219
    {
 
2220
      myPOSBL -= (15 - myMotionClockBL);
 
2221
      myMotionClockBL = 15;
 
2222
      if(value != 0x70 && value != 0x80)
 
2223
        myHMBLmmr = true;
 
2224
    }
 
2225
    if(myPOSBL < 0) { myPOSBL += 160; }  myPOSBL %= 160;
 
2226
  }
 
2227
  myHMBL = value;
 
2228
}
 
2229
 
 
2230
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
2231
// The following two methods apply extra clocks when a horizontal motion
 
2232
// register (HMxx) is modified during an HMOVE, before waiting for the
 
2233
// documented time of at least 24 CPU cycles.  The applicable explanation
 
2234
// from A. Towers Hardware Notes is as follows:
 
2235
//
 
2236
//   In theory then the side effects of modifying the HMxx registers
 
2237
//   during HMOVE should be quite straight-forward. If the internal
 
2238
//   counter has not yet reached the value in HMxx, a new value greater
 
2239
//   than this (in 0-15 terms) will work normally. Conversely, if
 
2240
//   the counter has already reached the value in HMxx, new values
 
2241
//   will have no effect because the latch will have been cleared.
 
2242
//
 
2243
// Most of the ideas in these methods come from MESS.
 
2244
// (used with permission from Wilbert Pol)
 
2245
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
2246
inline void TIA::applyActiveHMOVEMotion(int hpos, Int16& pos, Int32 motionClock)
 
2247
{
 
2248
  if(hpos < BSPF_min(myCurrentHMOVEPos + 6 + 16 * 4, 7))
 
2249
  {
 
2250
    Int32 decrements_passed = (hpos - (myCurrentHMOVEPos + 4)) >> 2;
 
2251
    pos += 8;
 
2252
    if((motionClock - decrements_passed) > 0)
 
2253
    {
 
2254
      pos -= (motionClock - decrements_passed);
 
2255
      if(pos < 0)  pos += 160;
 
2256
    }
 
2257
  }
 
2258
}
 
2259
 
 
2260
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
2261
inline void TIA::applyPreviousHMOVEMotion(int hpos, Int16& pos, uInt8 motion)
 
2262
{
 
2263
  if(myPreviousHMOVEPos != 0x7FFFFFFF)
 
2264
  {
 
2265
    uInt8 motclk = (motion ^ 0x80) >> 4;
 
2266
    if(hpos <= myPreviousHMOVEPos - 228 + 5 + motclk * 4)
 
2267
    {
 
2268
      uInt8 motclk_passed = (hpos - (myPreviousHMOVEPos - 228 + 6)) >> 2;
 
2269
      pos -= (motclk - motclk_passed);
 
2270
    }
 
2271
  }
 
2272
}
3118
2273
 
3119
2274
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
3120
2275
TIA::TIA(const TIA& c)
3121
 
    : myConsole(c.myConsole),
3122
 
      mySettings(c.mySettings),
3123
 
      mySound(c.mySound),
3124
 
      myCOLUBK(myColor[0]),
3125
 
      myCOLUPF(myColor[1]),
3126
 
      myCOLUP0(myColor[2]),
3127
 
      myCOLUP1(myColor[3])
 
2276
  : myConsole(c.myConsole),
 
2277
    mySound(c.mySound),
 
2278
    mySettings(c.mySettings)
3128
2279
{
3129
2280
  assert(false);
3130
2281
}
3133
2284
TIA& TIA::operator = (const TIA&)
3134
2285
{
3135
2286
  assert(false);
3136
 
 
3137
2287
  return *this;
3138
2288
}