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> #include <cstdlib>
#define STELLA_VERSION "3.9.3" #define STELLA_VERSION "3.9.4"
#define STELLA_BUILD atoi("$Rev: 2838 $" + 6) #define STELLA_BUILD atoi("$Rev: 2838 $" + 6)
#endif #endif

View File

@ -26,18 +26,24 @@
#include "Cart0840.hxx" #include "Cart0840.hxx"
#include "Cart2K.hxx" #include "Cart2K.hxx"
#include "Cart3E.hxx" #include "Cart3E.hxx"
// #include "Cart3EPlus.hxx"
#include "Cart3F.hxx" #include "Cart3F.hxx"
#include "Cart4A50.hxx" #include "Cart4A50.hxx"
#include "Cart4K.hxx" #include "Cart4K.hxx"
#include "Cart4KSC.hxx" #include "Cart4KSC.hxx"
#include "CartAR.hxx" #include "CartAR.hxx"
// #include "CartBUS.hxx"
// #include "CartCDF.hxx"
#include "CartCM.hxx" #include "CartCM.hxx"
#include "CartCTY.hxx" #include "CartCTY.hxx"
#include "CartCV.hxx" #include "CartCV.hxx"
// #include "CartCVPlus.hxx"
// #include "CartDASH.hxx"
#include "CartDPC.hxx" #include "CartDPC.hxx"
#include "CartDPCPlus.hxx" #include "CartDPCPlus.hxx"
#include "CartE0.hxx" #include "CartE0.hxx"
#include "CartE7.hxx" #include "CartE7.hxx"
// #include "CartE78K.hxx"
#include "CartEF.hxx" #include "CartEF.hxx"
#include "CartEFSC.hxx" #include "CartEFSC.hxx"
#include "CartBF.hxx" #include "CartBF.hxx"
@ -54,9 +60,10 @@
#include "CartFA.hxx" #include "CartFA.hxx"
#include "CartFA2.hxx" #include "CartFA2.hxx"
#include "CartFE.hxx" #include "CartFE.hxx"
#include "CartMC.hxx" // #include "CartMDM.hxx"
#include "CartSB.hxx" #include "CartSB.hxx"
#include "CartUA.hxx" #include "CartUA.hxx"
// #include "CartWD.hxx"
#include "CartX07.hxx" #include "CartX07.hxx"
#include "MD5.hxx" #include "MD5.hxx"
#include "Props.hxx" #include "Props.hxx"
@ -183,6 +190,8 @@ Cartridge* Cartridge::create(const uInt8* image, uInt32 size, string& md5,
cartridge = new Cartridge2K(image, size, settings); cartridge = new Cartridge2K(image, size, settings);
else if(type == "3E") else if(type == "3E")
cartridge = new Cartridge3E(image, size, settings); cartridge = new Cartridge3E(image, size, settings);
// else if(type == "3EP")
// cartridge = new Cartridge3EPlus(image, size, settings);
else if(type == "3F") else if(type == "3F")
cartridge = new Cartridge3F(image, size, settings); cartridge = new Cartridge3F(image, size, settings);
else if(type == "4A50") else if(type == "4A50")
@ -193,33 +202,43 @@ Cartridge* Cartridge::create(const uInt8* image, uInt32 size, string& md5,
cartridge = new Cartridge4KSC(image, size, settings); cartridge = new Cartridge4KSC(image, size, settings);
else if(type == "AR") else if(type == "AR")
cartridge = new CartridgeAR(image, size, settings); 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") else if(type == "CM")
cartridge = new CartridgeCM(image, size, settings); cartridge = new CartridgeCM(image, size, settings);
else if(type == "CTY") else if(type == "CTY")
cartridge = new CartridgeCTY(image, size, osystem); cartridge = new CartridgeCTY(image, size, osystem);
else if(type == "CV") else if(type == "CV")
cartridge = new CartridgeCV(image, size, settings); 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") else if(type == "DPC")
cartridge = new CartridgeDPC(image, size, settings); cartridge = new CartridgeDPC(image, size, settings);
else if(type == "DPC+") else if(type == "DPCP")
cartridge = new CartridgeDPCPlus(image, size, settings); cartridge = new CartridgeDPCPlus(image, size, settings);
else if(type == "E0") else if(type == "E0")
cartridge = new CartridgeE0(image, size, settings); cartridge = new CartridgeE0(image, size, settings);
else if(type == "E7") else if(type == "E7")
cartridge = new CartridgeE7(image, size, settings); cartridge = new CartridgeE7(image, size, settings);
// else if(type == "E78K")
// cartridge = new CartridgeE78K(image, size, settings);
else if(type == "EF") else if(type == "EF")
cartridge = new CartridgeEF(image, size, settings); cartridge = new CartridgeEF(image, size, settings);
else if(type == "EFSC") else if(type == "EFSC")
cartridge = new CartridgeEFSC(image, size, settings); cartridge = new CartridgeEFSC(image, size, settings);
else if(type == "BF") else if(type == "F0")
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")
cartridge = new CartridgeF0(image, size, settings); cartridge = new CartridgeF0(image, size, settings);
else if(type == "F4") else if(type == "F4")
cartridge = new CartridgeF4(image, size, settings); 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); cartridge = new CartridgeF8(image, size, md5, settings);
else if(type == "F8SC") else if(type == "F8SC")
cartridge = new CartridgeF8SC(image, size, settings); cartridge = new CartridgeF8SC(image, size, settings);
else if(type == "FA" || type == "FASC") else if(type == "FA")
cartridge = new CartridgeFA(image, size, settings); cartridge = new CartridgeFA(image, size, settings);
else if(type == "FA2") else if(type == "FA2")
cartridge = new CartridgeFA2(image, size, osystem); cartridge = new CartridgeFA2(image, size, osystem);
else if(type == "FE") else if(type == "FE")
cartridge = new CartridgeFE(image, size, settings); cartridge = new CartridgeFE(image, size, settings);
else if(type == "MC") // else if(type == "MDM")
cartridge = new CartridgeMC(image, size, settings); // cartridge = new CartridgeMDM(image, size, settings);
else if(type == "UA") else if(type == "UA")
cartridge = new CartridgeUA(image, size, settings); cartridge = new CartridgeUA(image, size, settings);
else if(type == "SB") else if(type == "SB")
cartridge = new CartridgeSB(image, size, settings); cartridge = new CartridgeSB(image, size, settings);
// else if(type == "WD")
// cartridge = new CartridgeWD(image, size, settings);
else if(type == "X07") else if(type == "X07")
cartridge = new CartridgeX07(image, size, settings); cartridge = new CartridgeX07(image, size, settings);
else if(dtype == "WRONG_SIZE") else if(dtype == "WRONG_SIZE")
@ -380,7 +401,11 @@ string Cartridge::autodetectType(const uInt8* image, uInt32 size)
// Guess type based on size // Guess type based on size
const char* type = 0; 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"; type = "AR";
} }
@ -395,9 +420,9 @@ string Cartridge::autodetectType(const uInt8* image, uInt32 size)
} }
else if(size == 4096) else if(size == 4096)
{ {
if(isProbablyCV(image,size)) if(isProbablyCV(image, size))
type = "CV"; type = "CV";
else if(isProbably4KSC(image,size)) else if(isProbably4KSC(image, size))
type = "4KSC"; type = "4KSC";
else else
type = "4K"; type = "4K";
@ -424,9 +449,15 @@ string Cartridge::autodetectType(const uInt8* image, uInt32 size)
type = "FE"; type = "FE";
else if(isProbably0840(image, size)) else if(isProbably0840(image, size))
type = "0840"; type = "0840";
else if(isProbablyE78K(image, size))
type = "E78K";
else else
type = "F8"; type = "F8";
} }
else if(size == 8*1024 + 3) // 8195 bytes (Experimental)
{
type = "WD";
}
else if(size >= 10240 && size <= 10496) // ~10K - Pitfall2 else if(size >= 10240 && size <= 10496) // ~10K - Pitfall2
{ {
type = "DPC"; type = "DPC";
@ -459,25 +490,36 @@ string Cartridge::autodetectType(const uInt8* image, uInt32 size)
if(isProbablyARM(image, size)) if(isProbablyARM(image, size))
type = "FA2"; type = "FA2";
else /*if(isProbablyDPCplus(image, size))*/ else /*if(isProbablyDPCplus(image, size))*/
type = "DPC+"; type = "DPCP";
} }
else if(size == 32*1024) // 32K else if(size == 32*1024) // 32K
{ {
if(isProbablySC(image, size)) if (isProbablyCTY(image, size))
type = "CTY";
else if(isProbablySC(image, size))
type = "F4SC"; type = "F4SC";
else if(isProbably3E(image, size)) else if(isProbably3E(image, size))
type = "3E"; type = "3E";
else if(isProbably3F(image, size)) else if(isProbably3F(image, size))
type = "3F"; type = "3F";
else if (isProbablyBUS(image, size))
type = "BUS";
else if (isProbablyCDF(image, size))
type = "CDF";
else if(isProbablyDPCplus(image, size)) else if(isProbablyDPCplus(image, size))
type = "DPC+"; type = "DPCP";
else if(isProbablyCTY(image, size))
type = "CTY";
else if(isProbablyFA2(image, size)) else if(isProbablyFA2(image, size))
type = "FA2"; type = "FA2";
else else
type = "F4"; type = "F4";
} }
else if(size == 60*1024) // 60K
{
if(isProbablyCTY(image, size))
type = "CTY";
else
type = "F4";
}
else if(size == 64*1024) // 64K else if(size == 64*1024) // 64K
{ {
if(isProbably3E(image, size)) if(isProbably3E(image, size))
@ -505,8 +547,6 @@ string Cartridge::autodetectType(const uInt8* image, uInt32 size)
type = "4A50"; type = "4A50";
else if(isProbablySB(image, size)) else if(isProbablySB(image, size))
type = "SB"; type = "SB";
else
type = "MC";
} }
else if(size == 256*1024) // 256K else if(size == 256*1024) // 256K
{ {
@ -529,13 +569,22 @@ string Cartridge::autodetectType(const uInt8* image, uInt32 size)
type = "4K"; // Most common bankswitching type 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; return type;
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool Cartridge::searchForBytes(const uInt8* image, uInt32 imagesize, bool Cartridge::searchForBytes(const uInt8* image, uInt32 imagesize,
const uInt8* signature, uInt32 sigsize, const uInt8* signature, uInt32 sigsize,
uInt32 minhits) uInt32 minhits)
{ {
uInt32 count = 0; uInt32 count = 0;
for(uInt32 i = 0; i < imagesize - sigsize; ++i) 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) bool Cartridge::isProbablySC(const uInt8* image, uInt32 size)
{ {
// We assume a Superchip cart contains the same bytes for its entire // We assume a Superchip cart repeats the first 128 bytes for the second
// RAM area; obviously this test will fail if it doesn't // 128 bytes in the RAM area, which is the first 256 bytes of each 4K bank
// The RAM area will be the first 256 bytes of each 4K bank const uInt8* ptr = image;
uInt32 banks = size / 4096; while(size)
for(uInt32 i = 0; i < banks; ++i)
{ {
uInt8 first = image[i*4096]; if(memcmp(ptr, ptr + 128, 128) != 0)
for(uInt32 j = 0; j < 256; ++j) return false;
{
if(image[i*4096+j] != first) ptr += 4096;
return false; size -= 4096;
}
} }
return true; return true;
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool Cartridge::isProbably4KSC(const uInt8* image, uInt32 size) bool Cartridge::isProbably4KSC(const uInt8* image, uInt32 size)
{ {
// We check if the first 256 bytes are identical *and* if there's // 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; return false;
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool Cartridge::isProbablyARM(const uInt8* image, uInt32 size) bool Cartridge::isProbablyARM(const uInt8* image, uInt32 size)
{ {
@ -605,10 +652,10 @@ bool Cartridge::isProbablyARM(const uInt8* image, uInt32 size)
{ 0xA0, 0xC1, 0x1F, 0xE0 }, { 0xA0, 0xC1, 0x1F, 0xE0 },
{ 0x00, 0x80, 0x02, 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; return true;
else 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); 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) 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) 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); 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) bool Cartridge::isProbablyDPCplus(const uInt8* image, uInt32 size)
{ {
// DPC+ ARM code has 2 occurrences of the string DPC+ // 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', '+' }; uInt8 signature[] = { 'D', 'P', 'C', '+' };
return searchForBytes(image, size, signature, 4, 2); 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 // Thanks to "stella@casperkitty.com" for this advice
// These signatures are attributed to the MESS project // These signatures are attributed to the MESS project
uInt8 signature[8][3] = { uInt8 signature[8][3] = {
{ 0x8D, 0xE0, 0x1F }, // STA $1FE0 { 0x8D, 0xE0, 0x1F }, // STA $1FE0
{ 0x8D, 0xE0, 0x5F }, // STA $5FE0 { 0x8D, 0xE0, 0x5F }, // STA $5FE0
{ 0x8D, 0xE9, 0xFF }, // STA $FFE9 { 0x8D, 0xE9, 0xFF }, // STA $FFE9
{ 0x0C, 0xE0, 0x1F }, // NOP $1FE0 { 0x0C, 0xE0, 0x1F }, // NOP $1FE0
{ 0xAD, 0xE0, 0x1F }, // LDA $1FE0 { 0xAD, 0xE0, 0x1F }, // LDA $1FE0
{ 0xAD, 0xE9, 0xFF }, // LDA $FFE9 { 0xAD, 0xE9, 0xFF }, // LDA $FFE9
{ 0xAD, 0xED, 0xFF }, // LDA $FFED { 0xAD, 0xED, 0xFF }, // LDA $FFED
{ 0xAD, 0xF3, 0xBF } // LDA $BFF3 { 0xAD, 0xF3, 0xBF } // LDA $BFF3
}; };
for(uInt32 i = 0; i < 8; ++i) for(uInt32 i = 0; i < 8; ++i)
if(searchForBytes(image, size, signature[i], 3, 1)) 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 // Thanks to "stella@casperkitty.com" for this advice
// These signatures are attributed to the MESS project // These signatures are attributed to the MESS project
uInt8 signature[7][3] = { uInt8 signature[7][3] = {
{ 0xAD, 0xE2, 0xFF }, // LDA $FFE2 { 0xAD, 0xE2, 0xFF }, // LDA $FFE2
{ 0xAD, 0xE5, 0xFF }, // LDA $FFE5 { 0xAD, 0xE5, 0xFF }, // LDA $FFE5
{ 0xAD, 0xE5, 0x1F }, // LDA $1FE5 { 0xAD, 0xE5, 0x1F }, // LDA $1FE5
{ 0xAD, 0xE7, 0x1F }, // LDA $1FE7 { 0xAD, 0xE7, 0x1F }, // LDA $1FE7
{ 0x0C, 0xE7, 0x1F }, // NOP $1FE7 { 0x0C, 0xE7, 0x1F }, // NOP $1FE7
{ 0x8D, 0xE7, 0xFF }, // STA $FFE7 { 0x8D, 0xE7, 0xFF }, // STA $FFE7
{ 0x8D, 0xE7, 0x1F } // STA $1FE7 { 0x8D, 0xE7, 0x1F } // STA $1FE7
}; };
for(uInt32 i = 0; i < 7; ++i) for(uInt32 i = 0; i < 7; ++i)
if(searchForBytes(image, size, signature[i], 3, 1)) if(searchForBytes(image, size, signature[i], 3, 1))
@ -755,6 +831,25 @@ bool Cartridge::isProbablyE7(const uInt8* image, uInt32 size)
return false; 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) 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 efef[] = { 'E', 'F', 'E', 'F' };
uInt8 efsc[] = { 'E', 'F', 'S', 'C' }; uInt8 efsc[] = { 'E', 'F', 'S', 'C' };
if(searchForBytes(image+size-8, 8, efef, 4, 1)) if(searchForBytes(image+size-8, 8, efef, 4, 1))
{ {
type = "EF"; type = "EF";
return true; 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 // BF carts store strings 'BFBF' and 'BFSC' starting at address $FFF8
// This signature is attributed to "RevEng" of AtariAge // 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' }; uInt8 bfsc[] = { 'B', 'F', 'S', 'C' };
if(searchForBytes(image+size-8, 8, bf, 4, 1)) 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; 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) bool Cartridge::isProbablyDF(const uInt8* image, uInt32 size, const char*& type)
{ {
// BF carts store strings 'DFDF' and 'DFSC' starting at address $FFF8 // BF carts store strings 'DFDF' and 'DFSC' starting at address $FFF8
// This signature is attributed to "RevEng" of AtariAge // 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' }; uInt8 dfsc[] = { 'D', 'F', 'S', 'C' };
if(searchForBytes(image+size-8, 8, df, 4, 1)) if(searchForBytes(image+size-8, 8, df, 4, 1))
{ {
type = "DF"; type = "DF";
return true; return true;
} }
else if(searchForBytes(image+size-8, 8, dfsc, 4, 1)) 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; 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 // 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 // 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; 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) bool Cartridge::isProbablySB(const uInt8* image, uInt32 size)
{ {

View File

@ -202,6 +202,13 @@ class Cartridge : public Device
*/ */
virtual void setRomName(const string& name) { } 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 Get debugger widget responsible for accessing the inner workings
of the cart. This will need to be overridden and implemented by of the cart. This will need to be overridden and implemented by
@ -279,12 +286,13 @@ class Cartridge : public Device
uInt32 minhits); 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); 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); static bool isProbably4KSC(const uInt8* image, uInt32 size);
@ -303,6 +311,11 @@ class Cartridge : public Device
*/ */
static bool isProbably3E(const uInt8* image, uInt32 size); 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 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); 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 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); 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 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); 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 Returns true if the image is probably an EF/EFSC bankswitching cartridge
*/ */
static bool isProbablyEF(const uInt8* image, uInt32 size, const char*& type); 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 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 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); 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 Returns true if the image is probably a SB bankswitching cartridge
*/ */

View File

@ -459,7 +459,7 @@ void CartridgeCTY::loadTune(uInt8 index)
void CartridgeCTY::loadScore(uInt8 index) void CartridgeCTY::loadScore(uInt8 index)
{ {
Serializer serializer(myEEPROMFile, true); Serializer serializer(myEEPROMFile, true);
if(serializer.isValid()) if(serializer)
{ {
uInt8 scoreRAM[256]; uInt8 scoreRAM[256];
try try
@ -479,7 +479,7 @@ void CartridgeCTY::loadScore(uInt8 index)
void CartridgeCTY::saveScore(uInt8 index) void CartridgeCTY::saveScore(uInt8 index)
{ {
Serializer serializer(myEEPROMFile); Serializer serializer(myEEPROMFile);
if(serializer.isValid()) if(serializer)
{ {
// Load score RAM // Load score RAM
uInt8 scoreRAM[256]; uInt8 scoreRAM[256];
@ -496,7 +496,7 @@ void CartridgeCTY::saveScore(uInt8 index)
memcpy(scoreRAM + (index << 6) + 4, myRAM+4, 60); memcpy(scoreRAM + (index << 6) + 4, myRAM+4, 60);
// Save score RAM // Save score RAM
serializer.reset(); serializer.rewind();
try try
{ {
serializer.putByteArray(scoreRAM, 256); serializer.putByteArray(scoreRAM, 256);
@ -513,7 +513,7 @@ void CartridgeCTY::saveScore(uInt8 index)
void CartridgeCTY::wipeAllScores() void CartridgeCTY::wipeAllScores()
{ {
Serializer serializer(myEEPROMFile); Serializer serializer(myEEPROMFile);
if(serializer.isValid()) if(serializer)
{ {
// Erase score RAM // Erase score RAM
uInt8 scoreRAM[256]; uInt8 scoreRAM[256];

View File

@ -31,21 +31,24 @@
CartridgeDPCPlus::CartridgeDPCPlus(const uInt8* image, uInt32 size, CartridgeDPCPlus::CartridgeDPCPlus(const uInt8* image, uInt32 size,
const Settings& settings) const Settings& settings)
: Cartridge(settings), : Cartridge(settings),
mySize(std::min(size, 32768u)),
myFastFetch(false), myFastFetch(false),
myLDAimmediate(false), myLDAimmediate(false),
myParameterPointer(0), myParameterPointer(0),
mySystemCycles(0), myAudioCycles(0),
myFractionalClocks(0.0) myARMCycles(0),
myFractionalClocks(0.0),
myBankOffset(0)
{ {
// Store image, making sure it's at least 29KB // Image is always 32K, but in the case of ROM > 29K, the image is
uInt32 minsize = 4096 * 6 + 4096 + 1024 + 255; // copied to the end of the buffer
mySize = BSPF_max(minsize, size); if(mySize < 32768u)
myImage = new uInt8[mySize]; memset(myImage, 0, 32768);
memcpy(myImage, image, size); memcpy(myImage + (32768u - mySize), image, size);
createCodeAccessBase(4096 * 6); createCodeAccessBase(4096 * 6);
// Pointer to the program ROM (24K @ 0 byte offset) // Pointer to the program ROM (24K @ 3072 byte offset; ignore first 3K)
myProgramImage = myImage; myProgramImage = myImage + 0xC00;
// Pointer to the display RAM // Pointer to the display RAM
myDisplayImage = myDPCRAM + 0xC00; myDisplayImage = myDPCRAM + 0xC00;
@ -53,16 +56,14 @@ CartridgeDPCPlus::CartridgeDPCPlus(const uInt8* image, uInt32 size,
// Pointer to the Frequency RAM // Pointer to the Frequency RAM
myFrequencyImage = myDisplayImage + 0x1000; 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 #ifdef THUMB_SUPPORT
// Create Thumbulator ARM emulator // Create Thumbulator ARM emulator
myThumbEmulator = new Thumbulator((uInt16*)(myProgramImage-0xC00), myThumbEmulator = make_unique<Thumbulator>
(uInt16*)myDPCRAM, (reinterpret_cast<uInt16*>(myImage),
settings.getBool("thumb.trapfatal")); reinterpret_cast<uInt16*>(myDPCRAM),
settings.getBool("thumb.trapfatal"),
Thumbulator::ConfigureFor::DPCplus,
this);
#endif #endif
setInitialState(); setInitialState();
@ -70,21 +71,11 @@ CartridgeDPCPlus::CartridgeDPCPlus(const uInt8* image, uInt32 size,
myStartBank = 5; myStartBank = 5;
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
CartridgeDPCPlus::~CartridgeDPCPlus()
{
delete[] myImage;
#ifdef THUMB_SUPPORT
delete myThumbEmulator;
#endif
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void CartridgeDPCPlus::reset() void CartridgeDPCPlus::reset()
{ {
// Update cycles to the current system cycles // Update cycles to the current system cycles
mySystemCycles = mySystem->cycles(); myAudioCycles = myARMCycles = mySystem->cycles();
myFractionalClocks = 0.0; myFractionalClocks = 0.0;
setInitialState(); setInitialState();
@ -104,8 +95,11 @@ void CartridgeDPCPlus::setInitialState()
// Initialize the DPC data fetcher registers // Initialize the DPC data fetcher registers
for(int i = 0; i < 8; ++i) 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; myFractionalCounters[i] = 0;
myCounters[i] = 0;
}
// Set waveforms to first waveform entry // Set waveforms to first waveform entry
myMusicWaveforms[0] = myMusicWaveforms[1] = myMusicWaveforms[2] = 0; myMusicWaveforms[0] = myMusicWaveforms[1] = myMusicWaveforms[2] = 0;
@ -121,7 +115,8 @@ void CartridgeDPCPlus::systemCyclesReset()
uInt32 cycles = mySystem->cycles(); uInt32 cycles = mySystem->cycles();
// Adjust the cycle counter so that it reflects the new value // 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() inline void CartridgeDPCPlus::priorClockRandomNumberGenerator()
{ {
// Update random number generator (32-bit LFSR, reversed) // Update random number generator (32-bit LFSR, reversed)
myRandomNumber = ((myRandomNumber & (1<<31)) ? myRandomNumber = ((myRandomNumber & (1u<<31)) ?
((0x10adab1e^myRandomNumber) << 11) | ((0x10adab1e^myRandomNumber) >> 21) : ((0x10adab1e^myRandomNumber) << 11) | ((0x10adab1e^myRandomNumber) >> 21) :
(myRandomNumber << 11) | (myRandomNumber >> 21)); (myRandomNumber << 11) | (myRandomNumber >> 21));
} }
@ -165,24 +160,18 @@ inline void CartridgeDPCPlus::priorClockRandomNumberGenerator()
inline void CartridgeDPCPlus::updateMusicModeDataFetchers() inline void CartridgeDPCPlus::updateMusicModeDataFetchers()
{ {
// Calculate the number of cycles since the last update // Calculate the number of cycles since the last update
Int32 cycles = mySystem->cycles() - mySystemCycles; uInt32 cycles = uInt32(mySystem->cycles() - myAudioCycles);
mySystemCycles = mySystem->cycles(); 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; double clocks = ((20000.0 * cycles) / 1193191.66666667) + myFractionalClocks;
Int32 wholeClocks = (Int32)clocks; uInt32 wholeClocks = uInt32(clocks);
myFractionalClocks = clocks - (double)wholeClocks; myFractionalClocks = clocks - double(wholeClocks);
if(wholeClocks <= 0)
{
return;
}
// Let's update counters and flags of the music mode data fetchers // Let's update counters and flags of the music mode data fetchers
for(int x = 0; x <= 2; ++x) 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; myParameterPointer = 0;
break; break;
#ifdef THUMB_SUPPORT #ifdef THUMB_SUPPORT
case 254: case 254: // call with IRQ driven audio, no special handling needed at this
case 255: // 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) // Call user written ARM code (most likely be C compiled for ARM)
try { 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()) if(!mySystem->autodetectMode())
{ {
#ifdef DEBUGGER_SUPPORT #ifdef DEBUGGER_SUPPORT
Debugger::debugger().startWithFatalError(error); Debugger::debugger().startWithFatalError(e.what());
#else #else
cout << error << endl; cout << e.what() << endl;
#endif #endif
} }
} }
@ -233,7 +226,7 @@ uInt8 CartridgeDPCPlus::peek(uInt16 address)
{ {
address &= 0x0FFF; address &= 0x0FFF;
uInt8 peekvalue = myProgramImage[(myCurrentBank << 12) + address]; uInt8 peekvalue = myProgramImage[myBankOffset + address];
uInt8 flag; uInt8 flag;
// In debugger/bank-locked mode, we ignore all hotspots and in general // 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[1] << 5) + (myMusicCounters[1] >> 27)] +
myDisplayImage[(myMusicWaveforms[2] << 5) + (myMusicCounters[2] >> 27)]; myDisplayImage[(myMusicWaveforms[2] << 5) + (myMusicCounters[2] >> 27)];
result = (uInt8)i; result = uInt8(i);
break; break;
} }
@ -425,12 +418,12 @@ bool CartridgeDPCPlus::poke(uInt16 address, uInt8 value)
{ {
//DFxFRACLOW - fractional data pointer low byte //DFxFRACLOW - fractional data pointer low byte
case 0x00: case 0x00:
myFractionalCounters[index] = (myFractionalCounters[index] & 0x0F0000) | ((uInt16)value << 8); myFractionalCounters[index] = (myFractionalCounters[index] & 0x0F00FF) | (uInt16(value) << 8);
break; break;
// DFxFRACHI - fractional data pointer high byte // DFxFRACHI - fractional data pointer high byte
case 0x01: case 0x01:
myFractionalCounters[index] = (((uInt16)value & 0x0F) << 16) | (myFractionalCounters[index] & 0x00ffff); myFractionalCounters[index] = ((uInt16(value) & 0x0F) << 16) | (myFractionalCounters[index] & 0x00ffff);
break; break;
//DFxFRACINC - Fractional Increment amount //DFxFRACINC - Fractional Increment amount
@ -494,7 +487,7 @@ bool CartridgeDPCPlus::poke(uInt16 address, uInt8 value)
// DFxHI - data pointer high byte // DFxHI - data pointer high byte
case 0x08: case 0x08:
{ {
myCounters[index] = (((uInt16)value & 0x0F) << 8) | (myCounters[index] & 0x00ff); myCounters[index] = ((uInt16(value) & 0x0F) << 8) | (myCounters[index] & 0x00ff);
break; break;
} }
@ -605,17 +598,16 @@ bool CartridgeDPCPlus::bank(uInt16 bank)
if(bankLocked()) return false; if(bankLocked()) return false;
// Remember what bank we're in // Remember what bank we're in
myCurrentBank = bank; myBankOffset = bank << 12;
uInt16 offset = myCurrentBank << 12;
uInt16 shift = mySystem->pageShift(); uInt16 shift = mySystem->pageShift();
// Setup the page access methods for the current bank // Setup the page access methods for the current bank
System::PageAccess access(0, 0, 0, this, System::PA_READ); System::PageAccess access(0, 0, 0, this, System::PA_READ);
// Map Program ROM image into the system // 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); mySystem->setPageAccess(address >> shift, access);
} }
return myBankChanged = true; return myBankChanged = true;
@ -624,7 +616,7 @@ bool CartridgeDPCPlus::bank(uInt16 bank)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt16 CartridgeDPCPlus::bank() const 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 // For now, we ignore attempts to patch the DPC address space
if(address >= 0x0080) if(address >= 0x0080)
{ {
myProgramImage[(myCurrentBank << 12) + (address & 0x0FFF)] = value; myProgramImage[myBankOffset + (address & 0x0FFF)] = value;
return myBankChanged = true; return myBankChanged = true;
} }
else else
@ -652,7 +644,7 @@ bool CartridgeDPCPlus::patch(uInt16 address, uInt8 value)
const uInt8* CartridgeDPCPlus::getImage(int& size) const const uInt8* CartridgeDPCPlus::getImage(int& size) const
{ {
size = mySize; size = mySize;
return myImage; return myImage + (32768u - mySize);
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -660,10 +652,8 @@ bool CartridgeDPCPlus::save(Serializer& out) const
{ {
try try
{ {
out.putString(name());
// Indicates which bank is currently active // Indicates which bank is currently active
out.putShort(myCurrentBank); out.putShort(myBankOffset);
// Harmony RAM // Harmony RAM
out.putByteArray(myDPCRAM, 8192); out.putByteArray(myDPCRAM, 8192);
@ -702,8 +692,12 @@ bool CartridgeDPCPlus::save(Serializer& out) const
// The random number generator register // The random number generator register
out.putInt(myRandomNumber); out.putInt(myRandomNumber);
out.putInt(mySystemCycles); // Get system cycles and fractional clocks
out.putInt((uInt32)(myFractionalClocks * 100000000.0)); out.putLong(myAudioCycles);
out.putDouble(myFractionalClocks);
// Clock info for Thumbulator
out.putLong(myARMCycles);
} }
catch(...) catch(...)
{ {
@ -719,11 +713,8 @@ bool CartridgeDPCPlus::load(Serializer& in)
{ {
try try
{ {
if(in.getString() != name())
return false;
// Indicates which bank is currently active // Indicates which bank is currently active
myCurrentBank = in.getShort(); myBankOffset = in.getShort();
// Harmony RAM // Harmony RAM
in.getByteArray(myDPCRAM, 8192); in.getByteArray(myDPCRAM, 8192);
@ -762,9 +753,12 @@ bool CartridgeDPCPlus::load(Serializer& in)
// The random number generator register // The random number generator register
myRandomNumber = in.getInt(); myRandomNumber = in.getInt();
// Get system cycles and fractional clocks // Get audio cycles and fractional clocks
mySystemCycles = (Int32)in.getInt(); myAudioCycles = in.getLong();
myFractionalClocks = (double)in.getInt() / 100000000.0; myFractionalClocks = in.getDouble();
// Clock info for Thumbulator
myARMCycles = in.getLong();
} }
catch(...) catch(...)
{ {
@ -773,7 +767,7 @@ bool CartridgeDPCPlus::load(Serializer& in)
} }
// Now, go to the current bank // Now, go to the current bank
bank(myCurrentBank); bank(myBankOffset >> 12);
return true; return true;
} }

View File

@ -56,11 +56,7 @@ class CartridgeDPCPlus : public Cartridge
@param settings A reference to the various settings (read-only) @param settings A reference to the various settings (read-only)
*/ */
CartridgeDPCPlus(const uInt8* image, uInt32 size, const Settings& settings); CartridgeDPCPlus(const uInt8* image, uInt32 size, const Settings& settings);
virtual ~CartridgeDPCPlus() = default;
/**
Destructor
*/
virtual ~CartridgeDPCPlus();
public: public:
/** /**
@ -198,7 +194,7 @@ class CartridgeDPCPlus : public Cartridge
private: private:
// The ROM image and size // The ROM image and size
uInt8* myImage; uInt8 myImage[32768];
uInt32 mySize; uInt32 mySize;
// Pointer to the 24K program ROM image of the cartridge // 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 // Pointer to the 4K display ROM image of the cartridge
uInt8* myDisplayImage; 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]; uInt8 myDPCRAM[8192];
#ifdef THUMB_SUPPORT #ifdef THUMB_SUPPORT
// Pointer to the Thumb ARM emulator object // Pointer to the Thumb ARM emulator object
Thumbulator* myThumbEmulator; unique_ptr<Thumbulator> myThumbEmulator;
#endif #endif
// Pointer to the 1K frequency table // Pointer to the 1K frequency table
uInt8* myFrequencyImage; uInt8* myFrequencyImage;
// Indicates which bank is currently active
uInt16 myCurrentBank;
// The top registers for the data fetchers // The top registers for the data fetchers
uInt8 myTops[8]; uInt8 myTops[8];
@ -261,10 +257,16 @@ class CartridgeDPCPlus : public Cartridge
uInt32 myRandomNumber; uInt32 myRandomNumber;
// System cycle count when the last update to music data fetchers occurred // 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 // Fractional DPC music OSC clocks unused during the last update
double myFractionalClocks; double myFractionalClocks;
// Indicates the offset into the ROM image (aligns to current bank)
uInt16 myBankOffset;
}; };
#endif #endif

View File

@ -385,7 +385,7 @@ uInt8 CartridgeFA2::ramReadWrite()
// We go ahead and do the access now, and only return when a sufficient // We go ahead and do the access now, and only return when a sufficient
// amount of time has passed // amount of time has passed
Serializer serializer(myFlashFile); Serializer serializer(myFlashFile);
if(serializer.isValid()) if(serializer)
{ {
if(myRAM[255] == 1) // read if(myRAM[255] == 1) // read
{ {
@ -437,7 +437,7 @@ uInt8 CartridgeFA2::ramReadWrite()
void CartridgeFA2::flash(uInt8 operation) void CartridgeFA2::flash(uInt8 operation)
{ {
Serializer serializer(myFlashFile); Serializer serializer(myFlashFile);
if(serializer.isValid()) if(serializer)
{ {
if(operation == 0) // erase 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; class System;
#include "ConsoleTiming.hxx"
#include "Serializable.hxx" #include "Serializable.hxx"
#include "bspf.hxx" #include "bspf.hxx"
@ -55,6 +56,15 @@ class Device : public Serializable
*/ */
virtual void reset() = 0; 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 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

View File

@ -8,13 +8,11 @@
// SS SS tt ee ll ll aa aa // SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa // 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 // and the Stella Team
// //
// See the file "License.txt" for information on usage and redistribution of // See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES. // this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: Serializer.cxx 2838 2014-01-17 23:34:03Z stephena $
//============================================================================ //============================================================================
#include <fstream> #include <fstream>
@ -23,25 +21,25 @@
#include "FSNode.hxx" #include "FSNode.hxx"
#include "Serializer.hxx" #include "Serializer.hxx"
using std::ios;
using std::ios_base;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Serializer::Serializer(const string& filename, bool readonly) Serializer::Serializer(const string& filename, bool readonly)
: myStream(NULL), : myStream(nullptr)
myUseFilestream(true)
{ {
if(readonly) if(readonly)
{ {
FilesystemNode node(filename); FilesystemNode node(filename);
if(node.isFile() && node.isReadable()) 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()) if(str && str->is_open())
{ {
myStream = str; myStream = std::move(str);
myStream->exceptions( ios_base::failbit | ios_base::badbit | ios_base::eofbit ); myStream->exceptions( ios_base::failbit | ios_base::badbit | ios_base::eofbit );
reset(); rewind();
} }
else
delete str;
} }
} }
else 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 // 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 // when necessary, and the append doesn't delete any data if it
// already exists // already exists
fstream temp(filename.c_str(), ios::out | ios::app); fstream temp(filename, ios::out | ios::app);
temp.close(); 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()) if(str && str->is_open())
{ {
myStream = str; myStream = std::move(str);
myStream->exceptions( ios_base::failbit | ios_base::badbit | ios_base::eofbit ); myStream->exceptions( ios_base::failbit | ios_base::badbit | ios_base::eofbit );
reset(); rewind();
} }
else
delete str;
} }
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Serializer::Serializer(void) Serializer::Serializer()
: myStream(NULL), : myStream(nullptr)
myUseFilestream(false)
{ {
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 // For some reason, Windows and possibly OSX needs to store something in
// the stream before it is used for the first time // 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 ); myStream->exceptions( ios_base::failbit | ios_base::badbit | ios_base::eofbit );
putBool(true); putBool(true);
reset(); rewind();
} }
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Serializer::~Serializer(void) void Serializer::rewind()
{
if(myStream != NULL)
{
if(myUseFilestream)
((fstream*)myStream)->close();
delete myStream;
myStream = NULL;
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool Serializer::isValid(void)
{
return myStream != NULL;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Serializer::reset(void)
{ {
myStream->clear(); myStream->clear();
myStream->seekg(ios_base::beg); myStream->seekg(ios_base::beg);
@ -113,7 +89,7 @@ void Serializer::reset(void)
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt8 Serializer::getByte(void) uInt8 Serializer::getByte() const
{ {
char buf; char buf;
myStream->read(&buf, 1); 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; uInt16 val = 0;
myStream->read((char*)&val, sizeof(uInt16)); myStream->read(reinterpret_cast<char*>(&val), sizeof(uInt16));
return val; 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; uInt32 val = 0;
myStream->read((char*)&val, sizeof(uInt32)); myStream->read(reinterpret_cast<char*>(&val), sizeof(uInt32));
return val; 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(); int len = getInt();
string str; string str;
@ -169,7 +163,7 @@ string Serializer::getString(void)
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool Serializer::getBool(void) bool Serializer::getBool() const
{ {
return getByte() == TruePattern; return getByte() == TruePattern;
} }
@ -177,43 +171,55 @@ bool Serializer::getBool(void)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Serializer::putByte(uInt8 value) 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) 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) 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) 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) 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) 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) void Serializer::putString(const string& str)
{ {
int len = str.length(); int len = int(str.length());
putInt(len); putInt(len);
myStream->write(str.data(), len); myStream->write(str.data(), len);
} }

View File

@ -8,19 +8,16 @@
// SS SS tt ee ll ll aa aa // SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa // 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 // and the Stella Team
// //
// See the file "License.txt" for information on usage and redistribution of // See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES. // this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: Serializer.hxx 2838 2014-01-17 23:34:03Z stephena $
//============================================================================ //============================================================================
#ifndef SERIALIZER_HXX #ifndef SERIALIZER_HXX
#define SERIALIZER_HXX #define SERIALIZER_HXX
#include <iostream>
#include "bspf.hxx" #include "bspf.hxx"
/** /**
@ -29,15 +26,11 @@
stream can be either an actual file, or an in-memory structure. stream can be either an actual file, or an in-memory structure.
Bytes are written as characters, shorts as 2 characters (16-bits), Bytes are written as characters, shorts as 2 characters (16-bits),
integers as 4 characters (32-bits), strings are written as characters integers as 4 characters (32-bits), long integers as 8 bytes (64-bits),
prepended by the length of the string, boolean values are written using strings are written as characters prepended by the length of the string,
a special character pattern. 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.
@author Stephen Anthony @author Stephen Anthony
@version $Id: Serializer.hxx 2838 2014-01-17 23:34:03Z stephena $
*/ */
class Serializer class Serializer
{ {
@ -50,35 +43,30 @@ class Serializer
If a file is opened readonly, we can never write to it. 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. was correctly initialized.
*/ */
Serializer(const string& filename, bool readonly = false); Serializer(const string& filename, bool readonly = false);
Serializer(void); Serializer();
/**
Destructor
*/
virtual ~Serializer(void);
public: public:
/** /**
Answers whether the serializer is currently initialized for reading Answers whether the serializer is currently initialized for reading
and writing. and writing.
*/ */
bool isValid(void); explicit operator bool() const { return myStream != nullptr; }
/** /**
Resets the read/write location to the beginning of the stream. 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. Reads a byte value (unsigned 8-bit) from the current input stream.
@result The byte value which has been read from the 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. 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 array The location to store the bytes read
@param size The size of the array (number of bytes to 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. Reads a short value (unsigned 16-bit) from the current input stream.
@result The short value which has been read from the 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. 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 array The location to store the shorts read
@param size The size of the array (number of shorts to 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. Reads an int value (unsigned 32-bit) from the current input stream.
@result The int value which has been read from the 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. 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 array The location to store the integers read
@param size The size of the array (number of integers to 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. Reads a string from the current input stream.
@result The string which has been read from the 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. Reads a boolean value from the current input stream.
@result The boolean value which has been read from the 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. 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); 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. Writes a string to the current output stream.
@ -194,13 +209,19 @@ class Serializer
private: private:
// The stream to send the serialized data to. // The stream to send the serialized data to.
iostream* myStream; unique_ptr<iostream> myStream;
bool myUseFilestream;
enum { enum {
TruePattern = 0xfe, TruePattern = 0xfe,
FalsePattern = 0x01 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 #endif

View File

@ -30,7 +30,7 @@
#include "StateManager.hxx" #include "StateManager.hxx"
#define STATE_HEADER "03090100state" #define STATE_HEADER "03090400state"
#define MOVIE_HEADER "03030000movie" #define MOVIE_HEADER "03030000movie"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -186,7 +186,7 @@ void StateManager::loadState(int slot)
// Make sure the file can be opened in read-only mode // Make sure the file can be opened in read-only mode
Serializer in(buf.str(), true); Serializer in(buf.str(), true);
if(!in.isValid()) if(!in)
{ {
buf.str(""); buf.str("");
#if !defined(RETRON77) #if !defined(RETRON77)
@ -251,7 +251,7 @@ void StateManager::saveState(int slot)
// Make sure the file can be opened for writing // Make sure the file can be opened for writing
Serializer out(buf.str()); Serializer out(buf.str());
if(!out.isValid()) if(!out)
{ {
buf.str(""); buf.str("");
buf << "Can't open/save to state file " << " " << myOSystem->stateDir() << name << ".st" << slot; 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()) if(&myOSystem->console())
{ {
// Make sure the file can be opened for reading // 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 // First test if we have a valid header and cart type
// If so, do a complete state load using the Console // If so, do a complete state load using the Console
@ -329,7 +329,7 @@ bool StateManager::saveState(Serializer& out)
if(&myOSystem->console()) if(&myOSystem->console())
{ {
// Make sure the file can be opened for writing // 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, // 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 // 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 // SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa // 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 // and the Stella Team
// //
// See the file "License.txt" for information on usage and redistribution of // See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES. // this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: 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 // 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 "bspf.hxx"
#include "Console.hxx"
#define ROMADDMASK 0x7FFF #define ROMADDMASK 0x7FFF
#define RAMADDMASK 0x1FFF #define RAMADDMASK 0x1FFF
@ -34,39 +36,28 @@
#define ROMSIZE (ROMADDMASK+1) #define ROMSIZE (ROMADDMASK+1)
#define RAMSIZE (RAMADDMASK+1) #define RAMSIZE (RAMADDMASK+1)
//0b10000 User PC, R14 to R0, CPSR #define CPSR_N (1u<<31)
//0b10001 FIQ PC, R14_fiq to R8_fiq, R7 to R0, CPSR, SPSR_fiq #define CPSR_Z (1u<<30)
//0b10010 IRQ PC, R14_irq, R13_irq, R12 to R0, CPSR, SPSR_irq #define CPSR_C (1u<<29)
//0b10011 Supervisor PC, R14_svc, R13_svc, R12 to R0, CPSR, SPSR_svc #define CPSR_V (1u<<28)
//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)
class Thumbulator class Thumbulator
{ {
public: public:
Thumbulator(const uInt16* rom, uInt16* ram, bool traponfatal); // control cartridge specific features of the Thumbulator class,
~Thumbulator(); // 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 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. actual error, and the contents of the registers at that point in time.
@ -74,6 +65,7 @@ class Thumbulator
otherwise an empty string otherwise an empty string
*/ */
string run(); string run();
string run(uInt32 cycles);
/** /**
Normally when a fatal error is encountered, the ARM emulation Normally when a fatal error is encountered, the ARM emulation
@ -89,59 +81,75 @@ class Thumbulator
*/ */
static void trapFatalErrors(bool enable) { trapOnFatal = enable; } 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: private:
uInt32 read_register ( uInt32 reg ); uInt32 read_register(uInt32 reg);
uInt32 write_register ( uInt32 reg, uInt32 data ); void write_register(uInt32 reg, uInt32 data);
uInt32 fetch16 ( uInt32 addr ); uInt32 fetch16(uInt32 addr);
uInt32 fetch32 ( uInt32 addr ); uInt32 fetch32(uInt32 addr);
uInt32 read16 ( uInt32 addr ); uInt32 read16(uInt32 addr);
uInt32 read32 ( uInt32 ); uInt32 read32(uInt32 addr);
void write16 ( uInt32 addr, uInt32 data ); bool isProtected(uInt32 addr);
void write32 ( uInt32 addr, uInt32 data ); void write16(uInt32 addr, uInt32 data);
void write32(uInt32 addr, uInt32 data);
void updateTimer(uInt32 cycles);
void do_zflag ( uInt32 x ); void do_zflag(uInt32 x);
void do_nflag ( uInt32 x ); void do_nflag(uInt32 x);
void do_cflag ( uInt32 a, uInt32 b, uInt32 c ); void do_cflag(uInt32 a, uInt32 b, uInt32 c);
void do_sub_vflag ( uInt32 a, uInt32 b, uInt32 c ); void do_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_cflag_bit ( uInt32 x ); void do_vflag_bit(uInt32 x);
void do_vflag_bit ( uInt32 x );
// Throw a string exception containing an error referencing the given // Throw a runtime_error exception containing an error referencing the
// message and variables // given message and variables
// Note that the return value is never used in these methods // 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, const char* msg);
int fatalError(const char* opcode, uInt32 v1, uInt32 v2, const char* msg); int fatalError(const char* opcode, uInt32 v1, uInt32 v2, const char* msg);
void dump_counters ( void ); void dump_counters();
void dump_regs( void ); void dump_regs();
int execute ( void ); int execute();
int reset ( void ); int reset();
private: private:
const uInt16* rom; const uInt16* rom;
uInt16* ram; uInt16* ram;
//Int32 copydata;
uInt32 halfadd; uInt32 reg_norm[16]; // normal execution mode, do not have a thread mode
uInt32 cpsr; uInt32 cpsr, mamcr;
//uInt32 reg_usr[16]; //User mode bool handler_mode;
uInt32 reg_sys[16]; //System mode uInt32 systick_ctrl, systick_reload, systick_count, systick_calibrate;
uInt32 reg_svc[16]; //Supervisor mode uInt64 instructions, fetches, reads, writes, systick_ints;
//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;
uInt64 instructions; // For emulation of LPC2103's timer 1, used for NTSC/PAL/SECAM detection.
uInt64 fetches; // Register names from documentation:
uInt64 reads; // http://www.nxp.com/documents/user_manual/UM10161.pdf
uInt64 writes; uInt32 T1TCR; // Timer 1 Timer Control Register
uInt32 T1TC; // Timer 1 Timer Counter
double timing_factor;
ostringstream statusMsg; ostringstream statusMsg;
static bool trapOnFatal; 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/CartFA.o \
src/emucore/CartFA2.o \ src/emucore/CartFA2.o \
src/emucore/CartFE.o \ src/emucore/CartFE.o \
src/emucore/CartMC.o \
src/emucore/CartSB.o \ src/emucore/CartSB.o \
src/emucore/CartUA.o \ src/emucore/CartUA.o \
src/emucore/CartX07.o \ src/emucore/CartX07.o \