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:
Stephen Anthony 2018-11-19 15:21:20 -03:30
parent 4f9470968b
commit e09bc90d17
17 changed files with 2052 additions and 2001 deletions

View File

@ -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

View File

@ -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,11 +420,11 @@ 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
else
type = "4K";
}
else if(size == 8*1024) // 8K
@ -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,13 +569,22 @@ 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;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool Cartridge::searchForBytes(const uInt8* image, uInt32 imagesize,
const uInt8* signature, uInt32 sigsize,
uInt32 minhits)
const uInt8* signature, uInt32 sigsize,
uInt32 minhits)
{
uInt32 count = 0;
for(uInt32 i = 0; i < imagesize - sigsize; ++i)
@ -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)
return false;
}
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);
}
@ -714,14 +790,14 @@ bool Cartridge::isProbablyE0(const uInt8* image, uInt32 size)
// Thanks to "stella@casperkitty.com" for this advice
// These signatures are attributed to the MESS project
uInt8 signature[8][3] = {
{ 0x8D, 0xE0, 0x1F }, // STA $1FE0
{ 0x8D, 0xE0, 0x5F }, // STA $5FE0
{ 0x8D, 0xE9, 0xFF }, // STA $FFE9
{ 0x0C, 0xE0, 0x1F }, // NOP $1FE0
{ 0xAD, 0xE0, 0x1F }, // LDA $1FE0
{ 0xAD, 0xE9, 0xFF }, // LDA $FFE9
{ 0xAD, 0xED, 0xFF }, // LDA $FFED
{ 0xAD, 0xF3, 0xBF } // LDA $BFF3
{ 0x8D, 0xE0, 0x1F }, // STA $1FE0
{ 0x8D, 0xE0, 0x5F }, // STA $5FE0
{ 0x8D, 0xE9, 0xFF }, // STA $FFE9
{ 0x0C, 0xE0, 0x1F }, // NOP $1FE0
{ 0xAD, 0xE0, 0x1F }, // LDA $1FE0
{ 0xAD, 0xE9, 0xFF }, // LDA $FFE9
{ 0xAD, 0xED, 0xFF }, // LDA $FFED
{ 0xAD, 0xF3, 0xBF } // LDA $BFF3
};
for(uInt32 i = 0; i < 8; ++i)
if(searchForBytes(image, size, signature[i], 3, 1))
@ -740,13 +816,13 @@ bool Cartridge::isProbablyE7(const uInt8* image, uInt32 size)
// Thanks to "stella@casperkitty.com" for this advice
// These signatures are attributed to the MESS project
uInt8 signature[7][3] = {
{ 0xAD, 0xE2, 0xFF }, // LDA $FFE2
{ 0xAD, 0xE5, 0xFF }, // LDA $FFE5
{ 0xAD, 0xE5, 0x1F }, // LDA $1FE5
{ 0xAD, 0xE7, 0x1F }, // LDA $1FE7
{ 0x0C, 0xE7, 0x1F }, // NOP $1FE7
{ 0x8D, 0xE7, 0xFF }, // STA $FFE7
{ 0x8D, 0xE7, 0x1F } // STA $1FE7
{ 0xAD, 0xE2, 0xFF }, // LDA $FFE2
{ 0xAD, 0xE5, 0xFF }, // LDA $FFE5
{ 0xAD, 0xE5, 0x1F }, // LDA $1FE5
{ 0xAD, 0xE7, 0x1F }, // LDA $1FE7
{ 0x0C, 0xE7, 0x1F }, // NOP $1FE7
{ 0x8D, 0xE7, 0xFF }, // STA $FFE7
{ 0x8D, 0xE7, 0x1F } // STA $1FE7
};
for(uInt32 i = 0; i < 7; ++i)
if(searchForBytes(image, size, signature[i], 3, 1))
@ -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;
}
@ -808,7 +903,7 @@ bool Cartridge::isProbablyBF(const uInt8* image, uInt32 size, const char*& type)
{
// BF carts store strings 'BFBF' and 'BFSC' starting at address $FFF8
// This signature is attributed to "RevEng" of AtariAge
uInt8 bf[] = { 'B', 'F', 'B', 'F' };
uInt8 bf[] = { 'B', 'F', 'B', 'F' };
uInt8 bfsc[] = { 'B', 'F', 'S', 'C' };
if(searchForBytes(image+size-8, 8, bf, 4, 1))
{
@ -824,18 +919,38 @@ 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)
{
// BF carts store strings 'DFDF' and 'DFSC' starting at address $FFF8
// This signature is attributed to "RevEng" of AtariAge
uInt8 df[] = { 'D', 'F', 'D', 'F' };
uInt8 df[] = { 'D', 'F', 'D', 'F' };
uInt8 dfsc[] = { 'D', 'F', 'S', 'C' };
if(searchForBytes(image+size-8, 8, df, 4, 1))
{
type = "DF";
return true;
return true;
}
else if(searchForBytes(image+size-8, 8, dfsc, 4, 1))
{
@ -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)
{

View File

@ -1,8 +1,8 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// 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
@ -40,11 +40,11 @@ struct RamArea {
typedef Common::Array<RamArea> RamAreaList;
/**
A cartridge is a device which contains the machine code for a
A cartridge is a device which contains the machine code for a
game and handles any bankswitching performed by the cartridge.
A 'bank' is defined as a 4K block that is visible in the
0x1000-0x2000 area (or its mirrors).
@author Bradford W. Mott
@version $Id: Cart.hxx 2838 2014-01-17 23:34:03Z stephena $
*/
@ -56,7 +56,7 @@ class Cartridge : public Device
type of cartridge created depends on the properties object.
@param image A pointer to the ROM image
@param size The size of the ROM image
@param size The size of the ROM image
@param md5 The md5sum for the given ROM image (can be updated)
@param dtype The detected bankswitch type of the ROM image
@param id Any extra info about the ROM (currently which part
@ -75,7 +75,7 @@ class Cartridge : public Device
@param settings A reference to the various settings (read-only)
*/
Cartridge(const Settings& settings);
/**
Destructor
*/
@ -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
@ -244,7 +251,7 @@ class Cartridge : public Device
multi-ROM image.
@param image A pointer to the ROM image
@param size The size of the ROM image
@param size The size of the ROM image
@param numroms The number of ROMs in the multicart
@param md5 The md5sum for the specific cart in the ROM image
@param id The ID for the specific cart in the ROM image
@ -258,7 +265,7 @@ class Cartridge : public Device
Try to auto-detect the bankswitching type of the cartridge
@param image A pointer to the ROM image
@param size The size of the ROM image
@param size The size of the ROM image
@return The "best guess" for the cartridge type
*/
static string autodetectType(const uInt8* image, uInt32 size);
@ -267,7 +274,7 @@ class Cartridge : public Device
Search the image for the specified byte signature
@param image A pointer to the ROM image
@param imagesize The size of the ROM image
@param imagesize The size of the ROM image
@param signature The byte sequence to search for
@param sigsize The number of bytes in the signature
@param minhits The minimum number of times a signature is to be found
@ -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
*/

View File

@ -1,8 +1,8 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// 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
@ -250,7 +250,7 @@ bool CartridgeCTY::poke(uInt16 address, uInt8 value)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool CartridgeCTY::bank(uInt16 bank)
{
{
if(bankLocked()) return false;
// Remember what bank we're in
@ -295,7 +295,7 @@ bool CartridgeCTY::patch(uInt16 address, uInt8 value)
myImage[myCurrentBank + address] = value;
return myBankChanged = true;
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
const uInt8* CartridgeCTY::getImage(int& size) const
@ -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];

View File

@ -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
for(int x = 0; x <= 2; ++x)
{
myMusicCounters[x] += myMusicFrequencies[x];
}
if(wholeClocks > 0)
for(int x = 0; x <= 2; ++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
@ -258,9 +251,9 @@ uInt8 CartridgeDPCPlus::peek(uInt16 address)
uInt32 index = address & 0x07;
uInt32 function = (address >> 3) & 0x07;
// Update flag for selected data fetcher
// Update flag for selected data fetcher
flag = (((myTops[index]-(myCounters[index] & 0x00ff)) & 0xFF) > ((myTops[index]-myBottoms[index]) & 0xFF)) ? 0xFF : 0;
switch(function)
{
case 0x00:
@ -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;
}

View File

@ -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:
/**
@ -170,35 +166,35 @@ class CartridgeDPCPlus : public Cartridge
bool poke(uInt16 address, uInt8 value);
private:
/**
/**
Sets the initial state of the DPC pointers and RAM
*/
void setInitialState();
/**
/**
Clocks the random number generator to move it to its next state
*/
void clockRandomNumberGenerator();
/**
/**
Clocks the random number generator to move it to its prior state
*/
void priorClockRandomNumberGenerator();
/**
/**
Updates any data fetchers in music mode based on the number of
CPU cycles which have passed since the last update.
*/
void updateMusicModeDataFetchers();
/**
/**
Call Special Functions
*/
void callFunction(uInt8 value);
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];
@ -229,7 +225,7 @@ class CartridgeDPCPlus : public Cartridge
// The counter registers for the data fetchers
uInt16 myCounters[8];
// The counter registers for the fractional data fetchers
uInt32 myFractionalCounters[8];
@ -238,7 +234,7 @@ class CartridgeDPCPlus : public Cartridge
// The Fast Fetcher Enabled flag
bool myFastFetch;
// Flags that last byte peeked was A9 (LDA #)
bool myLDAimmediate;
@ -253,18 +249,24 @@ class CartridgeDPCPlus : public Cartridge
// The music frequency
uInt32 myMusicFrequencies[3];
// The music waveforms
uInt16 myMusicWaveforms[3];
// The random number generator register
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

View File

@ -1,8 +1,8 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// 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
@ -36,7 +36,7 @@ CartridgeFA2::CartridgeFA2(const uInt8* image, uInt32 size, const OSystem& osyst
if(size >= 29 * 1024)
{
image += 1024;
mySize = 28 * 1024;
mySize = 28 * 1024;
}
// Allocate array for the ROM image
@ -52,7 +52,7 @@ CartridgeFA2::CartridgeFA2(const uInt8* image, uInt32 size, const OSystem& osyst
// Remember startup bank
myStartBank = 0;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
CartridgeFA2::~CartridgeFA2()
{
@ -93,7 +93,7 @@ void CartridgeFA2::install(System& system)
access.codeAccessBase = &myCodeAccessBase[j & 0x00FF];
mySystem->setPageAccess(j >> shift, access);
}
// Set the page accessing method for the RAM reading pages
access.directPokeBase = 0;
access.type = System::PA_READ;
@ -137,7 +137,7 @@ uInt8 CartridgeFA2::peek(uInt16 address)
// Set the current bank to the third 4k bank
bank(2);
break;
case 0x0FF8:
// Set the current bank to the fourth 4k bank
bank(3);
@ -175,7 +175,7 @@ uInt8 CartridgeFA2::peek(uInt16 address)
triggerReadFromWritePort(peekAddress);
return myRAM[address] = value;
}
}
}
else
return myImage[(myCurrentBank << 12) + address];
}
@ -208,7 +208,7 @@ bool CartridgeFA2::poke(uInt16 address, uInt8)
// Set the current bank to the third 4k bank
bank(2);
break;
case 0x0FF8:
// Set the current bank to the fourth 4k bank
bank(3);
@ -299,7 +299,7 @@ bool CartridgeFA2::patch(uInt16 address, uInt8 value)
myImage[(myCurrentBank << 12) + address] = value;
return myBankChanged = true;
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
const uInt8* CartridgeFA2::getImage(int& size) const
@ -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
{

View File

@ -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;
}

View File

@ -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

View File

@ -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

View File

@ -22,6 +22,7 @@
class System;
#include "ConsoleTiming.hxx"
#include "Serializable.hxx"
#include "bspf.hxx"
@ -55,9 +56,18 @@ 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
system resets its cycle counter to zero. It may be necessary
to override this method for devices that remember cycle counts.
*/
virtual void systemCyclesReset() { }

View File

@ -1,20 +1,18 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// 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
// 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,59 +51,37 @@ 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
if(myStream)
{
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);
}

View File

@ -1,26 +1,23 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// 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
// 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

View File

@ -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

View File

@ -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

View File

@ -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 \
@ -76,5 +75,5 @@ MODULE_OBJS := \
MODULE_DIRS += \
src/emucore
# Include common rules
# Include common rules
include $(srcdir)/common.rules