diff --git a/docs/index.html b/docs/index.html
index 88c55e07c..e98355786 100644
--- a/docs/index.html
+++ b/docs/index.html
@@ -2779,6 +2779,7 @@ Ms Pac-Man (Stella extended codes):
AR ² | Supercharger |
CV | Commavid extra ram |
DPC ² | Pitfall II |
+ DPC+ ² | Enhanced DPC |
E0 | 8K Parker Bros |
E7 | 16K M-network |
EF | 64K Homestar Runner |
diff --git a/src/emucore/Cart.cxx b/src/emucore/Cart.cxx
index f40189800..caf595351 100644
--- a/src/emucore/Cart.cxx
+++ b/src/emucore/Cart.cxx
@@ -30,6 +30,7 @@
#include "Cart4K.hxx"
#include "CartAR.hxx"
#include "CartDPC.hxx"
+#include "CartDPCPlus.hxx"
#include "CartE0.hxx"
#include "CartE7.hxx"
#include "CartEF.hxx"
@@ -144,6 +145,8 @@ Cartridge* Cartridge::create(const uInt8* image, uInt32 size, string& md5,
cartridge = new CartridgeAR(image, size, settings);
else if(type == "DPC")
cartridge = new CartridgeDPC(image, size);
+ else if(type == "DPC+")
+ cartridge = new CartridgeDPCPlus(image, size);
else if(type == "E0")
cartridge = new CartridgeE0(image);
else if(type == "E7")
@@ -317,7 +320,7 @@ string Cartridge::autodetectType(const uInt8* image, uInt32 size)
else
type = "4K";
}
- else if(size == 8192) // 8K
+ else if(size == 8*1024) // 8K
{
if(isProbablySC(image, size))
type = "F8SC";
@@ -342,7 +345,7 @@ string Cartridge::autodetectType(const uInt8* image, uInt32 size)
{
type = "DPC";
}
- else if(size == 12288) // 12K
+ else if(size == 12*1024) // 12K
{
// TODO - this should really be in a method that checks the first
// 512 bytes of ROM and finds if either the lower 256 bytes or
@@ -350,7 +353,7 @@ string Cartridge::autodetectType(const uInt8* image, uInt32 size)
// all carts of 12K are CBS RAM Plus/FA.
type = "FA";
}
- else if(size == 16384) // 16K
+ else if(size == 16*1024) // 16K
{
if(isProbablySC(image, size))
type = "F6SC";
@@ -363,7 +366,11 @@ string Cartridge::autodetectType(const uInt8* image, uInt32 size)
else
type = "F6";
}
- else if(size == 32768) // 32K
+ else if(size == 28*1024) // 28K
+ {
+ type = "DPC+";
+ }
+ else if(size == 32*1024) // 32K
{
if(isProbablySC(image, size))
type = "F4SC";
@@ -374,7 +381,7 @@ string Cartridge::autodetectType(const uInt8* image, uInt32 size)
else
type = "F4";
}
- else if(size == 65536) // 64K
+ else if(size == 64*1024) // 64K
{
if(isProbably3E(image, size))
type = "3E";
diff --git a/src/emucore/CartDPC.cxx b/src/emucore/CartDPC.cxx
index f24e0ac45..4ec02ed80 100644
--- a/src/emucore/CartDPC.cxx
+++ b/src/emucore/CartDPC.cxx
@@ -33,7 +33,7 @@ CartridgeDPC::CartridgeDPC(const uInt8* image, uInt32 size)
{
// Make a copy of the entire image as-is, for use by getImage()
// (this wastes 12K of RAM, should be controlled by a #ifdef)
- memcpy(myImageCopy, image, size);
+ memcpy(myImageCopy, image, BSPF_min(size, 8192u + 2048u + 255u));
// Copy the program ROM image into my buffer
memcpy(myProgramImage, image, 8192);
diff --git a/src/emucore/CartDPC.hxx b/src/emucore/CartDPC.hxx
index 5b10683f5..0e0cb6efc 100644
--- a/src/emucore/CartDPC.hxx
+++ b/src/emucore/CartDPC.hxx
@@ -16,8 +16,8 @@
// $Id$
//============================================================================
-#ifndef CARTRIDGEDCP_HXX
-#define CARTRIDGEDCP_HXX
+#ifndef CARTRIDGE_DPC_HXX
+#define CARTRIDGE_DPC_HXX
class System;
diff --git a/src/emucore/CartDPCPlus.cxx b/src/emucore/CartDPCPlus.cxx
new file mode 100644
index 000000000..008faed05
--- /dev/null
+++ b/src/emucore/CartDPCPlus.cxx
@@ -0,0 +1,633 @@
+//============================================================================
+//
+// SSSS tt lll lll
+// SS SS tt ll ll
+// SS tttttt eeee ll ll aaaa
+// SSSS tt ee ee ll ll aa
+// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
+// SS SS tt ee ll ll aa aa
+// SSSS ttt eeeee llll llll aaaaa
+//
+// Copyright (c) 1995-2010 by Bradford W. Mott and the Stella Team
+//
+// See the file "License.txt" for information on usage and redistribution of
+// this file, and for a DISCLAIMER OF ALL WARRANTIES.
+//
+// $Id$
+//============================================================================
+
+#include
+#include
+#include
+
+#include "System.hxx"
+#include "CartDPCPlus.hxx"
+
+// TODO - properly handle read from write port functionality
+// Note: do r/w port restrictions even exist for this scheme??
+// Port to new CartDebug/disassembler scheme
+// Add bankchanged code
+
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+CartridgeDPCPlus::CartridgeDPCPlus(const uInt8* image, uInt32 size)
+{
+ // Make a copy of the entire image as-is, for use by getImage()
+ // (this wastes 28K of RAM, should be controlled by a #ifdef)
+ memcpy(myImageCopy, image, size);
+
+ // Copy the program ROM image into my buffer
+ memcpy(myProgramImage, image, 4096 * 6);
+
+ // Copy the display ROM image into my buffer
+ memcpy(myDisplayImage, image + 4096 * 6, 4096);
+
+ // Initialize the DPC data fetcher registers
+ for(uInt16 i = 0; i < 8; ++i)
+ myTops[i] = myBottoms[i] = myCounters[i] = myFlags[i] = 0;
+
+ // None of the data fetchers are in music mode
+ myMusicMode[0] = myMusicMode[1] = myMusicMode[2] = false;
+
+ // Initialize the DPC's random number generator register (must be non-zero)
+ myRandomNumber = 1;
+
+ // Initialize the system cycles counter & fractional clock values
+ mySystemCycles = 0;
+ myFractionalClocks = 0.0;
+
+ // Remember startup bank
+ myStartBank = 1;
+}
+
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+CartridgeDPCPlus::~CartridgeDPCPlus()
+{
+}
+
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+void CartridgeDPCPlus::reset()
+{
+ // Update cycles to the current system cycles
+ mySystemCycles = mySystem->cycles();
+ myFractionalClocks = 0.0;
+
+ // Upon reset we switch to the startup bank
+ bank(myStartBank);
+}
+
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+void CartridgeDPCPlus::systemCyclesReset()
+{
+ // Get the current system cycle
+ uInt32 cycles = mySystem->cycles();
+
+ // Adjust the cycle counter so that it reflects the new value
+ mySystemCycles -= cycles;
+}
+
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+void CartridgeDPCPlus::install(System& system)
+{
+ mySystem = &system;
+ uInt16 shift = mySystem->pageShift();
+ uInt16 mask = mySystem->pageMask();
+
+ // Make sure the system we're being installed in has a page size that'll work
+ assert(((0x1080 & mask) == 0) && ((0x1100 & mask) == 0));
+
+ // Set the page accessing methods for the hot spots
+ System::PageAccess access;
+ for(uInt32 i = (0x1FF6 & ~mask); i < 0x2000; i += (1 << shift))
+ {
+ access.directPeekBase = 0;
+ access.directPokeBase = 0;
+ access.device = this;
+ mySystem->setPageAccess(i >> shift, access);
+ }
+
+ // Set the page accessing method for the DPC reading & writing pages
+ for(uInt32 j = 0x1000; j < 0x1080; j += (1 << shift))
+ {
+ access.directPeekBase = 0;
+ access.directPokeBase = 0;
+ access.device = this;
+ mySystem->setPageAccess(j >> shift, access);
+ }
+
+ // Install pages for bank 5
+ bank(5);
+}
+
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+inline void CartridgeDPCPlus::clockRandomNumberGenerator()
+{
+ // Table for computing the input bit of the random number generator's
+ // shift register (it's the NOT of the EOR of four bits)
+ static const uInt8 f[16] = {
+ 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1
+ };
+
+ // Using bits 7, 5, 4, & 3 of the shift register compute the input
+ // bit for the shift register
+ uInt8 bit = f[((myRandomNumber >> 3) & 0x07) |
+ ((myRandomNumber & 0x80) ? 0x08 : 0x00)];
+
+ // Update the shift register
+ myRandomNumber = (myRandomNumber << 1) | bit;
+}
+
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+inline void CartridgeDPCPlus::updateMusicModeDataFetchers()
+{
+ // Calculate the number of cycles since the last update
+ Int32 cycles = mySystem->cycles() - mySystemCycles;
+ mySystemCycles = mySystem->cycles();
+
+ // Calculate the number of DPC OSC clocks since the last update
+ double clocks = ((20000.0 * cycles) / 1193191.66666667) + myFractionalClocks;
+ Int32 wholeClocks = (Int32)clocks;
+ myFractionalClocks = clocks - (double)wholeClocks;
+
+ if(wholeClocks <= 0)
+ {
+ return;
+ }
+
+ // Let's update counters and flags of the music mode data fetchers
+ for(int x = 5; x <= 7; ++x)
+ {
+ // Update only if the data fetcher is in music mode
+ if(myMusicMode[x - 5])
+ {
+ Int32 top = myTops[x] + 1;
+ Int32 newLow = (Int32)(myCounters[x] & 0x00ff);
+
+ if(myTops[x] != 0)
+ {
+ newLow -= (wholeClocks % top);
+ if(newLow < 0)
+ {
+ newLow += top;
+ }
+ }
+ else
+ {
+ newLow = 0;
+ }
+
+ // Update flag register for this data fetcher
+ if(newLow <= myBottoms[x])
+ {
+ myFlags[x] = 0x00;
+ }
+ else if(newLow <= myTops[x])
+ {
+ myFlags[x] = 0xff;
+ }
+
+ myCounters[x] = (myCounters[x] & 0x0F00) | (uInt16)newLow;
+ }
+ }
+}
+
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+uInt8 CartridgeDPCPlus::peek(uInt16 address)
+{
+ address &= 0x0FFF;
+
+ // Clock the random number generator. This should be done for every
+ // cartridge access, however, we're only doing it for the DPC and
+ // hot-spot accesses to save time.
+ clockRandomNumberGenerator();
+
+ if(address < 0x0040)
+ {
+ uInt8 result = 0;
+
+ // Get the index of the data fetcher that's being accessed
+ uInt32 index = address & 0x07;
+ uInt32 function = (address >> 3) & 0x07;
+
+ // Update flag register for selected data fetcher
+ if((myCounters[index] & 0x00ff) == myTops[index])
+ {
+ myFlags[index] = 0xff;
+ }
+ else if((myCounters[index] & 0x00ff) == myBottoms[index])
+ {
+ myFlags[index] = 0x00;
+ }
+
+ switch(function)
+ {
+ case 0x00:
+ {
+ // Is this a random number read
+ if(index < 4)
+ {
+ result = myRandomNumber;
+ }
+ // No, it's a music read
+ else
+ {
+ static const uInt8 musicAmplitudes[8] = {
+ 0x00, 0x04, 0x05, 0x09, 0x06, 0x0a, 0x0b, 0x0f
+ };
+
+ // Update the music data fetchers (counter & flag)
+ updateMusicModeDataFetchers();
+
+ uInt8 i = 0;
+ if(myMusicMode[0] && myFlags[5])
+ {
+ i |= 0x01;
+ }
+ if(myMusicMode[1] && myFlags[6])
+ {
+ i |= 0x02;
+ }
+ if(myMusicMode[2] && myFlags[7])
+ {
+ i |= 0x04;
+ }
+
+ result = musicAmplitudes[i];
+ }
+ break;
+ }
+
+ // DFx display data read
+ case 0x01:
+ {
+ result = myDisplayImage[ /* 4095 - */ myCounters[index]];
+ break;
+ }
+
+ // DFx display data read AND'd w/flag
+ case 0x02:
+ {
+ result = myDisplayImage[ /* 4095 - */ myCounters[index]] & myFlags[index];
+ break;
+ }
+
+ // DFx flag
+ case 0x07:
+ {
+ result = myFlags[index];
+ break;
+ }
+
+ default:
+ {
+ result = 0;
+ }
+ }
+
+ // Clock the selected data fetcher's counter if needed
+ if((index < 5) || ((index >= 5) && (!myMusicMode[index - 5])))
+ {
+ myCounters[index] = (myCounters[index] /*-*/ + 1) & 0x0fff;
+ }
+
+ return result;
+ }
+ else
+ {
+ // Switch banks if necessary
+ switch(address)
+ {
+ case 0x0FF6:
+ // Set the current bank to the first 4k bank
+ bank(0);
+ break;
+
+ case 0x0FF7:
+ // Set the current bank to the second 4k bank
+ bank(1);
+ break;
+
+ case 0x0FF8:
+ // Set the current bank to the third 4k bank
+ bank(2);
+ break;
+
+ case 0x0FF9:
+ // Set the current bank to the fourth 4k bank
+ bank(3);
+ break;
+
+ case 0x0FFA:
+ // Set the current bank to the fifth 4k bank
+ bank(4);
+ break;
+
+ case 0x0FFB:
+ // Set the current bank to the last 4k bank
+ bank(5);
+ break;
+
+ default:
+ break;
+ }
+ return myProgramImage[myCurrentBank * 4096 + address];
+ }
+}
+
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+void CartridgeDPCPlus::poke(uInt16 address, uInt8 value)
+{
+ address &= 0x0FFF;
+
+ // Clock the random number generator. This should be done for every
+ // cartridge access, however, we're only doing it for the DPC and
+ // hot-spot accesses to save time.
+ clockRandomNumberGenerator();
+
+ if((address >= 0x0040) && (address < 0x0080))
+ {
+ // Get the index of the data fetcher that's being accessed
+ uInt32 index = address & 0x07;
+ uInt32 function = (address >> 3) & 0x07;
+
+ switch(function)
+ {
+ // DFx top count
+ case 0x00:
+ {
+ myTops[index] = value;
+ myFlags[index] = 0x00;
+ break;
+ }
+
+ // DFx bottom count
+ case 0x01:
+ {
+ myBottoms[index] = value;
+ break;
+ }
+
+ // DFx counter low
+ case 0x02:
+ {
+ if((index >= 5) && myMusicMode[index - 5])
+ {
+ // Data fecther is in music mode so its low counter value
+ // should be loaded from the top register not the poked value
+ myCounters[index] = (myCounters[index] & 0x0F00) |
+ (uInt16)myTops[index];
+ }
+ else
+ {
+ // Data fecther is either not a music mode data fecther or it
+ // isn't in music mode so it's low counter value should be loaded
+ // with the poked value
+ myCounters[index] = (myCounters[index] & 0x0F00) | (uInt16)value;
+ }
+ break;
+ }
+
+ // DFx counter high
+ case 0x03:
+ {
+ myCounters[index] = (((uInt16)value & 0x0F) << 8) |
+ (myCounters[index] & 0x00ff);
+
+ // Execute special code for music mode data fetchers
+ if(index >= 5)
+ {
+ myMusicMode[index - 5] = (value & 0x10);
+
+ // NOTE: We are not handling the clock source input for
+ // the music mode data fetchers. We're going to assume
+ // they always use the OSC input.
+ }
+ break;
+ }
+
+ // Random Number Generator Reset
+ case 0x06:
+ {
+ myRandomNumber = 1;
+ break;
+ }
+
+ default:
+ {
+ break;
+ }
+ }
+ }
+ else
+ {
+ // Switch banks if necessary
+ switch(address)
+ {
+ case 0x0FF6:
+ // Set the current bank to the first 4k bank
+ bank(0);
+ break;
+
+ case 0x0FF7:
+ // Set the current bank to the second 4k bank
+ bank(1);
+ break;
+
+ case 0x0FF8:
+ // Set the current bank to the third 4k bank
+ bank(2);
+ break;
+
+ case 0x0FF9:
+ // Set the current bank to the fourth 4k bank
+ bank(3);
+ break;
+
+ case 0x0FFA:
+ // Set the current bank to the fifth 4k bank
+ bank(4);
+ break;
+
+ case 0x0FFB:
+ // Set the current bank to the last 4k bank
+ bank(5);
+ break;
+
+ default:
+ break;
+ }
+ }
+}
+
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+void CartridgeDPCPlus::bank(uInt16 bank)
+{
+ if(bankLocked()) return;
+
+ // Remember what bank we're in
+ myCurrentBank = bank;
+ uInt16 offset = myCurrentBank * 4096;
+ uInt16 shift = mySystem->pageShift();
+ uInt16 mask = mySystem->pageMask();
+
+ // Setup the page access methods for the current bank
+ System::PageAccess access;
+ access.device = this;
+ access.directPokeBase = 0;
+
+ // Map Program ROM image into the system
+ for(uInt32 address = 0x1080; address < (0x1FF8U & ~mask);
+ address += (1 << shift))
+ {
+ access.directPeekBase = &myProgramImage[offset + (address & 0x0FFF)];
+ mySystem->setPageAccess(address >> shift, access);
+ }
+}
+
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+int CartridgeDPCPlus::bank()
+{
+ return myCurrentBank;
+}
+
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+int CartridgeDPCPlus::bankCount()
+{
+ // TODO - add support for debugger (support the display ROM somehow)
+ return 6;
+}
+
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+bool CartridgeDPCPlus::patch(uInt16 address, uInt8 value)
+{
+ // TODO - check if this actually works
+ myProgramImage[(myCurrentBank << 12) + (address & 0x0FFF)] = value;
+ return true;
+}
+
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+uInt8* CartridgeDPCPlus::getImage(int& size)
+{
+ size = 4096 * 6 + 4096 + 255;
+
+ int i;
+ for(i = 0; i < 4096 * 6; i++)
+ myImageCopy[i] = myProgramImage[i];
+
+ for(i = 0; i < 4096; i++)
+ myImageCopy[i + 4096 * 6] = myDisplayImage[i];
+
+ return &myImageCopy[0];
+}
+
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+bool CartridgeDPCPlus::save(Serializer& out) const
+{
+ const string& cart = name();
+
+ try
+ {
+ uInt32 i;
+
+ out.putString(cart);
+
+ // Indicates which bank is currently active
+ out.putInt(myCurrentBank);
+
+ // The top registers for the data fetchers
+ out.putInt(8);
+ for(i = 0; i < 8; ++i)
+ out.putByte((char)myTops[i]);
+
+ // The bottom registers for the data fetchers
+ out.putInt(8);
+ for(i = 0; i < 8; ++i)
+ out.putByte((char)myBottoms[i]);
+
+ // The counter registers for the data fetchers
+ out.putInt(8);
+ for(i = 0; i < 8; ++i)
+ out.putInt(myCounters[i]);
+
+ // The flag registers for the data fetchers
+ out.putInt(8);
+ for(i = 0; i < 8; ++i)
+ out.putByte((char)myFlags[i]);
+
+ // The music mode flags for the data fetchers
+ out.putInt(3);
+ for(i = 0; i < 3; ++i)
+ out.putBool(myMusicMode[i]);
+
+ // The random number generator register
+ out.putByte((char)myRandomNumber);
+
+ out.putInt(mySystemCycles);
+ out.putInt((uInt32)(myFractionalClocks * 100000000.0));
+ }
+ catch(const char* msg)
+ {
+ cerr << "ERROR: CartridgeDPCPlus::save" << endl << " " << msg << endl;
+ return false;
+ }
+
+ return true;
+}
+
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+bool CartridgeDPCPlus::load(Serializer& in)
+{
+ const string& cart = name();
+
+ try
+ {
+ if(in.getString() != cart)
+ return false;
+
+ uInt32 i, limit;
+
+ // Indicates which bank is currently active
+ myCurrentBank = (uInt16) in.getInt();
+
+ // The top registers for the data fetchers
+ limit = (uInt32) in.getInt();
+ for(i = 0; i < limit; ++i)
+ myTops[i] = (uInt8) in.getByte();
+
+ // The bottom registers for the data fetchers
+ limit = (uInt32) in.getInt();
+ for(i = 0; i < limit; ++i)
+ myBottoms[i] = (uInt8) in.getByte();
+
+ // The counter registers for the data fetchers
+ limit = (uInt32) in.getInt();
+ for(i = 0; i < limit; ++i)
+ myCounters[i] = (uInt16) in.getInt();
+
+ // The flag registers for the data fetchers
+ limit = (uInt32) in.getInt();
+ for(i = 0; i < limit; ++i)
+ myFlags[i] = (uInt8) in.getByte();
+
+ // The music mode flags for the data fetchers
+ limit = (uInt32) in.getInt();
+ for(i = 0; i < limit; ++i)
+ myMusicMode[i] = in.getBool();
+
+ // The random number generator register
+ myRandomNumber = (uInt8) in.getByte();
+
+ // Get system cycles and fractional clocks
+ mySystemCycles = in.getInt();
+ myFractionalClocks = (double)in.getInt() / 100000000.0;
+ }
+ catch(const char* msg)
+ {
+ cerr << "ERROR: CartridgeDPCPlus::load" << endl << " " << msg << endl;
+ return false;
+ }
+
+ // Now, go to the current bank
+ bank(myCurrentBank);
+
+ return true;
+}
diff --git a/src/emucore/CartDPCPlus.hxx b/src/emucore/CartDPCPlus.hxx
new file mode 100644
index 000000000..02b7e5275
--- /dev/null
+++ b/src/emucore/CartDPCPlus.hxx
@@ -0,0 +1,196 @@
+//============================================================================
+//
+// SSSS tt lll lll
+// SS SS tt ll ll
+// SS tttttt eeee ll ll aaaa
+// SSSS tt ee ee ll ll aa
+// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
+// SS SS tt ee ll ll aa aa
+// SSSS ttt eeeee llll llll aaaaa
+//
+// Copyright (c) 1995-2010 by Bradford W. Mott and the Stella Team
+//
+// See the file "License.txt" for information on usage and redistribution of
+// this file, and for a DISCLAIMER OF ALL WARRANTIES.
+//
+// $Id$
+//============================================================================
+
+#ifndef CARTRIDGE_DPC_PLUS_HXX
+#define CARTRIDGE_DPC_PLUS_HXX
+
+class System;
+
+#include "bspf.hxx"
+#include "Cart.hxx"
+
+/**
+ Cartridge class used for DPC+. There are six 4K program banks, a
+ 4K display bank, and the DPC chip. For complete details on the DPC chip
+ see David P. Crane's United States Patent Number 4,644,495.
+
+ @author Spiceware, Batari, Stephen Anthony
+ @version $Id$
+*/
+class CartridgeDPCPlus : public Cartridge
+{
+ public:
+ /**
+ Create a new cartridge using the specified image
+
+ @param image Pointer to the ROM image
+ */
+ CartridgeDPCPlus(const uInt8* image, uInt32 size);
+
+ /**
+ Destructor
+ */
+ virtual ~CartridgeDPCPlus();
+
+ public:
+ /**
+ Reset device to its power-on state
+ */
+ virtual void reset();
+
+ /**
+ Notification method invoked by the system right before the
+ system resets its cycle counter to zero. It may be necessary
+ to override this method for devices that remember cycle counts.
+ */
+ virtual void systemCyclesReset();
+
+ /**
+ Install cartridge in the specified system. Invoked by the system
+ when the cartridge is attached to it.
+
+ @param system The system the device should install itself in
+ */
+ virtual void install(System& system);
+
+ /**
+ Install pages for the specified bank in the system.
+
+ @param bank The bank that should be installed in the system
+ */
+ virtual void bank(uInt16 bank);
+
+ /**
+ Get the current bank.
+
+ @return The current bank, or -1 if bankswitching not supported
+ */
+ virtual int bank();
+
+ /**
+ Query the number of banks supported by the cartridge.
+ */
+ virtual int bankCount();
+
+ /**
+ Patch the cartridge ROM.
+
+ @param address The ROM address to patch
+ @param value The value to place into the address
+ @return Success or failure of the patch operation
+ */
+ virtual bool patch(uInt16 address, uInt8 value);
+
+ /**
+ Access the internal ROM image for this cartridge.
+
+ @param size Set to the size of the internal ROM image data
+ @return A pointer to the internal ROM image data
+ */
+ virtual uInt8* getImage(int& size);
+
+ /**
+ Save the current state of this cart to the given Serializer.
+
+ @param out The Serializer object to use
+ @return False on any errors, else true
+ */
+ virtual bool save(Serializer& out) const;
+
+ /**
+ Load the current state of this cart from the given Serializer.
+
+ @param in The Serializer object to use
+ @return False on any errors, else true
+ */
+ virtual bool load(Serializer& in);
+
+ /**
+ Get a descriptor for the device name (used in error checking).
+
+ @return The name of the object
+ */
+ virtual string name() const { return "CartridgeDPCPlus"; }
+
+ public:
+ /**
+ Get the byte at the specified address.
+
+ @return The byte at the specified address
+ */
+ virtual uInt8 peek(uInt16 address);
+
+ /**
+ Change the byte at the specified address to the given value
+
+ @param address The address where the value should be stored
+ @param value The value to be stored at the address
+ */
+ virtual void poke(uInt16 address, uInt8 value);
+
+ private:
+ /**
+ Clocks the random number generator to move it to its next state
+ */
+ void clockRandomNumberGenerator();
+
+ /**
+ Updates any data fetchers in music mode based on the number of
+ CPU cycles which have passed since the last update.
+ */
+ void updateMusicModeDataFetchers();
+
+ private:
+ // Indicates which bank is currently active
+ uInt16 myCurrentBank;
+
+ // The 8K program ROM image of the cartridge
+ uInt8 myProgramImage[4096 * 6];
+
+ // The 2K display ROM image of the cartridge
+ uInt8 myDisplayImage[4096];
+
+ // Copy of the raw image, for use by getImage()
+ uInt8 myImageCopy[4096 * 6 + 4096 + 255];
+
+ // The top registers for the data fetchers
+ uInt8 myTops[8];
+
+ // The bottom registers for the data fetchers
+ uInt8 myBottoms[8];
+
+ // The counter registers for the data fetchers
+ uInt16 myCounters[8];
+
+ // The flag registers for the data fetchers
+ uInt8 myFlags[8];
+
+ // The music mode DF5, DF6, & DF7 enabled flags
+ bool myMusicMode[3];
+
+ // The random number generator register
+ uInt8 myRandomNumber;
+
+ // System cycle count when the last update to music data fetchers occurred
+ Int32 mySystemCycles;
+
+ // Fractional DPC music OSC clocks unused during the last update
+ double myFractionalClocks;
+};
+
+#endif
diff --git a/src/emucore/module.mk b/src/emucore/module.mk
index 01ab96649..4cf482e1c 100644
--- a/src/emucore/module.mk
+++ b/src/emucore/module.mk
@@ -12,6 +12,7 @@ MODULE_OBJS := \
src/emucore/CartCV.o \
src/emucore/Cart.o \
src/emucore/CartDPC.o \
+ src/emucore/CartDPCPlus.o \
src/emucore/CartE0.o \
src/emucore/CartE7.o \
src/emucore/CartEF.o \
diff --git a/src/gui/GameInfoDialog.cxx b/src/gui/GameInfoDialog.cxx
index ef36f34ae..81a4e9761 100644
--- a/src/gui/GameInfoDialog.cxx
+++ b/src/gui/GameInfoDialog.cxx
@@ -147,6 +147,7 @@ GameInfoDialog::GameInfoDialog(
items.push_back("AR (Supercharger)", "AR" );
items.push_back("CV (Commavid extra ram)", "CV" );
items.push_back("DPC (Pitfall II)", "DPC" );
+ items.push_back("DPC+ (Enhanced DPC)", "DPC+" );
items.push_back("E0 (8K Parker Bros)", "E0" );
items.push_back("E7 (16K M-network)", "E7" );
items.push_back("EF (64K H. Runner)", "EF" );
diff --git a/src/gui/GlobalPropsDialog.cxx b/src/gui/GlobalPropsDialog.cxx
index 2c1182d6e..dd73dcb60 100644
--- a/src/gui/GlobalPropsDialog.cxx
+++ b/src/gui/GlobalPropsDialog.cxx
@@ -78,6 +78,7 @@ GlobalPropsDialog::
items.push_back("AR (Supercharger)", "AR" );
items.push_back("CV (Commavid extra ram)", "CV" );
items.push_back("DPC (Pitfall II)", "DPC" );
+ items.push_back("DPC+ (Enhanced DPC)", "DPC+" );
items.push_back("E0 (8K Parker Bros)", "E0" );
items.push_back("E7 (16K M-network)", "E7" );
items.push_back("EF (64K H. Runner)", "EF" );