Some cleanups to the TIA class. Made the various peek/poke addresses

use descriptive enum'ed names instead of raw numbers.  This has no effect
on the emulator, but certainly makes it easier on me to read it.

Fixed issue with paddle emulation in Activision Casino ROM.  In fact,
the paddle emulation feels better in other paddle games too.

We'll soon be ready for a new release.  I'm itching to start the TIA
rewrite, years after I said it would be done ...


git-svn-id: svn://svn.code.sf.net/p/stella/code/trunk@1615 8b62c5a3-ac7e-4cc8-8f21-d9a121418aba
This commit is contained in:
stephena 2009-01-13 01:18:25 +00:00
parent b8320c2785
commit bd278bc5bb
3 changed files with 235 additions and 215 deletions

View File

@ -33,7 +33,7 @@ X * Add infrastructure to show all info gathered from a ROM (similar to what
is currently displayed on the commandline). Add a UI part for this, so is currently displayed on the commandline). Add a UI part for this, so
users aren't forced to use the commandline to see it. users aren't forced to use the commandline to see it.
* Fix issue with paddle support in Activision Casino ROM. X * Fix issue with paddle support in Activision Casino ROM.
* More work to the ROM launcher, including at least the following: * More work to the ROM launcher, including at least the following:
X (1) Only show files with certain extensions (including a UI part to X (1) Only show files with certain extensions (including a UI part to

View File

@ -13,10 +13,11 @@
// See the file "license" for information on usage and redistribution of // See the file "license" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES. // this file, and for a DISCLAIMER OF ALL WARRANTIES.
// //
// $Id: TIA.cxx,v 1.98 2009-01-12 01:07:29 stephena Exp $ // $Id: TIA.cxx,v 1.99 2009-01-13 01:18:24 stephena Exp $
//============================================================================ //============================================================================
//#define DEBUG_HMOVE //#define DEBUG_HMOVE
//#define NO_HMOVE_FIXES
#include <cassert> #include <cassert>
#include <cstdlib> #include <cstdlib>
@ -198,8 +199,9 @@ void TIA::reset()
myFloatTIAOutputPins = mySettings.getBool("tiafloat"); myFloatTIAOutputPins = mySettings.getBool("tiafloat");
myAutoFrameEnabled = (mySettings.getInt("framerate") <= 0); myAutoFrameEnabled = (mySettings.getInt("framerate") <= 0);
myFramerate = myConsole.getFramerate();
if(myConsole.getFramerate() > 55.0) // NTSC if(myFramerate > 55.0) // NTSC
{ {
myColorLossEnabled = false; myColorLossEnabled = false;
myMaximumNumberOfScanlines = 290; myMaximumNumberOfScanlines = 290;
@ -596,10 +598,9 @@ inline void TIA::endFrame()
// Recalculate framerate. attempting to auto-correct for scanline 'jumps' // Recalculate framerate. attempting to auto-correct for scanline 'jumps'
if(myFrameCounter % 32 == 0 && myAutoFrameEnabled) if(myFrameCounter % 32 == 0 && myAutoFrameEnabled)
{ {
float framerate = myFramerate = (myScanlineCountForLastFrame > 285 ? 15600.0 : 15720.0) /
(myScanlineCountForLastFrame > 285 ? 15600.0 : 15720.0) / myScanlineCountForLastFrame;
myScanlineCountForLastFrame; myConsole.setFramerate(myFramerate);
myConsole.setFramerate(framerate);
} }
myFrameGreyed = false; myFrameGreyed = false;
@ -1973,176 +1974,125 @@ void TIA::greyOutFrame()
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void TIA::clearBuffers() void TIA::clearBuffers()
{ {
for(uInt32 i = 0; i < 160 * 300; ++i) memset(myCurrentFrameBuffer, 0, 160 * 300);
memset(myPreviousFrameBuffer, 0, 160 * 300);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
inline uInt8 TIA::dumpedInputPort(int resistance)
{
if(resistance == Controller::minimumResistance)
{ {
myCurrentFrameBuffer[i] = myPreviousFrameBuffer[i] = 0; return 0x80;
} }
else if((resistance == Controller::maximumResistance) || myDumpEnabled)
{
return 0x00;
}
else
{
uInt32 needed = (uInt32) (1.6 * resistance * 0.01e-6 *
myScanlineCountForLastFrame * 228 * myFramerate / 3);
if((mySystem->cycles() - myDumpDisabledCycle) > needed)
{
return 0x80;
}
else
{
return 0x00;
}
}
return 0x00;
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt8 TIA::peek(uInt16 addr) uInt8 TIA::peek(uInt16 addr)
{ {
// TODO - convert all constants to enums (TIA.cs/530)
// Update frame to current color clock before we look at anything! // Update frame to current color clock before we look at anything!
updateFrame(mySystem->cycles() * 3); updateFrame(mySystem->cycles() * 3);
uInt8 noise = mySystem->getDataBusState() & 0x3F; uInt8 retval = 0x00;
switch(addr & 0x000f)
{
case CXM0P:
retval = ((myCollision & 0x0001) ? 0x80 : 0x00) |
((myCollision & 0x0002) ? 0x40 : 0x00);
break;
case CXM1P:
retval = ((myCollision & 0x0004) ? 0x80 : 0x00) |
((myCollision & 0x0008) ? 0x40 : 0x00);
break;
case CXP0FB:
retval = ((myCollision & 0x0010) ? 0x80 : 0x00) |
((myCollision & 0x0020) ? 0x40 : 0x00);
break;
case CXP1FB:
retval = ((myCollision & 0x0040) ? 0x80 : 0x00) |
((myCollision & 0x0080) ? 0x40 : 0x00);
break;
case CXM0FB:
retval = ((myCollision & 0x0100) ? 0x80 : 0x00) |
((myCollision & 0x0200) ? 0x40 : 0x00);
break;
case CXM1FB:
retval = ((myCollision & 0x0400) ? 0x80 : 0x00) |
((myCollision & 0x0800) ? 0x40 : 0x00);
break;
case CXBLPF:
retval = (myCollision & 0x1000) ? 0x80 : 0x00;
break;
case CXPPMM:
retval = ((myCollision & 0x2000) ? 0x80 : 0x00) |
((myCollision & 0x4000) ? 0x40 : 0x00);
break;
case INPT0:
retval = dumpedInputPort(myConsole.controller(Controller::Left).read(Controller::Nine));
break;
case INPT1:
retval = dumpedInputPort(myConsole.controller(Controller::Left).read(Controller::Five));
break;
case INPT2:
retval = dumpedInputPort(myConsole.controller(Controller::Right).read(Controller::Nine));
break;
case INPT3:
retval = dumpedInputPort(myConsole.controller(Controller::Right).read(Controller::Five));
break;
case INPT4:
retval = myConsole.controller(Controller::Left).read(Controller::Six) ?
0x80 : 0x00;
break;
case INPT5:
retval = myConsole.controller(Controller::Right).read(Controller::Six) ?
0x80 : 0x00;
break;
case 0x0e: // TODO - document this address
default:
break;
}
// On certain CMOS EPROM chips the unused TIA pins on a read are not // On certain CMOS EPROM chips the unused TIA pins on a read are not
// floating but pulled high. Programmers might want to check their // floating but pulled high. Programmers might want to check their
// games for compatibility, so we make this optional. // games for compatibility, so we make this optional.
if(!myFloatTIAOutputPins) noise = 0x3F; retval |= myFloatTIAOutputPins ? (mySystem->getDataBusState() & 0x3F) : 0x3F;
switch(addr & 0x000f) return retval;
{
case 0x00: // CXM0P
return ((myCollision & 0x0001) ? 0x80 : 0x00) |
((myCollision & 0x0002) ? 0x40 : 0x00) | noise;
case 0x01: // CXM1P
return ((myCollision & 0x0004) ? 0x80 : 0x00) |
((myCollision & 0x0008) ? 0x40 : 0x00) | noise;
case 0x02: // CXP0FB
return ((myCollision & 0x0010) ? 0x80 : 0x00) |
((myCollision & 0x0020) ? 0x40 : 0x00) | noise;
case 0x03: // CXP1FB
return ((myCollision & 0x0040) ? 0x80 : 0x00) |
((myCollision & 0x0080) ? 0x40 : 0x00) | noise;
case 0x04: // CXM0FB
return ((myCollision & 0x0100) ? 0x80 : 0x00) |
((myCollision & 0x0200) ? 0x40 : 0x00) | noise;
case 0x05: // CXM1FB
return ((myCollision & 0x0400) ? 0x80 : 0x00) |
((myCollision & 0x0800) ? 0x40 : 0x00) | noise;
case 0x06: // CXBLPF
return ((myCollision & 0x1000) ? 0x80 : 0x00) | noise;
case 0x07: // CXPPMM
return ((myCollision & 0x2000) ? 0x80 : 0x00) |
((myCollision & 0x4000) ? 0x40 : 0x00) | noise;
case 0x08: // INPT0
{
Int32 r = myConsole.controller(Controller::Left).read(Controller::Nine);
if(r == Controller::minimumResistance)
{
return 0x80 | noise;
}
else if((r == Controller::maximumResistance) || myDumpEnabled)
{
return noise;
}
else
{
double t = (1.5327 * r * 0.01E-6);
uInt32 needed = (uInt32)(t * 1.19E6);
if((mySystem->cycles() - myDumpDisabledCycle) > needed)
{
return 0x80 | noise;
}
else
{
return noise;
}
}
}
case 0x09: // INPT1
{
Int32 r = myConsole.controller(Controller::Left).read(Controller::Five);
if(r == Controller::minimumResistance)
{
return 0x80 | noise;
}
else if((r == Controller::maximumResistance) || myDumpEnabled)
{
return noise;
}
else
{
double t = (1.5327 * r * 0.01E-6);
uInt32 needed = (uInt32)(t * 1.19E6);
if((mySystem->cycles() - myDumpDisabledCycle) > needed)
{
return 0x80 | noise;
}
else
{
return noise;
}
}
}
case 0x0A: // INPT2
{
Int32 r = myConsole.controller(Controller::Right).read(Controller::Nine);
if(r == Controller::minimumResistance)
{
return 0x80 | noise;
}
else if((r == Controller::maximumResistance) || myDumpEnabled)
{
return noise;
}
else
{
double t = (1.5327 * r * 0.01E-6);
uInt32 needed = (uInt32)(t * 1.19E6);
if((mySystem->cycles() - myDumpDisabledCycle) > needed)
{
return 0x80 | noise;
}
else
{
return noise;
}
}
}
case 0x0B: // INPT3
{
Int32 r = myConsole.controller(Controller::Right).read(Controller::Five);
if(r == Controller::minimumResistance)
{
return 0x80 | noise;
}
else if((r == Controller::maximumResistance) || myDumpEnabled)
{
return noise;
}
else
{
double t = (1.5327 * r * 0.01E-6);
uInt32 needed = (uInt32)(t * 1.19E6);
if((mySystem->cycles() - myDumpDisabledCycle) > needed)
{
return 0x80 | noise;
}
else
{
return noise;
}
}
}
case 0x0C: // INPT4
return myConsole.controller(Controller::Left).read(Controller::Six) ?
(0x80 | noise) : noise;
case 0x0D: // INPT5
return myConsole.controller(Controller::Right).read(Controller::Six) ?
(0x80 | noise) : noise;
case 0x0e:
return noise;
default:
return noise;
}
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -2173,7 +2123,7 @@ void TIA::poke(uInt16 addr, uInt8 value)
switch(addr) switch(addr)
{ {
case 0x00: // Vertical sync set-clear case VSYNC: // Vertical sync set-clear
{ {
myVSYNC = value; myVSYNC = value;
@ -2196,7 +2146,7 @@ void TIA::poke(uInt16 addr, uInt8 value)
break; break;
} }
case 0x01: // Vertical blank set-clear case VBLANK: // Vertical blank set-clear
{ {
// Is the dump to ground path being set for I0, I1, I2, and I3? // Is the dump to ground path being set for I0, I1, I2, and I3?
if(!(myVBLANK & 0x80) && (value & 0x80)) if(!(myVBLANK & 0x80) && (value & 0x80))
@ -2215,7 +2165,7 @@ void TIA::poke(uInt16 addr, uInt8 value)
break; break;
} }
case 0x02: // Wait for leading edge of HBLANK case WSYNC: // Wait for leading edge of HBLANK
{ {
// It appears that the 6507 only halts during a read cycle so // It appears that the 6507 only halts during a read cycle so
// we test here for follow-on writes which should be ignored as // we test here for follow-on writes which should be ignored as
@ -2232,13 +2182,13 @@ void TIA::poke(uInt16 addr, uInt8 value)
break; break;
} }
case 0x03: // Reset horizontal sync counter case RSYNC: // Reset horizontal sync counter
{ {
// cerr << "TIA Poke: " << hex << addr << endl; // cerr << "TIA Poke: " << hex << addr << endl;
break; break;
} }
case 0x04: // Number-size of player-missle 0 case NUSIZ0: // Number-size of player-missle 0
{ {
myNUSIZ0 = value; myNUSIZ0 = value;
@ -2254,7 +2204,7 @@ void TIA::poke(uInt16 addr, uInt8 value)
break; break;
} }
case 0x05: // Number-size of player-missle 1 case NUSIZ1: // Number-size of player-missle 1
{ {
myNUSIZ1 = value; myNUSIZ1 = value;
@ -2270,7 +2220,7 @@ void TIA::poke(uInt16 addr, uInt8 value)
break; break;
} }
case 0x06: // Color-Luminance Player 0 case COLUP0: // Color-Luminance Player 0
{ {
uInt32 color = (uInt32)(value & 0xfe); uInt32 color = (uInt32)(value & 0xfe);
if(myColorLossEnabled && (myScanlineCountForLastFrame & 0x01)) if(myColorLossEnabled && (myScanlineCountForLastFrame & 0x01))
@ -2281,7 +2231,7 @@ void TIA::poke(uInt16 addr, uInt8 value)
break; break;
} }
case 0x07: // Color-Luminance Player 1 case COLUP1: // Color-Luminance Player 1
{ {
uInt32 color = (uInt32)(value & 0xfe); uInt32 color = (uInt32)(value & 0xfe);
if(myColorLossEnabled && (myScanlineCountForLastFrame & 0x01)) if(myColorLossEnabled && (myScanlineCountForLastFrame & 0x01))
@ -2292,7 +2242,7 @@ void TIA::poke(uInt16 addr, uInt8 value)
break; break;
} }
case 0x08: // Color-Luminance Playfield case COLUPF: // Color-Luminance Playfield
{ {
uInt32 color = (uInt32)(value & 0xfe); uInt32 color = (uInt32)(value & 0xfe);
if(myColorLossEnabled && (myScanlineCountForLastFrame & 0x01)) if(myColorLossEnabled && (myScanlineCountForLastFrame & 0x01))
@ -2303,7 +2253,7 @@ void TIA::poke(uInt16 addr, uInt8 value)
break; break;
} }
case 0x09: // Color-Luminance Background case COLUBK: // Color-Luminance Background
{ {
uInt32 color = (uInt32)(value & 0xfe); uInt32 color = (uInt32)(value & 0xfe);
if(myColorLossEnabled && (myScanlineCountForLastFrame & 0x01)) if(myColorLossEnabled && (myScanlineCountForLastFrame & 0x01))
@ -2314,7 +2264,7 @@ void TIA::poke(uInt16 addr, uInt8 value)
break; break;
} }
case 0x0A: // Control Playfield, Ball size, Collisions case CTRLPF: // Control Playfield, Ball size, Collisions
{ {
myCTRLPF = value; myCTRLPF = value;
@ -2336,7 +2286,7 @@ void TIA::poke(uInt16 addr, uInt8 value)
break; break;
} }
case 0x0B: // Reflect Player 0 case REFP0: // Reflect Player 0
{ {
// See if the reflection state of the player is being changed // See if the reflection state of the player is being changed
if(((value & 0x08) && !myREFP0) || (!(value & 0x08) && myREFP0)) if(((value & 0x08) && !myREFP0) || (!(value & 0x08) && myREFP0))
@ -2347,7 +2297,7 @@ void TIA::poke(uInt16 addr, uInt8 value)
break; break;
} }
case 0x0C: // Reflect Player 1 case REFP1: // Reflect Player 1
{ {
// See if the reflection state of the player is being changed // See if the reflection state of the player is being changed
if(((value & 0x08) && !myREFP1) || (!(value & 0x08) && myREFP1)) if(((value & 0x08) && !myREFP1) || (!(value & 0x08) && myREFP1))
@ -2358,7 +2308,7 @@ void TIA::poke(uInt16 addr, uInt8 value)
break; break;
} }
case 0x0D: // Playfield register byte 0 case PF0: // Playfield register byte 0
{ {
myPF = (myPF & 0x000FFFF0) | ((value >> 4) & 0x0F); myPF = (myPF & 0x000FFFF0) | ((value >> 4) & 0x0F);
@ -2370,7 +2320,7 @@ void TIA::poke(uInt16 addr, uInt8 value)
break; break;
} }
case 0x0E: // Playfield register byte 1 case PF1: // Playfield register byte 1
{ {
myPF = (myPF & 0x000FF00F) | ((uInt32)value << 4); myPF = (myPF & 0x000FF00F) | ((uInt32)value << 4);
@ -2382,7 +2332,7 @@ void TIA::poke(uInt16 addr, uInt8 value)
break; break;
} }
case 0x0F: // Playfield register byte 2 case PF2: // Playfield register byte 2
{ {
myPF = (myPF & 0x00000FFF) | ((uInt32)value << 12); myPF = (myPF & 0x00000FFF) | ((uInt32)value << 12);
@ -2394,7 +2344,7 @@ void TIA::poke(uInt16 addr, uInt8 value)
break; break;
} }
case 0x10: // Reset Player 0 case RESP0: // Reset Player 0
{ {
Int32 hpos = (clock - myClockWhenFrameStarted) % 228; Int32 hpos = (clock - myClockWhenFrameStarted) % 228;
Int32 newx = hpos < HBLANK ? 3 : (((hpos - HBLANK) + 5) % 160); Int32 newx = hpos < HBLANK ? 3 : (((hpos - HBLANK) + 5) % 160);
@ -2445,7 +2395,7 @@ void TIA::poke(uInt16 addr, uInt8 value)
break; break;
} }
case 0x11: // Reset Player 1 case RESP1: // Reset Player 1
{ {
Int32 hpos = (clock - myClockWhenFrameStarted) % 228; Int32 hpos = (clock - myClockWhenFrameStarted) % 228;
Int32 newx = hpos < HBLANK ? 3 : (((hpos - HBLANK) + 5) % 160); Int32 newx = hpos < HBLANK ? 3 : (((hpos - HBLANK) + 5) % 160);
@ -2496,7 +2446,7 @@ void TIA::poke(uInt16 addr, uInt8 value)
break; break;
} }
case 0x12: // Reset Missle 0 case RESM0: // Reset Missle 0
{ {
int hpos = (clock - myClockWhenFrameStarted) % 228; int hpos = (clock - myClockWhenFrameStarted) % 228;
myPOSM0 = hpos < HBLANK ? 2 : (((hpos - HBLANK) + 4) % 160); myPOSM0 = hpos < HBLANK ? 2 : (((hpos - HBLANK) + 4) % 160);
@ -2508,6 +2458,7 @@ void TIA::poke(uInt16 addr, uInt8 value)
<< " hpos: " << hpos << ", myPOSM0 = " << myPOSM0 << endl; << " hpos: " << hpos << ", myPOSM0 = " << myPOSM0 << endl;
#endif #endif
#ifndef NO_HMOVE_FIXES
// TODO: Remove the following special hack for Dolphin by // TODO: Remove the following special hack for Dolphin by
// figuring out what really happens when Reset Missle // figuring out what really happens when Reset Missle
// occurs 20 cycles after an HMOVE (04/13/02). // occurs 20 cycles after an HMOVE (04/13/02).
@ -2522,13 +2473,13 @@ void TIA::poke(uInt16 addr, uInt8 value)
{ {
myPOSM0 = 8; myPOSM0 = 8;
} }
#endif
myCurrentM0Mask = &ourMissleMaskTable[myPOSM0 & 0x03] myCurrentM0Mask = &ourMissleMaskTable[myPOSM0 & 0x03]
[myNUSIZ0 & 0x07][(myNUSIZ0 & 0x30) >> 4][160 - (myPOSM0 & 0xFC)]; [myNUSIZ0 & 0x07][(myNUSIZ0 & 0x30) >> 4][160 - (myPOSM0 & 0xFC)];
break; break;
} }
case 0x13: // Reset Missle 1 case RESM1: // Reset Missle 1
{ {
int hpos = (clock - myClockWhenFrameStarted) % 228; int hpos = (clock - myClockWhenFrameStarted) % 228;
myPOSM1 = hpos < HBLANK ? 2 : (((hpos - HBLANK) + 4) % 160); myPOSM1 = hpos < HBLANK ? 2 : (((hpos - HBLANK) + 4) % 160);
@ -2540,6 +2491,7 @@ void TIA::poke(uInt16 addr, uInt8 value)
<< " hpos: " << hpos << ", myPOSM1 = " << myPOSM1 << endl; << " hpos: " << hpos << ", myPOSM1 = " << myPOSM1 << endl;
#endif #endif
#ifndef NO_HMOVE_FIXES
// TODO: Remove the following special hack for Pitfall II by // TODO: Remove the following special hack for Pitfall II by
// figuring out what really happens when Reset Missle // figuring out what really happens when Reset Missle
// occurs 3 cycles after an HMOVE (04/13/02). // occurs 3 cycles after an HMOVE (04/13/02).
@ -2547,13 +2499,13 @@ void TIA::poke(uInt16 addr, uInt8 value)
{ {
myPOSM1 = 3; myPOSM1 = 3;
} }
#endif
myCurrentM1Mask = &ourMissleMaskTable[myPOSM1 & 0x03] myCurrentM1Mask = &ourMissleMaskTable[myPOSM1 & 0x03]
[myNUSIZ1 & 0x07][(myNUSIZ1 & 0x30) >> 4][160 - (myPOSM1 & 0xFC)]; [myNUSIZ1 & 0x07][(myNUSIZ1 & 0x30) >> 4][160 - (myPOSM1 & 0xFC)];
break; break;
} }
case 0x14: // Reset Ball case RESBL: // Reset Ball
{ {
int hpos = (clock - myClockWhenFrameStarted) % 228 ; int hpos = (clock - myClockWhenFrameStarted) % 228 ;
myPOSBL = hpos < HBLANK ? 2 : (((hpos - HBLANK) + 4) % 160); myPOSBL = hpos < HBLANK ? 2 : (((hpos - HBLANK) + 4) % 160);
@ -2565,6 +2517,7 @@ void TIA::poke(uInt16 addr, uInt8 value)
<< " hpos: " << hpos << ", myPOSBL = " << myPOSBL << endl; << " hpos: " << hpos << ", myPOSBL = " << myPOSBL << endl;
#endif #endif
#ifndef NO_HMOVE_FIXES
// TODO: Remove the following special hack by figuring out what // TODO: Remove the following special hack by figuring out what
// really happens when Reset Ball occurs 18 cycles after an HMOVE. // really happens when Reset Ball occurs 18 cycles after an HMOVE.
if((clock - myLastHMOVEClock) == (18 * 3)) if((clock - myLastHMOVEClock) == (18 * 3))
@ -2618,55 +2571,55 @@ void TIA::poke(uInt16 addr, uInt8 value)
{ {
myPOSBL = 8; myPOSBL = 8;
} }
#endif
myCurrentBLMask = &ourBallMaskTable[myPOSBL & 0x03] myCurrentBLMask = &ourBallMaskTable[myPOSBL & 0x03]
[(myCTRLPF & 0x30) >> 4][160 - (myPOSBL & 0xFC)]; [(myCTRLPF & 0x30) >> 4][160 - (myPOSBL & 0xFC)];
break; break;
} }
case 0x15: // Audio control 0 case AUDC0: // Audio control 0
{ {
myAUDC0 = value & 0x0f; myAUDC0 = value & 0x0f;
mySound->set(addr, value, mySystem->cycles()); mySound->set(addr, value, mySystem->cycles());
break; break;
} }
case 0x16: // Audio control 1 case AUDC1: // Audio control 1
{ {
myAUDC1 = value & 0x0f; myAUDC1 = value & 0x0f;
mySound->set(addr, value, mySystem->cycles()); mySound->set(addr, value, mySystem->cycles());
break; break;
} }
case 0x17: // Audio frequency 0 case AUDF0: // Audio frequency 0
{ {
myAUDF0 = value & 0x1f; myAUDF0 = value & 0x1f;
mySound->set(addr, value, mySystem->cycles()); mySound->set(addr, value, mySystem->cycles());
break; break;
} }
case 0x18: // Audio frequency 1 case AUDF1: // Audio frequency 1
{ {
myAUDF1 = value & 0x1f; myAUDF1 = value & 0x1f;
mySound->set(addr, value, mySystem->cycles()); mySound->set(addr, value, mySystem->cycles());
break; break;
} }
case 0x19: // Audio volume 0 case AUDV0: // Audio volume 0
{ {
myAUDV0 = value & 0x0f; myAUDV0 = value & 0x0f;
mySound->set(addr, value, mySystem->cycles()); mySound->set(addr, value, mySystem->cycles());
break; break;
} }
case 0x1A: // Audio volume 1 case AUDV1: // Audio volume 1
{ {
myAUDV1 = value & 0x0f; myAUDV1 = value & 0x0f;
mySound->set(addr, value, mySystem->cycles()); mySound->set(addr, value, mySystem->cycles());
break; break;
} }
case 0x1B: // Graphics Player 0 case GRP0: // Graphics Player 0
{ {
// Set player 0 graphics // Set player 0 graphics
myGRP0 = (myBitEnabled[TIA::P0] ? value : 0); myGRP0 = (myBitEnabled[TIA::P0] ? value : 0);
@ -2696,7 +2649,7 @@ void TIA::poke(uInt16 addr, uInt8 value)
break; break;
} }
case 0x1C: // Graphics Player 1 case GRP1: // Graphics Player 1
{ {
// Set player 1 graphics // Set player 1 graphics
myGRP1 = (myBitEnabled[TIA::P1] ? value : 0); myGRP1 = (myBitEnabled[TIA::P1] ? value : 0);
@ -2734,7 +2687,7 @@ void TIA::poke(uInt16 addr, uInt8 value)
break; break;
} }
case 0x1D: // Enable Missile 0 graphics case ENAM0: // Enable Missile 0 graphics
{ {
myENAM0 = (myBitEnabled[TIA::M0] ? value & 0x02 : 0); myENAM0 = (myBitEnabled[TIA::M0] ? value & 0x02 : 0);
@ -2745,7 +2698,7 @@ void TIA::poke(uInt16 addr, uInt8 value)
break; break;
} }
case 0x1E: // Enable Missile 1 graphics case ENAM1: // Enable Missile 1 graphics
{ {
myENAM1 = (myBitEnabled[TIA::M1] ? value & 0x02 : 0); myENAM1 = (myBitEnabled[TIA::M1] ? value & 0x02 : 0);
@ -2756,7 +2709,7 @@ void TIA::poke(uInt16 addr, uInt8 value)
break; break;
} }
case 0x1F: // Enable Ball graphics case ENABL: // Enable Ball graphics
{ {
myENABL = (myBitEnabled[TIA::BL] ? value & 0x02 : 0); myENABL = (myBitEnabled[TIA::BL] ? value & 0x02 : 0);
@ -2768,19 +2721,19 @@ void TIA::poke(uInt16 addr, uInt8 value)
break; break;
} }
case 0x20: // Horizontal Motion Player 0 case HMP0: // Horizontal Motion Player 0
{ {
myHMP0 = value >> 4; myHMP0 = value >> 4;
break; break;
} }
case 0x21: // Horizontal Motion Player 1 case HMP1: // Horizontal Motion Player 1
{ {
myHMP1 = value >> 4; myHMP1 = value >> 4;
break; break;
} }
case 0x22: // Horizontal Motion Missle 0 case HMM0: // Horizontal Motion Missle 0
{ {
Int8 tmp = value >> 4; Int8 tmp = value >> 4;
@ -2795,19 +2748,19 @@ void TIA::poke(uInt16 addr, uInt8 value)
break; break;
} }
case 0x23: // Horizontal Motion Missle 1 case HMM1: // Horizontal Motion Missle 1
{ {
myHMM1 = value >> 4; myHMM1 = value >> 4;
break; break;
} }
case 0x24: // Horizontal Motion Ball case HMBL: // Horizontal Motion Ball
{ {
myHMBL = value >> 4; myHMBL = value >> 4;
break; break;
} }
case 0x25: // Vertial Delay Player 0 case VDELP0: // Vertial Delay Player 0
{ {
myVDELP0 = value & 0x01; myVDELP0 = value & 0x01;
@ -2821,7 +2774,7 @@ void TIA::poke(uInt16 addr, uInt8 value)
break; break;
} }
case 0x26: // Vertial Delay Player 1 case VDELP1: // Vertial Delay Player 1
{ {
myVDELP1 = value & 0x01; myVDELP1 = value & 0x01;
@ -2835,7 +2788,7 @@ void TIA::poke(uInt16 addr, uInt8 value)
break; break;
} }
case 0x27: // Vertial Delay Ball case VDELBL: // Vertial Delay Ball
{ {
myVDELBL = value & 0x01; myVDELBL = value & 0x01;
@ -2846,7 +2799,7 @@ void TIA::poke(uInt16 addr, uInt8 value)
break; break;
} }
case 0x28: // Reset missle 0 to player 0 case RESMP0: // Reset missle 0 to player 0
{ {
if(myRESMP0 && !(value & 0x02)) if(myRESMP0 && !(value & 0x02))
{ {
@ -2874,7 +2827,7 @@ void TIA::poke(uInt16 addr, uInt8 value)
break; break;
} }
case 0x29: // Reset missle 1 to player 1 case RESMP1: // Reset missle 1 to player 1
{ {
if(myRESMP1 && !(value & 0x02)) if(myRESMP1 && !(value & 0x02))
{ {
@ -2901,7 +2854,7 @@ void TIA::poke(uInt16 addr, uInt8 value)
break; break;
} }
case 0x2A: // Apply horizontal motion case HMOVE: // Apply horizontal motion
{ {
// Figure out what cycle we're at // Figure out what cycle we're at
Int32 x = ((clock - myClockWhenFrameStarted) % 228) / 3; Int32 x = ((clock - myClockWhenFrameStarted) % 228) / 3;
@ -2965,7 +2918,7 @@ void TIA::poke(uInt16 addr, uInt8 value)
break; break;
} }
case 0x2b: // Clear horizontal motion registers case HMCLR: // Clear horizontal motion registers
{ {
myHMP0 = 0; myHMP0 = 0;
myHMP1 = 0; myHMP1 = 0;
@ -2975,7 +2928,7 @@ void TIA::poke(uInt16 addr, uInt8 value)
break; break;
} }
case 0x2c: // Clear collision latches case CXCLR: // Clear collision latches
{ {
myCollision = 0; myCollision = 0;
break; break;

View File

@ -13,7 +13,7 @@
// See the file "license" for information on usage and redistribution of // See the file "license" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES. // this file, and for a DISCLAIMER OF ALL WARRANTIES.
// //
// $Id: TIA.hxx,v 1.49 2009-01-12 01:07:29 stephena Exp $ // $Id: TIA.hxx,v 1.50 2009-01-13 01:18:25 stephena Exp $
//============================================================================ //============================================================================
#ifndef TIA_HXX #ifndef TIA_HXX
@ -40,7 +40,7 @@ class Settings;
be displayed on screen. be displayed on screen.
@author Bradford W. Mott @author Bradford W. Mott
@version $Id: TIA.hxx,v 1.49 2009-01-12 01:07:29 stephena Exp $ @version $Id: TIA.hxx,v 1.50 2009-01-13 01:18:25 stephena Exp $
*/ */
class TIA : public Device , public MediaSource class TIA : public Device , public MediaSource
{ {
@ -294,6 +294,9 @@ class TIA : public Device , public MediaSource
// Update bookkeeping at end of frame // Update bookkeeping at end of frame
void endFrame(); void endFrame();
// Convert resistance from ports to dumped value
uInt8 dumpedInputPort(int resistance);
private: private:
// Console the TIA is associated with // Console the TIA is associated with
Console& myConsole; Console& myConsole;
@ -372,26 +375,9 @@ class TIA : public Device , public MediaSource
// Indicates the maximum number of scanlines to be generated for a frame // Indicates the maximum number of scanlines to be generated for a frame
Int32 myMaximumNumberOfScanlines; Int32 myMaximumNumberOfScanlines;
private:
// Color clock when VSYNC ending causes a new frame to be started // Color clock when VSYNC ending causes a new frame to be started
Int32 myVSYNCFinishClock; Int32 myVSYNCFinishClock;
private:
enum
{
myP0Bit = 0x01, // Bit for Player 0
myM0Bit = 0x02, // Bit for Missle 0
myP1Bit = 0x04, // Bit for Player 1
myM1Bit = 0x08, // Bit for Missle 1
myBLBit = 0x10, // Bit for Ball
myPFBit = 0x20, // Bit for Playfield
ScoreBit = 0x40, // Bit for Playfield score mode
PriorityBit = 0x080 // Bit for Playfield priority
};
// Bitmap of the objects that should be considered while drawing
uInt8 myEnabledObjects;
private: private:
uInt8 myVSYNC; // Holds the VSYNC register value uInt8 myVSYNC; // Holds the VSYNC register value
uInt8 myVBLANK; // Holds the VBLANK register value uInt8 myVBLANK; // Holds the VBLANK register value
@ -515,6 +501,9 @@ class TIA : public Device , public MediaSource
// Counter used for TIA M0 "bug" // Counter used for TIA M0 "bug"
uInt32 myM0CosmicArkCounter; uInt32 myM0CosmicArkCounter;
// Bitmap of the objects that should be considered while drawing
uInt8 myEnabledObjects;
// Answers whether specified bits (from TIABit) are enabled or disabled // Answers whether specified bits (from TIABit) are enabled or disabled
bool myBitEnabled[6]; bool myBitEnabled[6];
@ -524,7 +513,85 @@ class TIA : public Device , public MediaSource
// Automatic framerate correction based on number of scanlines // Automatic framerate correction based on number of scanlines
bool myAutoFrameEnabled; bool myAutoFrameEnabled;
// The framerate currently in use by the Console
float myFramerate;
private: private:
enum { // TODO - convert these to match TIA.cs
myP0Bit = 0x01, // Bit for Player 0
myM0Bit = 0x02, // Bit for Missle 0
myP1Bit = 0x04, // Bit for Player 1
myM1Bit = 0x08, // Bit for Missle 1
myBLBit = 0x10, // Bit for Ball
myPFBit = 0x20, // Bit for Playfield
ScoreBit = 0x40, // Bit for Playfield score mode
PriorityBit = 0x80 // Bit for Playfield priority
};
// TIA Write/Read register names
enum {
VSYNC = 0x00, // Write: vertical sync set-clear (D1)
VBLANK = 0x01, // Write: vertical blank set-clear (D7-6,D1)
WSYNC = 0x02, // Write: wait for leading edge of hrz. blank (strobe)
RSYNC = 0x03, // Write: reset hrz. sync counter (strobe)
NUSIZ0 = 0x04, // Write: number-size player-missle 0 (D5-0)
NUSIZ1 = 0x05, // Write: number-size player-missle 1 (D5-0)
COLUP0 = 0x06, // Write: color-lum player 0 (D7-1)
COLUP1 = 0x07, // Write: color-lum player 1 (D7-1)
COLUPF = 0x08, // Write: color-lum playfield (D7-1)
COLUBK = 0x09, // Write: color-lum background (D7-1)
CTRLPF = 0x0a, // Write: cntrl playfield ballsize & coll. (D5-4,D2-0)
REFP0 = 0x0b, // Write: reflect player 0 (D3)
REFP1 = 0x0c, // Write: reflect player 1 (D3)
PF0 = 0x0d, // Write: playfield register byte 0 (D7-4)
PF1 = 0x0e, // Write: playfield register byte 1 (D7-0)
PF2 = 0x0f, // Write: playfield register byte 2 (D7-0)
RESP0 = 0x10, // Write: reset player 0 (strobe)
RESP1 = 0x11, // Write: reset player 1 (strobe)
RESM0 = 0x12, // Write: reset missle 0 (strobe)
RESM1 = 0x13, // Write: reset missle 1 (strobe)
RESBL = 0x14, // Write: reset ball (strobe)
AUDC0 = 0x15, // Write: audio control 0 (D3-0)
AUDC1 = 0x16, // Write: audio control 1 (D4-0)
AUDF0 = 0x17, // Write: audio frequency 0 (D4-0)
AUDF1 = 0x18, // Write: audio frequency 1 (D3-0)
AUDV0 = 0x19, // Write: audio volume 0 (D3-0)
AUDV1 = 0x1a, // Write: audio volume 1 (D3-0)
GRP0 = 0x1b, // Write: graphics player 0 (D7-0)
GRP1 = 0x1c, // Write: graphics player 1 (D7-0)
ENAM0 = 0x1d, // Write: graphics (enable) missle 0 (D1)
ENAM1 = 0x1e, // Write: graphics (enable) missle 1 (D1)
ENABL = 0x1f, // Write: graphics (enable) ball (D1)
HMP0 = 0x20, // Write: horizontal motion player 0 (D7-4)
HMP1 = 0x21, // Write: horizontal motion player 1 (D7-4)
HMM0 = 0x22, // Write: horizontal motion missle 0 (D7-4)
HMM1 = 0x23, // Write: horizontal motion missle 1 (D7-4)
HMBL = 0x24, // Write: horizontal motion ball (D7-4)
VDELP0 = 0x25, // Write: vertical delay player 0 (D0)
VDELP1 = 0x26, // Write: vertical delay player 1 (D0)
VDELBL = 0x27, // Write: vertical delay ball (D0)
RESMP0 = 0x28, // Write: reset missle 0 to player 0 (D1)
RESMP1 = 0x29, // Write: reset missle 1 to player 1 (D1)
HMOVE = 0x2a, // Write: apply horizontal motion (strobe)
HMCLR = 0x2b, // Write: clear horizontal motion registers (strobe)
CXCLR = 0x2c, // Write: clear collision latches (strobe)
CXM0P = 0x00, // Read collision: D7=(M0,P1); D6=(M0,P0)
CXM1P = 0x01, // Read collision: D7=(M1,P0); D6=(M1,P1)
CXP0FB = 0x02, // Read collision: D7=(P0,PF); D6=(P0,BL)
CXP1FB = 0x03, // Read collision: D7=(P1,PF); D6=(P1,BL)
CXM0FB = 0x04, // Read collision: D7=(M0,PF); D6=(M0,BL)
CXM1FB = 0x05, // Read collision: D7=(M1,PF); D6=(M1,BL)
CXBLPF = 0x06, // Read collision: D7=(BL,PF); D6=(unused)
CXPPMM = 0x07, // Read collision: D7=(P0,P1); D6=(M0,M1)
INPT0 = 0x08, // Read pot port: D7
INPT1 = 0x09, // Read pot port: D7
INPT2 = 0x0a, // Read pot port: D7
INPT3 = 0x0b, // Read pot port: D7
INPT4 = 0x0c, // Read P1 joystick trigger: D7
INPT5 = 0x0d // Read P2 joystick trigger: D7
};
// Ball mask table (entries are true or false) // Ball mask table (entries are true or false)
static uInt8 ourBallMaskTable[4][4][320]; static uInt8 ourBallMaskTable[4][4][320];