mirror of https://github.com/stella-emu/stella.git
First attempt to backport relevant BS schemes to R77 branch.
- There are 8 new BS schemes since version 3.9.3 has been released - CDF is probably the most wanted, but I will consider the others as time permits
This commit is contained in:
parent
4f9470968b
commit
e09bc90d17
|
@ -22,7 +22,7 @@
|
|||
|
||||
#include <cstdlib>
|
||||
|
||||
#define STELLA_VERSION "3.9.3"
|
||||
#define STELLA_VERSION "3.9.4"
|
||||
#define STELLA_BUILD atoi("$Rev: 2838 $" + 6)
|
||||
|
||||
#endif
|
||||
|
|
|
@ -26,18 +26,24 @@
|
|||
#include "Cart0840.hxx"
|
||||
#include "Cart2K.hxx"
|
||||
#include "Cart3E.hxx"
|
||||
// #include "Cart3EPlus.hxx"
|
||||
#include "Cart3F.hxx"
|
||||
#include "Cart4A50.hxx"
|
||||
#include "Cart4K.hxx"
|
||||
#include "Cart4KSC.hxx"
|
||||
#include "CartAR.hxx"
|
||||
// #include "CartBUS.hxx"
|
||||
// #include "CartCDF.hxx"
|
||||
#include "CartCM.hxx"
|
||||
#include "CartCTY.hxx"
|
||||
#include "CartCV.hxx"
|
||||
// #include "CartCVPlus.hxx"
|
||||
// #include "CartDASH.hxx"
|
||||
#include "CartDPC.hxx"
|
||||
#include "CartDPCPlus.hxx"
|
||||
#include "CartE0.hxx"
|
||||
#include "CartE7.hxx"
|
||||
// #include "CartE78K.hxx"
|
||||
#include "CartEF.hxx"
|
||||
#include "CartEFSC.hxx"
|
||||
#include "CartBF.hxx"
|
||||
|
@ -54,9 +60,10 @@
|
|||
#include "CartFA.hxx"
|
||||
#include "CartFA2.hxx"
|
||||
#include "CartFE.hxx"
|
||||
#include "CartMC.hxx"
|
||||
// #include "CartMDM.hxx"
|
||||
#include "CartSB.hxx"
|
||||
#include "CartUA.hxx"
|
||||
// #include "CartWD.hxx"
|
||||
#include "CartX07.hxx"
|
||||
#include "MD5.hxx"
|
||||
#include "Props.hxx"
|
||||
|
@ -183,6 +190,8 @@ Cartridge* Cartridge::create(const uInt8* image, uInt32 size, string& md5,
|
|||
cartridge = new Cartridge2K(image, size, settings);
|
||||
else if(type == "3E")
|
||||
cartridge = new Cartridge3E(image, size, settings);
|
||||
// else if(type == "3EP")
|
||||
// cartridge = new Cartridge3EPlus(image, size, settings);
|
||||
else if(type == "3F")
|
||||
cartridge = new Cartridge3F(image, size, settings);
|
||||
else if(type == "4A50")
|
||||
|
@ -193,33 +202,43 @@ Cartridge* Cartridge::create(const uInt8* image, uInt32 size, string& md5,
|
|||
cartridge = new Cartridge4KSC(image, size, settings);
|
||||
else if(type == "AR")
|
||||
cartridge = new CartridgeAR(image, size, settings);
|
||||
else if(type == "BF")
|
||||
cartridge = new CartridgeBF(image, size, settings);
|
||||
else if(type == "BFSC")
|
||||
cartridge = new CartridgeBFSC(image, size, settings);
|
||||
// else if(type == "BUS")
|
||||
// cartridge = new CartridgeBUS(image, size, settings);
|
||||
// else if(type == "CDF")
|
||||
// cartridge = new CartridgeCDF(image, size, settings);
|
||||
else if(type == "CM")
|
||||
cartridge = new CartridgeCM(image, size, settings);
|
||||
else if(type == "CTY")
|
||||
cartridge = new CartridgeCTY(image, size, osystem);
|
||||
else if(type == "CV")
|
||||
cartridge = new CartridgeCV(image, size, settings);
|
||||
// else if(type == "CVP")
|
||||
// cartridge = new CartridgeCVPlus(image, size, settings);
|
||||
// else if(type == "DASH")
|
||||
// cartridge = new CartridgeDASH(image, size, settings);
|
||||
else if(type == "DF")
|
||||
cartridge = new CartridgeDF(image, size, settings);
|
||||
else if(type == "DFSC")
|
||||
cartridge = new CartridgeDFSC(image, size, settings);
|
||||
else if(type == "DPC")
|
||||
cartridge = new CartridgeDPC(image, size, settings);
|
||||
else if(type == "DPC+")
|
||||
else if(type == "DPCP")
|
||||
cartridge = new CartridgeDPCPlus(image, size, settings);
|
||||
else if(type == "E0")
|
||||
cartridge = new CartridgeE0(image, size, settings);
|
||||
else if(type == "E7")
|
||||
cartridge = new CartridgeE7(image, size, settings);
|
||||
// else if(type == "E78K")
|
||||
// cartridge = new CartridgeE78K(image, size, settings);
|
||||
else if(type == "EF")
|
||||
cartridge = new CartridgeEF(image, size, settings);
|
||||
else if(type == "EFSC")
|
||||
cartridge = new CartridgeEFSC(image, size, settings);
|
||||
else if(type == "BF")
|
||||
cartridge = new CartridgeBF(image, size, settings);
|
||||
else if(type == "BFSC")
|
||||
cartridge = new CartridgeBFSC(image, size, settings);
|
||||
else if(type == "DF")
|
||||
cartridge = new CartridgeDF(image, size, settings);
|
||||
else if(type == "DFSC")
|
||||
cartridge = new CartridgeDFSC(image, size, settings);
|
||||
else if(type == "F0" || type == "MB")
|
||||
else if(type == "F0")
|
||||
cartridge = new CartridgeF0(image, size, settings);
|
||||
else if(type == "F4")
|
||||
cartridge = new CartridgeF4(image, size, settings);
|
||||
|
@ -233,18 +252,20 @@ Cartridge* Cartridge::create(const uInt8* image, uInt32 size, string& md5,
|
|||
cartridge = new CartridgeF8(image, size, md5, settings);
|
||||
else if(type == "F8SC")
|
||||
cartridge = new CartridgeF8SC(image, size, settings);
|
||||
else if(type == "FA" || type == "FASC")
|
||||
else if(type == "FA")
|
||||
cartridge = new CartridgeFA(image, size, settings);
|
||||
else if(type == "FA2")
|
||||
cartridge = new CartridgeFA2(image, size, osystem);
|
||||
else if(type == "FE")
|
||||
cartridge = new CartridgeFE(image, size, settings);
|
||||
else if(type == "MC")
|
||||
cartridge = new CartridgeMC(image, size, settings);
|
||||
// else if(type == "MDM")
|
||||
// cartridge = new CartridgeMDM(image, size, settings);
|
||||
else if(type == "UA")
|
||||
cartridge = new CartridgeUA(image, size, settings);
|
||||
else if(type == "SB")
|
||||
cartridge = new CartridgeSB(image, size, settings);
|
||||
// else if(type == "WD")
|
||||
// cartridge = new CartridgeWD(image, size, settings);
|
||||
else if(type == "X07")
|
||||
cartridge = new CartridgeX07(image, size, settings);
|
||||
else if(dtype == "WRONG_SIZE")
|
||||
|
@ -380,7 +401,11 @@ string Cartridge::autodetectType(const uInt8* image, uInt32 size)
|
|||
// Guess type based on size
|
||||
const char* type = 0;
|
||||
|
||||
if((size % 8448) == 0 || size == 6144)
|
||||
if(isProbablyCVPlus(image,size))
|
||||
{
|
||||
type = "CVP";
|
||||
}
|
||||
else if((size % 8448) == 0 || size == 6144)
|
||||
{
|
||||
type = "AR";
|
||||
}
|
||||
|
@ -395,9 +420,9 @@ string Cartridge::autodetectType(const uInt8* image, uInt32 size)
|
|||
}
|
||||
else if(size == 4096)
|
||||
{
|
||||
if(isProbablyCV(image,size))
|
||||
if(isProbablyCV(image, size))
|
||||
type = "CV";
|
||||
else if(isProbably4KSC(image,size))
|
||||
else if(isProbably4KSC(image, size))
|
||||
type = "4KSC";
|
||||
else
|
||||
type = "4K";
|
||||
|
@ -424,9 +449,15 @@ string Cartridge::autodetectType(const uInt8* image, uInt32 size)
|
|||
type = "FE";
|
||||
else if(isProbably0840(image, size))
|
||||
type = "0840";
|
||||
else if(isProbablyE78K(image, size))
|
||||
type = "E78K";
|
||||
else
|
||||
type = "F8";
|
||||
}
|
||||
else if(size == 8*1024 + 3) // 8195 bytes (Experimental)
|
||||
{
|
||||
type = "WD";
|
||||
}
|
||||
else if(size >= 10240 && size <= 10496) // ~10K - Pitfall2
|
||||
{
|
||||
type = "DPC";
|
||||
|
@ -459,25 +490,36 @@ string Cartridge::autodetectType(const uInt8* image, uInt32 size)
|
|||
if(isProbablyARM(image, size))
|
||||
type = "FA2";
|
||||
else /*if(isProbablyDPCplus(image, size))*/
|
||||
type = "DPC+";
|
||||
type = "DPCP";
|
||||
}
|
||||
else if(size == 32*1024) // 32K
|
||||
{
|
||||
if(isProbablySC(image, size))
|
||||
if (isProbablyCTY(image, size))
|
||||
type = "CTY";
|
||||
else if(isProbablySC(image, size))
|
||||
type = "F4SC";
|
||||
else if(isProbably3E(image, size))
|
||||
type = "3E";
|
||||
else if(isProbably3F(image, size))
|
||||
type = "3F";
|
||||
else if (isProbablyBUS(image, size))
|
||||
type = "BUS";
|
||||
else if (isProbablyCDF(image, size))
|
||||
type = "CDF";
|
||||
else if(isProbablyDPCplus(image, size))
|
||||
type = "DPC+";
|
||||
else if(isProbablyCTY(image, size))
|
||||
type = "CTY";
|
||||
type = "DPCP";
|
||||
else if(isProbablyFA2(image, size))
|
||||
type = "FA2";
|
||||
else
|
||||
type = "F4";
|
||||
}
|
||||
else if(size == 60*1024) // 60K
|
||||
{
|
||||
if(isProbablyCTY(image, size))
|
||||
type = "CTY";
|
||||
else
|
||||
type = "F4";
|
||||
}
|
||||
else if(size == 64*1024) // 64K
|
||||
{
|
||||
if(isProbably3E(image, size))
|
||||
|
@ -505,8 +547,6 @@ string Cartridge::autodetectType(const uInt8* image, uInt32 size)
|
|||
type = "4A50";
|
||||
else if(isProbablySB(image, size))
|
||||
type = "SB";
|
||||
else
|
||||
type = "MC";
|
||||
}
|
||||
else if(size == 256*1024) // 256K
|
||||
{
|
||||
|
@ -529,6 +569,15 @@ string Cartridge::autodetectType(const uInt8* image, uInt32 size)
|
|||
type = "4K"; // Most common bankswitching type
|
||||
}
|
||||
|
||||
// Variable sized ROM formats are independent of image size and come last
|
||||
if(isProbablyDASH(image, size))
|
||||
type = "DASH";
|
||||
else if(isProbably3EPlus(image, size))
|
||||
type = "3EP";
|
||||
else if(isProbablyMDM(image, size))
|
||||
type = "MDM";
|
||||
|
||||
cerr << type << endl;
|
||||
return type;
|
||||
}
|
||||
|
||||
|
@ -563,22 +612,21 @@ bool Cartridge::searchForBytes(const uInt8* image, uInt32 imagesize,
|
|||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
bool Cartridge::isProbablySC(const uInt8* image, uInt32 size)
|
||||
{
|
||||
// We assume a Superchip cart contains the same bytes for its entire
|
||||
// RAM area; obviously this test will fail if it doesn't
|
||||
// The RAM area will be the first 256 bytes of each 4K bank
|
||||
uInt32 banks = size / 4096;
|
||||
for(uInt32 i = 0; i < banks; ++i)
|
||||
// We assume a Superchip cart repeats the first 128 bytes for the second
|
||||
// 128 bytes in the RAM area, which is the first 256 bytes of each 4K bank
|
||||
const uInt8* ptr = image;
|
||||
while(size)
|
||||
{
|
||||
uInt8 first = image[i*4096];
|
||||
for(uInt32 j = 0; j < 256; ++j)
|
||||
{
|
||||
if(image[i*4096+j] != first)
|
||||
if(memcmp(ptr, ptr + 128, 128) != 0)
|
||||
return false;
|
||||
}
|
||||
|
||||
ptr += 4096;
|
||||
size -= 4096;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
bool Cartridge::isProbably4KSC(const uInt8* image, uInt32 size)
|
||||
{
|
||||
// We check if the first 256 bytes are identical *and* if there's
|
||||
|
@ -595,7 +643,6 @@ bool Cartridge::isProbably4KSC(const uInt8* image, uInt32 size)
|
|||
return false;
|
||||
}
|
||||
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
bool Cartridge::isProbablyARM(const uInt8* image, uInt32 size)
|
||||
{
|
||||
|
@ -605,10 +652,10 @@ bool Cartridge::isProbablyARM(const uInt8* image, uInt32 size)
|
|||
{ 0xA0, 0xC1, 0x1F, 0xE0 },
|
||||
{ 0x00, 0x80, 0x02, 0xE0 }
|
||||
};
|
||||
if(searchForBytes(image, 1024, signature[0], 4, 1))
|
||||
if(searchForBytes(image, std::min(size, 1024u), signature[0], 4, 1))
|
||||
return true;
|
||||
else
|
||||
return searchForBytes(image, 1024, signature[1], 4, 1);
|
||||
return searchForBytes(image, std::min(size, 1024u), signature[1], 4, 1);
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
@ -646,6 +693,14 @@ bool Cartridge::isProbably3E(const uInt8* image, uInt32 size)
|
|||
return searchForBytes(image, size, signature, 4, 1);
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
bool Cartridge::isProbably3EPlus(const uInt8* image, uInt32 size)
|
||||
{
|
||||
// 3E+ cart is identified key 'TJ3E' in the ROM
|
||||
uInt8 signature[] = { 'T', 'J', '3', 'E' };
|
||||
return searchForBytes(image, size, signature, 4, 1);
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
bool Cartridge::isProbably3F(const uInt8* image, uInt32 size)
|
||||
{
|
||||
|
@ -678,7 +733,8 @@ bool Cartridge::isProbably4A50(const uInt8* image, uInt32 size)
|
|||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
bool Cartridge::isProbablyCTY(const uInt8* image, uInt32 size)
|
||||
{
|
||||
return false; // TODO - add autodetection
|
||||
uInt8 signature[] = { 'L', 'E', 'N', 'I', 'N' };
|
||||
return searchForBytes(image, size, signature, 5, 1);
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
@ -696,10 +752,30 @@ bool Cartridge::isProbablyCV(const uInt8* image, uInt32 size)
|
|||
return searchForBytes(image, size, signature[1], 3, 1);
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
bool Cartridge::isProbablyCVPlus(const uInt8* image, uInt32)
|
||||
{
|
||||
// CV+ cart is identified key 'commavidplus' @ $04 in the ROM
|
||||
// We inspect only this area to speed up the search
|
||||
uInt8 signature[12] = { 'c', 'o', 'm', 'm', 'a', 'v', 'i', 'd',
|
||||
'p', 'l', 'u', 's' };
|
||||
return searchForBytes(image+4, 24, signature, 12, 1);
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
bool Cartridge::isProbablyDASH(const uInt8* image, uInt32 size)
|
||||
{
|
||||
// DASH cart is identified key 'TJAD' in the ROM
|
||||
uInt8 signature[] = { 'T', 'J', 'A', 'D' };
|
||||
return searchForBytes(image, size, signature, 4, 1);
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
bool Cartridge::isProbablyDPCplus(const uInt8* image, uInt32 size)
|
||||
{
|
||||
// DPC+ ARM code has 2 occurrences of the string DPC+
|
||||
// Note: all Harmony/Melody custom drivers also contain the value
|
||||
// 0x10adab1e (LOADABLE) if needed for future improvement
|
||||
uInt8 signature[] = { 'D', 'P', 'C', '+' };
|
||||
return searchForBytes(image, size, signature, 4, 2);
|
||||
}
|
||||
|
@ -755,6 +831,25 @@ bool Cartridge::isProbablyE7(const uInt8* image, uInt32 size)
|
|||
return false;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
bool Cartridge::isProbablyE78K(const uInt8* image, uInt32 size)
|
||||
{
|
||||
// E78K cart bankswitching is triggered by accessing addresses
|
||||
// $FE4 to $FE6 using absolute non-indexed addressing
|
||||
// To eliminate false positives (and speed up processing), we
|
||||
// search for only certain known signatures
|
||||
uInt8 signature[3][3] = {
|
||||
{ 0xAD, 0xE4, 0xFF }, // LDA $FFE4
|
||||
{ 0xAD, 0xE5, 0xFF }, // LDA $FFE5
|
||||
{ 0xAD, 0xE6, 0xFF }, // LDA $FFE6
|
||||
};
|
||||
for(uInt32 i = 0; i < 3; ++i)
|
||||
if(searchForBytes(image, size, signature[i], 3, 1))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
bool Cartridge::isProbablyEF(const uInt8* image, uInt32 size, const char*& type)
|
||||
{
|
||||
|
@ -763,7 +858,7 @@ bool Cartridge::isProbablyEF(const uInt8* image, uInt32 size, const char*& type)
|
|||
uInt8 efef[] = { 'E', 'F', 'E', 'F' };
|
||||
uInt8 efsc[] = { 'E', 'F', 'S', 'C' };
|
||||
if(searchForBytes(image+size-8, 8, efef, 4, 1))
|
||||
{
|
||||
{
|
||||
type = "EF";
|
||||
return true;
|
||||
}
|
||||
|
@ -824,6 +919,26 @@ bool Cartridge::isProbablyBF(const uInt8* image, uInt32 size, const char*& type)
|
|||
return false;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
bool Cartridge::isProbablyBUS(const uInt8* image, uInt32 size)
|
||||
{
|
||||
// BUS ARM code has 2 occurrences of the string BUS
|
||||
// Note: all Harmony/Melody custom drivers also contain the value
|
||||
// 0x10adab1e (LOADABLE) if needed for future improvement
|
||||
uInt8 bus[] = { 'B', 'U', 'S'};
|
||||
return searchForBytes(image, size, bus, 3, 2);
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
bool Cartridge::isProbablyCDF(const uInt8* image, uInt32 size)
|
||||
{
|
||||
// CDF ARM code has 3 occurrences of the string DPC+
|
||||
// Note: all Harmony/Melody custom drivers also contain the value
|
||||
// 0x10adab1e (LOADABLE) if needed for future improvement
|
||||
uInt8 signature[] = { 'C', 'D', 'F' };
|
||||
return searchForBytes(image, size, signature, 3, 3);
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
bool Cartridge::isProbablyDF(const uInt8* image, uInt32 size, const char*& type)
|
||||
{
|
||||
|
@ -846,9 +961,8 @@ bool Cartridge::isProbablyDF(const uInt8* image, uInt32 size, const char*& type)
|
|||
return false;
|
||||
}
|
||||
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
bool Cartridge::isProbablyFA2(const uInt8* image, uInt32 size)
|
||||
bool Cartridge::isProbablyFA2(const uInt8* image, uInt32)
|
||||
{
|
||||
// This currently tests only the 32K version of FA2; the 24 and 28K
|
||||
// versions are easy, in that they're the only possibility with those
|
||||
|
@ -881,6 +995,14 @@ bool Cartridge::isProbablyFE(const uInt8* image, uInt32 size)
|
|||
return false;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
bool Cartridge::isProbablyMDM(const uInt8* image, uInt32 size)
|
||||
{
|
||||
// MDM cart is identified key 'MDMC' in the first 8K of ROM
|
||||
uInt8 signature[] = { 'M', 'D', 'M', 'C' };
|
||||
return searchForBytes(image, std::min(size, 8192u), signature, 4, 1);
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
bool Cartridge::isProbablySB(const uInt8* image, uInt32 size)
|
||||
{
|
||||
|
|
|
@ -202,6 +202,13 @@ class Cartridge : public Device
|
|||
*/
|
||||
virtual void setRomName(const string& name) { }
|
||||
|
||||
/**
|
||||
Thumbulator only supports 16-bit ARM code. Some Harmony/Melody drivers,
|
||||
such as BUS and CDF, feature 32-bit ARM code subroutines. This is used
|
||||
to pass values back to the cartridge class to emulate those subroutines.
|
||||
*/
|
||||
virtual uInt32 thumbCallback(uInt8 function, uInt32 value1, uInt32 value2) { return 0; }
|
||||
|
||||
/**
|
||||
Get debugger widget responsible for accessing the inner workings
|
||||
of the cart. This will need to be overridden and implemented by
|
||||
|
@ -279,12 +286,13 @@ class Cartridge : public Device
|
|||
uInt32 minhits);
|
||||
|
||||
/**
|
||||
Returns true if the image is probably a SuperChip (256 bytes RAM)
|
||||
Returns true if the image is probably a SuperChip (128 bytes RAM)
|
||||
Note: should be called only on ROMs with size multiple of 4K
|
||||
*/
|
||||
static bool isProbablySC(const uInt8* image, uInt32 size);
|
||||
|
||||
/**
|
||||
Returns true if the image is probably a 4K SuperChip (256 bytes RAM)
|
||||
Returns true if the image is probably a 4K SuperChip (128 bytes RAM)
|
||||
*/
|
||||
static bool isProbably4KSC(const uInt8* image, uInt32 size);
|
||||
|
||||
|
@ -303,6 +311,11 @@ class Cartridge : public Device
|
|||
*/
|
||||
static bool isProbably3E(const uInt8* image, uInt32 size);
|
||||
|
||||
/**
|
||||
Returns true if the image is probably a 3E+ bankswitching cartridge
|
||||
*/
|
||||
static bool isProbably3EPlus(const uInt8* image, uInt32 size);
|
||||
|
||||
/**
|
||||
Returns true if the image is probably a 3F bankswitching cartridge
|
||||
*/
|
||||
|
@ -313,6 +326,21 @@ class Cartridge : public Device
|
|||
*/
|
||||
static bool isProbably4A50(const uInt8* image, uInt32 size);
|
||||
|
||||
/**
|
||||
Returns true if the image is probably a BF/BFSC bankswitching cartridge
|
||||
*/
|
||||
static bool isProbablyBF(const uInt8* image, uInt32 size, const char*& type);
|
||||
|
||||
/**
|
||||
Returns true if the image is probably a BUS bankswitching cartridge
|
||||
*/
|
||||
static bool isProbablyBUS(const uInt8* image, uInt32 size);
|
||||
|
||||
/**
|
||||
Returns true if the image is probably a CDF bankswitching cartridge
|
||||
*/
|
||||
static bool isProbablyCDF(const uInt8* image, uInt32 size);
|
||||
|
||||
/**
|
||||
Returns true if the image is probably a CTY bankswitching cartridge
|
||||
*/
|
||||
|
@ -323,6 +351,21 @@ class Cartridge : public Device
|
|||
*/
|
||||
static bool isProbablyCV(const uInt8* image, uInt32 size);
|
||||
|
||||
/**
|
||||
Returns true if the image is probably a CV+ bankswitching cartridge
|
||||
*/
|
||||
static bool isProbablyCVPlus(const uInt8* image, uInt32 size);
|
||||
|
||||
/**
|
||||
Returns true if the image is probably a DASH bankswitching cartridge
|
||||
*/
|
||||
static bool isProbablyDASH(const uInt8* image, uInt32 size);
|
||||
|
||||
/**
|
||||
Returns true if the image is probably a DF/DFSC bankswitching cartridge
|
||||
*/
|
||||
static bool isProbablyDF(const uInt8* image, uInt32 size, const char*& type);
|
||||
|
||||
/**
|
||||
Returns true if the image is probably a DPC+ bankswitching cartridge
|
||||
*/
|
||||
|
@ -338,24 +381,20 @@ class Cartridge : public Device
|
|||
*/
|
||||
static bool isProbablyE7(const uInt8* image, uInt32 size);
|
||||
|
||||
/**
|
||||
Returns true if the image is probably a E78K bankswitching cartridge
|
||||
*/
|
||||
static bool isProbablyE78K(const uInt8* image, uInt32 size);
|
||||
|
||||
/**
|
||||
Returns true if the image is probably an EF/EFSC bankswitching cartridge
|
||||
*/
|
||||
static bool isProbablyEF(const uInt8* image, uInt32 size, const char*& type);
|
||||
|
||||
/**
|
||||
Returns true if the image is probably a BF/BFSC bankswitching cartridge
|
||||
*/
|
||||
static bool isProbablyBF(const uInt8* image, uInt32 size, const char*& type);
|
||||
/**
|
||||
Returns true if the image is probably a DF/DFSC bankswitching cartridge
|
||||
*/
|
||||
static bool isProbablyDF(const uInt8* image, uInt32 size, const char*& type);
|
||||
|
||||
/**
|
||||
Returns true if the image is probably an F6 bankswitching cartridge
|
||||
*/
|
||||
static bool isProbablyF6(const uInt8* image, uInt32 size);
|
||||
//static bool isProbablyF6(const uInt8* image, uInt32 size);
|
||||
|
||||
/**
|
||||
Returns true if the image is probably an FA2 bankswitching cartridge
|
||||
|
@ -367,6 +406,11 @@ class Cartridge : public Device
|
|||
*/
|
||||
static bool isProbablyFE(const uInt8* image, uInt32 size);
|
||||
|
||||
/**
|
||||
Returns true if the image is probably a MDM bankswitching cartridge
|
||||
*/
|
||||
static bool isProbablyMDM(const uInt8* image, uInt32 size);
|
||||
|
||||
/**
|
||||
Returns true if the image is probably a SB bankswitching cartridge
|
||||
*/
|
||||
|
|
|
@ -459,7 +459,7 @@ void CartridgeCTY::loadTune(uInt8 index)
|
|||
void CartridgeCTY::loadScore(uInt8 index)
|
||||
{
|
||||
Serializer serializer(myEEPROMFile, true);
|
||||
if(serializer.isValid())
|
||||
if(serializer)
|
||||
{
|
||||
uInt8 scoreRAM[256];
|
||||
try
|
||||
|
@ -479,7 +479,7 @@ void CartridgeCTY::loadScore(uInt8 index)
|
|||
void CartridgeCTY::saveScore(uInt8 index)
|
||||
{
|
||||
Serializer serializer(myEEPROMFile);
|
||||
if(serializer.isValid())
|
||||
if(serializer)
|
||||
{
|
||||
// Load score RAM
|
||||
uInt8 scoreRAM[256];
|
||||
|
@ -496,7 +496,7 @@ void CartridgeCTY::saveScore(uInt8 index)
|
|||
memcpy(scoreRAM + (index << 6) + 4, myRAM+4, 60);
|
||||
|
||||
// Save score RAM
|
||||
serializer.reset();
|
||||
serializer.rewind();
|
||||
try
|
||||
{
|
||||
serializer.putByteArray(scoreRAM, 256);
|
||||
|
@ -513,7 +513,7 @@ void CartridgeCTY::saveScore(uInt8 index)
|
|||
void CartridgeCTY::wipeAllScores()
|
||||
{
|
||||
Serializer serializer(myEEPROMFile);
|
||||
if(serializer.isValid())
|
||||
if(serializer)
|
||||
{
|
||||
// Erase score RAM
|
||||
uInt8 scoreRAM[256];
|
||||
|
|
|
@ -31,21 +31,24 @@
|
|||
CartridgeDPCPlus::CartridgeDPCPlus(const uInt8* image, uInt32 size,
|
||||
const Settings& settings)
|
||||
: Cartridge(settings),
|
||||
mySize(std::min(size, 32768u)),
|
||||
myFastFetch(false),
|
||||
myLDAimmediate(false),
|
||||
myParameterPointer(0),
|
||||
mySystemCycles(0),
|
||||
myFractionalClocks(0.0)
|
||||
myAudioCycles(0),
|
||||
myARMCycles(0),
|
||||
myFractionalClocks(0.0),
|
||||
myBankOffset(0)
|
||||
{
|
||||
// Store image, making sure it's at least 29KB
|
||||
uInt32 minsize = 4096 * 6 + 4096 + 1024 + 255;
|
||||
mySize = BSPF_max(minsize, size);
|
||||
myImage = new uInt8[mySize];
|
||||
memcpy(myImage, image, size);
|
||||
// Image is always 32K, but in the case of ROM > 29K, the image is
|
||||
// copied to the end of the buffer
|
||||
if(mySize < 32768u)
|
||||
memset(myImage, 0, 32768);
|
||||
memcpy(myImage + (32768u - mySize), image, size);
|
||||
createCodeAccessBase(4096 * 6);
|
||||
|
||||
// Pointer to the program ROM (24K @ 0 byte offset)
|
||||
myProgramImage = myImage;
|
||||
// Pointer to the program ROM (24K @ 3072 byte offset; ignore first 3K)
|
||||
myProgramImage = myImage + 0xC00;
|
||||
|
||||
// Pointer to the display RAM
|
||||
myDisplayImage = myDPCRAM + 0xC00;
|
||||
|
@ -53,16 +56,14 @@ CartridgeDPCPlus::CartridgeDPCPlus(const uInt8* image, uInt32 size,
|
|||
// Pointer to the Frequency RAM
|
||||
myFrequencyImage = myDisplayImage + 0x1000;
|
||||
|
||||
// If the image is larger than 29K, we assume any excess at the
|
||||
// beginning is ARM code, and skip over it
|
||||
if(size > 29 * 1024)
|
||||
myProgramImage += (size - 29 * 1024);
|
||||
|
||||
#ifdef THUMB_SUPPORT
|
||||
// Create Thumbulator ARM emulator
|
||||
myThumbEmulator = new Thumbulator((uInt16*)(myProgramImage-0xC00),
|
||||
(uInt16*)myDPCRAM,
|
||||
settings.getBool("thumb.trapfatal"));
|
||||
myThumbEmulator = make_unique<Thumbulator>
|
||||
(reinterpret_cast<uInt16*>(myImage),
|
||||
reinterpret_cast<uInt16*>(myDPCRAM),
|
||||
settings.getBool("thumb.trapfatal"),
|
||||
Thumbulator::ConfigureFor::DPCplus,
|
||||
this);
|
||||
#endif
|
||||
setInitialState();
|
||||
|
||||
|
@ -70,21 +71,11 @@ CartridgeDPCPlus::CartridgeDPCPlus(const uInt8* image, uInt32 size,
|
|||
myStartBank = 5;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
CartridgeDPCPlus::~CartridgeDPCPlus()
|
||||
{
|
||||
delete[] myImage;
|
||||
|
||||
#ifdef THUMB_SUPPORT
|
||||
delete myThumbEmulator;
|
||||
#endif
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void CartridgeDPCPlus::reset()
|
||||
{
|
||||
// Update cycles to the current system cycles
|
||||
mySystemCycles = mySystem->cycles();
|
||||
myAudioCycles = myARMCycles = mySystem->cycles();
|
||||
myFractionalClocks = 0.0;
|
||||
|
||||
setInitialState();
|
||||
|
@ -104,8 +95,11 @@ void CartridgeDPCPlus::setInitialState()
|
|||
|
||||
// Initialize the DPC data fetcher registers
|
||||
for(int i = 0; i < 8; ++i)
|
||||
myTops[i] = myBottoms[i] = myCounters[i] = myFractionalIncrements[i] =
|
||||
{
|
||||
myTops[i] = myBottoms[i] = myFractionalIncrements[i] = 0;
|
||||
myFractionalCounters[i] = 0;
|
||||
myCounters[i] = 0;
|
||||
}
|
||||
|
||||
// Set waveforms to first waveform entry
|
||||
myMusicWaveforms[0] = myMusicWaveforms[1] = myMusicWaveforms[2] = 0;
|
||||
|
@ -121,7 +115,8 @@ void CartridgeDPCPlus::systemCyclesReset()
|
|||
uInt32 cycles = mySystem->cycles();
|
||||
|
||||
// Adjust the cycle counter so that it reflects the new value
|
||||
mySystemCycles -= cycles;
|
||||
myAudioCycles -= cycles;
|
||||
myARMCycles -= cycles;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
@ -156,7 +151,7 @@ inline void CartridgeDPCPlus::clockRandomNumberGenerator()
|
|||
inline void CartridgeDPCPlus::priorClockRandomNumberGenerator()
|
||||
{
|
||||
// Update random number generator (32-bit LFSR, reversed)
|
||||
myRandomNumber = ((myRandomNumber & (1<<31)) ?
|
||||
myRandomNumber = ((myRandomNumber & (1u<<31)) ?
|
||||
((0x10adab1e^myRandomNumber) << 11) | ((0x10adab1e^myRandomNumber) >> 21) :
|
||||
(myRandomNumber << 11) | (myRandomNumber >> 21));
|
||||
}
|
||||
|
@ -165,24 +160,18 @@ inline void CartridgeDPCPlus::priorClockRandomNumberGenerator()
|
|||
inline void CartridgeDPCPlus::updateMusicModeDataFetchers()
|
||||
{
|
||||
// Calculate the number of cycles since the last update
|
||||
Int32 cycles = mySystem->cycles() - mySystemCycles;
|
||||
mySystemCycles = mySystem->cycles();
|
||||
uInt32 cycles = uInt32(mySystem->cycles() - myAudioCycles);
|
||||
myAudioCycles = mySystem->cycles();
|
||||
|
||||
// Calculate the number of DPC OSC clocks since the last update
|
||||
// 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;
|
||||
}
|
||||
uInt32 wholeClocks = uInt32(clocks);
|
||||
myFractionalClocks = clocks - double(wholeClocks);
|
||||
|
||||
// Let's update counters and flags of the music mode data fetchers
|
||||
if(wholeClocks > 0)
|
||||
for(int x = 0; x <= 2; ++x)
|
||||
{
|
||||
myMusicCounters[x] += myMusicFrequencies[x];
|
||||
}
|
||||
myMusicCounters[x] += myMusicFrequencies[x] * wholeClocks;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
@ -206,19 +195,23 @@ inline void CartridgeDPCPlus::callFunction(uInt8 value)
|
|||
myParameterPointer = 0;
|
||||
break;
|
||||
#ifdef THUMB_SUPPORT
|
||||
case 254:
|
||||
case 255:
|
||||
case 254: // call with IRQ driven audio, no special handling needed at this
|
||||
// time for Stella as ARM code "runs in zero 6507 cycles".
|
||||
case 255: // call without IRQ driven audio
|
||||
// Call user written ARM code (most likely be C compiled for ARM)
|
||||
try {
|
||||
myThumbEmulator->run();
|
||||
Int32 cycles = Int32(mySystem->cycles() - myARMCycles);
|
||||
myARMCycles = mySystem->cycles();
|
||||
|
||||
myThumbEmulator->run(cycles);
|
||||
}
|
||||
catch(const string& error) {
|
||||
catch(const runtime_error& e) {
|
||||
if(!mySystem->autodetectMode())
|
||||
{
|
||||
#ifdef DEBUGGER_SUPPORT
|
||||
Debugger::debugger().startWithFatalError(error);
|
||||
Debugger::debugger().startWithFatalError(e.what());
|
||||
#else
|
||||
cout << error << endl;
|
||||
cout << e.what() << endl;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
@ -233,7 +226,7 @@ uInt8 CartridgeDPCPlus::peek(uInt16 address)
|
|||
{
|
||||
address &= 0x0FFF;
|
||||
|
||||
uInt8 peekvalue = myProgramImage[(myCurrentBank << 12) + address];
|
||||
uInt8 peekvalue = myProgramImage[myBankOffset + address];
|
||||
uInt8 flag;
|
||||
|
||||
// In debugger/bank-locked mode, we ignore all hotspots and in general
|
||||
|
@ -300,7 +293,7 @@ uInt8 CartridgeDPCPlus::peek(uInt16 address)
|
|||
myDisplayImage[(myMusicWaveforms[1] << 5) + (myMusicCounters[1] >> 27)] +
|
||||
myDisplayImage[(myMusicWaveforms[2] << 5) + (myMusicCounters[2] >> 27)];
|
||||
|
||||
result = (uInt8)i;
|
||||
result = uInt8(i);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -425,12 +418,12 @@ bool CartridgeDPCPlus::poke(uInt16 address, uInt8 value)
|
|||
{
|
||||
//DFxFRACLOW - fractional data pointer low byte
|
||||
case 0x00:
|
||||
myFractionalCounters[index] = (myFractionalCounters[index] & 0x0F0000) | ((uInt16)value << 8);
|
||||
myFractionalCounters[index] = (myFractionalCounters[index] & 0x0F00FF) | (uInt16(value) << 8);
|
||||
break;
|
||||
|
||||
// DFxFRACHI - fractional data pointer high byte
|
||||
case 0x01:
|
||||
myFractionalCounters[index] = (((uInt16)value & 0x0F) << 16) | (myFractionalCounters[index] & 0x00ffff);
|
||||
myFractionalCounters[index] = ((uInt16(value) & 0x0F) << 16) | (myFractionalCounters[index] & 0x00ffff);
|
||||
break;
|
||||
|
||||
//DFxFRACINC - Fractional Increment amount
|
||||
|
@ -494,7 +487,7 @@ bool CartridgeDPCPlus::poke(uInt16 address, uInt8 value)
|
|||
// DFxHI - data pointer high byte
|
||||
case 0x08:
|
||||
{
|
||||
myCounters[index] = (((uInt16)value & 0x0F) << 8) | (myCounters[index] & 0x00ff);
|
||||
myCounters[index] = ((uInt16(value) & 0x0F) << 8) | (myCounters[index] & 0x00ff);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -605,17 +598,16 @@ bool CartridgeDPCPlus::bank(uInt16 bank)
|
|||
if(bankLocked()) return false;
|
||||
|
||||
// Remember what bank we're in
|
||||
myCurrentBank = bank;
|
||||
uInt16 offset = myCurrentBank << 12;
|
||||
myBankOffset = bank << 12;
|
||||
uInt16 shift = mySystem->pageShift();
|
||||
|
||||
// Setup the page access methods for the current bank
|
||||
System::PageAccess access(0, 0, 0, this, System::PA_READ);
|
||||
|
||||
// Map Program ROM image into the system
|
||||
for(uInt32 address = 0x1080; address < 0x2000; address += (1 << shift))
|
||||
for(uInt16 address = 0x1080; address < 0x2000; address += (1 << shift))
|
||||
{
|
||||
access.codeAccessBase = &myCodeAccessBase[offset + (address & 0x0FFF)];
|
||||
access.codeAccessBase = &myCodeAccessBase[myBankOffset + (address & 0x0FFF)];
|
||||
mySystem->setPageAccess(address >> shift, access);
|
||||
}
|
||||
return myBankChanged = true;
|
||||
|
@ -624,7 +616,7 @@ bool CartridgeDPCPlus::bank(uInt16 bank)
|
|||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
uInt16 CartridgeDPCPlus::bank() const
|
||||
{
|
||||
return myCurrentBank;
|
||||
return myBankOffset >> 12;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
@ -641,7 +633,7 @@ bool CartridgeDPCPlus::patch(uInt16 address, uInt8 value)
|
|||
// For now, we ignore attempts to patch the DPC address space
|
||||
if(address >= 0x0080)
|
||||
{
|
||||
myProgramImage[(myCurrentBank << 12) + (address & 0x0FFF)] = value;
|
||||
myProgramImage[myBankOffset + (address & 0x0FFF)] = value;
|
||||
return myBankChanged = true;
|
||||
}
|
||||
else
|
||||
|
@ -652,7 +644,7 @@ bool CartridgeDPCPlus::patch(uInt16 address, uInt8 value)
|
|||
const uInt8* CartridgeDPCPlus::getImage(int& size) const
|
||||
{
|
||||
size = mySize;
|
||||
return myImage;
|
||||
return myImage + (32768u - mySize);
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
@ -660,10 +652,8 @@ bool CartridgeDPCPlus::save(Serializer& out) const
|
|||
{
|
||||
try
|
||||
{
|
||||
out.putString(name());
|
||||
|
||||
// Indicates which bank is currently active
|
||||
out.putShort(myCurrentBank);
|
||||
out.putShort(myBankOffset);
|
||||
|
||||
// Harmony RAM
|
||||
out.putByteArray(myDPCRAM, 8192);
|
||||
|
@ -702,8 +692,12 @@ bool CartridgeDPCPlus::save(Serializer& out) const
|
|||
// The random number generator register
|
||||
out.putInt(myRandomNumber);
|
||||
|
||||
out.putInt(mySystemCycles);
|
||||
out.putInt((uInt32)(myFractionalClocks * 100000000.0));
|
||||
// Get system cycles and fractional clocks
|
||||
out.putLong(myAudioCycles);
|
||||
out.putDouble(myFractionalClocks);
|
||||
|
||||
// Clock info for Thumbulator
|
||||
out.putLong(myARMCycles);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
|
@ -719,11 +713,8 @@ bool CartridgeDPCPlus::load(Serializer& in)
|
|||
{
|
||||
try
|
||||
{
|
||||
if(in.getString() != name())
|
||||
return false;
|
||||
|
||||
// Indicates which bank is currently active
|
||||
myCurrentBank = in.getShort();
|
||||
myBankOffset = in.getShort();
|
||||
|
||||
// Harmony RAM
|
||||
in.getByteArray(myDPCRAM, 8192);
|
||||
|
@ -762,9 +753,12 @@ bool CartridgeDPCPlus::load(Serializer& in)
|
|||
// The random number generator register
|
||||
myRandomNumber = in.getInt();
|
||||
|
||||
// Get system cycles and fractional clocks
|
||||
mySystemCycles = (Int32)in.getInt();
|
||||
myFractionalClocks = (double)in.getInt() / 100000000.0;
|
||||
// Get audio cycles and fractional clocks
|
||||
myAudioCycles = in.getLong();
|
||||
myFractionalClocks = in.getDouble();
|
||||
|
||||
// Clock info for Thumbulator
|
||||
myARMCycles = in.getLong();
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
|
@ -773,7 +767,7 @@ bool CartridgeDPCPlus::load(Serializer& in)
|
|||
}
|
||||
|
||||
// Now, go to the current bank
|
||||
bank(myCurrentBank);
|
||||
bank(myBankOffset >> 12);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -56,11 +56,7 @@ class CartridgeDPCPlus : public Cartridge
|
|||
@param settings A reference to the various settings (read-only)
|
||||
*/
|
||||
CartridgeDPCPlus(const uInt8* image, uInt32 size, const Settings& settings);
|
||||
|
||||
/**
|
||||
Destructor
|
||||
*/
|
||||
virtual ~CartridgeDPCPlus();
|
||||
virtual ~CartridgeDPCPlus() = default;
|
||||
|
||||
public:
|
||||
/**
|
||||
|
@ -198,7 +194,7 @@ class CartridgeDPCPlus : public Cartridge
|
|||
|
||||
private:
|
||||
// The ROM image and size
|
||||
uInt8* myImage;
|
||||
uInt8 myImage[32768];
|
||||
uInt32 mySize;
|
||||
|
||||
// Pointer to the 24K program ROM image of the cartridge
|
||||
|
@ -207,20 +203,20 @@ class CartridgeDPCPlus : public Cartridge
|
|||
// Pointer to the 4K display ROM image of the cartridge
|
||||
uInt8* myDisplayImage;
|
||||
|
||||
// The DPC 8k RAM image
|
||||
// The DPC 8k RAM image, used as:
|
||||
// 3K DPC+ driver
|
||||
// 4K Display Data
|
||||
// 1K Frequency Data
|
||||
uInt8 myDPCRAM[8192];
|
||||
|
||||
#ifdef THUMB_SUPPORT
|
||||
// Pointer to the Thumb ARM emulator object
|
||||
Thumbulator* myThumbEmulator;
|
||||
unique_ptr<Thumbulator> myThumbEmulator;
|
||||
#endif
|
||||
|
||||
// Pointer to the 1K frequency table
|
||||
uInt8* myFrequencyImage;
|
||||
|
||||
// Indicates which bank is currently active
|
||||
uInt16 myCurrentBank;
|
||||
|
||||
// The top registers for the data fetchers
|
||||
uInt8 myTops[8];
|
||||
|
||||
|
@ -261,10 +257,16 @@ class CartridgeDPCPlus : public Cartridge
|
|||
uInt32 myRandomNumber;
|
||||
|
||||
// System cycle count when the last update to music data fetchers occurred
|
||||
Int32 mySystemCycles;
|
||||
uInt64 myAudioCycles;
|
||||
|
||||
// System cycle count when the last Thumbulator::run() occurred
|
||||
uInt64 myARMCycles;
|
||||
|
||||
// Fractional DPC music OSC clocks unused during the last update
|
||||
double myFractionalClocks;
|
||||
|
||||
// Indicates the offset into the ROM image (aligns to current bank)
|
||||
uInt16 myBankOffset;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -385,7 +385,7 @@ uInt8 CartridgeFA2::ramReadWrite()
|
|||
// We go ahead and do the access now, and only return when a sufficient
|
||||
// amount of time has passed
|
||||
Serializer serializer(myFlashFile);
|
||||
if(serializer.isValid())
|
||||
if(serializer)
|
||||
{
|
||||
if(myRAM[255] == 1) // read
|
||||
{
|
||||
|
@ -437,7 +437,7 @@ uInt8 CartridgeFA2::ramReadWrite()
|
|||
void CartridgeFA2::flash(uInt8 operation)
|
||||
{
|
||||
Serializer serializer(myFlashFile);
|
||||
if(serializer.isValid())
|
||||
if(serializer)
|
||||
{
|
||||
if(operation == 0) // erase
|
||||
{
|
||||
|
|
|
@ -1,290 +0,0 @@
|
|||
//============================================================================
|
||||
//
|
||||
// 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-2014 by Bradford W. Mott, Stephen Anthony
|
||||
// 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: CartMC.cxx 2838 2014-01-17 23:34:03Z stephena $
|
||||
//============================================================================
|
||||
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
|
||||
#include "System.hxx"
|
||||
#include "CartMC.hxx"
|
||||
|
||||
// TODO - much more testing of this scheme is required
|
||||
// No test ROMs exist as of 2009-11-08, so we can't be sure how
|
||||
// accurate the emulation is
|
||||
// Bankchange and RAM modification cannot be completed until
|
||||
// adequate test ROMs are available
|
||||
// TODO (2010-10-03) - support CodeAccessBase functionality somehow
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
CartridgeMC::CartridgeMC(const uInt8* image, uInt32 size,
|
||||
const Settings& settings)
|
||||
: Cartridge(settings),
|
||||
mySlot3Locked(false)
|
||||
{
|
||||
// Make sure size is reasonable
|
||||
assert(size <= 131072);
|
||||
|
||||
// Set the contents of the entire ROM to 0
|
||||
memset(myImage, 0, 131072);
|
||||
|
||||
// Copy the ROM image to the end of the ROM buffer
|
||||
memcpy(myImage + 131072 - size, image, size);
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
CartridgeMC::~CartridgeMC()
|
||||
{
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void CartridgeMC::reset()
|
||||
{
|
||||
// Initialize RAM
|
||||
if(mySettings.getBool("ramrandom"))
|
||||
for(uInt32 i = 0; i < 32768; ++i)
|
||||
myRAM[i] = mySystem->randGenerator().next();
|
||||
else
|
||||
memset(myRAM, 0, 32768);
|
||||
|
||||
myBankChanged = true;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void CartridgeMC::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(((0x1000 & mask) == 0) && ((0x1400 & mask) == 0) &&
|
||||
((0x1800 & mask) == 0) && ((0x1C00 & mask) == 0));
|
||||
|
||||
// Set the page accessing methods for the hot spots in the TIA. For
|
||||
// correct emulation I would need to chain any accesses below 0x40 to
|
||||
// the TIA but for now I'll just forget about them.
|
||||
//
|
||||
// TODO: These TIA accesses may need to be chained, however, at this
|
||||
// point Chris isn't sure if the hardware will allow it or not
|
||||
//
|
||||
System::PageAccess access(0, 0, 0, this, System::PA_READWRITE);
|
||||
|
||||
for(uInt32 i = 0x00; i < 0x40; i += (1 << shift))
|
||||
mySystem->setPageAccess(i >> shift, access);
|
||||
|
||||
// Map the cartridge into the system
|
||||
access.type = System::PA_READ; // We don't yet indicate RAM areas
|
||||
for(uInt32 j = 0x1000; j < 0x2000; j += (1 << shift))
|
||||
mySystem->setPageAccess(j >> shift, access);
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
uInt8 CartridgeMC::peek(uInt16 address)
|
||||
{
|
||||
uInt16 peekAddress = address;
|
||||
address &= 0x1FFF;
|
||||
|
||||
// Accessing the RESET vector so lets handle the powerup special case
|
||||
if((address == 0x1FFC) || (address == 0x1FFD))
|
||||
{
|
||||
// Indicate that slot 3 is locked for now
|
||||
mySlot3Locked = true;
|
||||
}
|
||||
// Should we unlock slot 3?
|
||||
else if(mySlot3Locked && (address >= 0x1000) && (address <= 0x1BFF))
|
||||
{
|
||||
// Indicate that slot 3 is unlocked now
|
||||
mySlot3Locked = false;
|
||||
}
|
||||
|
||||
// Handle reads made to the TIA addresses
|
||||
if(address < 0x1000)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
uInt8 block;
|
||||
|
||||
if(mySlot3Locked && ((address & 0x0C00) == 0x0C00))
|
||||
{
|
||||
block = 0xFF;
|
||||
}
|
||||
else
|
||||
{
|
||||
block = myCurrentBlock[(address & 0x0C00) >> 10];
|
||||
}
|
||||
|
||||
// Is this a RAM or a ROM access
|
||||
if(block & 0x80)
|
||||
{
|
||||
// ROM access
|
||||
return myImage[(uInt32)((block & 0x7F) << 10) + (address & 0x03FF)];
|
||||
}
|
||||
else
|
||||
{
|
||||
// This is a RAM access, however, is it to the read or write port?
|
||||
if(address & 0x0200)
|
||||
{
|
||||
// Reading from the read port of the RAM block
|
||||
return myRAM[(uInt32)((block & 0x3F) << 9) + (address & 0x01FF)];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Oops, reading from the write port of the RAM block!
|
||||
// Reading from the write port triggers an unwanted write
|
||||
uInt8 value = mySystem->getDataBusState(0xFF);
|
||||
|
||||
if(bankLocked())
|
||||
return value;
|
||||
else
|
||||
{
|
||||
triggerReadFromWritePort(peekAddress);
|
||||
return myRAM[(uInt32)((block & 0x3F) << 9) + (address & 0x01FF)] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
bool CartridgeMC::poke(uInt16 address, uInt8 value)
|
||||
{
|
||||
address &= 0x1FFF;
|
||||
|
||||
// Accessing the RESET vector so lets handle the powerup special case
|
||||
if((address == 0x1FFC) || (address == 0x1FFD))
|
||||
{
|
||||
// Indicate that slot 3 is locked for now
|
||||
mySlot3Locked = true;
|
||||
}
|
||||
// Should we unlock slot 3?
|
||||
else if(mySlot3Locked && (address >= 0x1000) && (address <= 0x1BFF))
|
||||
{
|
||||
// Indicate that slot 3 is unlocked now
|
||||
mySlot3Locked = false;
|
||||
}
|
||||
|
||||
// Handle bank-switching writes
|
||||
if((address >= 0x003C) && (address <= 0x003F))
|
||||
{
|
||||
myCurrentBlock[address - 0x003C] = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
uInt8 block;
|
||||
|
||||
if(mySlot3Locked && ((address & 0x0C00) == 0x0C00))
|
||||
{
|
||||
block = 0xFF;
|
||||
}
|
||||
else
|
||||
{
|
||||
block = myCurrentBlock[(address & 0x0C00) >> 10];
|
||||
}
|
||||
|
||||
// Is this a RAM write access
|
||||
if(!(block & 0x80) && !(address & 0x0200))
|
||||
{
|
||||
// Handle the write to RAM
|
||||
myRAM[(uInt32)((block & 0x3F) << 9) + (address & 0x01FF)] = value;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
bool CartridgeMC::bank(uInt16 b)
|
||||
{
|
||||
// Doesn't support bankswitching in the normal sense
|
||||
return false;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
uInt16 CartridgeMC::bank() const
|
||||
{
|
||||
// TODO - add support for debugger
|
||||
return 0;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
uInt16 CartridgeMC::bankCount() const
|
||||
{
|
||||
// TODO - add support for debugger
|
||||
return 1;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
bool CartridgeMC::patch(uInt16 address, uInt8 value)
|
||||
{
|
||||
// TODO - add support for debugger
|
||||
return false;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
const uInt8* CartridgeMC::getImage(int& size) const
|
||||
{
|
||||
size = 128 * 1024;
|
||||
return myImage;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
bool CartridgeMC::save(Serializer& out) const
|
||||
{
|
||||
try
|
||||
{
|
||||
out.putString(name());
|
||||
|
||||
// The currentBlock array
|
||||
out.putByteArray(myCurrentBlock, 4);
|
||||
|
||||
// The 32K of RAM
|
||||
out.putByteArray(myRAM, 32 * 1024);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
cerr << "ERROR: CartridgeMC::save" << endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
bool CartridgeMC::load(Serializer& in)
|
||||
{
|
||||
try
|
||||
{
|
||||
if(in.getString() != name())
|
||||
return false;
|
||||
|
||||
// The currentBlock array
|
||||
in.getByteArray(myCurrentBlock, 4);
|
||||
|
||||
// The 32K of RAM
|
||||
in.getByteArray(myRAM, 32 * 1024);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
cerr << "ERROR: CartridgeMC::load" << endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
|
@ -1,277 +0,0 @@
|
|||
//============================================================================
|
||||
//
|
||||
// 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-2014 by Bradford W. Mott, Stephen Anthony
|
||||
// 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: CartMC.hxx 2838 2014-01-17 23:34:03Z stephena $
|
||||
//============================================================================
|
||||
|
||||
#ifndef CARTRIDGEMC_HXX
|
||||
#define CARTRIDGEMC_HXX
|
||||
|
||||
class System;
|
||||
|
||||
#include "bspf.hxx"
|
||||
#include "Cart.hxx"
|
||||
#ifdef DEBUGGER_SUPPORT
|
||||
#include "CartMCWidget.hxx"
|
||||
#endif
|
||||
|
||||
/**
|
||||
This is the cartridge class for Chris Wilkson's Megacart. It does not
|
||||
handle battery-backed RAM at this time and the code could use some serious
|
||||
speed improvements. It is based on the following Megacart specification:
|
||||
|
||||
|
||||
Megacart Specification, Rev1.1
|
||||
(c) 1997 Chris Wilkson
|
||||
cwilkson@mit.edu
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
The Megacart is an external memory cartridge for the Atari 2600 and compatible
|
||||
home video game consoles. It plugs into the standard cartridge port, and
|
||||
contains a total of 128K bytes of ROM storage and 32K bytes of battery-backed
|
||||
RAM storage.
|
||||
|
||||
General Operation
|
||||
-----------------
|
||||
|
||||
The Megacart uses "bank switching" to fit the 160K bytes of physical memory
|
||||
into the console's available 4K address space. Physical memory is divided
|
||||
into 64 RAM blocks of 512 bytes each, and 128 ROM blocks of 1K bytes each.
|
||||
RAM blocks are numbered $00 through $3F, and ROM blocks are numbered $80
|
||||
through $FF.
|
||||
|
||||
The console's address space is divided into 4 slots of 1K each. Any physical
|
||||
memory block can be switched into any memory slot by writing its block number
|
||||
to the "hot address" for the desired slot. Memory locations $3C through $3F
|
||||
serve as "hot addresses" for memory slots 0 through 3, respectively.
|
||||
|
||||
|
||||
Example:
|
||||
|
||||
To make ROM addresses $1A400-$1A7FF (block $E9) available to the console at
|
||||
memory locations $F800-$FBFF (slot 2), write $E9 to memory location $3e.
|
||||
|
||||
Caution:
|
||||
|
||||
Note that these memory locations are write only. Trying to read the contents
|
||||
of memory locations $3C through $3F will not only return invalid data, but
|
||||
will also corrupt the contents causing the software to crash. Reading these
|
||||
addresses should not be attempted.
|
||||
|
||||
Special Case - RAM
|
||||
-------------------
|
||||
|
||||
RAM blocks differ from ROM blocks in that one of the console's address lines,
|
||||
A9 in this case, must be used as a read/write select. Because of this, RAM
|
||||
blocks are limited to 512 bytes each, yet still occupy an entire 1K slot.
|
||||
To store a value A9 must be low. To retrieve a value A9 must high.
|
||||
|
||||
Example:
|
||||
|
||||
First, let's set slot 0 (console addresses $F000-$F3FF) to point to RAM
|
||||
block $9 (RAM $1200-$13ff). To do this, write $9 to console address $3c.
|
||||
To store the value $69 in RAM location $1234, write $69 to console address
|
||||
$F034 (A9=0). To retrieve the value of RAM location $1234, read from console
|
||||
address $F234 (A9=1).
|
||||
|
||||
Special Case - Powerup
|
||||
-----------------------
|
||||
|
||||
Because the console's memory is randomized at powerup, there is no way to
|
||||
predict the data initially contained in the "hot addresses". Therefore,
|
||||
hardware will force slot 3 to always point to ROM block $FF immediately
|
||||
after any read or write to the RESET vector at $FFFC-$FFFD. Block $FF
|
||||
must contain code to initialize the 4 memory slots to point to the desired
|
||||
physical memory blocks before any other code can be executed. After program
|
||||
execution jumps out of the boot code, the hardware will release slot 3 and
|
||||
it will function just like any other slot.
|
||||
|
||||
Example (the first column is the physical ROM address):
|
||||
|
||||
$00C00 JUNK ... ; random code and data
|
||||
...
|
||||
...
|
||||
...
|
||||
...
|
||||
$1F400 START ... ; program starts here
|
||||
... ; slot 3 now points to rom block $83
|
||||
...
|
||||
...
|
||||
...
|
||||
$1FFDD BOOT SEI ; disable interrupts
|
||||
$1FFDE CLD ; set hexadecimal arithmetic mode
|
||||
$1FFDF LDX #$FF ;
|
||||
$1FFE1 TXS ; set stack pointer to $ff
|
||||
$1FFE2 LDA #$00
|
||||
$1FFE4 ZERO STA 00,X ; clear RIOT and TIA -BEFORE- setting
|
||||
$1FFE6 DEX ; up banks
|
||||
$1FFE7 BNE ZERO
|
||||
$1FFE9 BANKS LDA #$00 ; ram block 0 ($0000-$01ff)
|
||||
$1FFEB STA SLOT0 ; slot 0 points to ram block 0
|
||||
$1FFED LDA #$34 ; ram block $34 ($6800-$69ff)
|
||||
$1FFEF STA SLOT1 ; slot 1 points to ram block $34
|
||||
$1FFF1 LDA #$FD ; rom block $fd ($1f400-$1f7ff)
|
||||
$1FFF3 STA SLOT2 ; slot 2 points to rom block $fd
|
||||
$1FFF5 LDA #$83 ; rom block $83 ($00C00-$01000)
|
||||
$1FFF7 STA SLOT3 ; slot 3 points to bootcode
|
||||
; (rom block $ff)
|
||||
; until jumping out of slot 3
|
||||
$1FFF9 JMP $F800 ; jump to slot 2
|
||||
$1FFFC RESET .WORD $FFDD ; powerup reset vector
|
||||
$1FFFE SWI .WORD $FFDD ; software interrupt vector (BRK)
|
||||
|
||||
|
||||
@author Bradford W. Mott
|
||||
@version $Id: CartMC.hxx 2838 2014-01-17 23:34:03Z stephena $
|
||||
*/
|
||||
class CartridgeMC : public Cartridge
|
||||
{
|
||||
friend class CartridgeMCWidget;
|
||||
|
||||
public:
|
||||
/**
|
||||
Create a new cartridge using the specified image and size. If the
|
||||
size of the image is less than 128K then the cartridge will pad the
|
||||
beginning of the 128K ROM with zeros.
|
||||
|
||||
@param image Pointer to the ROM image
|
||||
@param size The size of the ROM image
|
||||
@param settings A reference to the various settings (read-only)
|
||||
*/
|
||||
CartridgeMC(const uInt8* image, uInt32 size, const Settings& settings);
|
||||
|
||||
/**
|
||||
Destructor
|
||||
*/
|
||||
virtual ~CartridgeMC();
|
||||
|
||||
public:
|
||||
/**
|
||||
Reset device to its power-on state
|
||||
*/
|
||||
void reset();
|
||||
|
||||
/**
|
||||
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
|
||||
*/
|
||||
void install(System& system);
|
||||
|
||||
/**
|
||||
Install pages for the specified bank in the system.
|
||||
|
||||
@param bank The bank that should be installed in the system
|
||||
*/
|
||||
bool bank(uInt16 bank);
|
||||
|
||||
/**
|
||||
Get the current bank.
|
||||
*/
|
||||
uInt16 bank() const;
|
||||
|
||||
/**
|
||||
Query the number of banks supported by the cartridge.
|
||||
*/
|
||||
uInt16 bankCount() const;
|
||||
|
||||
/**
|
||||
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
|
||||
*/
|
||||
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
|
||||
*/
|
||||
const uInt8* getImage(int& size) const;
|
||||
|
||||
/**
|
||||
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
|
||||
*/
|
||||
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
|
||||
*/
|
||||
bool load(Serializer& in);
|
||||
|
||||
/**
|
||||
Get a descriptor for the device name (used in error checking).
|
||||
|
||||
@return The name of the object
|
||||
*/
|
||||
string name() const { return "CartridgeMC"; }
|
||||
|
||||
#ifdef DEBUGGER_SUPPORT
|
||||
/**
|
||||
Get debugger widget responsible for accessing the inner workings
|
||||
of the cart.
|
||||
*/
|
||||
CartDebugWidget* debugWidget(GuiObject* boss, const GUI::Font& lfont,
|
||||
const GUI::Font& nfont, int x, int y, int w, int h)
|
||||
{
|
||||
return new CartridgeMCWidget(boss, lfont, nfont, x, y, w, h, *this);
|
||||
}
|
||||
#endif
|
||||
|
||||
public:
|
||||
/**
|
||||
Get the byte at the specified address
|
||||
|
||||
@return The byte at the specified address
|
||||
*/
|
||||
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
|
||||
@return True if the poke changed the device address space, else false
|
||||
*/
|
||||
bool poke(uInt16 address, uInt8 value);
|
||||
|
||||
private:
|
||||
// The 128K ROM image for the cartridge
|
||||
uInt8 myImage[131072];
|
||||
|
||||
// The 32K of RAM for the cartridge
|
||||
uInt8 myRAM[32768];
|
||||
|
||||
// Indicates which block is currently active for the four segments
|
||||
uInt8 myCurrentBlock[4];
|
||||
|
||||
// Indicates if slot 3 is locked to block $FF or not
|
||||
bool mySlot3Locked;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,31 @@
|
|||
//============================================================================
|
||||
//
|
||||
// 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-2018 by Bradford W. Mott, Stephen Anthony
|
||||
// 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.
|
||||
//============================================================================
|
||||
|
||||
#ifndef CONSOLE_TIMING_HXX
|
||||
#define CONSOLE_TIMING_HXX
|
||||
|
||||
/**
|
||||
Contains timing information about the specified console.
|
||||
*/
|
||||
enum class ConsoleTiming
|
||||
{
|
||||
ntsc, // console with CPU running at 1.193182 MHz, NTSC colours
|
||||
pal, // console with CPU running at 1.182298 MHz, PAL colours
|
||||
secam // console with CPU running at 1.187500 MHz, SECAM colours
|
||||
};
|
||||
|
||||
#endif // CONSOLE_TIMING_HXX
|
|
@ -22,6 +22,7 @@
|
|||
|
||||
class System;
|
||||
|
||||
#include "ConsoleTiming.hxx"
|
||||
#include "Serializable.hxx"
|
||||
#include "bspf.hxx"
|
||||
|
||||
|
@ -55,6 +56,15 @@ class Device : public Serializable
|
|||
*/
|
||||
virtual void reset() = 0;
|
||||
|
||||
/**
|
||||
Notification method invoked by the system when the console type
|
||||
has changed. It may be necessary to override this method for
|
||||
devices that want to know about console changes.
|
||||
|
||||
@param timing Enum representing the new console type
|
||||
*/
|
||||
virtual void consoleChanged(ConsoleTiming timing) { }
|
||||
|
||||
/**
|
||||
Notification method invoked by the system right before the
|
||||
system resets its cycle counter to zero. It may be necessary
|
||||
|
|
|
@ -8,13 +8,11 @@
|
|||
// SS SS tt ee ll ll aa aa
|
||||
// SSSS ttt eeeee llll llll aaaaa
|
||||
//
|
||||
// Copyright (c) 1995-2014 by Bradford W. Mott, Stephen Anthony
|
||||
// Copyright (c) 1995-2018 by Bradford W. Mott, Stephen Anthony
|
||||
// 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: Serializer.cxx 2838 2014-01-17 23:34:03Z stephena $
|
||||
//============================================================================
|
||||
|
||||
#include <fstream>
|
||||
|
@ -23,25 +21,25 @@
|
|||
#include "FSNode.hxx"
|
||||
#include "Serializer.hxx"
|
||||
|
||||
using std::ios;
|
||||
using std::ios_base;
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
Serializer::Serializer(const string& filename, bool readonly)
|
||||
: myStream(NULL),
|
||||
myUseFilestream(true)
|
||||
: myStream(nullptr)
|
||||
{
|
||||
if(readonly)
|
||||
{
|
||||
FilesystemNode node(filename);
|
||||
if(node.isFile() && node.isReadable())
|
||||
{
|
||||
fstream* str = new fstream(filename.c_str(), ios::in | ios::binary);
|
||||
unique_ptr<fstream> str = make_unique<fstream>(filename, ios::in | ios::binary);
|
||||
if(str && str->is_open())
|
||||
{
|
||||
myStream = str;
|
||||
myStream = std::move(str);
|
||||
myStream->exceptions( ios_base::failbit | ios_base::badbit | ios_base::eofbit );
|
||||
reset();
|
||||
rewind();
|
||||
}
|
||||
else
|
||||
delete str;
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -53,27 +51,24 @@ Serializer::Serializer(const string& filename, bool readonly)
|
|||
// So we open in write and append mode - the write creates the file
|
||||
// when necessary, and the append doesn't delete any data if it
|
||||
// already exists
|
||||
fstream temp(filename.c_str(), ios::out | ios::app);
|
||||
fstream temp(filename, ios::out | ios::app);
|
||||
temp.close();
|
||||
|
||||
fstream* str = new fstream(filename.c_str(), ios::in | ios::out | ios::binary);
|
||||
unique_ptr<fstream> str = make_unique<fstream>(filename, ios::in | ios::out | ios::binary);
|
||||
if(str && str->is_open())
|
||||
{
|
||||
myStream = str;
|
||||
myStream = std::move(str);
|
||||
myStream->exceptions( ios_base::failbit | ios_base::badbit | ios_base::eofbit );
|
||||
reset();
|
||||
rewind();
|
||||
}
|
||||
else
|
||||
delete str;
|
||||
}
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
Serializer::Serializer(void)
|
||||
: myStream(NULL),
|
||||
myUseFilestream(false)
|
||||
Serializer::Serializer()
|
||||
: myStream(nullptr)
|
||||
{
|
||||
myStream = new stringstream(ios::in | ios::out | ios::binary);
|
||||
myStream = make_unique<stringstream>(ios::in | ios::out | ios::binary);
|
||||
|
||||
// For some reason, Windows and possibly OSX needs to store something in
|
||||
// the stream before it is used for the first time
|
||||
|
@ -81,31 +76,12 @@ Serializer::Serializer(void)
|
|||
{
|
||||
myStream->exceptions( ios_base::failbit | ios_base::badbit | ios_base::eofbit );
|
||||
putBool(true);
|
||||
reset();
|
||||
rewind();
|
||||
}
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
Serializer::~Serializer(void)
|
||||
{
|
||||
if(myStream != NULL)
|
||||
{
|
||||
if(myUseFilestream)
|
||||
((fstream*)myStream)->close();
|
||||
|
||||
delete myStream;
|
||||
myStream = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
bool Serializer::isValid(void)
|
||||
{
|
||||
return myStream != NULL;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void Serializer::reset(void)
|
||||
void Serializer::rewind()
|
||||
{
|
||||
myStream->clear();
|
||||
myStream->seekg(ios_base::beg);
|
||||
|
@ -113,7 +89,7 @@ void Serializer::reset(void)
|
|||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
uInt8 Serializer::getByte(void)
|
||||
uInt8 Serializer::getByte() const
|
||||
{
|
||||
char buf;
|
||||
myStream->read(&buf, 1);
|
||||
|
@ -122,43 +98,61 @@ uInt8 Serializer::getByte(void)
|
|||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void Serializer::getByteArray(uInt8* array, uInt32 size)
|
||||
void Serializer::getByteArray(uInt8* array, uInt32 size) const
|
||||
{
|
||||
myStream->read((char*)array, size);
|
||||
myStream->read(reinterpret_cast<char*>(array), size);
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
uInt16 Serializer::getShort(void)
|
||||
uInt16 Serializer::getShort() const
|
||||
{
|
||||
uInt16 val = 0;
|
||||
myStream->read((char*)&val, sizeof(uInt16));
|
||||
myStream->read(reinterpret_cast<char*>(&val), sizeof(uInt16));
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void Serializer::getShortArray(uInt16* array, uInt32 size)
|
||||
void Serializer::getShortArray(uInt16* array, uInt32 size) const
|
||||
{
|
||||
myStream->read((char*)array, sizeof(uInt16)*size);
|
||||
myStream->read(reinterpret_cast<char*>(array), sizeof(uInt16)*size);
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
uInt32 Serializer::getInt(void)
|
||||
uInt32 Serializer::getInt() const
|
||||
{
|
||||
uInt32 val = 0;
|
||||
myStream->read((char*)&val, sizeof(uInt32));
|
||||
myStream->read(reinterpret_cast<char*>(&val), sizeof(uInt32));
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void Serializer::getIntArray(uInt32* array, uInt32 size)
|
||||
void Serializer::getIntArray(uInt32* array, uInt32 size) const
|
||||
{
|
||||
myStream->read((char*)array, sizeof(uInt32)*size);
|
||||
myStream->read(reinterpret_cast<char*>(array), sizeof(uInt32)*size);
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
string Serializer::getString(void)
|
||||
uInt64 Serializer::getLong() const
|
||||
{
|
||||
uInt64 val = 0;
|
||||
myStream->read(reinterpret_cast<char*>(&val), sizeof(uInt64));
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
double Serializer::getDouble() const
|
||||
{
|
||||
double val = 0.0;
|
||||
myStream->read(reinterpret_cast<char*>(&val), sizeof(double));
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
string Serializer::getString() const
|
||||
{
|
||||
int len = getInt();
|
||||
string str;
|
||||
|
@ -169,7 +163,7 @@ string Serializer::getString(void)
|
|||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
bool Serializer::getBool(void)
|
||||
bool Serializer::getBool() const
|
||||
{
|
||||
return getByte() == TruePattern;
|
||||
}
|
||||
|
@ -177,43 +171,55 @@ bool Serializer::getBool(void)
|
|||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void Serializer::putByte(uInt8 value)
|
||||
{
|
||||
myStream->write((char*)&value, 1);
|
||||
myStream->write(reinterpret_cast<char*>(&value), 1);
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void Serializer::putByteArray(const uInt8* array, uInt32 size)
|
||||
{
|
||||
myStream->write((char*)array, size);
|
||||
myStream->write(reinterpret_cast<const char*>(array), size);
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void Serializer::putShort(uInt16 value)
|
||||
{
|
||||
myStream->write((char*)&value, sizeof(uInt16));
|
||||
myStream->write(reinterpret_cast<char*>(&value), sizeof(uInt16));
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void Serializer::putShortArray(const uInt16* array, uInt32 size)
|
||||
{
|
||||
myStream->write((char*)array, sizeof(uInt16)*size);
|
||||
myStream->write(reinterpret_cast<const char*>(array), sizeof(uInt16)*size);
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void Serializer::putInt(uInt32 value)
|
||||
{
|
||||
myStream->write((char*)&value, sizeof(uInt32));
|
||||
myStream->write(reinterpret_cast<char*>(&value), sizeof(uInt32));
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void Serializer::putIntArray(const uInt32* array, uInt32 size)
|
||||
{
|
||||
myStream->write((char*)array, sizeof(uInt32)*size);
|
||||
myStream->write(reinterpret_cast<const char*>(array), sizeof(uInt32)*size);
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void Serializer::putLong(uInt64 value)
|
||||
{
|
||||
myStream->write(reinterpret_cast<char*>(&value), sizeof(uInt64));
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void Serializer::putDouble(double value)
|
||||
{
|
||||
myStream->write(reinterpret_cast<char*>(&value), sizeof(double));
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
void Serializer::putString(const string& str)
|
||||
{
|
||||
int len = str.length();
|
||||
int len = int(str.length());
|
||||
putInt(len);
|
||||
myStream->write(str.data(), len);
|
||||
}
|
||||
|
|
|
@ -8,19 +8,16 @@
|
|||
// SS SS tt ee ll ll aa aa
|
||||
// SSSS ttt eeeee llll llll aaaaa
|
||||
//
|
||||
// Copyright (c) 1995-2014 by Bradford W. Mott, Stephen Anthony
|
||||
// Copyright (c) 1995-2018 by Bradford W. Mott, Stephen Anthony
|
||||
// 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: Serializer.hxx 2838 2014-01-17 23:34:03Z stephena $
|
||||
//============================================================================
|
||||
|
||||
#ifndef SERIALIZER_HXX
|
||||
#define SERIALIZER_HXX
|
||||
|
||||
#include <iostream>
|
||||
#include "bspf.hxx"
|
||||
|
||||
/**
|
||||
|
@ -29,15 +26,11 @@
|
|||
stream can be either an actual file, or an in-memory structure.
|
||||
|
||||
Bytes are written as characters, shorts as 2 characters (16-bits),
|
||||
integers as 4 characters (32-bits), strings are written as characters
|
||||
prepended by the length of the string, boolean values are written using
|
||||
a special character pattern.
|
||||
|
||||
All bytes, shorts and ints should be cast to their appropriate data type upon
|
||||
method return.
|
||||
integers as 4 characters (32-bits), long integers as 8 bytes (64-bits),
|
||||
strings are written as characters prepended by the length of the string,
|
||||
boolean values are written using a special character pattern.
|
||||
|
||||
@author Stephen Anthony
|
||||
@version $Id: Serializer.hxx 2838 2014-01-17 23:34:03Z stephena $
|
||||
*/
|
||||
class Serializer
|
||||
{
|
||||
|
@ -50,35 +43,30 @@ class Serializer
|
|||
|
||||
If a file is opened readonly, we can never write to it.
|
||||
|
||||
The isValid() method must immediately be called to verify the stream
|
||||
The valid() method must immediately be called to verify the stream
|
||||
was correctly initialized.
|
||||
*/
|
||||
Serializer(const string& filename, bool readonly = false);
|
||||
Serializer(void);
|
||||
|
||||
/**
|
||||
Destructor
|
||||
*/
|
||||
virtual ~Serializer(void);
|
||||
Serializer();
|
||||
|
||||
public:
|
||||
/**
|
||||
Answers whether the serializer is currently initialized for reading
|
||||
and writing.
|
||||
*/
|
||||
bool isValid(void);
|
||||
explicit operator bool() const { return myStream != nullptr; }
|
||||
|
||||
/**
|
||||
Resets the read/write location to the beginning of the stream.
|
||||
*/
|
||||
void reset(void);
|
||||
void rewind();
|
||||
|
||||
/**
|
||||
Reads a byte value (unsigned 8-bit) from the current input stream.
|
||||
|
||||
@result The byte value which has been read from the stream.
|
||||
*/
|
||||
uInt8 getByte(void);
|
||||
uInt8 getByte() const;
|
||||
|
||||
/**
|
||||
Reads a byte array (unsigned 8-bit) from the current input stream.
|
||||
|
@ -86,15 +74,14 @@ class Serializer
|
|||
@param array The location to store the bytes read
|
||||
@param size The size of the array (number of bytes to read)
|
||||
*/
|
||||
void getByteArray(uInt8* array, uInt32 size);
|
||||
|
||||
void getByteArray(uInt8* array, uInt32 size) const;
|
||||
|
||||
/**
|
||||
Reads a short value (unsigned 16-bit) from the current input stream.
|
||||
|
||||
@result The short value which has been read from the stream.
|
||||
*/
|
||||
uInt16 getShort(void);
|
||||
uInt16 getShort() const;
|
||||
|
||||
/**
|
||||
Reads a short array (unsigned 16-bit) from the current input stream.
|
||||
|
@ -102,14 +89,14 @@ class Serializer
|
|||
@param array The location to store the shorts read
|
||||
@param size The size of the array (number of shorts to read)
|
||||
*/
|
||||
void getShortArray(uInt16* array, uInt32 size);
|
||||
void getShortArray(uInt16* array, uInt32 size) const;
|
||||
|
||||
/**
|
||||
Reads an int value (unsigned 32-bit) from the current input stream.
|
||||
|
||||
@result The int value which has been read from the stream.
|
||||
*/
|
||||
uInt32 getInt(void);
|
||||
uInt32 getInt() const;
|
||||
|
||||
/**
|
||||
Reads an integer array (unsigned 32-bit) from the current input stream.
|
||||
|
@ -117,21 +104,35 @@ class Serializer
|
|||
@param array The location to store the integers read
|
||||
@param size The size of the array (number of integers to read)
|
||||
*/
|
||||
void getIntArray(uInt32* array, uInt32 size);
|
||||
void getIntArray(uInt32* array, uInt32 size) const;
|
||||
|
||||
/**
|
||||
Reads a long int value (unsigned 64-bit) from the current input stream.
|
||||
|
||||
@result The long int value which has been read from the stream.
|
||||
*/
|
||||
uInt64 getLong() const;
|
||||
|
||||
/**
|
||||
Reads a double value (signed 64-bit) from the current input stream.
|
||||
|
||||
@result The double value which has been read from the stream.
|
||||
*/
|
||||
double getDouble() const;
|
||||
|
||||
/**
|
||||
Reads a string from the current input stream.
|
||||
|
||||
@result The string which has been read from the stream.
|
||||
*/
|
||||
string getString(void);
|
||||
string getString() const;
|
||||
|
||||
/**
|
||||
Reads a boolean value from the current input stream.
|
||||
|
||||
@result The boolean value which has been read from the stream.
|
||||
*/
|
||||
bool getBool(void);
|
||||
bool getBool() const;
|
||||
|
||||
/**
|
||||
Writes an byte value (unsigned 8-bit) to the current output stream.
|
||||
|
@ -178,6 +179,20 @@ class Serializer
|
|||
*/
|
||||
void putIntArray(const uInt32* array, uInt32 size);
|
||||
|
||||
/**
|
||||
Writes a long int value (unsigned 64-bit) to the current output stream.
|
||||
|
||||
@param value The long int value to write to the output stream.
|
||||
*/
|
||||
void putLong(uInt64 value);
|
||||
|
||||
/**
|
||||
Writes a double value (signed 64-bit) to the current output stream.
|
||||
|
||||
@param value The double value to write to the output stream.
|
||||
*/
|
||||
void putDouble(double value);
|
||||
|
||||
/**
|
||||
Writes a string to the current output stream.
|
||||
|
||||
|
@ -194,13 +209,19 @@ class Serializer
|
|||
|
||||
private:
|
||||
// The stream to send the serialized data to.
|
||||
iostream* myStream;
|
||||
bool myUseFilestream;
|
||||
unique_ptr<iostream> myStream;
|
||||
|
||||
enum {
|
||||
TruePattern = 0xfe,
|
||||
FalsePattern = 0x01
|
||||
};
|
||||
|
||||
private:
|
||||
// Following constructors and assignment operators not supported
|
||||
Serializer(const Serializer&) = delete;
|
||||
Serializer(Serializer&&) = delete;
|
||||
Serializer& operator=(const Serializer&) = delete;
|
||||
Serializer& operator=(Serializer&&) = delete;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
|
||||
#include "StateManager.hxx"
|
||||
|
||||
#define STATE_HEADER "03090100state"
|
||||
#define STATE_HEADER "03090400state"
|
||||
#define MOVIE_HEADER "03030000movie"
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
@ -186,7 +186,7 @@ void StateManager::loadState(int slot)
|
|||
|
||||
// Make sure the file can be opened in read-only mode
|
||||
Serializer in(buf.str(), true);
|
||||
if(!in.isValid())
|
||||
if(!in)
|
||||
{
|
||||
buf.str("");
|
||||
#if !defined(RETRON77)
|
||||
|
@ -251,7 +251,7 @@ void StateManager::saveState(int slot)
|
|||
|
||||
// Make sure the file can be opened for writing
|
||||
Serializer out(buf.str());
|
||||
if(!out.isValid())
|
||||
if(!out)
|
||||
{
|
||||
buf.str("");
|
||||
buf << "Can't open/save to state file " << " " << myOSystem->stateDir() << name << ".st" << slot;
|
||||
|
@ -309,7 +309,7 @@ bool StateManager::loadState(Serializer& in)
|
|||
if(&myOSystem->console())
|
||||
{
|
||||
// Make sure the file can be opened for reading
|
||||
if(in.isValid())
|
||||
if(in)
|
||||
{
|
||||
// First test if we have a valid header and cart type
|
||||
// If so, do a complete state load using the Console
|
||||
|
@ -329,7 +329,7 @@ bool StateManager::saveState(Serializer& out)
|
|||
if(&myOSystem->console())
|
||||
{
|
||||
// Make sure the file can be opened for writing
|
||||
if(out.isValid())
|
||||
if(out)
|
||||
{
|
||||
// Add header so that if the state format changes in the future,
|
||||
// we'll know right away, without having to parse the rest of the file
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -8,13 +8,11 @@
|
|||
// SS SS tt ee ll ll aa aa
|
||||
// SSSS ttt eeeee llll llll aaaaa
|
||||
//
|
||||
// Copyright (c) 1995-2014 by Bradford W. Mott, Stephen Anthony
|
||||
// Copyright (c) 1995-2018 by Bradford W. Mott, Stephen Anthony
|
||||
// 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: Thumbulator.hxx 2838 2014-01-17 23:34:03Z stephena $
|
||||
//============================================================================
|
||||
|
||||
//============================================================================
|
||||
|
@ -24,9 +22,13 @@
|
|||
// Code is public domain and used with the author's consent
|
||||
//============================================================================
|
||||
|
||||
#ifdef THUMB_SUPPORT
|
||||
#ifndef THUMBULATOR_HXX
|
||||
#define THUMBULATOR_HXX
|
||||
|
||||
class Cartridge;
|
||||
|
||||
#include "bspf.hxx"
|
||||
#include "Console.hxx"
|
||||
|
||||
#define ROMADDMASK 0x7FFF
|
||||
#define RAMADDMASK 0x1FFF
|
||||
|
@ -34,39 +36,28 @@
|
|||
#define ROMSIZE (ROMADDMASK+1)
|
||||
#define RAMSIZE (RAMADDMASK+1)
|
||||
|
||||
//0b10000 User PC, R14 to R0, CPSR
|
||||
//0b10001 FIQ PC, R14_fiq to R8_fiq, R7 to R0, CPSR, SPSR_fiq
|
||||
//0b10010 IRQ PC, R14_irq, R13_irq, R12 to R0, CPSR, SPSR_irq
|
||||
//0b10011 Supervisor PC, R14_svc, R13_svc, R12 to R0, CPSR, SPSR_svc
|
||||
//0b10111 Abort PC, R14_abt, R13_abt, R12 to R0, CPSR, SPSR_abt
|
||||
//0b11011 Undefined PC, R14_und, R13_und, R12 to R0, CPSR, SPSR_und
|
||||
//0b11111 System
|
||||
|
||||
#define MODE_USR 0x10
|
||||
#define MODE_FIQ 0x11
|
||||
#define MODE_IRQ 0x12
|
||||
#define MODE_SVC 0x13
|
||||
#define MODE_ABT 0x17
|
||||
#define MODE_UND 0x1B
|
||||
#define MODE_SYS 0x1F
|
||||
|
||||
#define CPSR_T (1<<5)
|
||||
#define CPSR_F (1<<6)
|
||||
#define CPSR_I (1<<7)
|
||||
#define CPSR_N (1<<31)
|
||||
#define CPSR_Z (1<<30)
|
||||
#define CPSR_C (1<<29)
|
||||
#define CPSR_V (1<<28)
|
||||
#define CPSR_Q (1<<27)
|
||||
#define CPSR_N (1u<<31)
|
||||
#define CPSR_Z (1u<<30)
|
||||
#define CPSR_C (1u<<29)
|
||||
#define CPSR_V (1u<<28)
|
||||
|
||||
class Thumbulator
|
||||
{
|
||||
public:
|
||||
Thumbulator(const uInt16* rom, uInt16* ram, bool traponfatal);
|
||||
~Thumbulator();
|
||||
// control cartridge specific features of the Thumbulator class,
|
||||
// such as the start location for calling custom code
|
||||
enum ConfigureFor {
|
||||
BUS, // cartridges of type BUS
|
||||
CDF, // cartridges of type CDF
|
||||
CDF1, // cartridges of type CDF version 1
|
||||
DPCplus // cartridges of type DPC+
|
||||
};
|
||||
|
||||
Thumbulator(const uInt16* rom, uInt16* ram, bool traponfatal,
|
||||
Thumbulator::ConfigureFor configurefor, Cartridge* cartridge);
|
||||
|
||||
/**
|
||||
Run the ARM code, and return when finished. A string exception is
|
||||
Run the ARM code, and return when finished. A runtime_error exception is
|
||||
thrown in case of any fatal errors/aborts (if enabled), containing the
|
||||
actual error, and the contents of the registers at that point in time.
|
||||
|
||||
|
@ -74,6 +65,7 @@ class Thumbulator
|
|||
otherwise an empty string
|
||||
*/
|
||||
string run();
|
||||
string run(uInt32 cycles);
|
||||
|
||||
/**
|
||||
Normally when a fatal error is encountered, the ARM emulation
|
||||
|
@ -89,59 +81,75 @@ class Thumbulator
|
|||
*/
|
||||
static void trapFatalErrors(bool enable) { trapOnFatal = enable; }
|
||||
|
||||
/**
|
||||
Inform the Thumbulator class about the console currently in use,
|
||||
which is used to accurately determine how many 6507 cycles have
|
||||
passed while ARM code is being executed.
|
||||
*/
|
||||
void setConsoleTiming(ConsoleTiming timing);
|
||||
|
||||
private:
|
||||
uInt32 read_register ( uInt32 reg );
|
||||
uInt32 write_register ( uInt32 reg, uInt32 data );
|
||||
uInt32 fetch16 ( uInt32 addr );
|
||||
uInt32 fetch32 ( uInt32 addr );
|
||||
uInt32 read16 ( uInt32 addr );
|
||||
uInt32 read32 ( uInt32 );
|
||||
void write16 ( uInt32 addr, uInt32 data );
|
||||
void write32 ( uInt32 addr, uInt32 data );
|
||||
uInt32 read_register(uInt32 reg);
|
||||
void write_register(uInt32 reg, uInt32 data);
|
||||
uInt32 fetch16(uInt32 addr);
|
||||
uInt32 fetch32(uInt32 addr);
|
||||
uInt32 read16(uInt32 addr);
|
||||
uInt32 read32(uInt32 addr);
|
||||
bool isProtected(uInt32 addr);
|
||||
void write16(uInt32 addr, uInt32 data);
|
||||
void write32(uInt32 addr, uInt32 data);
|
||||
void updateTimer(uInt32 cycles);
|
||||
|
||||
void do_zflag ( uInt32 x );
|
||||
void do_nflag ( uInt32 x );
|
||||
void do_cflag ( uInt32 a, uInt32 b, uInt32 c );
|
||||
void do_sub_vflag ( uInt32 a, uInt32 b, uInt32 c );
|
||||
void do_add_vflag ( uInt32 a, uInt32 b, uInt32 c );
|
||||
void do_cflag_bit ( uInt32 x );
|
||||
void do_vflag_bit ( uInt32 x );
|
||||
void do_zflag(uInt32 x);
|
||||
void do_nflag(uInt32 x);
|
||||
void do_cflag(uInt32 a, uInt32 b, uInt32 c);
|
||||
void do_vflag(uInt32 a, uInt32 b, uInt32 c);
|
||||
void do_cflag_bit(uInt32 x);
|
||||
void do_vflag_bit(uInt32 x);
|
||||
|
||||
// Throw a string exception containing an error referencing the given
|
||||
// message and variables
|
||||
// Throw a runtime_error exception containing an error referencing the
|
||||
// given message and variables
|
||||
// Note that the return value is never used in these methods
|
||||
int fatalError(const char* opcode, uInt32 v1, const char* msg);
|
||||
int fatalError(const char* opcode, uInt32 v1, uInt32 v2, const char* msg);
|
||||
|
||||
void dump_counters ( void );
|
||||
void dump_regs( void );
|
||||
int execute ( void );
|
||||
int reset ( void );
|
||||
void dump_counters();
|
||||
void dump_regs();
|
||||
int execute();
|
||||
int reset();
|
||||
|
||||
private:
|
||||
const uInt16* rom;
|
||||
uInt16* ram;
|
||||
//Int32 copydata;
|
||||
|
||||
uInt32 halfadd;
|
||||
uInt32 cpsr;
|
||||
//uInt32 reg_usr[16]; //User mode
|
||||
uInt32 reg_sys[16]; //System mode
|
||||
uInt32 reg_svc[16]; //Supervisor mode
|
||||
//uInt32 reg_abt[16]; //Abort mode
|
||||
//uInt32 reg_und[16]; //Undefined mode
|
||||
//uInt32 reg_irq[16]; //Interrupt mode
|
||||
//uInt32 reg_fiq[16]; //Fast Interrupt mode
|
||||
uInt32 mamcr;
|
||||
uInt32 reg_norm[16]; // normal execution mode, do not have a thread mode
|
||||
uInt32 cpsr, mamcr;
|
||||
bool handler_mode;
|
||||
uInt32 systick_ctrl, systick_reload, systick_count, systick_calibrate;
|
||||
uInt64 instructions, fetches, reads, writes, systick_ints;
|
||||
|
||||
uInt64 instructions;
|
||||
uInt64 fetches;
|
||||
uInt64 reads;
|
||||
uInt64 writes;
|
||||
// For emulation of LPC2103's timer 1, used for NTSC/PAL/SECAM detection.
|
||||
// Register names from documentation:
|
||||
// http://www.nxp.com/documents/user_manual/UM10161.pdf
|
||||
uInt32 T1TCR; // Timer 1 Timer Control Register
|
||||
uInt32 T1TC; // Timer 1 Timer Counter
|
||||
double timing_factor;
|
||||
|
||||
ostringstream statusMsg;
|
||||
|
||||
static bool trapOnFatal;
|
||||
|
||||
ConfigureFor configuration;
|
||||
|
||||
Cartridge* myCartridge;
|
||||
|
||||
private:
|
||||
// Following constructors and assignment operators not supported
|
||||
Thumbulator() = delete;
|
||||
Thumbulator(const Thumbulator&) = delete;
|
||||
Thumbulator(Thumbulator&&) = delete;
|
||||
Thumbulator& operator=(const Thumbulator&) = delete;
|
||||
Thumbulator& operator=(Thumbulator&&) = delete;
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif // THUMBULATOR_HXX
|
||||
|
|
|
@ -35,7 +35,6 @@ MODULE_OBJS := \
|
|||
src/emucore/CartFA.o \
|
||||
src/emucore/CartFA2.o \
|
||||
src/emucore/CartFE.o \
|
||||
src/emucore/CartMC.o \
|
||||
src/emucore/CartSB.o \
|
||||
src/emucore/CartUA.o \
|
||||
src/emucore/CartX07.o \
|
||||
|
|
Loading…
Reference in New Issue