diff --git a/src/common/Version.hxx b/src/common/Version.hxx index 268ac3fd2..7121e86a1 100644 --- a/src/common/Version.hxx +++ b/src/common/Version.hxx @@ -22,7 +22,7 @@ #include -#define STELLA_VERSION "3.9.3" +#define STELLA_VERSION "3.9.4" #define STELLA_BUILD atoi("$Rev: 2838 $" + 6) #endif diff --git a/src/emucore/Cart.cxx b/src/emucore/Cart.cxx index 58f289198..a830e0486 100644 --- a/src/emucore/Cart.cxx +++ b/src/emucore/Cart.cxx @@ -26,18 +26,24 @@ #include "Cart0840.hxx" #include "Cart2K.hxx" #include "Cart3E.hxx" +// #include "Cart3EPlus.hxx" #include "Cart3F.hxx" #include "Cart4A50.hxx" #include "Cart4K.hxx" #include "Cart4KSC.hxx" #include "CartAR.hxx" +// #include "CartBUS.hxx" +// #include "CartCDF.hxx" #include "CartCM.hxx" #include "CartCTY.hxx" #include "CartCV.hxx" +// #include "CartCVPlus.hxx" +// #include "CartDASH.hxx" #include "CartDPC.hxx" #include "CartDPCPlus.hxx" #include "CartE0.hxx" #include "CartE7.hxx" +// #include "CartE78K.hxx" #include "CartEF.hxx" #include "CartEFSC.hxx" #include "CartBF.hxx" @@ -54,9 +60,10 @@ #include "CartFA.hxx" #include "CartFA2.hxx" #include "CartFE.hxx" -#include "CartMC.hxx" +// #include "CartMDM.hxx" #include "CartSB.hxx" #include "CartUA.hxx" +// #include "CartWD.hxx" #include "CartX07.hxx" #include "MD5.hxx" #include "Props.hxx" @@ -183,6 +190,8 @@ Cartridge* Cartridge::create(const uInt8* image, uInt32 size, string& md5, cartridge = new Cartridge2K(image, size, settings); else if(type == "3E") cartridge = new Cartridge3E(image, size, settings); +// else if(type == "3EP") +// cartridge = new Cartridge3EPlus(image, size, settings); else if(type == "3F") cartridge = new Cartridge3F(image, size, settings); else if(type == "4A50") @@ -193,33 +202,43 @@ Cartridge* Cartridge::create(const uInt8* image, uInt32 size, string& md5, cartridge = new Cartridge4KSC(image, size, settings); else if(type == "AR") cartridge = new CartridgeAR(image, size, settings); + else if(type == "BF") + cartridge = new CartridgeBF(image, size, settings); + else if(type == "BFSC") + cartridge = new CartridgeBFSC(image, size, settings); +// else if(type == "BUS") +// cartridge = new CartridgeBUS(image, size, settings); +// else if(type == "CDF") +// cartridge = new CartridgeCDF(image, size, settings); else if(type == "CM") cartridge = new CartridgeCM(image, size, settings); else if(type == "CTY") cartridge = new CartridgeCTY(image, size, osystem); else if(type == "CV") cartridge = new CartridgeCV(image, size, settings); +// else if(type == "CVP") +// cartridge = new CartridgeCVPlus(image, size, settings); +// else if(type == "DASH") +// cartridge = new CartridgeDASH(image, size, settings); + else if(type == "DF") + cartridge = new CartridgeDF(image, size, settings); + else if(type == "DFSC") + cartridge = new CartridgeDFSC(image, size, settings); else if(type == "DPC") cartridge = new CartridgeDPC(image, size, settings); - else if(type == "DPC+") + else if(type == "DPCP") cartridge = new CartridgeDPCPlus(image, size, settings); else if(type == "E0") cartridge = new CartridgeE0(image, size, settings); else if(type == "E7") cartridge = new CartridgeE7(image, size, settings); +// else if(type == "E78K") +// cartridge = new CartridgeE78K(image, size, settings); else if(type == "EF") cartridge = new CartridgeEF(image, size, settings); else if(type == "EFSC") cartridge = new CartridgeEFSC(image, size, settings); - else if(type == "BF") - cartridge = new CartridgeBF(image, size, settings); - else if(type == "BFSC") - cartridge = new CartridgeBFSC(image, size, settings); - else if(type == "DF") - cartridge = new CartridgeDF(image, size, settings); - else if(type == "DFSC") - cartridge = new CartridgeDFSC(image, size, settings); - else if(type == "F0" || type == "MB") + else if(type == "F0") cartridge = new CartridgeF0(image, size, settings); else if(type == "F4") cartridge = new CartridgeF4(image, size, settings); @@ -233,18 +252,20 @@ Cartridge* Cartridge::create(const uInt8* image, uInt32 size, string& md5, cartridge = new CartridgeF8(image, size, md5, settings); else if(type == "F8SC") cartridge = new CartridgeF8SC(image, size, settings); - else if(type == "FA" || type == "FASC") + else if(type == "FA") cartridge = new CartridgeFA(image, size, settings); else if(type == "FA2") cartridge = new CartridgeFA2(image, size, osystem); else if(type == "FE") cartridge = new CartridgeFE(image, size, settings); - else if(type == "MC") - cartridge = new CartridgeMC(image, size, settings); +// else if(type == "MDM") +// cartridge = new CartridgeMDM(image, size, settings); else if(type == "UA") cartridge = new CartridgeUA(image, size, settings); else if(type == "SB") cartridge = new CartridgeSB(image, size, settings); +// else if(type == "WD") +// cartridge = new CartridgeWD(image, size, settings); else if(type == "X07") cartridge = new CartridgeX07(image, size, settings); else if(dtype == "WRONG_SIZE") @@ -380,7 +401,11 @@ string Cartridge::autodetectType(const uInt8* image, uInt32 size) // Guess type based on size const char* type = 0; - if((size % 8448) == 0 || size == 6144) + if(isProbablyCVPlus(image,size)) + { + type = "CVP"; + } + else if((size % 8448) == 0 || size == 6144) { type = "AR"; } @@ -395,11 +420,11 @@ string Cartridge::autodetectType(const uInt8* image, uInt32 size) } else if(size == 4096) { - if(isProbablyCV(image,size)) + if(isProbablyCV(image, size)) type = "CV"; - else if(isProbably4KSC(image,size)) + else if(isProbably4KSC(image, size)) type = "4KSC"; - else + else type = "4K"; } else if(size == 8*1024) // 8K @@ -424,9 +449,15 @@ string Cartridge::autodetectType(const uInt8* image, uInt32 size) type = "FE"; else if(isProbably0840(image, size)) type = "0840"; + else if(isProbablyE78K(image, size)) + type = "E78K"; else type = "F8"; } + else if(size == 8*1024 + 3) // 8195 bytes (Experimental) + { + type = "WD"; + } else if(size >= 10240 && size <= 10496) // ~10K - Pitfall2 { type = "DPC"; @@ -459,25 +490,36 @@ string Cartridge::autodetectType(const uInt8* image, uInt32 size) if(isProbablyARM(image, size)) type = "FA2"; else /*if(isProbablyDPCplus(image, size))*/ - type = "DPC+"; + type = "DPCP"; } else if(size == 32*1024) // 32K { - if(isProbablySC(image, size)) + if (isProbablyCTY(image, size)) + type = "CTY"; + else if(isProbablySC(image, size)) type = "F4SC"; else if(isProbably3E(image, size)) type = "3E"; else if(isProbably3F(image, size)) type = "3F"; + else if (isProbablyBUS(image, size)) + type = "BUS"; + else if (isProbablyCDF(image, size)) + type = "CDF"; else if(isProbablyDPCplus(image, size)) - type = "DPC+"; - else if(isProbablyCTY(image, size)) - type = "CTY"; + type = "DPCP"; else if(isProbablyFA2(image, size)) type = "FA2"; else type = "F4"; } + else if(size == 60*1024) // 60K + { + if(isProbablyCTY(image, size)) + type = "CTY"; + else + type = "F4"; + } else if(size == 64*1024) // 64K { if(isProbably3E(image, size)) @@ -505,8 +547,6 @@ string Cartridge::autodetectType(const uInt8* image, uInt32 size) type = "4A50"; else if(isProbablySB(image, size)) type = "SB"; - else - type = "MC"; } else if(size == 256*1024) // 256K { @@ -529,13 +569,22 @@ string Cartridge::autodetectType(const uInt8* image, uInt32 size) type = "4K"; // Most common bankswitching type } + // Variable sized ROM formats are independent of image size and come last + if(isProbablyDASH(image, size)) + type = "DASH"; + else if(isProbably3EPlus(image, size)) + type = "3EP"; + else if(isProbablyMDM(image, size)) + type = "MDM"; + +cerr << type << endl; return type; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - bool Cartridge::searchForBytes(const uInt8* image, uInt32 imagesize, - const uInt8* signature, uInt32 sigsize, - uInt32 minhits) + const uInt8* signature, uInt32 sigsize, + uInt32 minhits) { uInt32 count = 0; for(uInt32 i = 0; i < imagesize - sigsize; ++i) @@ -563,22 +612,21 @@ bool Cartridge::searchForBytes(const uInt8* image, uInt32 imagesize, // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - bool Cartridge::isProbablySC(const uInt8* image, uInt32 size) { - // We assume a Superchip cart contains the same bytes for its entire - // RAM area; obviously this test will fail if it doesn't - // The RAM area will be the first 256 bytes of each 4K bank - uInt32 banks = size / 4096; - for(uInt32 i = 0; i < banks; ++i) + // We assume a Superchip cart repeats the first 128 bytes for the second + // 128 bytes in the RAM area, which is the first 256 bytes of each 4K bank + const uInt8* ptr = image; + while(size) { - uInt8 first = image[i*4096]; - for(uInt32 j = 0; j < 256; ++j) - { - if(image[i*4096+j] != first) - return false; - } + if(memcmp(ptr, ptr + 128, 128) != 0) + return false; + + ptr += 4096; + size -= 4096; } return true; } +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - bool Cartridge::isProbably4KSC(const uInt8* image, uInt32 size) { // We check if the first 256 bytes are identical *and* if there's @@ -595,7 +643,6 @@ bool Cartridge::isProbably4KSC(const uInt8* image, uInt32 size) return false; } - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - bool Cartridge::isProbablyARM(const uInt8* image, uInt32 size) { @@ -605,10 +652,10 @@ bool Cartridge::isProbablyARM(const uInt8* image, uInt32 size) { 0xA0, 0xC1, 0x1F, 0xE0 }, { 0x00, 0x80, 0x02, 0xE0 } }; - if(searchForBytes(image, 1024, signature[0], 4, 1)) + if(searchForBytes(image, std::min(size, 1024u), signature[0], 4, 1)) return true; else - return searchForBytes(image, 1024, signature[1], 4, 1); + return searchForBytes(image, std::min(size, 1024u), signature[1], 4, 1); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -646,6 +693,14 @@ bool Cartridge::isProbably3E(const uInt8* image, uInt32 size) return searchForBytes(image, size, signature, 4, 1); } +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +bool Cartridge::isProbably3EPlus(const uInt8* image, uInt32 size) +{ + // 3E+ cart is identified key 'TJ3E' in the ROM + uInt8 signature[] = { 'T', 'J', '3', 'E' }; + return searchForBytes(image, size, signature, 4, 1); +} + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - bool Cartridge::isProbably3F(const uInt8* image, uInt32 size) { @@ -678,7 +733,8 @@ bool Cartridge::isProbably4A50(const uInt8* image, uInt32 size) // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - bool Cartridge::isProbablyCTY(const uInt8* image, uInt32 size) { - return false; // TODO - add autodetection + uInt8 signature[] = { 'L', 'E', 'N', 'I', 'N' }; + return searchForBytes(image, size, signature, 5, 1); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -696,10 +752,30 @@ bool Cartridge::isProbablyCV(const uInt8* image, uInt32 size) return searchForBytes(image, size, signature[1], 3, 1); } +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +bool Cartridge::isProbablyCVPlus(const uInt8* image, uInt32) +{ + // CV+ cart is identified key 'commavidplus' @ $04 in the ROM + // We inspect only this area to speed up the search + uInt8 signature[12] = { 'c', 'o', 'm', 'm', 'a', 'v', 'i', 'd', + 'p', 'l', 'u', 's' }; + return searchForBytes(image+4, 24, signature, 12, 1); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +bool Cartridge::isProbablyDASH(const uInt8* image, uInt32 size) +{ + // DASH cart is identified key 'TJAD' in the ROM + uInt8 signature[] = { 'T', 'J', 'A', 'D' }; + return searchForBytes(image, size, signature, 4, 1); +} + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - bool Cartridge::isProbablyDPCplus(const uInt8* image, uInt32 size) { // DPC+ ARM code has 2 occurrences of the string DPC+ + // Note: all Harmony/Melody custom drivers also contain the value + // 0x10adab1e (LOADABLE) if needed for future improvement uInt8 signature[] = { 'D', 'P', 'C', '+' }; return searchForBytes(image, size, signature, 4, 2); } @@ -714,14 +790,14 @@ bool Cartridge::isProbablyE0(const uInt8* image, uInt32 size) // Thanks to "stella@casperkitty.com" for this advice // These signatures are attributed to the MESS project uInt8 signature[8][3] = { - { 0x8D, 0xE0, 0x1F }, // STA $1FE0 - { 0x8D, 0xE0, 0x5F }, // STA $5FE0 - { 0x8D, 0xE9, 0xFF }, // STA $FFE9 - { 0x0C, 0xE0, 0x1F }, // NOP $1FE0 - { 0xAD, 0xE0, 0x1F }, // LDA $1FE0 - { 0xAD, 0xE9, 0xFF }, // LDA $FFE9 - { 0xAD, 0xED, 0xFF }, // LDA $FFED - { 0xAD, 0xF3, 0xBF } // LDA $BFF3 + { 0x8D, 0xE0, 0x1F }, // STA $1FE0 + { 0x8D, 0xE0, 0x5F }, // STA $5FE0 + { 0x8D, 0xE9, 0xFF }, // STA $FFE9 + { 0x0C, 0xE0, 0x1F }, // NOP $1FE0 + { 0xAD, 0xE0, 0x1F }, // LDA $1FE0 + { 0xAD, 0xE9, 0xFF }, // LDA $FFE9 + { 0xAD, 0xED, 0xFF }, // LDA $FFED + { 0xAD, 0xF3, 0xBF } // LDA $BFF3 }; for(uInt32 i = 0; i < 8; ++i) if(searchForBytes(image, size, signature[i], 3, 1)) @@ -740,13 +816,13 @@ bool Cartridge::isProbablyE7(const uInt8* image, uInt32 size) // Thanks to "stella@casperkitty.com" for this advice // These signatures are attributed to the MESS project uInt8 signature[7][3] = { - { 0xAD, 0xE2, 0xFF }, // LDA $FFE2 - { 0xAD, 0xE5, 0xFF }, // LDA $FFE5 - { 0xAD, 0xE5, 0x1F }, // LDA $1FE5 - { 0xAD, 0xE7, 0x1F }, // LDA $1FE7 - { 0x0C, 0xE7, 0x1F }, // NOP $1FE7 - { 0x8D, 0xE7, 0xFF }, // STA $FFE7 - { 0x8D, 0xE7, 0x1F } // STA $1FE7 + { 0xAD, 0xE2, 0xFF }, // LDA $FFE2 + { 0xAD, 0xE5, 0xFF }, // LDA $FFE5 + { 0xAD, 0xE5, 0x1F }, // LDA $1FE5 + { 0xAD, 0xE7, 0x1F }, // LDA $1FE7 + { 0x0C, 0xE7, 0x1F }, // NOP $1FE7 + { 0x8D, 0xE7, 0xFF }, // STA $FFE7 + { 0x8D, 0xE7, 0x1F } // STA $1FE7 }; for(uInt32 i = 0; i < 7; ++i) if(searchForBytes(image, size, signature[i], 3, 1)) @@ -755,6 +831,25 @@ bool Cartridge::isProbablyE7(const uInt8* image, uInt32 size) return false; } +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +bool Cartridge::isProbablyE78K(const uInt8* image, uInt32 size) +{ + // E78K cart bankswitching is triggered by accessing addresses + // $FE4 to $FE6 using absolute non-indexed addressing + // To eliminate false positives (and speed up processing), we + // search for only certain known signatures + uInt8 signature[3][3] = { + { 0xAD, 0xE4, 0xFF }, // LDA $FFE4 + { 0xAD, 0xE5, 0xFF }, // LDA $FFE5 + { 0xAD, 0xE6, 0xFF }, // LDA $FFE6 + }; + for(uInt32 i = 0; i < 3; ++i) + if(searchForBytes(image, size, signature[i], 3, 1)) + return true; + + return false; +} + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - bool Cartridge::isProbablyEF(const uInt8* image, uInt32 size, const char*& type) { @@ -763,7 +858,7 @@ bool Cartridge::isProbablyEF(const uInt8* image, uInt32 size, const char*& type) uInt8 efef[] = { 'E', 'F', 'E', 'F' }; uInt8 efsc[] = { 'E', 'F', 'S', 'C' }; if(searchForBytes(image+size-8, 8, efef, 4, 1)) -{ + { type = "EF"; return true; } @@ -808,7 +903,7 @@ bool Cartridge::isProbablyBF(const uInt8* image, uInt32 size, const char*& type) { // BF carts store strings 'BFBF' and 'BFSC' starting at address $FFF8 // This signature is attributed to "RevEng" of AtariAge - uInt8 bf[] = { 'B', 'F', 'B', 'F' }; + uInt8 bf[] = { 'B', 'F', 'B', 'F' }; uInt8 bfsc[] = { 'B', 'F', 'S', 'C' }; if(searchForBytes(image+size-8, 8, bf, 4, 1)) { @@ -824,18 +919,38 @@ bool Cartridge::isProbablyBF(const uInt8* image, uInt32 size, const char*& type) return false; } +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +bool Cartridge::isProbablyBUS(const uInt8* image, uInt32 size) +{ + // BUS ARM code has 2 occurrences of the string BUS + // Note: all Harmony/Melody custom drivers also contain the value + // 0x10adab1e (LOADABLE) if needed for future improvement + uInt8 bus[] = { 'B', 'U', 'S'}; + return searchForBytes(image, size, bus, 3, 2); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +bool Cartridge::isProbablyCDF(const uInt8* image, uInt32 size) +{ + // CDF ARM code has 3 occurrences of the string DPC+ + // Note: all Harmony/Melody custom drivers also contain the value + // 0x10adab1e (LOADABLE) if needed for future improvement + uInt8 signature[] = { 'C', 'D', 'F' }; + return searchForBytes(image, size, signature, 3, 3); +} + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - bool Cartridge::isProbablyDF(const uInt8* image, uInt32 size, const char*& type) { // BF carts store strings 'DFDF' and 'DFSC' starting at address $FFF8 // This signature is attributed to "RevEng" of AtariAge - uInt8 df[] = { 'D', 'F', 'D', 'F' }; + uInt8 df[] = { 'D', 'F', 'D', 'F' }; uInt8 dfsc[] = { 'D', 'F', 'S', 'C' }; if(searchForBytes(image+size-8, 8, df, 4, 1)) { type = "DF"; - return true; + return true; } else if(searchForBytes(image+size-8, 8, dfsc, 4, 1)) { @@ -846,9 +961,8 @@ bool Cartridge::isProbablyDF(const uInt8* image, uInt32 size, const char*& type) return false; } - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool Cartridge::isProbablyFA2(const uInt8* image, uInt32 size) +bool Cartridge::isProbablyFA2(const uInt8* image, uInt32) { // This currently tests only the 32K version of FA2; the 24 and 28K // versions are easy, in that they're the only possibility with those @@ -881,6 +995,14 @@ bool Cartridge::isProbablyFE(const uInt8* image, uInt32 size) return false; } +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +bool Cartridge::isProbablyMDM(const uInt8* image, uInt32 size) +{ + // MDM cart is identified key 'MDMC' in the first 8K of ROM + uInt8 signature[] = { 'M', 'D', 'M', 'C' }; + return searchForBytes(image, std::min(size, 8192u), signature, 4, 1); +} + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - bool Cartridge::isProbablySB(const uInt8* image, uInt32 size) { diff --git a/src/emucore/Cart.hxx b/src/emucore/Cart.hxx index 77b98bf6c..6b4835ea7 100644 --- a/src/emucore/Cart.hxx +++ b/src/emucore/Cart.hxx @@ -1,8 +1,8 @@ //============================================================================ // -// SSSS tt lll lll -// SS SS tt ll ll -// SS tttttt eeee ll ll aaaa +// SSSS tt lll lll +// SS SS tt ll ll +// SS tttttt eeee ll ll aaaa // SSSS tt ee ee ll ll aa // SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" // SS SS tt ee ll ll aa aa @@ -40,11 +40,11 @@ struct RamArea { typedef Common::Array RamAreaList; /** - A cartridge is a device which contains the machine code for a + A cartridge is a device which contains the machine code for a game and handles any bankswitching performed by the cartridge. A 'bank' is defined as a 4K block that is visible in the 0x1000-0x2000 area (or its mirrors). - + @author Bradford W. Mott @version $Id: Cart.hxx 2838 2014-01-17 23:34:03Z stephena $ */ @@ -56,7 +56,7 @@ class Cartridge : public Device type of cartridge created depends on the properties object. @param image A pointer to the ROM image - @param size The size of the ROM image + @param size The size of the ROM image @param md5 The md5sum for the given ROM image (can be updated) @param dtype The detected bankswitch type of the ROM image @param id Any extra info about the ROM (currently which part @@ -75,7 +75,7 @@ class Cartridge : public Device @param settings A reference to the various settings (read-only) */ Cartridge(const Settings& settings); - + /** Destructor */ @@ -202,6 +202,13 @@ class Cartridge : public Device */ virtual void setRomName(const string& name) { } + /** + Thumbulator only supports 16-bit ARM code. Some Harmony/Melody drivers, + such as BUS and CDF, feature 32-bit ARM code subroutines. This is used + to pass values back to the cartridge class to emulate those subroutines. + */ + virtual uInt32 thumbCallback(uInt8 function, uInt32 value1, uInt32 value2) { return 0; } + /** Get debugger widget responsible for accessing the inner workings of the cart. This will need to be overridden and implemented by @@ -244,7 +251,7 @@ class Cartridge : public Device multi-ROM image. @param image A pointer to the ROM image - @param size The size of the ROM image + @param size The size of the ROM image @param numroms The number of ROMs in the multicart @param md5 The md5sum for the specific cart in the ROM image @param id The ID for the specific cart in the ROM image @@ -258,7 +265,7 @@ class Cartridge : public Device Try to auto-detect the bankswitching type of the cartridge @param image A pointer to the ROM image - @param size The size of the ROM image + @param size The size of the ROM image @return The "best guess" for the cartridge type */ static string autodetectType(const uInt8* image, uInt32 size); @@ -267,7 +274,7 @@ class Cartridge : public Device Search the image for the specified byte signature @param image A pointer to the ROM image - @param imagesize The size of the ROM image + @param imagesize The size of the ROM image @param signature The byte sequence to search for @param sigsize The number of bytes in the signature @param minhits The minimum number of times a signature is to be found @@ -279,12 +286,13 @@ class Cartridge : public Device uInt32 minhits); /** - Returns true if the image is probably a SuperChip (256 bytes RAM) + Returns true if the image is probably a SuperChip (128 bytes RAM) + Note: should be called only on ROMs with size multiple of 4K */ static bool isProbablySC(const uInt8* image, uInt32 size); /** - Returns true if the image is probably a 4K SuperChip (256 bytes RAM) + Returns true if the image is probably a 4K SuperChip (128 bytes RAM) */ static bool isProbably4KSC(const uInt8* image, uInt32 size); @@ -303,6 +311,11 @@ class Cartridge : public Device */ static bool isProbably3E(const uInt8* image, uInt32 size); + /** + Returns true if the image is probably a 3E+ bankswitching cartridge + */ + static bool isProbably3EPlus(const uInt8* image, uInt32 size); + /** Returns true if the image is probably a 3F bankswitching cartridge */ @@ -313,6 +326,21 @@ class Cartridge : public Device */ static bool isProbably4A50(const uInt8* image, uInt32 size); + /** + Returns true if the image is probably a BF/BFSC bankswitching cartridge + */ + static bool isProbablyBF(const uInt8* image, uInt32 size, const char*& type); + + /** + Returns true if the image is probably a BUS bankswitching cartridge + */ + static bool isProbablyBUS(const uInt8* image, uInt32 size); + + /** + Returns true if the image is probably a CDF bankswitching cartridge + */ + static bool isProbablyCDF(const uInt8* image, uInt32 size); + /** Returns true if the image is probably a CTY bankswitching cartridge */ @@ -323,6 +351,21 @@ class Cartridge : public Device */ static bool isProbablyCV(const uInt8* image, uInt32 size); + /** + Returns true if the image is probably a CV+ bankswitching cartridge + */ + static bool isProbablyCVPlus(const uInt8* image, uInt32 size); + + /** + Returns true if the image is probably a DASH bankswitching cartridge + */ + static bool isProbablyDASH(const uInt8* image, uInt32 size); + + /** + Returns true if the image is probably a DF/DFSC bankswitching cartridge + */ + static bool isProbablyDF(const uInt8* image, uInt32 size, const char*& type); + /** Returns true if the image is probably a DPC+ bankswitching cartridge */ @@ -338,24 +381,20 @@ class Cartridge : public Device */ static bool isProbablyE7(const uInt8* image, uInt32 size); + /** + Returns true if the image is probably a E78K bankswitching cartridge + */ + static bool isProbablyE78K(const uInt8* image, uInt32 size); + /** Returns true if the image is probably an EF/EFSC bankswitching cartridge */ static bool isProbablyEF(const uInt8* image, uInt32 size, const char*& type); - /** - Returns true if the image is probably a BF/BFSC bankswitching cartridge - */ - static bool isProbablyBF(const uInt8* image, uInt32 size, const char*& type); - /** - Returns true if the image is probably a DF/DFSC bankswitching cartridge - */ - static bool isProbablyDF(const uInt8* image, uInt32 size, const char*& type); - /** Returns true if the image is probably an F6 bankswitching cartridge */ - static bool isProbablyF6(const uInt8* image, uInt32 size); + //static bool isProbablyF6(const uInt8* image, uInt32 size); /** Returns true if the image is probably an FA2 bankswitching cartridge @@ -367,6 +406,11 @@ class Cartridge : public Device */ static bool isProbablyFE(const uInt8* image, uInt32 size); + /** + Returns true if the image is probably a MDM bankswitching cartridge + */ + static bool isProbablyMDM(const uInt8* image, uInt32 size); + /** Returns true if the image is probably a SB bankswitching cartridge */ diff --git a/src/emucore/CartCTY.cxx b/src/emucore/CartCTY.cxx index 310c458a6..f51b2c682 100644 --- a/src/emucore/CartCTY.cxx +++ b/src/emucore/CartCTY.cxx @@ -1,8 +1,8 @@ //============================================================================ // -// SSSS tt lll lll -// SS SS tt ll ll -// SS tttttt eeee ll ll aaaa +// SSSS tt lll lll +// SS SS tt ll ll +// SS tttttt eeee ll ll aaaa // SSSS tt ee ee ll ll aa // SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" // SS SS tt ee ll ll aa aa @@ -250,7 +250,7 @@ bool CartridgeCTY::poke(uInt16 address, uInt8 value) // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - bool CartridgeCTY::bank(uInt16 bank) -{ +{ if(bankLocked()) return false; // Remember what bank we're in @@ -295,7 +295,7 @@ bool CartridgeCTY::patch(uInt16 address, uInt8 value) myImage[myCurrentBank + address] = value; return myBankChanged = true; -} +} // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - const uInt8* CartridgeCTY::getImage(int& size) const @@ -459,7 +459,7 @@ void CartridgeCTY::loadTune(uInt8 index) void CartridgeCTY::loadScore(uInt8 index) { Serializer serializer(myEEPROMFile, true); - if(serializer.isValid()) + if(serializer) { uInt8 scoreRAM[256]; try @@ -479,7 +479,7 @@ void CartridgeCTY::loadScore(uInt8 index) void CartridgeCTY::saveScore(uInt8 index) { Serializer serializer(myEEPROMFile); - if(serializer.isValid()) + if(serializer) { // Load score RAM uInt8 scoreRAM[256]; @@ -496,7 +496,7 @@ void CartridgeCTY::saveScore(uInt8 index) memcpy(scoreRAM + (index << 6) + 4, myRAM+4, 60); // Save score RAM - serializer.reset(); + serializer.rewind(); try { serializer.putByteArray(scoreRAM, 256); @@ -513,7 +513,7 @@ void CartridgeCTY::saveScore(uInt8 index) void CartridgeCTY::wipeAllScores() { Serializer serializer(myEEPROMFile); - if(serializer.isValid()) + if(serializer) { // Erase score RAM uInt8 scoreRAM[256]; diff --git a/src/emucore/CartDPCPlus.cxx b/src/emucore/CartDPCPlus.cxx index 4a55c5293..577b32e69 100644 --- a/src/emucore/CartDPCPlus.cxx +++ b/src/emucore/CartDPCPlus.cxx @@ -31,21 +31,24 @@ CartridgeDPCPlus::CartridgeDPCPlus(const uInt8* image, uInt32 size, const Settings& settings) : Cartridge(settings), + mySize(std::min(size, 32768u)), myFastFetch(false), myLDAimmediate(false), myParameterPointer(0), - mySystemCycles(0), - myFractionalClocks(0.0) + myAudioCycles(0), + myARMCycles(0), + myFractionalClocks(0.0), + myBankOffset(0) { - // Store image, making sure it's at least 29KB - uInt32 minsize = 4096 * 6 + 4096 + 1024 + 255; - mySize = BSPF_max(minsize, size); - myImage = new uInt8[mySize]; - memcpy(myImage, image, size); + // Image is always 32K, but in the case of ROM > 29K, the image is + // copied to the end of the buffer + if(mySize < 32768u) + memset(myImage, 0, 32768); + memcpy(myImage + (32768u - mySize), image, size); createCodeAccessBase(4096 * 6); - // Pointer to the program ROM (24K @ 0 byte offset) - myProgramImage = myImage; + // Pointer to the program ROM (24K @ 3072 byte offset; ignore first 3K) + myProgramImage = myImage + 0xC00; // Pointer to the display RAM myDisplayImage = myDPCRAM + 0xC00; @@ -53,16 +56,14 @@ CartridgeDPCPlus::CartridgeDPCPlus(const uInt8* image, uInt32 size, // Pointer to the Frequency RAM myFrequencyImage = myDisplayImage + 0x1000; - // If the image is larger than 29K, we assume any excess at the - // beginning is ARM code, and skip over it - if(size > 29 * 1024) - myProgramImage += (size - 29 * 1024); - #ifdef THUMB_SUPPORT // Create Thumbulator ARM emulator - myThumbEmulator = new Thumbulator((uInt16*)(myProgramImage-0xC00), - (uInt16*)myDPCRAM, - settings.getBool("thumb.trapfatal")); + myThumbEmulator = make_unique + (reinterpret_cast(myImage), + reinterpret_cast(myDPCRAM), + settings.getBool("thumb.trapfatal"), + Thumbulator::ConfigureFor::DPCplus, + this); #endif setInitialState(); @@ -70,21 +71,11 @@ CartridgeDPCPlus::CartridgeDPCPlus(const uInt8* image, uInt32 size, myStartBank = 5; } -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -CartridgeDPCPlus::~CartridgeDPCPlus() -{ - delete[] myImage; - -#ifdef THUMB_SUPPORT - delete myThumbEmulator; -#endif -} - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void CartridgeDPCPlus::reset() { // Update cycles to the current system cycles - mySystemCycles = mySystem->cycles(); + myAudioCycles = myARMCycles = mySystem->cycles(); myFractionalClocks = 0.0; setInitialState(); @@ -104,8 +95,11 @@ void CartridgeDPCPlus::setInitialState() // Initialize the DPC data fetcher registers for(int i = 0; i < 8; ++i) - myTops[i] = myBottoms[i] = myCounters[i] = myFractionalIncrements[i] = + { + myTops[i] = myBottoms[i] = myFractionalIncrements[i] = 0; myFractionalCounters[i] = 0; + myCounters[i] = 0; + } // Set waveforms to first waveform entry myMusicWaveforms[0] = myMusicWaveforms[1] = myMusicWaveforms[2] = 0; @@ -121,7 +115,8 @@ void CartridgeDPCPlus::systemCyclesReset() uInt32 cycles = mySystem->cycles(); // Adjust the cycle counter so that it reflects the new value - mySystemCycles -= cycles; + myAudioCycles -= cycles; + myARMCycles -= cycles; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -156,7 +151,7 @@ inline void CartridgeDPCPlus::clockRandomNumberGenerator() inline void CartridgeDPCPlus::priorClockRandomNumberGenerator() { // Update random number generator (32-bit LFSR, reversed) - myRandomNumber = ((myRandomNumber & (1<<31)) ? + myRandomNumber = ((myRandomNumber & (1u<<31)) ? ((0x10adab1e^myRandomNumber) << 11) | ((0x10adab1e^myRandomNumber) >> 21) : (myRandomNumber << 11) | (myRandomNumber >> 21)); } @@ -165,24 +160,18 @@ inline void CartridgeDPCPlus::priorClockRandomNumberGenerator() inline void CartridgeDPCPlus::updateMusicModeDataFetchers() { // Calculate the number of cycles since the last update - Int32 cycles = mySystem->cycles() - mySystemCycles; - mySystemCycles = mySystem->cycles(); + uInt32 cycles = uInt32(mySystem->cycles() - myAudioCycles); + myAudioCycles = mySystem->cycles(); - // Calculate the number of DPC OSC clocks since the last update + // Calculate the number of DPC+ OSC clocks since the last update double clocks = ((20000.0 * cycles) / 1193191.66666667) + myFractionalClocks; - Int32 wholeClocks = (Int32)clocks; - myFractionalClocks = clocks - (double)wholeClocks; - - if(wholeClocks <= 0) - { - return; - } + uInt32 wholeClocks = uInt32(clocks); + myFractionalClocks = clocks - double(wholeClocks); // Let's update counters and flags of the music mode data fetchers - for(int x = 0; x <= 2; ++x) - { - myMusicCounters[x] += myMusicFrequencies[x]; - } + if(wholeClocks > 0) + for(int x = 0; x <= 2; ++x) + myMusicCounters[x] += myMusicFrequencies[x] * wholeClocks; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -206,19 +195,23 @@ inline void CartridgeDPCPlus::callFunction(uInt8 value) myParameterPointer = 0; break; #ifdef THUMB_SUPPORT - case 254: - case 255: + case 254: // call with IRQ driven audio, no special handling needed at this + // time for Stella as ARM code "runs in zero 6507 cycles". + case 255: // call without IRQ driven audio // Call user written ARM code (most likely be C compiled for ARM) try { - myThumbEmulator->run(); + Int32 cycles = Int32(mySystem->cycles() - myARMCycles); + myARMCycles = mySystem->cycles(); + + myThumbEmulator->run(cycles); } - catch(const string& error) { + catch(const runtime_error& e) { if(!mySystem->autodetectMode()) { #ifdef DEBUGGER_SUPPORT - Debugger::debugger().startWithFatalError(error); + Debugger::debugger().startWithFatalError(e.what()); #else - cout << error << endl; + cout << e.what() << endl; #endif } } @@ -233,7 +226,7 @@ uInt8 CartridgeDPCPlus::peek(uInt16 address) { address &= 0x0FFF; - uInt8 peekvalue = myProgramImage[(myCurrentBank << 12) + address]; + uInt8 peekvalue = myProgramImage[myBankOffset + address]; uInt8 flag; // In debugger/bank-locked mode, we ignore all hotspots and in general @@ -258,9 +251,9 @@ uInt8 CartridgeDPCPlus::peek(uInt16 address) uInt32 index = address & 0x07; uInt32 function = (address >> 3) & 0x07; - // Update flag for selected data fetcher + // Update flag for selected data fetcher flag = (((myTops[index]-(myCounters[index] & 0x00ff)) & 0xFF) > ((myTops[index]-myBottoms[index]) & 0xFF)) ? 0xFF : 0; - + switch(function) { case 0x00: @@ -300,7 +293,7 @@ uInt8 CartridgeDPCPlus::peek(uInt16 address) myDisplayImage[(myMusicWaveforms[1] << 5) + (myMusicCounters[1] >> 27)] + myDisplayImage[(myMusicWaveforms[2] << 5) + (myMusicCounters[2] >> 27)]; - result = (uInt8)i; + result = uInt8(i); break; } @@ -425,12 +418,12 @@ bool CartridgeDPCPlus::poke(uInt16 address, uInt8 value) { //DFxFRACLOW - fractional data pointer low byte case 0x00: - myFractionalCounters[index] = (myFractionalCounters[index] & 0x0F0000) | ((uInt16)value << 8); + myFractionalCounters[index] = (myFractionalCounters[index] & 0x0F00FF) | (uInt16(value) << 8); break; // DFxFRACHI - fractional data pointer high byte case 0x01: - myFractionalCounters[index] = (((uInt16)value & 0x0F) << 16) | (myFractionalCounters[index] & 0x00ffff); + myFractionalCounters[index] = ((uInt16(value) & 0x0F) << 16) | (myFractionalCounters[index] & 0x00ffff); break; //DFxFRACINC - Fractional Increment amount @@ -494,7 +487,7 @@ bool CartridgeDPCPlus::poke(uInt16 address, uInt8 value) // DFxHI - data pointer high byte case 0x08: { - myCounters[index] = (((uInt16)value & 0x0F) << 8) | (myCounters[index] & 0x00ff); + myCounters[index] = ((uInt16(value) & 0x0F) << 8) | (myCounters[index] & 0x00ff); break; } @@ -605,17 +598,16 @@ bool CartridgeDPCPlus::bank(uInt16 bank) if(bankLocked()) return false; // Remember what bank we're in - myCurrentBank = bank; - uInt16 offset = myCurrentBank << 12; + myBankOffset = bank << 12; uInt16 shift = mySystem->pageShift(); // Setup the page access methods for the current bank System::PageAccess access(0, 0, 0, this, System::PA_READ); // Map Program ROM image into the system - for(uInt32 address = 0x1080; address < 0x2000; address += (1 << shift)) + for(uInt16 address = 0x1080; address < 0x2000; address += (1 << shift)) { - access.codeAccessBase = &myCodeAccessBase[offset + (address & 0x0FFF)]; + access.codeAccessBase = &myCodeAccessBase[myBankOffset + (address & 0x0FFF)]; mySystem->setPageAccess(address >> shift, access); } return myBankChanged = true; @@ -624,7 +616,7 @@ bool CartridgeDPCPlus::bank(uInt16 bank) // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - uInt16 CartridgeDPCPlus::bank() const { - return myCurrentBank; + return myBankOffset >> 12; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -641,7 +633,7 @@ bool CartridgeDPCPlus::patch(uInt16 address, uInt8 value) // For now, we ignore attempts to patch the DPC address space if(address >= 0x0080) { - myProgramImage[(myCurrentBank << 12) + (address & 0x0FFF)] = value; + myProgramImage[myBankOffset + (address & 0x0FFF)] = value; return myBankChanged = true; } else @@ -652,7 +644,7 @@ bool CartridgeDPCPlus::patch(uInt16 address, uInt8 value) const uInt8* CartridgeDPCPlus::getImage(int& size) const { size = mySize; - return myImage; + return myImage + (32768u - mySize); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -660,10 +652,8 @@ bool CartridgeDPCPlus::save(Serializer& out) const { try { - out.putString(name()); - // Indicates which bank is currently active - out.putShort(myCurrentBank); + out.putShort(myBankOffset); // Harmony RAM out.putByteArray(myDPCRAM, 8192); @@ -702,8 +692,12 @@ bool CartridgeDPCPlus::save(Serializer& out) const // The random number generator register out.putInt(myRandomNumber); - out.putInt(mySystemCycles); - out.putInt((uInt32)(myFractionalClocks * 100000000.0)); + // Get system cycles and fractional clocks + out.putLong(myAudioCycles); + out.putDouble(myFractionalClocks); + + // Clock info for Thumbulator + out.putLong(myARMCycles); } catch(...) { @@ -719,11 +713,8 @@ bool CartridgeDPCPlus::load(Serializer& in) { try { - if(in.getString() != name()) - return false; - // Indicates which bank is currently active - myCurrentBank = in.getShort(); + myBankOffset = in.getShort(); // Harmony RAM in.getByteArray(myDPCRAM, 8192); @@ -762,9 +753,12 @@ bool CartridgeDPCPlus::load(Serializer& in) // The random number generator register myRandomNumber = in.getInt(); - // Get system cycles and fractional clocks - mySystemCycles = (Int32)in.getInt(); - myFractionalClocks = (double)in.getInt() / 100000000.0; + // Get audio cycles and fractional clocks + myAudioCycles = in.getLong(); + myFractionalClocks = in.getDouble(); + + // Clock info for Thumbulator + myARMCycles = in.getLong(); } catch(...) { @@ -773,7 +767,7 @@ bool CartridgeDPCPlus::load(Serializer& in) } // Now, go to the current bank - bank(myCurrentBank); + bank(myBankOffset >> 12); return true; } diff --git a/src/emucore/CartDPCPlus.hxx b/src/emucore/CartDPCPlus.hxx index 8b85c22c2..c5e7f8864 100644 --- a/src/emucore/CartDPCPlus.hxx +++ b/src/emucore/CartDPCPlus.hxx @@ -56,11 +56,7 @@ class CartridgeDPCPlus : public Cartridge @param settings A reference to the various settings (read-only) */ CartridgeDPCPlus(const uInt8* image, uInt32 size, const Settings& settings); - - /** - Destructor - */ - virtual ~CartridgeDPCPlus(); + virtual ~CartridgeDPCPlus() = default; public: /** @@ -170,35 +166,35 @@ class CartridgeDPCPlus : public Cartridge bool poke(uInt16 address, uInt8 value); private: - /** + /** Sets the initial state of the DPC pointers and RAM */ void setInitialState(); - /** + /** Clocks the random number generator to move it to its next state */ void clockRandomNumberGenerator(); - - /** + + /** Clocks the random number generator to move it to its prior state */ void priorClockRandomNumberGenerator(); - /** + /** Updates any data fetchers in music mode based on the number of CPU cycles which have passed since the last update. */ void updateMusicModeDataFetchers(); - /** + /** Call Special Functions */ void callFunction(uInt8 value); private: // The ROM image and size - uInt8* myImage; + uInt8 myImage[32768]; uInt32 mySize; // Pointer to the 24K program ROM image of the cartridge @@ -207,20 +203,20 @@ class CartridgeDPCPlus : public Cartridge // Pointer to the 4K display ROM image of the cartridge uInt8* myDisplayImage; - // The DPC 8k RAM image + // The DPC 8k RAM image, used as: + // 3K DPC+ driver + // 4K Display Data + // 1K Frequency Data uInt8 myDPCRAM[8192]; #ifdef THUMB_SUPPORT // Pointer to the Thumb ARM emulator object - Thumbulator* myThumbEmulator; + unique_ptr myThumbEmulator; #endif // Pointer to the 1K frequency table uInt8* myFrequencyImage; - // Indicates which bank is currently active - uInt16 myCurrentBank; - // The top registers for the data fetchers uInt8 myTops[8]; @@ -229,7 +225,7 @@ class CartridgeDPCPlus : public Cartridge // The counter registers for the data fetchers uInt16 myCounters[8]; - + // The counter registers for the fractional data fetchers uInt32 myFractionalCounters[8]; @@ -238,7 +234,7 @@ class CartridgeDPCPlus : public Cartridge // The Fast Fetcher Enabled flag bool myFastFetch; - + // Flags that last byte peeked was A9 (LDA #) bool myLDAimmediate; @@ -253,18 +249,24 @@ class CartridgeDPCPlus : public Cartridge // The music frequency uInt32 myMusicFrequencies[3]; - + // The music waveforms uInt16 myMusicWaveforms[3]; - + // The random number generator register uInt32 myRandomNumber; // System cycle count when the last update to music data fetchers occurred - Int32 mySystemCycles; + uInt64 myAudioCycles; + + // System cycle count when the last Thumbulator::run() occurred + uInt64 myARMCycles; // Fractional DPC music OSC clocks unused during the last update double myFractionalClocks; + + // Indicates the offset into the ROM image (aligns to current bank) + uInt16 myBankOffset; }; #endif diff --git a/src/emucore/CartFA2.cxx b/src/emucore/CartFA2.cxx index f381b9345..40d83ee9c 100644 --- a/src/emucore/CartFA2.cxx +++ b/src/emucore/CartFA2.cxx @@ -1,8 +1,8 @@ //============================================================================ // -// SSSS tt lll lll -// SS SS tt ll ll -// SS tttttt eeee ll ll aaaa +// SSSS tt lll lll +// SS SS tt ll ll +// SS tttttt eeee ll ll aaaa // SSSS tt ee ee ll ll aa // SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" // SS SS tt ee ll ll aa aa @@ -36,7 +36,7 @@ CartridgeFA2::CartridgeFA2(const uInt8* image, uInt32 size, const OSystem& osyst if(size >= 29 * 1024) { image += 1024; - mySize = 28 * 1024; + mySize = 28 * 1024; } // Allocate array for the ROM image @@ -52,7 +52,7 @@ CartridgeFA2::CartridgeFA2(const uInt8* image, uInt32 size, const OSystem& osyst // Remember startup bank myStartBank = 0; } - + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - CartridgeFA2::~CartridgeFA2() { @@ -93,7 +93,7 @@ void CartridgeFA2::install(System& system) access.codeAccessBase = &myCodeAccessBase[j & 0x00FF]; mySystem->setPageAccess(j >> shift, access); } - + // Set the page accessing method for the RAM reading pages access.directPokeBase = 0; access.type = System::PA_READ; @@ -137,7 +137,7 @@ uInt8 CartridgeFA2::peek(uInt16 address) // Set the current bank to the third 4k bank bank(2); break; - + case 0x0FF8: // Set the current bank to the fourth 4k bank bank(3); @@ -175,7 +175,7 @@ uInt8 CartridgeFA2::peek(uInt16 address) triggerReadFromWritePort(peekAddress); return myRAM[address] = value; } - } + } else return myImage[(myCurrentBank << 12) + address]; } @@ -208,7 +208,7 @@ bool CartridgeFA2::poke(uInt16 address, uInt8) // Set the current bank to the third 4k bank bank(2); break; - + case 0x0FF8: // Set the current bank to the fourth 4k bank bank(3); @@ -299,7 +299,7 @@ bool CartridgeFA2::patch(uInt16 address, uInt8 value) myImage[(myCurrentBank << 12) + address] = value; return myBankChanged = true; -} +} // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - const uInt8* CartridgeFA2::getImage(int& size) const @@ -385,7 +385,7 @@ uInt8 CartridgeFA2::ramReadWrite() // We go ahead and do the access now, and only return when a sufficient // amount of time has passed Serializer serializer(myFlashFile); - if(serializer.isValid()) + if(serializer) { if(myRAM[255] == 1) // read { @@ -437,7 +437,7 @@ uInt8 CartridgeFA2::ramReadWrite() void CartridgeFA2::flash(uInt8 operation) { Serializer serializer(myFlashFile); - if(serializer.isValid()) + if(serializer) { if(operation == 0) // erase { diff --git a/src/emucore/CartMC.cxx b/src/emucore/CartMC.cxx deleted file mode 100644 index e9e695a5d..000000000 --- a/src/emucore/CartMC.cxx +++ /dev/null @@ -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 -#include - -#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; -} diff --git a/src/emucore/CartMC.hxx b/src/emucore/CartMC.hxx deleted file mode 100644 index 4554359dd..000000000 --- a/src/emucore/CartMC.hxx +++ /dev/null @@ -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 diff --git a/src/emucore/ConsoleTiming.hxx b/src/emucore/ConsoleTiming.hxx new file mode 100644 index 000000000..515684c9e --- /dev/null +++ b/src/emucore/ConsoleTiming.hxx @@ -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 diff --git a/src/emucore/Device.hxx b/src/emucore/Device.hxx index 10a292247..a54b048f1 100644 --- a/src/emucore/Device.hxx +++ b/src/emucore/Device.hxx @@ -22,6 +22,7 @@ class System; +#include "ConsoleTiming.hxx" #include "Serializable.hxx" #include "bspf.hxx" @@ -55,9 +56,18 @@ class Device : public Serializable */ virtual void reset() = 0; + /** + Notification method invoked by the system when the console type + has changed. It may be necessary to override this method for + devices that want to know about console changes. + + @param timing Enum representing the new console type + */ + virtual void consoleChanged(ConsoleTiming timing) { } + /** Notification method invoked by the system right before the - system resets its cycle counter to zero. It may be necessary + system resets its cycle counter to zero. It may be necessary to override this method for devices that remember cycle counts. */ virtual void systemCyclesReset() { } diff --git a/src/emucore/Serializer.cxx b/src/emucore/Serializer.cxx index 9447226b5..4f41a41ed 100644 --- a/src/emucore/Serializer.cxx +++ b/src/emucore/Serializer.cxx @@ -1,20 +1,18 @@ //============================================================================ // -// SSSS tt lll lll -// SS SS tt ll ll -// SS tttttt eeee ll ll aaaa +// SSSS tt lll lll +// SS SS tt ll ll +// SS tttttt eeee ll ll aaaa // SSSS tt ee ee ll ll aa // SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" // SS SS tt ee ll ll aa aa // SSSS ttt eeeee llll llll aaaaa // -// Copyright (c) 1995-2014 by Bradford W. Mott, Stephen Anthony +// Copyright (c) 1995-2018 by Bradford W. Mott, Stephen Anthony // and the Stella Team // // See the file "License.txt" for information on usage and redistribution of // this file, and for a DISCLAIMER OF ALL WARRANTIES. -// -// $Id: Serializer.cxx 2838 2014-01-17 23:34:03Z stephena $ //============================================================================ #include @@ -23,25 +21,25 @@ #include "FSNode.hxx" #include "Serializer.hxx" +using std::ios; +using std::ios_base; + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Serializer::Serializer(const string& filename, bool readonly) - : myStream(NULL), - myUseFilestream(true) + : myStream(nullptr) { if(readonly) { FilesystemNode node(filename); if(node.isFile() && node.isReadable()) { - fstream* str = new fstream(filename.c_str(), ios::in | ios::binary); + unique_ptr str = make_unique(filename, ios::in | ios::binary); if(str && str->is_open()) { - myStream = str; + myStream = std::move(str); myStream->exceptions( ios_base::failbit | ios_base::badbit | ios_base::eofbit ); - reset(); + rewind(); } - else - delete str; } } else @@ -53,59 +51,37 @@ Serializer::Serializer(const string& filename, bool readonly) // So we open in write and append mode - the write creates the file // when necessary, and the append doesn't delete any data if it // already exists - fstream temp(filename.c_str(), ios::out | ios::app); + fstream temp(filename, ios::out | ios::app); temp.close(); - fstream* str = new fstream(filename.c_str(), ios::in | ios::out | ios::binary); + unique_ptr str = make_unique(filename, ios::in | ios::out | ios::binary); if(str && str->is_open()) { - myStream = str; + myStream = std::move(str); myStream->exceptions( ios_base::failbit | ios_base::badbit | ios_base::eofbit ); - reset(); + rewind(); } - else - delete str; } } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Serializer::Serializer(void) - : myStream(NULL), - myUseFilestream(false) +Serializer::Serializer() + : myStream(nullptr) { - myStream = new stringstream(ios::in | ios::out | ios::binary); - + myStream = make_unique(ios::in | ios::out | ios::binary); + // For some reason, Windows and possibly OSX needs to store something in // the stream before it is used for the first time if(myStream) { myStream->exceptions( ios_base::failbit | ios_base::badbit | ios_base::eofbit ); putBool(true); - reset(); + rewind(); } } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Serializer::~Serializer(void) -{ - if(myStream != NULL) - { - if(myUseFilestream) - ((fstream*)myStream)->close(); - - delete myStream; - myStream = NULL; - } -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool Serializer::isValid(void) -{ - return myStream != NULL; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void Serializer::reset(void) +void Serializer::rewind() { myStream->clear(); myStream->seekg(ios_base::beg); @@ -113,7 +89,7 @@ void Serializer::reset(void) } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt8 Serializer::getByte(void) +uInt8 Serializer::getByte() const { char buf; myStream->read(&buf, 1); @@ -122,43 +98,61 @@ uInt8 Serializer::getByte(void) } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void Serializer::getByteArray(uInt8* array, uInt32 size) +void Serializer::getByteArray(uInt8* array, uInt32 size) const { - myStream->read((char*)array, size); + myStream->read(reinterpret_cast(array), size); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt16 Serializer::getShort(void) +uInt16 Serializer::getShort() const { uInt16 val = 0; - myStream->read((char*)&val, sizeof(uInt16)); + myStream->read(reinterpret_cast(&val), sizeof(uInt16)); return val; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void Serializer::getShortArray(uInt16* array, uInt32 size) +void Serializer::getShortArray(uInt16* array, uInt32 size) const { - myStream->read((char*)array, sizeof(uInt16)*size); + myStream->read(reinterpret_cast(array), sizeof(uInt16)*size); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt32 Serializer::getInt(void) +uInt32 Serializer::getInt() const { uInt32 val = 0; - myStream->read((char*)&val, sizeof(uInt32)); + myStream->read(reinterpret_cast(&val), sizeof(uInt32)); return val; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void Serializer::getIntArray(uInt32* array, uInt32 size) +void Serializer::getIntArray(uInt32* array, uInt32 size) const { - myStream->read((char*)array, sizeof(uInt32)*size); + myStream->read(reinterpret_cast(array), sizeof(uInt32)*size); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -string Serializer::getString(void) +uInt64 Serializer::getLong() const +{ + uInt64 val = 0; + myStream->read(reinterpret_cast(&val), sizeof(uInt64)); + + return val; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +double Serializer::getDouble() const +{ + double val = 0.0; + myStream->read(reinterpret_cast(&val), sizeof(double)); + + return val; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +string Serializer::getString() const { int len = getInt(); string str; @@ -169,7 +163,7 @@ string Serializer::getString(void) } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool Serializer::getBool(void) +bool Serializer::getBool() const { return getByte() == TruePattern; } @@ -177,43 +171,55 @@ bool Serializer::getBool(void) // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void Serializer::putByte(uInt8 value) { - myStream->write((char*)&value, 1); + myStream->write(reinterpret_cast(&value), 1); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void Serializer::putByteArray(const uInt8* array, uInt32 size) { - myStream->write((char*)array, size); + myStream->write(reinterpret_cast(array), size); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void Serializer::putShort(uInt16 value) { - myStream->write((char*)&value, sizeof(uInt16)); + myStream->write(reinterpret_cast(&value), sizeof(uInt16)); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void Serializer::putShortArray(const uInt16* array, uInt32 size) { - myStream->write((char*)array, sizeof(uInt16)*size); + myStream->write(reinterpret_cast(array), sizeof(uInt16)*size); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void Serializer::putInt(uInt32 value) { - myStream->write((char*)&value, sizeof(uInt32)); + myStream->write(reinterpret_cast(&value), sizeof(uInt32)); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void Serializer::putIntArray(const uInt32* array, uInt32 size) { - myStream->write((char*)array, sizeof(uInt32)*size); + myStream->write(reinterpret_cast(array), sizeof(uInt32)*size); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void Serializer::putLong(uInt64 value) +{ + myStream->write(reinterpret_cast(&value), sizeof(uInt64)); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void Serializer::putDouble(double value) +{ + myStream->write(reinterpret_cast(&value), sizeof(double)); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void Serializer::putString(const string& str) { - int len = str.length(); + int len = int(str.length()); putInt(len); myStream->write(str.data(), len); } diff --git a/src/emucore/Serializer.hxx b/src/emucore/Serializer.hxx index 845055725..c92cdabaf 100644 --- a/src/emucore/Serializer.hxx +++ b/src/emucore/Serializer.hxx @@ -1,26 +1,23 @@ //============================================================================ // -// SSSS tt lll lll -// SS SS tt ll ll -// SS tttttt eeee ll ll aaaa +// SSSS tt lll lll +// SS SS tt ll ll +// SS tttttt eeee ll ll aaaa // SSSS tt ee ee ll ll aa // SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" // SS SS tt ee ll ll aa aa // SSSS ttt eeeee llll llll aaaaa // -// Copyright (c) 1995-2014 by Bradford W. Mott, Stephen Anthony +// Copyright (c) 1995-2018 by Bradford W. Mott, Stephen Anthony // and the Stella Team // // See the file "License.txt" for information on usage and redistribution of // this file, and for a DISCLAIMER OF ALL WARRANTIES. -// -// $Id: Serializer.hxx 2838 2014-01-17 23:34:03Z stephena $ //============================================================================ #ifndef SERIALIZER_HXX #define SERIALIZER_HXX -#include #include "bspf.hxx" /** @@ -29,15 +26,11 @@ stream can be either an actual file, or an in-memory structure. Bytes are written as characters, shorts as 2 characters (16-bits), - integers as 4 characters (32-bits), strings are written as characters - prepended by the length of the string, boolean values are written using - a special character pattern. - - All bytes, shorts and ints should be cast to their appropriate data type upon - method return. + integers as 4 characters (32-bits), long integers as 8 bytes (64-bits), + strings are written as characters prepended by the length of the string, + boolean values are written using a special character pattern. @author Stephen Anthony - @version $Id: Serializer.hxx 2838 2014-01-17 23:34:03Z stephena $ */ class Serializer { @@ -50,35 +43,30 @@ class Serializer If a file is opened readonly, we can never write to it. - The isValid() method must immediately be called to verify the stream + The valid() method must immediately be called to verify the stream was correctly initialized. */ Serializer(const string& filename, bool readonly = false); - Serializer(void); - - /** - Destructor - */ - virtual ~Serializer(void); + Serializer(); public: /** Answers whether the serializer is currently initialized for reading and writing. */ - bool isValid(void); + explicit operator bool() const { return myStream != nullptr; } /** Resets the read/write location to the beginning of the stream. */ - void reset(void); + void rewind(); /** Reads a byte value (unsigned 8-bit) from the current input stream. @result The byte value which has been read from the stream. */ - uInt8 getByte(void); + uInt8 getByte() const; /** Reads a byte array (unsigned 8-bit) from the current input stream. @@ -86,15 +74,14 @@ class Serializer @param array The location to store the bytes read @param size The size of the array (number of bytes to read) */ - void getByteArray(uInt8* array, uInt32 size); - + void getByteArray(uInt8* array, uInt32 size) const; /** Reads a short value (unsigned 16-bit) from the current input stream. @result The short value which has been read from the stream. */ - uInt16 getShort(void); + uInt16 getShort() const; /** Reads a short array (unsigned 16-bit) from the current input stream. @@ -102,14 +89,14 @@ class Serializer @param array The location to store the shorts read @param size The size of the array (number of shorts to read) */ - void getShortArray(uInt16* array, uInt32 size); + void getShortArray(uInt16* array, uInt32 size) const; /** Reads an int value (unsigned 32-bit) from the current input stream. @result The int value which has been read from the stream. */ - uInt32 getInt(void); + uInt32 getInt() const; /** Reads an integer array (unsigned 32-bit) from the current input stream. @@ -117,21 +104,35 @@ class Serializer @param array The location to store the integers read @param size The size of the array (number of integers to read) */ - void getIntArray(uInt32* array, uInt32 size); + void getIntArray(uInt32* array, uInt32 size) const; + + /** + Reads a long int value (unsigned 64-bit) from the current input stream. + + @result The long int value which has been read from the stream. + */ + uInt64 getLong() const; + + /** + Reads a double value (signed 64-bit) from the current input stream. + + @result The double value which has been read from the stream. + */ + double getDouble() const; /** Reads a string from the current input stream. @result The string which has been read from the stream. */ - string getString(void); + string getString() const; /** Reads a boolean value from the current input stream. @result The boolean value which has been read from the stream. */ - bool getBool(void); + bool getBool() const; /** Writes an byte value (unsigned 8-bit) to the current output stream. @@ -178,6 +179,20 @@ class Serializer */ void putIntArray(const uInt32* array, uInt32 size); + /** + Writes a long int value (unsigned 64-bit) to the current output stream. + + @param value The long int value to write to the output stream. + */ + void putLong(uInt64 value); + + /** + Writes a double value (signed 64-bit) to the current output stream. + + @param value The double value to write to the output stream. + */ + void putDouble(double value); + /** Writes a string to the current output stream. @@ -194,13 +209,19 @@ class Serializer private: // The stream to send the serialized data to. - iostream* myStream; - bool myUseFilestream; + unique_ptr myStream; enum { TruePattern = 0xfe, FalsePattern = 0x01 }; + + private: + // Following constructors and assignment operators not supported + Serializer(const Serializer&) = delete; + Serializer(Serializer&&) = delete; + Serializer& operator=(const Serializer&) = delete; + Serializer& operator=(Serializer&&) = delete; }; #endif diff --git a/src/emucore/StateManager.cxx b/src/emucore/StateManager.cxx index 1cd90f245..88a3b04c2 100644 --- a/src/emucore/StateManager.cxx +++ b/src/emucore/StateManager.cxx @@ -30,7 +30,7 @@ #include "StateManager.hxx" -#define STATE_HEADER "03090100state" +#define STATE_HEADER "03090400state" #define MOVIE_HEADER "03030000movie" // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -186,7 +186,7 @@ void StateManager::loadState(int slot) // Make sure the file can be opened in read-only mode Serializer in(buf.str(), true); - if(!in.isValid()) + if(!in) { buf.str(""); #if !defined(RETRON77) @@ -251,7 +251,7 @@ void StateManager::saveState(int slot) // Make sure the file can be opened for writing Serializer out(buf.str()); - if(!out.isValid()) + if(!out) { buf.str(""); buf << "Can't open/save to state file " << " " << myOSystem->stateDir() << name << ".st" << slot; @@ -309,7 +309,7 @@ bool StateManager::loadState(Serializer& in) if(&myOSystem->console()) { // Make sure the file can be opened for reading - if(in.isValid()) + if(in) { // First test if we have a valid header and cart type // If so, do a complete state load using the Console @@ -329,7 +329,7 @@ bool StateManager::saveState(Serializer& out) if(&myOSystem->console()) { // Make sure the file can be opened for writing - if(out.isValid()) + if(out) { // Add header so that if the state format changes in the future, // we'll know right away, without having to parse the rest of the file diff --git a/src/emucore/Thumbulator.cxx b/src/emucore/Thumbulator.cxx index 90a1c0cc1..858508928 100644 --- a/src/emucore/Thumbulator.cxx +++ b/src/emucore/Thumbulator.cxx @@ -8,13 +8,11 @@ // SS SS tt ee ll ll aa aa // SSSS ttt eeeee llll llll aaaaa // -// Copyright (c) 1995-2014 by Bradford W. Mott, Stephen Anthony +// Copyright (c) 1995-2018 by Bradford W. Mott, Stephen Anthony // and the Stella Team // // See the file "License.txt" for information on usage and redistribution of // this file, and for a DISCLAIMER OF ALL WARRANTIES. -// -// $Id: Thumbulator.cxx 2838 2014-01-17 23:34:03Z stephena $ //============================================================================ //============================================================================ @@ -24,12 +22,11 @@ // Code is public domain and used with the author's consent //============================================================================ -#ifdef THUMB_SUPPORT - #include "bspf.hxx" #include "Base.hxx" +#include "Cart.hxx" #include "Thumbulator.hxx" -using namespace Common; +using Common::Base; // Uncomment the following to enable specific functionality // WARNING!!! This slows the runtime to a crawl @@ -47,29 +44,39 @@ using namespace Common; #define DO_DBUG(statement) #endif +#ifdef __BIG_ENDIAN__ + #define CONV_DATA(d) (((d & 0xFFFF)>>8) | ((d & 0xffff)<<8)) & 0xffff; + #define CONV_RAMROM(d) ((d>>8) | (d<<8)) & 0xffff; +#else + #define CONV_DATA(d) (d & 0xFFFF); + #define CONV_RAMROM(d) (d); +#endif + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Thumbulator::Thumbulator(const uInt16* rom_ptr, uInt16* ram_ptr, bool traponfatal) +Thumbulator::Thumbulator(const uInt16* rom_ptr, uInt16* ram_ptr, bool traponfatal, + Thumbulator::ConfigureFor configurefor, Cartridge* cartridge) : rom(rom_ptr), - ram(ram_ptr) + ram(ram_ptr), + T1TCR(0), + T1TC(0), + configuration(configurefor), + myCartridge(cartridge) { + setConsoleTiming(ConsoleTiming::ntsc); trapFatalErrors(traponfatal); + reset(); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Thumbulator::~Thumbulator() -{ -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -string Thumbulator::run( void ) +string Thumbulator::run() { reset(); for(;;) { - if (execute()) break; - if (instructions > 500000) // way more than would otherwise be possible - throw "instructions > 500000"; - } + if(execute()) break; + if(instructions > 500000) // way more than would otherwise be possible + throw runtime_error("instructions > 500000"); + } #if defined(THUMB_DISS) || defined(THUMB_DBUG) dump_counters(); cout << statusMsg.str() << endl; @@ -77,6 +84,36 @@ string Thumbulator::run( void ) return statusMsg.str(); } +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void Thumbulator::setConsoleTiming(ConsoleTiming timing) +{ + // this sets how many ticks of the Harmony/Melody clock + // will occur per tick of the 6507 clock + constexpr double NTSC = 70.0 / 1.193182; // NTSC 6507 clock rate + constexpr double PAL = 70.0 / 1.182298; // PAL 6507 clock rate + constexpr double SECAM = 70.0 / 1.187500; // SECAM 6507 clock rate + + switch(timing) + { + case ConsoleTiming::ntsc: timing_factor = NTSC; break; + case ConsoleTiming::secam: timing_factor = SECAM; break; + case ConsoleTiming::pal: timing_factor = PAL; break; + } +} +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void Thumbulator::updateTimer(uInt32 cycles) +{ + if (T1TCR & 1) // bit 0 controls timer on/off + T1TC += uInt32(cycles * timing_factor); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +string Thumbulator::run(uInt32 cycles) +{ + updateTimer(cycles); + return run(); +} + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - inline int Thumbulator::fatalError(const char* opcode, uInt32 v1, const char* msg) { @@ -84,134 +121,124 @@ inline int Thumbulator::fatalError(const char* opcode, uInt32 v1, const char* ms << opcode << "(" << Base::HEX8 << v1 << "), " << msg << endl; dump_regs(); if(trapOnFatal) - throw statusMsg.str(); + throw runtime_error(statusMsg.str()); return 0; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - inline int Thumbulator::fatalError(const char* opcode, uInt32 v1, uInt32 v2, - const char* msg) + const char* msg) { statusMsg << "Thumb ARM emulation fatal error: " << endl << opcode << "(" << Base::HEX8 << v1 << "," << v2 << "), " << msg << endl; dump_regs(); if(trapOnFatal) - throw statusMsg.str(); + throw runtime_error(statusMsg.str()); return 0; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void Thumbulator::dump_counters ( void ) +void Thumbulator::dump_counters() { cout << endl << endl << "instructions " << instructions << endl << "fetches " << fetches << endl << "reads " << reads << endl << "writes " << writes << endl - << "memcycles " << (fetches+reads+writes) << endl; + << "memcycles " << (fetches+reads+writes) << endl + << "systick_ints " << systick_ints << endl; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void Thumbulator::dump_regs( void ) +void Thumbulator::dump_regs() { for (int cnt = 1; cnt < 14; cnt++) { - statusMsg << "R" << cnt << " = " << Base::HEX8 << reg_sys[cnt-1] << " "; + statusMsg << "R" << cnt << " = " << Base::HEX8 << reg_norm[cnt-1] << " "; if(cnt % 4 == 0) statusMsg << endl; } statusMsg << endl - << "SP = " << Base::HEX8 << reg_svc[13] << " " - << "LR = " << Base::HEX8 << reg_svc[14] << " " - << "PC = " << Base::HEX8 << reg_sys[15] << " " + << "SP = " << Base::HEX8 << reg_norm[13] << " " + << "LR = " << Base::HEX8 << reg_norm[14] << " " + << "PC = " << Base::HEX8 << reg_norm[15] << " " << endl; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt32 Thumbulator::fetch16 ( uInt32 addr ) +uInt32 Thumbulator::fetch16(uInt32 addr) { - fetches++; + ++fetches; uInt32 data; - switch(addr&0xF0000000) + switch(addr & 0xF0000000) { case 0x00000000: //ROM addr &= ROMADDMASK; - if(addr<0x50) + if(addr < 0x50) fatalError("fetch16", addr, "abort"); - addr>>=1; - #ifdef __BIG_ENDIAN__ - data=((rom[addr]>>8)|(rom[addr]<<8))&0xffff; - #else - data=rom[addr]; - #endif + addr >>= 1; + data = CONV_RAMROM(rom[addr]); DO_DBUG(statusMsg << "fetch16(" << Base::HEX8 << addr << ")=" << Base::HEX4 << data << endl); - return(data); + return data; case 0x40000000: //RAM addr &= RAMADDMASK; - addr>>=1; - #ifdef __BIG_ENDIAN__ - data=((ram[addr]>>8)|(ram[addr]<<8))&0xffff; - #else - data=ram[addr]; - #endif + addr >>= 1; + data=CONV_RAMROM(ram[addr]); DO_DBUG(statusMsg << "fetch16(" << Base::HEX8 << addr << ")=" << Base::HEX4 << data << endl); - return(data); + return data; } return fatalError("fetch16", addr, "abort"); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt32 Thumbulator::fetch32 ( uInt32 addr ) +uInt32 Thumbulator::fetch32(uInt32 addr) { uInt32 data; - switch(addr&0xF0000000) + switch(addr & 0xF0000000) { case 0x00000000: //ROM - if(addr<0x50) + if(addr < 0x50) { - data=read32(addr); + data = read32(addr); DO_DBUG(statusMsg << "fetch32(" << Base::HEX8 << addr << ")=" << Base::HEX8 << data << endl); - if(addr==0x00000000) return(data); - if(addr==0x00000004) return(data); + if(addr == 0x00000000) return data; + if(addr == 0x00000004) return data; + if(addr == 0x0000003C) return data; fatalError("fetch32", addr, "abort"); } + [[fallthrough]]; case 0x40000000: //RAM - data =fetch16(addr+2); - data<<=16; - data|=fetch16(addr+0); + data = read32(addr); DO_DBUG(statusMsg << "fetch32(" << Base::HEX8 << addr << ")=" << Base::HEX8 << data << endl); - return(data); + return data; } return fatalError("fetch32", addr, "abort"); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void Thumbulator::write16 ( uInt32 addr, uInt32 data ) +void Thumbulator::write16(uInt32 addr, uInt32 data) { - if((addr>0x40001fff)&&(addr<0x50000000)) + if((addr > 0x40001fff) && (addr < 0x50000000)) fatalError("write16", addr, "abort - out of range"); - else if((addr>0x40000028)&&(addr<0x40000c00)) - fatalError("write16", addr, "to bankswitch code area"); - if(addr&1) + + if (isProtected(addr)) fatalError("write16", addr, "to driver area"); + + if(addr & 1) fatalError("write16", addr, "abort - misaligned"); - writes++; + ++writes; DO_DBUG(statusMsg << "write16(" << Base::HEX8 << addr << "," << Base::HEX8 << data << ")" << endl); - switch(addr&0xF0000000) + switch(addr & 0xF0000000) { case 0x40000000: //RAM - addr&=RAMADDMASK; - addr>>=1; - #ifdef __BIG_ENDIAN__ - ram[addr]=(((data&0xFFFF)>>8)|((data&0xffff)<<8))&0xffff; - #else - ram[addr]=data&0xFFFF; - #endif + addr &= RAMADDMASK; + addr >>= 1; + ram[addr] = CONV_DATA(data); return; case 0xE0000000: //MAMCR @@ -226,18 +253,19 @@ void Thumbulator::write16 ( uInt32 addr, uInt32 data ) } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void Thumbulator::write32 ( uInt32 addr, uInt32 data ) +void Thumbulator::write32(uInt32 addr, uInt32 data) { - if(addr&3) + if(addr & 3) fatalError("write32", addr, "abort - misaligned"); + if (isProtected(addr)) fatalError("write32", addr, "to driver area"); DO_DBUG(statusMsg << "write32(" << Base::HEX8 << addr << "," << Base::HEX8 << data << ")" << endl); - switch(addr&0xF0000000) + switch(addr & 0xF0000000) { case 0xF0000000: //halt dump_counters(); - throw "HALT";// exit(0); + throw runtime_error("HALT"); case 0xE0000000: //periph switch(addr) @@ -245,59 +273,119 @@ void Thumbulator::write32 ( uInt32 addr, uInt32 data ) case 0xE0000000: DO_DISS(statusMsg << "uart: [" << char(data&0xFF) << "]" << endl); break; + + case 0xE0008004: // T1TCR - Timer 1 Control Register + T1TCR = data; + break; + + case 0xE0008008: // T1TC - Timer 1 Counter + T1TC = data; + break; + + case 0xE000E010: + { + uInt32 old = systick_ctrl; + systick_ctrl = data & 0x00010007; + if(((old & 1) == 0) && (systick_ctrl & 1)) + { + // timer started, load count + systick_count = systick_reload; + } + break; + } + + case 0xE000E014: + systick_reload = data & 0x00FFFFFF; + break; + + case 0xE000E018: + systick_count = data & 0x00FFFFFF; + break; + + case 0xE000E01C: + systick_calibrate = data & 0x00FFFFFF; + break; } return; case 0xD0000000: //debug - statusMsg << "[" << Base::HEX8 << read_register(14) << "][" - << addr << "] " << data << endl; + switch(addr & 0xFF) + { + case 0x00: + statusMsg << "[" << Base::HEX8 << read_register(14) << "][" + << addr << "] " << data << endl; + return; + + case 0x10: + statusMsg << Base::HEX8 << data << endl; + return; + + case 0x20: + statusMsg << Base::HEX8 << data << endl; + return; + } return; case 0x40000000: //RAM - write16(addr+0,(data>> 0)&0xFFFF); - write16(addr+2,(data>>16)&0xFFFF); + write16(addr+0, (data >> 0) & 0xFFFF); + write16(addr+2, (data >> 16) & 0xFFFF); return; } fatalError("write32", addr, data, "abort"); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt32 Thumbulator::read16 ( uInt32 addr ) +bool Thumbulator::isProtected(uInt32 addr) +{ + if (addr < 0x40000000) return false; + addr -= 0x40000000; + + switch (configuration) { + case ConfigureFor::DPCplus: + return (addr < 0x0c00) && (addr > 0x0028); + + case ConfigureFor::CDF: + return (addr < 0x0800) && (addr > 0x0028) && !((addr >= 0x06e0) && (addr < (0x0e60 + 284))); + + case ConfigureFor::CDF1: + return (addr < 0x0800) && (addr > 0x0028) && !((addr >= 0x00a0) && (addr < (0x00a0 + 284))); + + case ConfigureFor::BUS: + return (addr < 0x06d8) && (addr > 0x0028); + } + + return false; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +uInt32 Thumbulator::read16(uInt32 addr) { uInt32 data; - if((addr>0x40001fff)&&(addr<0x50000000)) + if((addr > 0x40001fff) && (addr < 0x50000000)) fatalError("read16", addr, "abort - out of range"); - else if((addr>0x7fff)&&(addr<0x10000000)) + else if((addr > 0x7fff) && (addr < 0x10000000)) fatalError("read16", addr, "abort - out of range"); - if(addr&1) + if(addr & 1) fatalError("read16", addr, "abort - misaligned"); - reads++; + ++reads; - switch(addr&0xF0000000) + switch(addr & 0xF0000000) { case 0x00000000: //ROM - addr&=ROMADDMASK; - addr>>=1; - #ifdef __BIG_ENDIAN__ - data=((rom[addr]>>8)|(rom[addr]<<8))&0xffff; - #else - data=rom[addr]; - #endif + addr &= ROMADDMASK; + addr >>= 1; + data = CONV_RAMROM(rom[addr]); DO_DBUG(statusMsg << "read16(" << Base::HEX8 << addr << ")=" << Base::HEX4 << data << endl); - return(data); + return data; case 0x40000000: //RAM - addr&=RAMADDMASK; - addr>>=1; - #ifdef __BIG_ENDIAN__ - data=((ram[addr]>>8)|(ram[addr]<<8))&0xffff; - #else - data=ram[addr]; - #endif + addr &= RAMADDMASK; + addr >>= 1; + data = CONV_RAMROM(ram[addr]); DO_DBUG(statusMsg << "read16(" << Base::HEX8 << addr << ")=" << Base::HEX4 << data << endl); - return(data); + return data; case 0xE0000000: //MAMCR if(addr == 0xE01FC000) @@ -310,187 +398,245 @@ uInt32 Thumbulator::read16 ( uInt32 addr ) } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt32 Thumbulator::read32 ( uInt32 addr ) +uInt32 Thumbulator::read32(uInt32 addr) { - if(addr&3) + if(addr & 3) fatalError("read32", addr, "abort - misaligned"); uInt32 data; - switch(addr&0xF0000000) + switch(addr & 0xF0000000) { case 0x00000000: //ROM case 0x40000000: //RAM - data =read16(addr+2); - data<<=16; - data|=read16(addr+0); + data = read16(addr+0); + data |= (uInt32(read16(addr+2))) << 16; DO_DBUG(statusMsg << "read32(" << Base::HEX8 << addr << ")=" << Base::HEX8 << data << endl); - return(data); + return data; + + case 0xE0000000: + { + switch(addr) + { + case 0xE0008004: // T1TCR - Timer 1 Control Register + data = T1TCR; + return data; + + case 0xE0008008: // T1TC - Timer 1 Counter + data = T1TC; + return data; + + case 0xE000E010: + data = systick_ctrl; + systick_ctrl &= (~0x00010000); + return data; + + case 0xE000E014: + data = systick_reload; + return data; + + case 0xE000E018: + data = systick_count; + return data; + + case 0xE000E01C: + data = systick_calibrate; + return data; + } + } } return fatalError("read32", addr, "abort"); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt32 Thumbulator::read_register ( uInt32 reg ) +uInt32 Thumbulator::read_register(uInt32 reg) { - reg&=0xF; + reg &= 0xF; - uInt32 data; - switch(cpsr&0x1F) + uInt32 data = reg_norm[reg]; + DO_DBUG(statusMsg << "read_register(" << dec << reg << ")=" << Base::HEX8 << data << endl); + if(reg == 15) { - case MODE_SVC: - switch(reg) // TODO (SA) - does this do anything other than default? - { - default: data=reg_sys[reg]; break; - case 13: case 14: data=reg_svc[reg]; break; - } - DO_DBUG(statusMsg << "read_register(" << dec << reg << ")=" << Base::HEX8 << data << endl); - return(data); + if(data & 1) + { + DO_DBUG(statusMsg << "pc has lsbit set 0x" << Base::HEX8 << data << endl); + } + data &= ~1; } - return fatalError("read_register", cpsr, "invalid cpsr mode"); + return data; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt32 Thumbulator::write_register ( uInt32 reg, uInt32 data ) +void Thumbulator::write_register(uInt32 reg, uInt32 data) { - reg&=0xF; + reg &= 0xF; DO_DBUG(statusMsg << "write_register(" << dec << reg << "," << Base::HEX8 << data << ")" << endl); - switch(cpsr&0x1F) - { - case MODE_SVC: - switch(reg) // TODO (SA) - does this do anything other than default? - { - default: reg_sys[reg]=data; break; - case 13: case 14: reg_svc[reg]=data; break; - } - return(data); - } - return fatalError("write_register", cpsr, "invalid cpsr mode"); + if(reg == 15) data &= ~1; + reg_norm[reg] = data; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void Thumbulator::do_zflag ( uInt32 x ) +void Thumbulator::do_zflag(uInt32 x) { - if(x==0) cpsr|=CPSR_Z; - else cpsr&=~CPSR_Z; + if(x == 0) cpsr |= CPSR_Z; else cpsr &= ~CPSR_Z; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void Thumbulator::do_nflag ( uInt32 x ) +void Thumbulator::do_nflag(uInt32 x) { - if(x&0x80000000) cpsr|=CPSR_N; - else cpsr&=~CPSR_N; + if(x & 0x80000000) cpsr|=CPSR_N; else cpsr&=~CPSR_N; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void Thumbulator::do_cflag ( uInt32 a, uInt32 b, uInt32 c ) +void Thumbulator::do_cflag(uInt32 a, uInt32 b, uInt32 c) { uInt32 rc; - cpsr&=~CPSR_C; - rc=(a&0x7FFFFFFF)+(b&0x7FFFFFFF)+c; //carry in - rc = (rc>>31)+(a>>31)+(b>>31); //carry out - if(rc&2) - cpsr|=CPSR_C; + cpsr &= ~CPSR_C; + rc = (a & 0x7FFFFFFF) + (b & 0x7FFFFFFF) + c; //carry in + rc = (rc >> 31) + (a >> 31) + (b >> 31); //carry out + if(rc & 2) + cpsr |= CPSR_C; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void Thumbulator::do_sub_vflag ( uInt32 a, uInt32 b, uInt32 c ) +void Thumbulator::do_vflag(uInt32 a, uInt32 b, uInt32 c) { - cpsr&=~CPSR_V; + uInt32 rc, rd; - //if the sign bits are different - if((a&0x80000000)^(b&0x80000000)) + cpsr &= ~CPSR_V; + rc = (a & 0x7FFFFFFF) + (b & 0x7FFFFFFF) + c; //carry in + rc >>= 31; //carry in in lsbit + rd = (rc & 1) + ((a >> 31) & 1) + ((b >> 31) & 1); //carry out + rd >>= 1; //carry out in lsbit + rc = (rc^rd) & 1; //if carry in != carry out then signed overflow + if(rc) + cpsr |= CPSR_V; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void Thumbulator::do_cflag_bit(uInt32 x) +{ + if(x) cpsr |= CPSR_C; else cpsr &= ~CPSR_C; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void Thumbulator::do_vflag_bit(uInt32 x) +{ + if(x) cpsr |= CPSR_V; else cpsr &= ~CPSR_V; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +int Thumbulator::execute() +{ + uInt32 pc, sp, inst, ra, rb, rc, rm, rd, rn, rs, op; + + pc = read_register(15); + +#if 0 + if(handler_mode) { - //and result matches b - if((b&0x80000000)==(c&0x80000000)) - cpsr|=CPSR_V; + if((pc & 0xF0000000) == 0xF0000000) + { + uInt32 sp = read_register(13); + handler_mode = false; + write_register(0, read32(sp)); sp += 4; + write_register(1, read32(sp)); sp += 4; + write_register(2, read32(sp)); sp += 4; + write_register(3, read32(sp)); sp += 4; + write_register(12, read32(sp)); sp += 4; + write_register(14, read32(sp)); sp += 4; + pc = read32(sp); sp += 4; + cpsr = read32(sp); sp += 4; + write_register(13, sp); + } } -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void Thumbulator::do_add_vflag ( uInt32 a, uInt32 b, uInt32 c ) -{ - cpsr&=~CPSR_V; - - //if sign bits are the same - if((a&0x80000000)==(b&0x80000000)) + if(systick_ctrl & 1) { - //and the result is different - if((b&0x80000000)!=(c&0x80000000)) - cpsr|=CPSR_V; + if(systick_count) + { + systick_count--; + } + else + { + systick_count = systick_reload; + systick_ctrl |= 0x00010000; + } } -} -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void Thumbulator::do_cflag_bit ( uInt32 x ) -{ - if(x) cpsr|=CPSR_C; - else cpsr&=~CPSR_C; -} + if((systick_ctrl & 3) == 3) + { + if(systick_ctrl & 0x00010000) + { + if(!handler_mode) + { + systick_ints++; + uInt32 sp = read_register(13); + sp -= 4; write32(sp, cpsr); + sp -= 4; write32(sp, pc); + sp -= 4; write32(sp, read_register(14)); + sp -= 4; write32(sp, read_register(12)); + sp -= 4; write32(sp, read_register(3)); + sp -= 4; write32(sp, read_register(2)); + sp -= 4; write32(sp, read_register(1)); + sp -= 4; write32(sp, read_register(0)); + write_register(13, sp); + pc = fetch32(0x0000003C); //systick vector + pc += 2; + //write_register(14, 0xFFFFFF00); + write_register(14, 0xFFFFFFF9); -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void Thumbulator::do_vflag_bit ( uInt32 x ) -{ - if(x) cpsr|=CPSR_V; - else cpsr&=~CPSR_V; -} + handler_mode = true; + } + } + } +#endif -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -int Thumbulator::execute ( void ) -{ - uInt32 pc, sp, inst, - ra,rb,rc, - rm,rd,rn,rs, - op; - - pc=read_register(15); - inst=fetch16(pc-2); - pc+=2; - write_register(15,pc); + inst = fetch16(pc-2); + pc += 2; + write_register(15, pc); DO_DISS(statusMsg << Base::HEX8 << (pc-5) << ": " << Base::HEX4 << inst << " "); - instructions++; + ++instructions; //ADC - if((inst&0xFFC0)==0x4140) + if((inst & 0xFFC0) == 0x4140) { - rd=(inst>>0)&0x07; - rm=(inst>>3)&0x07; + rd = (inst >> 0) & 0x07; + rm = (inst >> 3) & 0x07; DO_DISS(statusMsg << "adc r" << dec << rd << ",r" << dec << rm << endl); - ra=read_register(rd); - rb=read_register(rm); - rc=ra+rb; - if(cpsr&CPSR_C) - rc++; - write_register(rd,rc); + ra = read_register(rd); + rb = read_register(rm); + rc = ra + rb; + if(cpsr & CPSR_C) + ++rc; + write_register(rd, rc); do_nflag(rc); do_zflag(rc); - if(cpsr&CPSR_C) do_cflag(ra,rb,1); - else do_cflag(ra,rb,0); - do_add_vflag(ra,rb,rc); - return(0); + if(cpsr & CPSR_C) { do_cflag(ra, rb, 1); do_vflag(ra, rb, 1); } + else { do_cflag(ra, rb, 0); do_vflag(ra, rb, 0); } + return 0; } //ADD(1) small immediate two registers - if((inst&0xFE00)==0x1C00) + if((inst & 0xFE00) == 0x1C00) { - rd=(inst>>0)&0x7; - rn=(inst>>3)&0x7; - rb=(inst>>6)&0x7; + rd = (inst >> 0) & 0x7; + rn = (inst >> 3) & 0x7; + rb = (inst >> 6) & 0x7; if(rb) { DO_DISS(statusMsg << "adds r" << dec << rd << ",r" << dec << rn << "," << "#0x" << Base::HEX2 << rb << endl); - ra=read_register(rn); - rc=ra+rb; + ra = read_register(rn); + rc = ra + rb; //fprintf(stderr,"0x%08X = 0x%08X + 0x%08X\n",rc,ra,rb); - write_register(rd,rc); + write_register(rd, rc); do_nflag(rc); do_zflag(rc); - do_cflag(ra,rb,0); - do_add_vflag(ra,rb,rc); - return(0); + do_cflag(ra, rb, 0); + do_vflag(ra, rb, 0); + return 0; } else { @@ -499,326 +645,304 @@ int Thumbulator::execute ( void ) } //ADD(2) big immediate one register - if((inst&0xF800)==0x3000) + if((inst & 0xF800) == 0x3000) { - rb=(inst>>0)&0xFF; - rd=(inst>>8)&0x7; + rb = (inst >> 0) & 0xFF; + rd = (inst >> 8) & 0x7; DO_DISS(statusMsg << "adds r" << dec << rd << ",#0x" << Base::HEX2 << rb << endl); - ra=read_register(rd); - rc=ra+rb; - write_register(rd,rc); + ra = read_register(rd); + rc = ra + rb; + write_register(rd, rc); do_nflag(rc); do_zflag(rc); - do_cflag(ra,rb,0); - do_add_vflag(ra,-rb,rc); - return(0); + do_cflag(ra, rb, 0); + do_vflag(ra, rb, 0); + return 0; } //ADD(3) three registers - if((inst&0xFE00)==0x1800) + if((inst & 0xFE00) == 0x1800) { - rd=(inst>>0)&0x7; - rn=(inst>>3)&0x7; - rm=(inst>>6)&0x7; + rd = (inst >> 0) & 0x7; + rn = (inst >> 3) & 0x7; + rm = (inst >> 6) & 0x7; DO_DISS(statusMsg << "adds r" << dec << rd << ",r" << dec << rn << ",r" << rm << endl); - ra=read_register(rn); - rb=read_register(rm); - rc=ra+rb; - write_register(rd,rc); + ra = read_register(rn); + rb = read_register(rm); + rc = ra + rb; + write_register(rd, rc); do_nflag(rc); do_zflag(rc); - do_cflag(ra,rb,0); - do_add_vflag(ra,rb,rc); - return(0); + do_cflag(ra, rb, 0); + do_vflag(ra, rb, 0); + return 0; } //ADD(4) two registers one or both high no flags - if((inst&0xFF00)==0x4400) + if((inst & 0xFF00) == 0x4400) { - if((inst>>6)&3) + if((inst >> 6) & 3) { //UNPREDICTABLE } - rd=(inst>>0)&0x7; - rd|=(inst>>4)&0x8; - rm=(inst>>3)&0xF; + rd = (inst >> 0) & 0x7; + rd |= (inst >> 4) & 0x8; + rm = (inst >> 3) & 0xF; DO_DISS(statusMsg << "add r" << dec << rd << ",r" << dec << rm << endl); - ra=read_register(rd); - rb=read_register(rm); - rc=ra+rb; + ra = read_register(rd); + rb = read_register(rm); + rc = ra + rb; + if(rd == 15) + { + if((rc & 1) == 0) + fatalError("add pc", pc, rc, " produced an arm address"); + + rc &= ~1; //write_register may do this as well + rc += 2; //The program counter is special + } //fprintf(stderr,"0x%08X = 0x%08X + 0x%08X\n",rc,ra,rb); - write_register(rd,rc); - return(0); + write_register(rd, rc); + return 0; } //ADD(5) rd = pc plus immediate - if((inst&0xF800)==0xA000) + if((inst & 0xF800) == 0xA000) { - rb=(inst>>0)&0xFF; - rd=(inst>>8)&0x7; - rb<<=2; + rb = (inst >> 0) & 0xFF; + rd = (inst >> 8) & 0x7; + rb <<= 2; DO_DISS(statusMsg << "add r" << dec << rd << ",PC,#0x" << Base::HEX2 << rb << endl); - ra=read_register(15); - rc=(ra&(~3))+rb; - write_register(rd,rc); - return(0); + ra = read_register(15); + rc = (ra & (~3u)) + rb; + write_register(rd, rc); + return 0; } //ADD(6) rd = sp plus immediate - if((inst&0xF800)==0xA800) + if((inst & 0xF800) == 0xA800) { - rb=(inst>>0)&0xFF; - rd=(inst>>8)&0x7; - rb<<=2; + rb = (inst >> 0) & 0xFF; + rd = (inst >> 8) & 0x7; + rb <<= 2; DO_DISS(statusMsg << "add r" << dec << rd << ",SP,#0x" << Base::HEX2 << rb << endl); - ra=read_register(13); - rc=ra+rb; - write_register(rd,rc); - return(0); + ra = read_register(13); + rc = ra + rb; + write_register(rd, rc); + return 0; } //ADD(7) sp plus immediate - if((inst&0xFF80)==0xB000) + if((inst & 0xFF80) == 0xB000) { - rb=(inst>>0)&0x7F; - rb<<=2; + rb = (inst >> 0) & 0x7F; + rb <<= 2; DO_DISS(statusMsg << "add SP,#0x" << Base::HEX2 << rb << endl); - ra=read_register(13); - rc=ra+rb; - write_register(13,rc); - return(0); + ra = read_register(13); + rc = ra + rb; + write_register(13, rc); + return 0; } //AND - if((inst&0xFFC0)==0x4000) + if((inst & 0xFFC0) == 0x4000) { - rd=(inst>>0)&0x7; - rm=(inst>>3)&0x7; + rd = (inst >> 0) & 0x7; + rm = (inst >> 3) & 0x7; DO_DISS(statusMsg << "ands r" << dec << rd << ",r" << dec << rm << endl); - ra=read_register(rd); - rb=read_register(rm); - rc=ra&rb; - write_register(rd,rc); + ra = read_register(rd); + rb = read_register(rm); + rc = ra & rb; + write_register(rd, rc); do_nflag(rc); do_zflag(rc); - return(0); + return 0; } //ASR(1) two register immediate - if((inst&0xF800)==0x1000) + if((inst & 0xF800) == 0x1000) { - rd=(inst>>0)&0x07; - rm=(inst>>3)&0x07; - rb=(inst>>6)&0x1F; + rd = (inst >> 0) & 0x07; + rm = (inst >> 3) & 0x07; + rb = (inst >> 6) & 0x1F; DO_DISS(statusMsg << "asrs r" << dec << rd << ",r" << dec << rm << ",#0x" << Base::HEX2 << rb << endl); - rc=read_register(rm); - if(rb==0) + rc = read_register(rm); + if(rb == 0) { - if(rc&0x80000000) + if(rc & 0x80000000) { do_cflag_bit(1); - rc=~0; + rc = ~0u; } else { do_cflag_bit(0); - rc=0; + rc = 0; } } else { - do_cflag_bit(rc&(1<<(rb-1))); - ra=rc&0x80000000; - rc>>=rb; + do_cflag_bit(rc & (1 << (rb-1))); + ra = rc & 0x80000000; + rc >>= rb; if(ra) //asr, sign is shifted in - { - rc|=(~0)<<(32-rb); - } + rc |= (~0u) << (32-rb); } - write_register(rd,rc); + write_register(rd, rc); do_nflag(rc); do_zflag(rc); - return(0); + return 0; } //ASR(2) two register - if((inst&0xFFC0)==0x4100) + if((inst & 0xFFC0) == 0x4100) { - rd=(inst>>0)&0x07; - rs=(inst>>3)&0x07; + rd = (inst >> 0) & 0x07; + rs = (inst >> 3) & 0x07; DO_DISS(statusMsg << "asrs r" << dec << rd << ",r" << dec << rs << endl); - rc=read_register(rd); - rb=read_register(rs); - rb&=0xFF; - if(rb==0) + rc = read_register(rd); + rb = read_register(rs); + rb &= 0xFF; + if(rb == 0) { } - else if(rb<32) + else if(rb < 32) { - do_cflag_bit(rc&(1<<(rb-1))); - ra=rc&0x80000000; - rc>>=rb; + do_cflag_bit(rc & (1 << (rb-1))); + ra = rc & 0x80000000; + rc >>= rb; if(ra) //asr, sign is shifted in { - rc|=(~0)<<(32-rb); + rc |= (~0u) << (32-rb); } } else { - if(rc&0x80000000) + if(rc & 0x80000000) { do_cflag_bit(1); - rc=(~0); + rc = (~0u); } else { do_cflag_bit(0); - rc=0; + rc = 0; } } - write_register(rd,rc); + write_register(rd, rc); do_nflag(rc); do_zflag(rc); - return(0); + return 0; } //B(1) conditional branch - if((inst&0xF000)==0xD000) + if((inst & 0xF000) == 0xD000) { - rb=(inst>>0)&0xFF; - if(rb&0x80) - rb|=(~0)<<8; - op=(inst>>8)&0xF; - rb<<=1; - rb+=pc; - rb+=2; + rb = (inst >> 0) & 0xFF; + if(rb & 0x80) + rb |= (~0u) << 8; + op=(inst >> 8) & 0xF; + rb <<= 1; + rb += pc; + rb += 2; switch(op) { case 0x0: //b eq z set DO_DISS(statusMsg << "beq 0x" << Base::HEX8 << (rb-3) << endl); - if(cpsr&CPSR_Z) - { - write_register(15,rb); - } - return(0); + if(cpsr & CPSR_Z) + write_register(15, rb); + return 0; case 0x1: //b ne z clear DO_DISS(statusMsg << "bne 0x" << Base::HEX8 << (rb-3) << endl); - if(!(cpsr&CPSR_Z)) - { - write_register(15,rb); - } - return(0); + if(!(cpsr & CPSR_Z)) + write_register(15, rb); + return 0; case 0x2: //b cs c set DO_DISS(statusMsg << "bcs 0x" << Base::HEX8 << (rb-3) << endl); - if(cpsr&CPSR_C) - { - write_register(15,rb); - } - return(0); + if(cpsr & CPSR_C) + write_register(15, rb); + return 0; case 0x3: //b cc c clear DO_DISS(statusMsg << "bcc 0x" << Base::HEX8 << (rb-3) << endl); - if(!(cpsr&CPSR_C)) - { - write_register(15,rb); - } - return(0); + if(!(cpsr & CPSR_C)) + write_register(15, rb); + return 0; case 0x4: //b mi n set DO_DISS(statusMsg << "bmi 0x" << Base::HEX8 << (rb-3) << endl); - if(cpsr&CPSR_N) - { - write_register(15,rb); - } - return(0); + if(cpsr & CPSR_N) + write_register(15, rb); + return 0; case 0x5: //b pl n clear DO_DISS(statusMsg << "bpl 0x" << Base::HEX8 << (rb-3) << endl); - if(!(cpsr&CPSR_N)) - { - write_register(15,rb); - } - return(0); + if(!(cpsr & CPSR_N)) + write_register(15, rb); + return 0; case 0x6: //b vs v set DO_DISS(statusMsg << "bvs 0x" << Base::HEX8 << (rb-3) << endl); - if(cpsr&CPSR_V) - { + if(cpsr & CPSR_V) write_register(15,rb); - } - return(0); + return 0; case 0x7: //b vc v clear DO_DISS(statusMsg << "bvc 0x" << Base::HEX8 << (rb-3) << endl); - if(!(cpsr&CPSR_V)) - { - write_register(15,rb); - } - return(0); + if(!(cpsr & CPSR_V)) + write_register(15, rb); + return 0; case 0x8: //b hi c set z clear DO_DISS(statusMsg << "bhi 0x" << Base::HEX8 << (rb-3) << endl); - if((cpsr&CPSR_C)&&(!(cpsr&CPSR_Z))) - { - write_register(15,rb); - } - return(0); + if((cpsr & CPSR_C) && (!(cpsr & CPSR_Z))) + write_register(15, rb); + return 0; case 0x9: //b ls c clear or z set DO_DISS(statusMsg << "bls 0x" << Base::HEX8 << (rb-3) << endl); - if((cpsr&CPSR_Z)||(!(cpsr&CPSR_C))) - { - write_register(15,rb); - } - return(0); + if((cpsr & CPSR_Z) || (!(cpsr & CPSR_C))) + write_register(15, rb); + return 0; case 0xA: //b ge N == V DO_DISS(statusMsg << "bge 0x" << Base::HEX8 << (rb-3) << endl); - ra=0; - if( (cpsr&CPSR_N) && (cpsr&CPSR_V) ) ra++; - if((!(cpsr&CPSR_N))&&(!(cpsr&CPSR_V))) ra++; + ra = 0; + if( (cpsr & CPSR_N) && (cpsr & CPSR_V) ) ++ra; + if((!(cpsr & CPSR_N)) && (!(cpsr & CPSR_V))) ++ra; if(ra) - { - write_register(15,rb); - } - return(0); + write_register(15, rb); + return 0; case 0xB: //b lt N != V DO_DISS(statusMsg << "blt 0x" << Base::HEX8 << (rb-3) << endl); - ra=0; - if((!(cpsr&CPSR_N))&&(cpsr&CPSR_V)) ra++; - if((!(cpsr&CPSR_V))&&(cpsr&CPSR_N)) ra++; + ra = 0; + if((!(cpsr & CPSR_N)) && (cpsr & CPSR_V)) ++ra; + if((!(cpsr & CPSR_V)) && (cpsr & CPSR_N)) ++ra; if(ra) - { - write_register(15,rb); - } - return(0); + write_register(15, rb); + return 0; case 0xC: //b gt Z==0 and N == V DO_DISS(statusMsg << "bgt 0x" << Base::HEX8 << (rb-3) << endl); - ra=0; - if( (cpsr&CPSR_N) && (cpsr&CPSR_V) ) ra++; - if((!(cpsr&CPSR_N))&&(!(cpsr&CPSR_V))) ra++; - if(cpsr&CPSR_Z) ra=0; + ra = 0; + if( (cpsr & CPSR_N) && (cpsr & CPSR_V) ) ++ra; + if((!(cpsr & CPSR_N)) && (!(cpsr & CPSR_V))) ++ra; + if(cpsr & CPSR_Z) ra = 0; if(ra) - { - write_register(15,rb); - } - return(0); + write_register(15, rb); + return 0; case 0xD: //b le Z==1 or N != V DO_DISS(statusMsg << "ble 0x" << Base::HEX8 << (rb-3) << endl); - ra=0; - if((!(cpsr&CPSR_N))&&(cpsr&CPSR_V)) ra++; - if((!(cpsr&CPSR_V))&&(cpsr&CPSR_N)) ra++; - if(cpsr&CPSR_Z) ra++; + ra = 0; + if((!(cpsr & CPSR_N)) && (cpsr & CPSR_V)) ++ra; + if((!(cpsr & CPSR_V)) && (cpsr & CPSR_N)) ++ra; + if(cpsr & CPSR_Z) ++ra; if(ra) - { - write_register(15,rb); - } - return(0); + write_register(15, rb); + return 0; case 0xE: //undefined instruction @@ -831,1150 +955,1407 @@ int Thumbulator::execute ( void ) } //B(2) unconditional branch - if((inst&0xF800)==0xE000) + if((inst & 0xF800) == 0xE000) { - rb=(inst>>0)&0x7FF; - if(rb&(1<<10)) - rb|=(~0)<<11; - rb<<=1; - rb+=pc; - rb+=2; + rb = (inst >> 0) & 0x7FF; + if(rb & (1 << 10)) + rb |= (~0u) << 11; + rb <<= 1; + rb += pc; + rb += 2; DO_DISS(statusMsg << "B 0x" << Base::HEX8 << (rb-3) << endl); - write_register(15,rb); - return(0); + write_register(15, rb); + return 0; } //BIC - if((inst&0xFFC0)==0x4380) + if((inst & 0xFFC0) == 0x4380) { - rd=(inst>>0)&0x7; - rm=(inst>>3)&0x7; + rd = (inst >> 0) & 0x7; + rm = (inst >> 3) & 0x7; DO_DISS(statusMsg << "bics r" << dec << rd << ",r" << dec << rm << endl); - ra=read_register(rd); - rb=read_register(rm); - rc=ra&(~rb); - write_register(rd,rc); + ra = read_register(rd); + rb = read_register(rm); + rc = ra & (~rb); + write_register(rd, rc); do_nflag(rc); do_zflag(rc); - return(0); + return 0; } //BKPT - if((inst&0xFF00)==0xBE00) + if((inst & 0xFF00) == 0xBE00) { - rb=(inst>>0)&0xFF; + rb = (inst >> 0) & 0xFF; statusMsg << "bkpt 0x" << Base::HEX2 << rb << endl; - return(1); + return 1; } //BL/BLX(1) - if((inst&0xE000)==0xE000) //BL,BLX + if((inst & 0xE000) == 0xE000) //BL,BLX { - if((inst&0x1800)==0x1000) //H=b10 + if((inst & 0x1800) == 0x1000) //H=b10 { DO_DISS(statusMsg << endl); - halfadd=inst; - return(0); + rb = inst & ((1 << 11) - 1); + if(rb & 1<<10) rb |= (~((1 << 11) - 1)); //sign extend + rb <<= 12; + rb += pc; + write_register(14, rb); + return 0; } - else if((inst&0x1800)==0x1800) //H=b11 + else if((inst & 0x1800) == 0x1800) //H=b11 { //branch to thumb - rb=halfadd&((1<<11)-1); - if(rb&1<<10) - rb|=(~((1<<11)-1)); //sign extend - rb<<=11; - rb|=inst&((1<<11)-1); - rb<<=1; - rb+=pc; + rb = read_register(14); + rb += (inst & ((1 << 11) - 1)) << 1;; + rb += 2; DO_DISS(statusMsg << "bl 0x" << Base::HEX8 << (rb-3) << endl); - write_register(14,pc-2); - write_register(15,rb); - return(0); + write_register(14, (pc-2) | 1); + write_register(15, rb); + return 0; } - else if((inst&0x1800)==0x0800) //H=b01 + else if((inst & 0x1800) == 0x0800) //H=b01 { //fprintf(stderr,"cannot branch to arm 0x%08X 0x%04X\n",pc,inst); // fxq: this should exit the code without having to detect it - return(1); + rb = read_register(14); + rb += (inst & ((1 << 11) - 1)) << 1;; + rb &= 0xFFFFFFFC; + rb += 2; + DO_DISS(statusMsg << "bl 0x" << Base::HEX8 << (rb-3) << endl); + write_register(14, (pc-2) | 1); + write_register(15, rb); + return 0; } } //BLX(2) - if((inst&0xFF87)==0x4780) + if((inst & 0xFF87) == 0x4780) { - rm=(inst>>3)&0xF; + rm = (inst >> 3) & 0xF; DO_DISS(statusMsg << "blx r" << dec << rm << endl); - rc=read_register(rm); + rc = read_register(rm); //fprintf(stderr,"blx r%u 0x%X 0x%X\n",rm,rc,pc); - rc+=2; - if(rc&1) + rc += 2; + if(rc & 1) { - write_register(14,pc-2); - write_register(15,rc); - return(0); + write_register(14, (pc-2) | 1); + rc &= ~1; + write_register(15, rc); + return 0; } else { //fprintf(stderr,"cannot branch to arm 0x%08X 0x%04X\n",pc,inst); // fxq: this could serve as exit code - return(1); + return 1; } } //BX - if((inst&0xFF87)==0x4700) + if((inst & 0xFF87) == 0x4700) { - rm=(inst>>3)&0xF; + rm = (inst >> 3) & 0xF; DO_DISS(statusMsg << "bx r" << dec << rm << endl); - rc=read_register(rm); - rc+=2; + rc = read_register(rm); + rc += 2; //fprintf(stderr,"bx r%u 0x%X 0x%X\n",rm,rc,pc); - if(rc&1) + if(rc & 1) { - write_register(15,rc); - return(0); + // branch to odd address denotes 16 bit ARM code + rc &= ~1; + write_register(15, rc); + return 0; } else { - //fprintf(stderr,"cannot branch to arm 0x%08X 0x%04X\n",pc,inst); - // fxq: or maybe this one?? - return(1); + // branch to even address denotes 32 bit ARM code, which the Thumbulator + // class does not support. So capture relavent information and hand it + // off to the Cartridge class for it to handle. + + bool handled = false; + + switch(configuration) + { + case ConfigureFor::BUS: + // this subroutine interface is used in the BUS driver, + // it starts at address 0x000006d8 + // _SetNote: + // ldr r4, =NoteStore + // bx r4 // bx instruction at 0x000006da + // _ResetWave: + // ldr r4, =ResetWaveStore + // bx r4 // bx instruction at 0x000006de + // _GetWavePtr: + // ldr r4, =WavePtrFetch + // bx r4 // bx instruction at 0x000006e2 + // _SetWaveSize: + // ldr r4, =WaveSizeStore + // bx r4 // bx instruction at 0x000006e6 + + // address to test for is + 4 due to pipelining + +#define BUS_SetNote (0x000006da + 4) +#define BUS_ResetWave (0x000006de + 4) +#define BUS_GetWavePtr (0x000006e2 + 4) +#define BUS_SetWaveSize (0x000006e6 + 4) + + if (pc == BUS_SetNote) + { + myCartridge->thumbCallback(0, read_register(2), read_register(3)); + handled = true; + } + else if (pc == BUS_ResetWave) + { + myCartridge->thumbCallback(1, read_register(2), 0); + handled = true; + } + else if (pc == BUS_GetWavePtr) + { + write_register(2, myCartridge->thumbCallback(2, read_register(2), 0)); + handled = true; + } + else if (pc == BUS_SetWaveSize) + { + myCartridge->thumbCallback(3, read_register(2), read_register(3)); + handled = true; + } + else if (pc == 0x0000083a) + { + // exiting Custom ARM code, returning to BUS Driver control + } + else + { +#if 0 // uncomment this for testing + uInt32 r0 = read_register(0); + uInt32 r1 = read_register(1); + uInt32 r2 = read_register(2); + uInt32 r3 = read_register(3); + uInt32 r4 = read_register(4); +#endif + myCartridge->thumbCallback(255, 0, 0); + } + + break; + + case ConfigureFor::CDF: + // this subroutine interface is used in the CDF driver, + // it starts at address 0x000006e0 + // _SetNote: + // ldr r4, =NoteStore + // bx r4 // bx instruction at 0x000006e2 + // _ResetWave: + // ldr r4, =ResetWaveStore + // bx r4 // bx instruction at 0x000006e6 + // _GetWavePtr: + // ldr r4, =WavePtrFetch + // bx r4 // bx instruction at 0x000006ea + // _SetWaveSize: + // ldr r4, =WaveSizeStore + // bx r4 // bx instruction at 0x000006ee + + // address to test for is + 4 due to pipelining + + #define CDF_SetNote (0x000006e2 + 4) + #define CDF_ResetWave (0x000006e6 + 4) + #define CDF_GetWavePtr (0x000006ea + 4) + #define CDF_SetWaveSize (0x000006ee + 4) + + if (pc == CDF_SetNote) + { + myCartridge->thumbCallback(0, read_register(2), read_register(3)); + handled = true; + } + else if (pc == CDF_ResetWave) + { + myCartridge->thumbCallback(1, read_register(2), 0); + handled = true; + } + else if (pc == CDF_GetWavePtr) + { + write_register(2, myCartridge->thumbCallback(2, read_register(2), 0)); + handled = true; + } + else if (pc == CDF_SetWaveSize) + { + myCartridge->thumbCallback(3, read_register(2), read_register(3)); + handled = true; + } + else if (pc == 0x0000083a) + { + // exiting Custom ARM code, returning to BUS Driver control + } + else + { + #if 0 // uncomment this for testing + uInt32 r0 = read_register(0); + uInt32 r1 = read_register(1); + uInt32 r2 = read_register(2); + uInt32 r3 = read_register(3); + uInt32 r4 = read_register(4); + #endif + myCartridge->thumbCallback(255, 0, 0); + } + + break; + + case ConfigureFor::CDF1: + // this subroutine interface is used in the CDF driver, + // it starts at address 0x00000750 + // _SetNote: + // ldr r4, =NoteStore + // bx r4 // bx instruction at 0x000006e2 + // _ResetWave: + // ldr r4, =ResetWaveStore + // bx r4 // bx instruction at 0x000006e6 + // _GetWavePtr: + // ldr r4, =WavePtrFetch + // bx r4 // bx instruction at 0x000006ea + // _SetWaveSize: + // ldr r4, =WaveSizeStore + // bx r4 // bx instruction at 0x000006ee + + // address to test for is + 4 due to pipelining + +#define CDF1_SetNote (0x00000752 + 4) +#define CDF1_ResetWave (0x00000756 + 4) +#define CDF1_GetWavePtr (0x0000075a + 4) +#define CDF1_SetWaveSize (0x0000075e + 4) + + if (pc == CDF1_SetNote) + { + myCartridge->thumbCallback(0, read_register(2), read_register(3)); + handled = true; + } + else if (pc == CDF1_ResetWave) + { + myCartridge->thumbCallback(1, read_register(2), 0); + handled = true; + } + else if (pc == CDF1_GetWavePtr) + { + write_register(2, myCartridge->thumbCallback(2, read_register(2), 0)); + handled = true; + } + else if (pc == CDF1_SetWaveSize) + { + myCartridge->thumbCallback(3, read_register(2), read_register(3)); + handled = true; + } + else if (pc == 0x0000083a) + { + // exiting Custom ARM code, returning to BUS Driver control + } + else + { +#if 0 // uncomment this for testing + uInt32 r0 = read_register(0); + uInt32 r1 = read_register(1); + uInt32 r2 = read_register(2); + uInt32 r3 = read_register(3); + uInt32 r4 = read_register(4); +#endif + myCartridge->thumbCallback(255, 0, 0); + } + + break; + + case ConfigureFor::DPCplus: + // no 32-bit subroutines in DPC+ + break; + } + + if (handled) + { + rc = read_register(14); // lr + rc += 2; + rc &= ~1; + write_register(15, rc); + return 0; + } + + return 1; } } //CMN - if((inst&0xFFC0)==0x42C0) + if((inst & 0xFFC0) == 0x42C0) { - rn=(inst>>0)&0x7; - rm=(inst>>3)&0x7; + rn = (inst >> 0) & 0x7; + rm = (inst >> 3) & 0x7; DO_DISS(statusMsg << "cmns r" << dec << rn << ",r" << dec << rm << endl); - ra=read_register(rn); - rb=read_register(rm); - rc=ra+rb; + ra = read_register(rn); + rb = read_register(rm); + rc = ra + rb; do_nflag(rc); do_zflag(rc); - do_cflag(ra,rb,0); - do_add_vflag(ra,rb,rc); - return(0); + do_cflag(ra, rb, 0); + do_vflag(ra, rb, 0); + return 0; } //CMP(1) compare immediate - if((inst&0xF800)==0x2800) + if((inst & 0xF800) == 0x2800) { - rb=(inst>>0)&0xFF; - rn=(inst>>8)&0x07; + rb = (inst >> 0) & 0xFF; + rn = (inst >> 8) & 0x07; DO_DISS(statusMsg << "cmp r" << dec << rn << ",#0x" << Base::HEX2 << rb << endl); - ra=read_register(rn); - rc=ra-rb; + ra = read_register(rn); + rc = ra - rb; //fprintf(stderr,"0x%08X 0x%08X\n",ra,rb); do_nflag(rc); do_zflag(rc); - do_cflag(ra,~rb,1); - do_sub_vflag(ra,rb,rc); - return(0); + do_cflag(ra, ~rb, 1); + do_vflag(ra, ~rb, 1); + return 0; } //CMP(2) compare register - if((inst&0xFFC0)==0x4280) + if((inst & 0xFFC0) == 0x4280) { - rn=(inst>>0)&0x7; - rm=(inst>>3)&0x7; + rn = (inst >> 0) & 0x7; + rm = (inst >> 3) & 0x7; DO_DISS(statusMsg << "cmps r" << dec << rn << ",r" << dec << rm << endl); - ra=read_register(rn); - rb=read_register(rm); - rc=ra-rb; + ra = read_register(rn); + rb = read_register(rm); + rc = ra - rb; //fprintf(stderr,"0x%08X 0x%08X\n",ra,rb); do_nflag(rc); do_zflag(rc); - do_cflag(ra,~rb,1); - do_sub_vflag(ra,rb,rc); - return(0); + do_cflag(ra, ~rb, 1); + do_vflag(ra, ~rb, 1); + return 0; } //CMP(3) compare high register - if((inst&0xFF00)==0x4500) + if((inst & 0xFF00) == 0x4500) { - if(((inst>>6)&3)==0x0) + if(((inst >> 6) & 3) == 0x0) { //UNPREDICTABLE } - rn=(inst>>0)&0x7; - rn|=(inst>>4)&0x8; - if(rn==0xF) + rn = (inst >> 0) & 0x7; + rn |= (inst >> 4) & 0x8; + if(rn == 0xF) { //UNPREDICTABLE } - rm=(inst>>3)&0xF; + rm = (inst >> 3) & 0xF; DO_DISS(statusMsg << "cmps r" << dec << rn << ",r" << dec << rm << endl); - ra=read_register(rn); - rb=read_register(rm); - rc=ra-rb; + ra = read_register(rn); + rb = read_register(rm); + rc = ra - rb; do_nflag(rc); do_zflag(rc); - do_cflag(ra,~rb,1); - do_sub_vflag(ra,rb,rc); - -#if 0 - if(cpsr&CPSR_N) statusMsg << "N"; else statusMsg << "n"; - if(cpsr&CPSR_Z) statusMsg << "Z"; else statusMsg << "z"; - if(cpsr&CPSR_C) statusMsg << "C"; else statusMsg << "c"; - if(cpsr&CPSR_V) statusMsg << "V"; else statusMsg << "v"; - statusMsg << " -- 0x" << Base::HEX8 << ra << " 0x" << Base::HEX8 << rb << endl; -#endif - return(0); + do_cflag(ra, ~rb, 1); + do_vflag(ra, ~rb, 1); + return 0; } //CPS - if((inst&0xFFE8)==0xB660) + if((inst & 0xFFE8) == 0xB660) { DO_DISS(statusMsg << "cps TODO" << endl); - return(1); + return 1; } //CPY copy high register - if((inst&0xFFC0)==0x4600) + if((inst & 0xFFC0) == 0x4600) { //same as mov except you can use both low registers //going to let mov handle high registers - rd=(inst>>0)&0x7; - rm=(inst>>3)&0x7; + rd = (inst >> 0) & 0x7; + rm = (inst >> 3) & 0x7; DO_DISS(statusMsg << "cpy r" << dec << rd << ",r" << dec << rm << endl); - rc=read_register(rm); - write_register(rd,rc); - return(0); + rc = read_register(rm); + write_register(rd, rc); + return 0; } //EOR - if((inst&0xFFC0)==0x4040) + if((inst & 0xFFC0) == 0x4040) { - rd=(inst>>0)&0x7; - rm=(inst>>3)&0x7; + rd = (inst >> 0) & 0x7; + rm = (inst >> 3) & 0x7; DO_DISS(statusMsg << "eors r" << dec << rd << ",r" << dec << rm << endl); - ra=read_register(rd); - rb=read_register(rm); - rc=ra^rb; - write_register(rd,rc); + ra = read_register(rd); + rb = read_register(rm); + rc = ra ^ rb; + write_register(rd, rc); do_nflag(rc); do_zflag(rc); - return(0); + return 0; } //LDMIA - if((inst&0xF800)==0xC800) + if((inst & 0xF800) == 0xC800) { - rn=(inst>>8)&0x7; + rn = (inst >> 8) & 0x7; #if defined(THUMB_DISS) - statusMsg << "ldmia r" << dec << rn << "!,{"; - for(ra=0,rb=0x01,rc=0;rb;rb=(rb<<1)&0xFF,ra++) - { - if(inst&rb) - { - if(rc) statusMsg << ","; - statusMsg << "r" << dec << ra; - rc++; - } - } - statusMsg << "}" << endl; - #endif - sp=read_register(rn); - for(ra=0,rb=0x01;rb;rb=(rb<<1)&0xFF,ra++) + statusMsg << "ldmia r" << dec << rn << "!,{"; + for(ra=0,rb=0x01,rc=0;rb;rb=(rb<<1)&0xFF,++ra) { if(inst&rb) { - write_register(ra,read32(sp)); - sp+=4; + if(rc) statusMsg << ","; + statusMsg << "r" << dec << ra; + rc++; } } - write_register(rn,sp); - return(0); + statusMsg << "}" << endl; + #endif + sp = read_register(rn); + for(ra = 0, rb = 0x01; rb; rb = (rb << 1) & 0xFF, ++ra) + { + if(inst & rb) + { + write_register(ra, read32(sp)); + sp += 4; + } + } + //there is a write back exception. + if((inst & (1 << rn)) == 0) + write_register(rn, sp); + + return 0; } //LDR(1) two register immediate - if((inst&0xF800)==0x6800) + if((inst & 0xF800) == 0x6800) { - rd=(inst>>0)&0x07; - rn=(inst>>3)&0x07; - rb=(inst>>6)&0x1F; - rb<<=2; + rd = (inst >> 0) & 0x07; + rn = (inst >> 3) & 0x07; + rb = (inst >> 6) & 0x1F; + rb <<= 2; DO_DISS(statusMsg << "ldr r" << dec << rd << ",[r" << dec << rn << ",#0x" << Base::HEX2 << rb << "]" << endl); - rb=read_register(rn)+rb; - rc=read32(rb); - write_register(rd,rc); - return(0); + rb = read_register(rn) + rb; + rc = read32(rb); + write_register(rd, rc); + return 0; } //LDR(2) three register - if((inst&0xFE00)==0x5800) + if((inst & 0xFE00) == 0x5800) { - rd=(inst>>0)&0x7; - rn=(inst>>3)&0x7; - rm=(inst>>6)&0x7; + rd = (inst >> 0) & 0x7; + rn = (inst >> 3) & 0x7; + rm = (inst >> 6) & 0x7; DO_DISS(statusMsg << "ldr r" << dec << rd << ",[r" << dec << rn << ",r" << dec << "]" << endl); - rb=read_register(rn)+read_register(rm); - rc=read32(rb); - write_register(rd,rc); - return(0); + rb = read_register(rn) + read_register(rm); + rc = read32(rb); + write_register(rd, rc); + return 0; } //LDR(3) - if((inst&0xF800)==0x4800) + if((inst & 0xF800) == 0x4800) { - rb=(inst>>0)&0xFF; - rd=(inst>>8)&0x07; - rb<<=2; + rb = (inst >> 0) & 0xFF; + rd = (inst >> 8) & 0x07; + rb <<= 2; DO_DISS(statusMsg << "ldr r" << dec << rd << ",[PC+#0x" << Base::HEX2 << rb << "] "); - ra=read_register(15); - ra&=~3; - rb+=ra; + ra = read_register(15); + ra &= ~3; + rb += ra; DO_DISS(statusMsg << ";@ 0x" << Base::HEX2 << rb << endl); - rc=read32(rb); - write_register(rd,rc); - return(0); + rc = read32(rb); + write_register(rd, rc); + return 0; } //LDR(4) - if((inst&0xF800)==0x9800) + if((inst & 0xF800) == 0x9800) { - rb=(inst>>0)&0xFF; - rd=(inst>>8)&0x07; - rb<<=2; + rb = (inst >> 0) & 0xFF; + rd = (inst >> 8) & 0x07; + rb <<= 2; DO_DISS(statusMsg << "ldr r" << dec << rd << ",[SP+#0x" << Base::HEX2 << rb << "]" << endl); - ra=read_register(13); + ra = read_register(13); //ra&=~3; - rb+=ra; - rc=read32(rb); - write_register(rd,rc); - return(0); + rb += ra; + rc = read32(rb); + write_register(rd, rc); + return 0; } //LDRB(1) - if((inst&0xF800)==0x7800) + if((inst & 0xF800) == 0x7800) { - rd=(inst>>0)&0x07; - rn=(inst>>3)&0x07; - rb=(inst>>6)&0x1F; + rd = (inst >> 0) & 0x07; + rn = (inst >> 3) & 0x07; + rb = (inst >> 6) & 0x1F; DO_DISS(statusMsg << "ldrb r" << dec << rd << ",[r" << dec << rn << ",#0x" << Base::HEX2 << rb << "]" << endl); - rb=read_register(rn)+rb; - rc=read16(rb&(~1)); - if(rb&1) + rb = read_register(rn) + rb; + rc = read16(rb & (~1u)); + if(rb & 1) { - rc>>=8; + rc >>= 8; } else { } - write_register(rd,rc&0xFF); - return(0); + write_register(rd, rc & 0xFF); + return 0; } //LDRB(2) - if((inst&0xFE00)==0x5C00) + if((inst & 0xFE00) == 0x5C00) { - rd=(inst>>0)&0x7; - rn=(inst>>3)&0x7; - rm=(inst>>6)&0x7; + rd = (inst >> 0) & 0x7; + rn = (inst >> 3) & 0x7; + rm = (inst >> 6) & 0x7; DO_DISS(statusMsg << "ldrb r" << dec << rd << ",[r" << dec << rn << ",r" << dec << rm << "]" << endl); - rb=read_register(rn)+read_register(rm); - rc=read16(rb&(~1)); - if(rb&1) + rb = read_register(rn) + read_register(rm); + rc = read16(rb & (~1u)); + if(rb & 1) { - rc>>=8; + rc >>= 8; } else { } - write_register(rd,rc&0xFF); - return(0); + write_register(rd, rc & 0xFF); + return 0; } //LDRH(1) - if((inst&0xF800)==0x8800) + if((inst & 0xF800) == 0x8800) { - rd=(inst>>0)&0x07; - rn=(inst>>3)&0x07; - rb=(inst>>6)&0x1F; - rb<<=1; + rd = (inst >> 0) & 0x07; + rn = (inst >> 3) & 0x07; + rb = (inst >> 6) & 0x1F; + rb <<= 1; DO_DISS(statusMsg << "ldrh r" << dec << rd << ",[r" << dec << rn << ",#0x" << Base::HEX2 << rb << "]" << endl); - rb=read_register(rn)+rb; - rc=read16(rb); - write_register(rd,rc&0xFFFF); - return(0); + rb=read_register(rn) + rb; + rc = read16(rb); + write_register(rd, rc & 0xFFFF); + return 0; } //LDRH(2) - if((inst&0xFE00)==0x5A00) + if((inst & 0xFE00) == 0x5A00) { - rd=(inst>>0)&0x7; - rn=(inst>>3)&0x7; - rm=(inst>>6)&0x7; + rd = (inst >> 0) & 0x7; + rn = (inst >> 3) & 0x7; + rm = (inst >> 6) & 0x7; DO_DISS(statusMsg << "ldrh r" << dec << rd << ",[r" << dec << rn << ",r" << dec << rm << "]" << endl); - rb=read_register(rn)+read_register(rm); - rc=read16(rb); - write_register(rd,rc&0xFFFF); - return(0); + rb = read_register(rn) + read_register(rm); + rc = read16(rb); + write_register(rd, rc & 0xFFFF); + return 0; } //LDRSB - if((inst&0xFE00)==0x5600) + if((inst & 0xFE00) == 0x5600) { - rd=(inst>>0)&0x7; - rn=(inst>>3)&0x7; - rm=(inst>>6)&0x7; + rd = (inst >> 0) & 0x7; + rn = (inst >> 3) & 0x7; + rm = (inst >> 6) & 0x7; DO_DISS(statusMsg << "ldrsb r" << dec << rd << ",[r" << dec << rn << ",r" << dec << rm << "]" << endl); - rb=read_register(rn)+read_register(rm); - rc=read16(rb&(~1)); - if(rb&1) + rb = read_register(rn) + read_register(rm); + rc = read16(rb & (~1u)); + if(rb & 1) { - rc>>=8; + rc >>= 8; } else { } - rc&=0xFF; - if(rc&0x80) rc|=((~0)<<8); - write_register(rd,rc); - return(0); + rc &= 0xFF; + if(rc & 0x80) + rc |= ((~0u) << 8); + write_register(rd, rc); + return 0; } //LDRSH - if((inst&0xFE00)==0x5E00) + if((inst & 0xFE00) == 0x5E00) { - rd=(inst>>0)&0x7; - rn=(inst>>3)&0x7; - rm=(inst>>6)&0x7; + rd = (inst >> 0) & 0x7; + rn = (inst >> 3) & 0x7; + rm = (inst >> 6) & 0x7; DO_DISS(statusMsg << "ldrsh r" << dec << rd << ",[r" << dec << rn << ",r" << dec << rm << "]" << endl); - rb=read_register(rn)+read_register(rm); - rc=read16(rb); - rc&=0xFFFF; - if(rc&0x8000) rc|=((~0)<<16); - write_register(rd,rc); - return(0); + rb = read_register(rn) + read_register(rm); + rc = read16(rb); + rc &= 0xFFFF; + if(rc & 0x8000) + rc |= ((~0u) << 16); + write_register(rd, rc); + return 0; } //LSL(1) - if((inst&0xF800)==0x0000) + if((inst & 0xF800) == 0x0000) { - rd=(inst>>0)&0x07; - rm=(inst>>3)&0x07; - rb=(inst>>6)&0x1F; + rd = (inst >> 0) & 0x07; + rm = (inst >> 3) & 0x07; + rb = (inst >> 6) & 0x1F; DO_DISS(statusMsg << "lsls r" << dec << rd << ",r" << dec << rm << ",#0x" << Base::HEX2 << rb << endl); - rc=read_register(rm); - if(rb==0) + rc = read_register(rm); + if(rb == 0) { //if immed_5 == 0 - //C unnaffected + //C unaffected //result not shifted } else { //else immed_5 > 0 - do_cflag_bit(rc&(1<<(32-rb))); - rc<<=rb; + do_cflag_bit(rc & (1 << (32-rb))); + rc <<= rb; } - write_register(rd,rc); + write_register(rd, rc); do_nflag(rc); do_zflag(rc); - return(0); + return 0; } //LSL(2) two register - if((inst&0xFFC0)==0x4080) + if((inst & 0xFFC0) == 0x4080) { - rd=(inst>>0)&0x07; - rs=(inst>>3)&0x07; + rd = (inst >> 0) & 0x07; + rs = (inst >> 3) & 0x07; DO_DISS(statusMsg << "lsls r" << dec << rd << ",r" << dec << rs << endl); - rc=read_register(rd); - rb=read_register(rs); - rb&=0xFF; - if(rb==0) + rc = read_register(rd); + rb = read_register(rs); + rb &= 0xFF; + if(rb == 0) { } - else if(rb<32) + else if(rb < 32) { - do_cflag_bit(rc&(1<<(32-rb))); - rc<<=rb; + do_cflag_bit(rc & (1 << (32-rb))); + rc <<= rb; } - else if(rb==32) + else if(rb == 32) { - do_cflag_bit(rc&1); - rc=0; + do_cflag_bit(rc & 1); + rc = 0; } else { do_cflag_bit(0); - rc=0; + rc = 0; } - write_register(rd,rc); + write_register(rd, rc); do_nflag(rc); do_zflag(rc); - return(0); + return 0; } //LSR(1) two register immediate - if((inst&0xF800)==0x0800) + if((inst & 0xF800) == 0x0800) { - rd=(inst>>0)&0x07; - rm=(inst>>3)&0x07; - rb=(inst>>6)&0x1F; + rd = (inst >> 0) & 0x07; + rm = (inst >> 3) & 0x07; + rb = (inst >> 6) & 0x1F; DO_DISS(statusMsg << "lsrs r" << dec << rd << ",r" << dec << rm << ",#0x" << Base::HEX2 << rb << endl); - rc=read_register(rm); - if(rb==0) + rc = read_register(rm); + if(rb == 0) { - do_cflag_bit(rc&0x80000000); - rc=0; + do_cflag_bit(rc & 0x80000000); + rc = 0; } else { - do_cflag_bit(rc&(1<<(rb-1))); - rc>>=rb; + do_cflag_bit(rc & (1 << (rb-1))); + rc >>= rb; } - write_register(rd,rc); + write_register(rd, rc); do_nflag(rc); do_zflag(rc); - return(0); + return 0; } //LSR(2) two register - if((inst&0xFFC0)==0x40C0) + if((inst & 0xFFC0) == 0x40C0) { - rd=(inst>>0)&0x07; - rs=(inst>>3)&0x07; + rd = (inst >> 0) & 0x07; + rs = (inst >> 3) & 0x07; DO_DISS(statusMsg << "lsrs r" << dec << rd << ",r" << dec << rs << endl); - rc=read_register(rd); - rb=read_register(rs); - rb&=0xFF; - if(rb==0) + rc = read_register(rd); + rb = read_register(rs); + rb &= 0xFF; + if(rb == 0) { } - else if(rb<32) + else if(rb < 32) { - do_cflag_bit(rc&(1<<(32-rb))); - rc>>=rb; + do_cflag_bit(rc & (1 << (rb-1))); + rc >>= rb; } - else if(rb==32) + else if(rb == 32) { - do_cflag_bit(rc&0x80000000); - rc=0; + do_cflag_bit(rc & 0x80000000); + rc = 0; } else { do_cflag_bit(0); - rc=0; + rc = 0; } - write_register(rd,rc); + write_register(rd, rc); do_nflag(rc); do_zflag(rc); - return(0); + return 0; } //MOV(1) immediate - if((inst&0xF800)==0x2000) + if((inst & 0xF800) == 0x2000) { - rb=(inst>>0)&0xFF; - rd=(inst>>8)&0x07; + rb = (inst >> 0) & 0xFF; + rd = (inst >> 8) & 0x07; DO_DISS(statusMsg << "movs r" << dec << rd << ",#0x" << Base::HEX2 << rb << endl); - write_register(rd,rb); + write_register(rd, rb); do_nflag(rb); do_zflag(rb); - return(0); + return 0; } //MOV(2) two low registers - if((inst&0xFFC0)==0x1C00) + if((inst & 0xFFC0) == 0x1C00) { - rd=(inst>>0)&7; - rn=(inst>>3)&7; + rd = (inst >> 0) & 7; + rn = (inst >> 3) & 7; DO_DISS(statusMsg << "movs r" << dec << rd << ",r" << dec << rn << endl); - rc=read_register(rn); + rc = read_register(rn); //fprintf(stderr,"0x%08X\n",rc); - write_register(rd,rc); + write_register(rd, rc); do_nflag(rc); do_zflag(rc); do_cflag_bit(0); do_vflag_bit(0); - return(0); + return 0; } //MOV(3) - if((inst&0xFF00)==0x4600) + if((inst & 0xFF00) == 0x4600) { - rd=(inst>>0)&0x7; - rd|=(inst>>4)&0x8; - rm=(inst>>3)&0xF; + rd = (inst >> 0) & 0x7; + rd |= (inst >> 4) & 0x8; + rm = (inst >> 3) & 0xF; DO_DISS(statusMsg << "mov r" << dec << rd << ",r" << dec << rm << endl); - rc=read_register(rm); - if (rd==15) rc+=2; // fxq fix for MOV R15 - write_register(rd,rc); - return(0); + rc = read_register(rm); + if((rd == 14) && (rm == 15)) + { + //printf("mov lr,pc warning 0x%08X\n",pc-2); + //rc|=1; + } + if(rd == 15) + { + rc &= ~1; //write_register may do this as well + rc += 2; //The program counter is special + } + write_register(rd, rc); + return 0; } //MUL - if((inst&0xFFC0)==0x4340) + if((inst & 0xFFC0) == 0x4340) { - rd=(inst>>0)&0x7; - rm=(inst>>3)&0x7; + rd = (inst >> 0) & 0x7; + rm = (inst >> 3) & 0x7; DO_DISS(statusMsg << "muls r" << dec << rd << ",r" << dec << rm << endl); - ra=read_register(rd); - rb=read_register(rm); - rc=ra*rb; - write_register(rd,rc); + ra = read_register(rd); + rb = read_register(rm); + rc = ra * rb; + write_register(rd, rc); do_nflag(rc); do_zflag(rc); - return(0); + return 0; } //MVN - if((inst&0xFFC0)==0x43C0) + if((inst & 0xFFC0) == 0x43C0) { - rd=(inst>>0)&0x7; - rm=(inst>>3)&0x7; + rd = (inst >> 0) & 0x7; + rm = (inst >> 3) & 0x7; DO_DISS(statusMsg << "mvns r" << dec << rd << ",r" << dec << rm << endl); - ra=read_register(rm); - rc=(~ra); - write_register(rd,rc); + ra = read_register(rm); + rc = (~ra); + write_register(rd, rc); do_nflag(rc); do_zflag(rc); - return(0); + return 0; } //NEG - if((inst&0xFFC0)==0x4240) + if((inst & 0xFFC0) == 0x4240) { - rd=(inst>>0)&0x7; - rm=(inst>>3)&0x7; + rd = (inst >> 0) & 0x7; + rm = (inst >> 3) & 0x7; DO_DISS(statusMsg << "negs r" << dec << rd << ",r" << dec << rm << endl); - ra=read_register(rm); - rc=0-ra; - write_register(rd,rc); + ra = read_register(rm); + rc = 0 - ra; + write_register(rd, rc); do_nflag(rc); do_zflag(rc); - do_cflag(0,~ra,1); - do_sub_vflag(0,ra,rc); - return(0); + do_cflag(0, ~ra, 1); + do_vflag(0, ~ra, 1); + return 0; } //ORR - if((inst&0xFFC0)==0x4300) + if((inst & 0xFFC0) == 0x4300) { - rd=(inst>>0)&0x7; - rm=(inst>>3)&0x7; + rd = (inst >> 0) & 0x7; + rm = (inst >> 3) & 0x7; DO_DISS(statusMsg << "orrs r" << dec << rd << ",r" << dec << rm << endl); - ra=read_register(rd); - rb=read_register(rm); - rc=ra|rb; - write_register(rd,rc); + ra = read_register(rd); + rb = read_register(rm); + rc = ra | rb; + write_register(rd, rc); do_nflag(rc); do_zflag(rc); - return(0); + return 0; } //POP - if((inst&0xFE00)==0xBC00) + if((inst & 0xFE00) == 0xBC00) { #if defined(THUMB_DISS) - statusMsg << "pop {"; - for(ra=0,rb=0x01,rc=0;rb;rb=(rb<<1)&0xFF,ra++) - { - if(inst&rb) - { - if(rc) statusMsg << ","; - statusMsg << "r" << dec << ra; - rc++; - } - } - if(inst&0x100) - { - if(rc) statusMsg << ","; - statusMsg << "pc"; - } - statusMsg << "}" << endl; - #endif - - sp=read_register(13); - for(ra=0,rb=0x01;rb;rb=(rb<<1)&0xFF,ra++) + statusMsg << "pop {"; + for(ra=0,rb=0x01,rc=0;rb;rb=(rb<<1)&0xFF,++ra) { if(inst&rb) - { - write_register(ra,read32(sp)); - sp+=4; - } - } - if(inst&0x100) - { - rc=read32(sp); - rc+=2; - write_register(15,rc); - sp+=4; - } - write_register(13,sp); - return(0); - } - - //PUSH - if((inst&0xFE00)==0xB400) - { - #if defined(THUMB_DISS) - statusMsg << "push {"; - for(ra=0,rb=0x01,rc=0;rb;rb=(rb<<1)&0xFF,ra++) - { - if(inst&rb) - { - if(rc) statusMsg << ","; - statusMsg << "r" << dec << ra; - rc++; - } - } - if(inst&0x100) { if(rc) statusMsg << ","; - statusMsg << "lr"; - } - statusMsg << "}" << endl; - #endif - - sp=read_register(13); - //fprintf(stderr,"sp 0x%08X\n",sp); - for(ra=0,rb=0x01,rc=0;rb;rb=(rb<<1)&0xFF,ra++) - { - if(inst&rb) - { + statusMsg << "r" << dec << ra; rc++; } } - if(inst&0x100) rc++; - rc<<=2; - sp-=rc; - rd=sp; - for(ra=0,rb=0x01;rb;rb=(rb<<1)&0xFF,ra++) + if(inst&0x100) + { + if(rc) statusMsg << ","; + statusMsg << "pc"; + } + statusMsg << "}" << endl; + #endif + + sp = read_register(13); + for(ra = 0, rb = 0x01; rb; rb = (rb << 1) & 0xFF, ++ra) + { + if(inst & rb) + { + write_register(ra, read32(sp)); + sp += 4; + } + } + if(inst & 0x100) + { + rc = read32(sp); + rc += 2; + write_register(15, rc); + sp += 4; + } + write_register(13, sp); + return 0; + } + + //PUSH + if((inst & 0xFE00) == 0xB400) + { + #if defined(THUMB_DISS) + statusMsg << "push {"; + for(ra=0,rb=0x01,rc=0;rb;rb=(rb<<1)&0xFF,++ra) { if(inst&rb) { - write32(rd,read_register(ra)); - rd+=4; + if(rc) statusMsg << ","; + statusMsg << "r" << dec << ra; + rc++; } } if(inst&0x100) { - write32(rd,read_register(14)); + if(rc) statusMsg << ","; + statusMsg << "lr"; } - write_register(13,sp); - return(0); + statusMsg << "}" << endl; + #endif + + sp = read_register(13); + //fprintf(stderr,"sp 0x%08X\n",sp); + for(ra = 0, rb = 0x01, rc = 0; rb; rb = (rb << 1) & 0xFF, ++ra) + { + if(inst & rb) + { + ++rc; + } + } + if(inst & 0x100) ++rc; + rc <<= 2; + sp -= rc; + rd = sp; + for(ra = 0, rb = 0x01; rb; rb = (rb << 1) & 0xFF, ++ra) + { + if(inst & rb) + { + write32(rd, read_register(ra)); + rd += 4; + } + } + if(inst & 0x100) + { + rc = read_register(14); + write32(rd, rc); + if((rc & 1) == 0) + { + // FIXME fprintf(stderr,"push {lr} with an ARM address pc 0x%08X popped 0x%08X\n",pc,rc); + } + } + write_register(13, sp); + return 0; } //REV - if((inst&0xFFC0)==0xBA00) + if((inst & 0xFFC0) == 0xBA00) { - rd=(inst>>0)&0x7; - rn=(inst>>3)&0x7; + rd = (inst >> 0) & 0x7; + rn = (inst >> 3) & 0x7; DO_DISS(statusMsg << "rev r" << dec << rd << ",r" << dec << rn << endl); - ra=read_register(rn); - rc =((ra>> 0)&0xFF)<<24; - rc|=((ra>> 8)&0xFF)<<16; - rc|=((ra>>16)&0xFF)<< 8; - rc|=((ra>>24)&0xFF)<< 0; - write_register(rd,rc); - return(0); + ra = read_register(rn); + rc = ((ra >> 0) & 0xFF) << 24; + rc |= ((ra >> 8) & 0xFF) << 16; + rc |= ((ra >> 16) & 0xFF) << 8; + rc |= ((ra >> 24) & 0xFF) << 0; + write_register(rd, rc); + return 0; } //REV16 - if((inst&0xFFC0)==0xBA40) + if((inst & 0xFFC0) == 0xBA40) { - rd=(inst>>0)&0x7; - rn=(inst>>3)&0x7; + rd = (inst >> 0) & 0x7; + rn = (inst >> 3) & 0x7; DO_DISS(statusMsg << "rev16 r" << dec << rd << ",r" << dec << rn << endl); - ra=read_register(rn); - rc =((ra>> 0)&0xFF)<< 8; - rc|=((ra>> 8)&0xFF)<< 0; - rc|=((ra>>16)&0xFF)<<24; - rc|=((ra>>24)&0xFF)<<16; - write_register(rd,rc); - return(0); + ra = read_register(rn); + rc = ((ra >> 0) & 0xFF) << 8; + rc |= ((ra >> 8) & 0xFF) << 0; + rc |= ((ra >> 16) & 0xFF) << 24; + rc |= ((ra >> 24) & 0xFF) << 16; + write_register(rd, rc); + return 0; } //REVSH - if((inst&0xFFC0)==0xBAC0) + if((inst & 0xFFC0) == 0xBAC0) { - rd=(inst>>0)&0x7; - rn=(inst>>3)&0x7; + rd = (inst >> 0) & 0x7; + rn = (inst >> 3) & 0x7; DO_DISS(statusMsg << "revsh r" << dec << rd << ",r" << dec << rn << endl); - ra=read_register(rn); - rc =((ra>> 0)&0xFF)<< 8; - rc|=((ra>> 8)&0xFF)<< 0; - if(rc&0x8000) rc|=0xFFFF0000; - else rc&=0x0000FFFF; - write_register(rd,rc); - return(0); + ra = read_register(rn); + rc = ((ra >> 0) & 0xFF) << 8; + rc |= ((ra >> 8) & 0xFF) << 0; + if(rc & 0x8000) rc |= 0xFFFF0000; + else rc &= 0x0000FFFF; + write_register(rd, rc); + return 0; } //ROR - if((inst&0xFFC0)==0x41C0) + if((inst & 0xFFC0) == 0x41C0) { - rd=(inst>>0)&0x7; - rs=(inst>>3)&0x7; + rd = (inst >> 0) & 0x7; + rs = (inst >> 3) & 0x7; DO_DISS(statusMsg << "rors r" << dec << rd << ",r" << dec << rs << endl); - rc=read_register(rd); - ra=read_register(rs); - ra&=0xFF; - if(ra==0) + rc = read_register(rd); + ra = read_register(rs); + ra &= 0xFF; + if(ra == 0) { } else { - ra&=0x1F; - if(ra==0) + ra &= 0x1F; + if(ra == 0) { - do_cflag_bit(rc&0x80000000); + do_cflag_bit(rc & 0x80000000); } else { - do_cflag_bit(rc&(1<<(ra-1))); - rb=rc<<(32-ra); - rc>>=ra; - rc|=rb; + do_cflag_bit(rc & (1 << (ra-1))); + rb = rc << (32-ra); + rc >>= ra; + rc |= rb; } } - write_register(rd,rc); + write_register(rd, rc); do_nflag(rc); do_zflag(rc); - return(0); + return 0; } //SBC - if((inst&0xFFC0)==0x4180) + if((inst & 0xFFC0) == 0x4180) { - rd=(inst>>0)&0x7; - rm=(inst>>3)&0x7; + rd = (inst >> 0) & 0x7; + rm = (inst >> 3) & 0x7; DO_DISS(statusMsg << "sbc r" << dec << rd << ",r" << dec << rm << endl); - ra=read_register(rd); - rb=read_register(rm); - rc=ra-rb; - if(!(cpsr&CPSR_C)) rc--; - write_register(rd,rc); + ra = read_register(rd); + rb = read_register(rm); + rc = ra - rb; + if(!(cpsr & CPSR_C)) --rc; + write_register(rd, rc); do_nflag(rc); do_zflag(rc); - do_cflag(ra,rb,0); - do_sub_vflag(ra,rb,rc); - return(0); + if(cpsr & CPSR_C) + { + do_cflag(ra, ~rb, 1); + do_vflag(ra, ~rb, 1); + } + else + { + do_cflag(ra, ~rb, 0); + do_vflag(ra, ~rb, 0); + } + return 0; } //SETEND - if((inst&0xFFF7)==0xB650) + if((inst & 0xFFF7) == 0xB650) { statusMsg << "setend not implemented" << endl; - return(1); + return 1; } //STMIA - if((inst&0xF800)==0xC000) + if((inst & 0xF800) == 0xC000) { - rn=(inst>>8)&0x7; + rn = (inst >> 8) & 0x7; #if defined(THUMB_DISS) - statusMsg << "stmia r" << dec << rn << "!,{"; - for(ra=0,rb=0x01,rc=0;rb;rb=(rb<<1)&0xFF,ra++) - { - if(inst&rb) - { - if(rc) statusMsg << ","; - statusMsg << "r" << dec << ra; - rc++; - } - } - statusMsg << "}" << endl; - #endif - - sp=read_register(rn); - for(ra=0,rb=0x01;rb;rb=(rb<<1)&0xFF,ra++) + statusMsg << "stmia r" << dec << rn << "!,{"; + for(ra=0,rb=0x01,rc=0;rb;rb=(rb<<1)&0xFF,++ra) { - if(inst&rb) + if(inst & rb) { - write32(sp,read_register(ra)); - sp+=4; + if(rc) statusMsg << ","; + statusMsg << "r" << dec << ra; + rc++; } } - write_register(rn,sp); - return(0); + statusMsg << "}" << endl; + #endif + + sp = read_register(rn); + for(ra = 0, rb = 0x01; rb; rb = (rb << 1) & 0xFF, ++ra) + { + if(inst & rb) + { + write32(sp, read_register(ra)); + sp += 4; + } + } + write_register(rn, sp); + return 0; } //STR(1) - if((inst&0xF800)==0x6000) + if((inst & 0xF800) == 0x6000) { - rd=(inst>>0)&0x07; - rn=(inst>>3)&0x07; - rb=(inst>>6)&0x1F; - rb<<=2; + rd = (inst >> 0) & 0x07; + rn = (inst >> 3) & 0x07; + rb = (inst >> 6) & 0x1F; + rb <<= 2; DO_DISS(statusMsg << "str r" << dec << rd << ",[r" << dec << rn << ",#0x" << Base::HEX2 << rb << "]" << endl); - rb=read_register(rn)+rb; - rc=read_register(rd); - write32(rb,rc); - return(0); + rb = read_register(rn) + rb; + rc = read_register(rd); + write32(rb, rc); + return 0; } //STR(2) - if((inst&0xFE00)==0x5000) + if((inst & 0xFE00) == 0x5000) { - rd=(inst>>0)&0x7; - rn=(inst>>3)&0x7; - rm=(inst>>6)&0x7; + rd = (inst >> 0) & 0x7; + rn = (inst >> 3) & 0x7; + rm = (inst >> 6) & 0x7; DO_DISS(statusMsg << "str r" << dec << rd << ",[r" << dec << rn << ",r" << dec << rm << "]" << endl); - rb=read_register(rn)+read_register(rm); - rc=read_register(rd); - write32(rb,rc); - return(0); + rb = read_register(rn) + read_register(rm); + rc = read_register(rd); + write32(rb, rc); + return 0; } //STR(3) - if((inst&0xF800)==0x9000) + if((inst & 0xF800) == 0x9000) { - rb=(inst>>0)&0xFF; - rd=(inst>>8)&0x07; - rb<<=2; + rb = (inst >> 0) & 0xFF; + rd = (inst >> 8) & 0x07; + rb <<= 2; DO_DISS(statusMsg << "str r" << dec << rd << ",[SP,#0x" << Base::HEX2 << rb << "]" << endl); - rb=read_register(13)+rb; + rb = read_register(13) + rb; //fprintf(stderr,"0x%08X\n",rb); - rc=read_register(rd); - write32(rb,rc); - return(0); + rc = read_register(rd); + write32(rb, rc); + return 0; } //STRB(1) - if((inst&0xF800)==0x7000) + if((inst & 0xF800) == 0x7000) { - rd=(inst>>0)&0x07; - rn=(inst>>3)&0x07; - rb=(inst>>6)&0x1F; + rd = (inst >> 0) & 0x07; + rn = (inst >> 3) & 0x07; + rb = (inst >> 6) & 0x1F; DO_DISS(statusMsg << "strb r" << dec << rd << ",[r" << dec << rn << ",#0x" << Base::HEX8 << rb << "]" << endl); - rb=read_register(rn)+rb; - rc=read_register(rd); - ra=read16(rb&(~1)); - if(rb&1) + rb = read_register(rn) + rb; + rc = read_register(rd); + ra = read16(rb & (~1u)); + if(rb & 1) { - ra&=0x00FF; - ra|=rc<<8; + ra &= 0x00FF; + ra |= rc << 8; } else { - ra&=0xFF00; - ra|=rc&0x00FF; + ra &= 0xFF00; + ra |= rc & 0x00FF; } - write16(rb&(~1),ra&0xFFFF); - return(0); + write16(rb & (~1u), ra & 0xFFFF); + return 0; } //STRB(2) - if((inst&0xFE00)==0x5400) + if((inst & 0xFE00) == 0x5400) { - rd=(inst>>0)&0x7; - rn=(inst>>3)&0x7; - rm=(inst>>6)&0x7; + rd = (inst >> 0) & 0x7; + rn = (inst >> 3) & 0x7; + rm = (inst >> 6) & 0x7; DO_DISS(statusMsg << "strb r" << dec << rd << ",[r" << dec << rn << ",r" << rm << "]" << endl); - rb=read_register(rn)+read_register(rm); - rc=read_register(rd); - ra=read16(rb&(~1)); - if(rb&1) + rb = read_register(rn) + read_register(rm); + rc = read_register(rd); + ra = read16(rb & (~1u)); + if(rb & 1) { - ra&=0x00FF; - ra|=rc<<8; + ra &= 0x00FF; + ra |= rc << 8; } else { - ra&=0xFF00; - ra|=rc&0x00FF; + ra &= 0xFF00; + ra |= rc & 0x00FF; } - write16(rb&(~1),ra&0xFFFF); - return(0); + write16(rb & (~1u), ra & 0xFFFF); + return 0; } //STRH(1) - if((inst&0xF800)==0x8000) + if((inst & 0xF800) == 0x8000) { - rd=(inst>>0)&0x07; - rn=(inst>>3)&0x07; - rb=(inst>>6)&0x1F; - rb<<=1; + rd = (inst >> 0) & 0x07; + rn = (inst >> 3) & 0x07; + rb = (inst >> 6) & 0x1F; + rb <<= 1; DO_DISS(statusMsg << "strh r" << dec << rd << ",[r" << dec << rn << ",#0x" << Base::HEX2 << rb << "]" << endl); - rb=read_register(rn)+rb; - rc=read_register(rd); - write16(rb,rc&0xFFFF); - return(0); + rb = read_register(rn) + rb; + rc= read_register(rd); + write16(rb, rc & 0xFFFF); + return 0; } //STRH(2) - if((inst&0xFE00)==0x5200) + if((inst & 0xFE00) == 0x5200) { - rd=(inst>>0)&0x7; - rn=(inst>>3)&0x7; - rm=(inst>>6)&0x7; + rd = (inst >> 0) & 0x7; + rn = (inst >> 3) & 0x7; + rm = (inst >> 6) & 0x7; DO_DISS(statusMsg << "strh r" << dec << rd << ",[r" << dec << rn << ",r" << dec << rm << "]" << endl); - rb=read_register(rn)+read_register(rm); - rc=read_register(rd); - write16(rb,rc&0xFFFF); - return(0); + rb = read_register(rn) + read_register(rm); + rc = read_register(rd); + write16(rb, rc & 0xFFFF); + return 0; } //SUB(1) - if((inst&0xFE00)==0x1E00) + if((inst & 0xFE00) == 0x1E00) { - rd=(inst>>0)&7; - rn=(inst>>3)&7; - rb=(inst>>6)&7; + rd = (inst >> 0) & 0x7; + rn = (inst >> 3) & 0x7; + rb = (inst >> 6) & 0x7; DO_DISS(statusMsg << "subs r" << dec << rd << ",r" << dec << rn << ",#0x" << Base::HEX2 << rb << endl); - ra=read_register(rn); - rc=ra-rb; - write_register(rd,rc); + ra = read_register(rn); + rc = ra - rb; + write_register(rd, rc); do_nflag(rc); do_zflag(rc); - do_cflag(ra,~rb,1); - do_sub_vflag(ra,rb,rc); - return(0); + do_cflag(ra, ~rb, 1); + do_vflag(ra, ~rb, 1); + return 0; } //SUB(2) - if((inst&0xF800)==0x3800) + if((inst & 0xF800) == 0x3800) { - rb=(inst>>0)&0xFF; - rd=(inst>>8)&0x07; + rb = (inst >> 0) & 0xFF; + rd = (inst >> 8) & 0x07; DO_DISS(statusMsg << "subs r" << dec << rd << ",#0x" << Base::HEX2 << rb << endl); - ra=read_register(rd); - rc=ra-rb; - write_register(rd,rc); + ra = read_register(rd); + rc = ra - rb; + write_register(rd, rc); do_nflag(rc); do_zflag(rc); - do_cflag(ra,~rb,1); - do_sub_vflag(ra,rb,rc); - return(0); + do_cflag(ra, ~rb, 1); + do_vflag(ra, ~rb, 1); + return 0; } //SUB(3) - if((inst&0xFE00)==0x1A00) + if((inst & 0xFE00) == 0x1A00) { - rd=(inst>>0)&0x7; - rn=(inst>>3)&0x7; - rm=(inst>>6)&0x7; + rd = (inst >> 0) & 0x7; + rn = (inst >> 3) & 0x7; + rm = (inst >> 6) & 0x7; DO_DISS(statusMsg << "subs r" << dec << rd << ",r" << dec << rn << ",r" << dec << rm << endl); - ra=read_register(rn); - rb=read_register(rm); - rc=ra-rb; - write_register(rd,rc); + ra = read_register(rn); + rb = read_register(rm); + rc = ra - rb; + write_register(rd, rc); do_nflag(rc); do_zflag(rc); - do_cflag(ra,~rb,1); - do_sub_vflag(ra,rb,rc); - return(0); + do_cflag(ra, ~rb, 1); + do_vflag(ra, ~rb, 1); + return 0; } //SUB(4) - if((inst&0xFF80)==0xB080) + if((inst & 0xFF80) == 0xB080) { - rb=inst&0x7F; - rb<<=2; + rb = inst & 0x7F; + rb <<= 2; DO_DISS(statusMsg << "sub SP,#0x" << Base::HEX2 << rb << endl); - ra=read_register(13); - ra-=rb; - write_register(13,ra); - return(0); + ra = read_register(13); + ra -= rb; + write_register(13, ra); + return 0; } //SWI - if((inst&0xFF00)==0xDF00) + if((inst & 0xFF00) == 0xDF00) { - rb=inst&0xFF; + rb = inst & 0xFF; DO_DISS(statusMsg << "swi 0x" << Base::HEX2 << rb << endl); - statusMsg << endl << endl << "swi 0x" << Base::HEX2 << rb << endl; - return(1); + + if((inst & 0xFF) == 0xCC) + { + write_register(0, cpsr); + return 0; + } + else + { + statusMsg << endl << endl << "swi 0x" << Base::HEX2 << rb << endl; + return 1; + } } //SXTB - if((inst&0xFFC0)==0xB240) + if((inst & 0xFFC0) == 0xB240) { - rd=(inst>>0)&0x7; - rm=(inst>>3)&0x7; + rd = (inst >> 0) & 0x7; + rm = (inst >> 3) & 0x7; DO_DISS(statusMsg << "sxtb r" << dec << rd << ",r" << dec << rm << endl); - ra=read_register(rm); - rc=ra&0xFF; - if(rc&0x80) rc|=(~0)<<8; - write_register(rd,rc); - return(0); + ra = read_register(rm); + rc = ra & 0xFF; + if(rc & 0x80) + rc |= (~0u) << 8; + write_register(rd, rc); + return 0; } //SXTH - if((inst&0xFFC0)==0xB200) + if((inst & 0xFFC0) == 0xB200) { - rd=(inst>>0)&0x7; - rm=(inst>>3)&0x7; + rd = (inst >> 0) & 0x7; + rm = (inst >> 3) & 0x7; DO_DISS(statusMsg << "sxth r" << dec << rd << ",r" << dec << rm << endl); - ra=read_register(rm); - rc=ra&0xFFFF; - if(rc&0x8000) rc|=(~0)<<16; - write_register(rd,rc); - return(0); + ra = read_register(rm); + rc = ra & 0xFFFF; + if(rc & 0x8000) + rc |= (~0u) << 16; + write_register(rd, rc); + return 0; } //TST - if((inst&0xFFC0)==0x4200) + if((inst & 0xFFC0) == 0x4200) { - rn=(inst>>0)&0x7; - rm=(inst>>3)&0x7; + rn = (inst >> 0) & 0x7; + rm = (inst >> 3) & 0x7; DO_DISS(statusMsg << "tst r" << dec << rn << ",r" << dec << rm << endl); - ra=read_register(rn); - rb=read_register(rm); - rc=ra&rb; + ra = read_register(rn); + rb = read_register(rm); + rc = ra & rb; do_nflag(rc); do_zflag(rc); - return(0); + return 0; } //UXTB - if((inst&0xFFC0)==0xB2C0) + if((inst & 0xFFC0) == 0xB2C0) { - rd=(inst>>0)&0x7; - rm=(inst>>3)&0x7; + rd = (inst >> 0) & 0x7; + rm = (inst >> 3) & 0x7; DO_DISS(statusMsg << "uxtb r" << dec << rd << ",r" << dec << rm << endl); - ra=read_register(rm); - rc=ra&0xFF; - write_register(rd,rc); - return(0); + ra = read_register(rm); + rc = ra & 0xFF; + write_register(rd, rc); + return 0; } //UXTH - if((inst&0xFFC0)==0xB280) + if((inst & 0xFFC0) == 0xB280) { - rd=(inst>>0)&0x7; - rm=(inst>>3)&0x7; + rd = (inst >> 0) & 0x7; + rm = (inst >> 3) & 0x7; DO_DISS(statusMsg << "uxth r" << dec << rd << ",r" << dec << rm << endl); - ra=read_register(rm); - rc=ra&0xFFFF; - write_register(rd,rc); - return(0); + ra = read_register(rm); + rc = ra & 0xFFFF; + write_register(rd, rc); + return 0; } statusMsg << "invalid instruction " << Base::HEX8 << pc << " " << Base::HEX4 << inst << endl; - return(1); + return 1; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -int Thumbulator::reset ( void ) +int Thumbulator::reset() { - //memset(ram,0xFF,sizeof(ram)); - cpsr=CPSR_T|CPSR_I|CPSR_F|MODE_SVC; + std::fill(reg_norm, reg_norm+12, 0); + reg_norm[13] = 0x40001FB4; - reg_svc[13]=0x40001fb4; //sp - reg_svc[14]=0x00000c00; //lr (duz this use odd addrs) - reg_sys[15]=0x00000c0b; // entry point of 0xc09+2 - // reg_sys[15]+=2; - mamcr = 0; + switch(configuration) + { + // future 2K Harmony/Melody drivers will most likely use these settings + case ConfigureFor::BUS: + case ConfigureFor::CDF: + case ConfigureFor::CDF1: + reg_norm[14] = 0x00000800; // Link Register + reg_norm[15] = 0x0000080B; // Program Counter + break; + + // future 3K Harmony/Melody drivers will most likely use these settings + case ConfigureFor::DPCplus: + reg_norm[14] = 0x00000C00; // Link Register + reg_norm[15] = 0x00000C0B; // Program Counter + break; + } + + cpsr = mamcr = 0; + handler_mode = false; + + systick_ctrl = 0x00000004; + systick_reload = 0x00000000; + systick_count = 0x00000000; + systick_calibrate = 0x00ABCDEF; // fxq: don't care about below so much (maybe to guess timing???) - instructions=0; - fetches=0; - reads=0; - writes=0; + instructions = fetches = reads = writes = systick_ints = 0; statusMsg.str(""); - return(0); + return 0; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - bool Thumbulator::trapOnFatal = true; - -#endif diff --git a/src/emucore/Thumbulator.hxx b/src/emucore/Thumbulator.hxx index d7ac9669e..36d58db95 100644 --- a/src/emucore/Thumbulator.hxx +++ b/src/emucore/Thumbulator.hxx @@ -8,13 +8,11 @@ // SS SS tt ee ll ll aa aa // SSSS ttt eeeee llll llll aaaaa // -// Copyright (c) 1995-2014 by Bradford W. Mott, Stephen Anthony +// Copyright (c) 1995-2018 by Bradford W. Mott, Stephen Anthony // and the Stella Team // // See the file "License.txt" for information on usage and redistribution of // this file, and for a DISCLAIMER OF ALL WARRANTIES. -// -// $Id: Thumbulator.hxx 2838 2014-01-17 23:34:03Z stephena $ //============================================================================ //============================================================================ @@ -24,9 +22,13 @@ // Code is public domain and used with the author's consent //============================================================================ -#ifdef THUMB_SUPPORT +#ifndef THUMBULATOR_HXX +#define THUMBULATOR_HXX + +class Cartridge; #include "bspf.hxx" +#include "Console.hxx" #define ROMADDMASK 0x7FFF #define RAMADDMASK 0x1FFF @@ -34,39 +36,28 @@ #define ROMSIZE (ROMADDMASK+1) #define RAMSIZE (RAMADDMASK+1) -//0b10000 User PC, R14 to R0, CPSR -//0b10001 FIQ PC, R14_fiq to R8_fiq, R7 to R0, CPSR, SPSR_fiq -//0b10010 IRQ PC, R14_irq, R13_irq, R12 to R0, CPSR, SPSR_irq -//0b10011 Supervisor PC, R14_svc, R13_svc, R12 to R0, CPSR, SPSR_svc -//0b10111 Abort PC, R14_abt, R13_abt, R12 to R0, CPSR, SPSR_abt -//0b11011 Undefined PC, R14_und, R13_und, R12 to R0, CPSR, SPSR_und -//0b11111 System - -#define MODE_USR 0x10 -#define MODE_FIQ 0x11 -#define MODE_IRQ 0x12 -#define MODE_SVC 0x13 -#define MODE_ABT 0x17 -#define MODE_UND 0x1B -#define MODE_SYS 0x1F - -#define CPSR_T (1<<5) -#define CPSR_F (1<<6) -#define CPSR_I (1<<7) -#define CPSR_N (1<<31) -#define CPSR_Z (1<<30) -#define CPSR_C (1<<29) -#define CPSR_V (1<<28) -#define CPSR_Q (1<<27) +#define CPSR_N (1u<<31) +#define CPSR_Z (1u<<30) +#define CPSR_C (1u<<29) +#define CPSR_V (1u<<28) class Thumbulator { public: - Thumbulator(const uInt16* rom, uInt16* ram, bool traponfatal); - ~Thumbulator(); + // control cartridge specific features of the Thumbulator class, + // such as the start location for calling custom code + enum ConfigureFor { + BUS, // cartridges of type BUS + CDF, // cartridges of type CDF + CDF1, // cartridges of type CDF version 1 + DPCplus // cartridges of type DPC+ + }; + + Thumbulator(const uInt16* rom, uInt16* ram, bool traponfatal, + Thumbulator::ConfigureFor configurefor, Cartridge* cartridge); /** - Run the ARM code, and return when finished. A string exception is + Run the ARM code, and return when finished. A runtime_error exception is thrown in case of any fatal errors/aborts (if enabled), containing the actual error, and the contents of the registers at that point in time. @@ -74,6 +65,7 @@ class Thumbulator otherwise an empty string */ string run(); + string run(uInt32 cycles); /** Normally when a fatal error is encountered, the ARM emulation @@ -89,59 +81,75 @@ class Thumbulator */ static void trapFatalErrors(bool enable) { trapOnFatal = enable; } + /** + Inform the Thumbulator class about the console currently in use, + which is used to accurately determine how many 6507 cycles have + passed while ARM code is being executed. + */ + void setConsoleTiming(ConsoleTiming timing); + private: - uInt32 read_register ( uInt32 reg ); - uInt32 write_register ( uInt32 reg, uInt32 data ); - uInt32 fetch16 ( uInt32 addr ); - uInt32 fetch32 ( uInt32 addr ); - uInt32 read16 ( uInt32 addr ); - uInt32 read32 ( uInt32 ); - void write16 ( uInt32 addr, uInt32 data ); - void write32 ( uInt32 addr, uInt32 data ); + uInt32 read_register(uInt32 reg); + void write_register(uInt32 reg, uInt32 data); + uInt32 fetch16(uInt32 addr); + uInt32 fetch32(uInt32 addr); + uInt32 read16(uInt32 addr); + uInt32 read32(uInt32 addr); + bool isProtected(uInt32 addr); + void write16(uInt32 addr, uInt32 data); + void write32(uInt32 addr, uInt32 data); + void updateTimer(uInt32 cycles); - void do_zflag ( uInt32 x ); - void do_nflag ( uInt32 x ); - void do_cflag ( uInt32 a, uInt32 b, uInt32 c ); - void do_sub_vflag ( uInt32 a, uInt32 b, uInt32 c ); - void do_add_vflag ( uInt32 a, uInt32 b, uInt32 c ); - void do_cflag_bit ( uInt32 x ); - void do_vflag_bit ( uInt32 x ); + void do_zflag(uInt32 x); + void do_nflag(uInt32 x); + void do_cflag(uInt32 a, uInt32 b, uInt32 c); + void do_vflag(uInt32 a, uInt32 b, uInt32 c); + void do_cflag_bit(uInt32 x); + void do_vflag_bit(uInt32 x); - // Throw a string exception containing an error referencing the given - // message and variables + // Throw a runtime_error exception containing an error referencing the + // given message and variables // Note that the return value is never used in these methods int fatalError(const char* opcode, uInt32 v1, const char* msg); int fatalError(const char* opcode, uInt32 v1, uInt32 v2, const char* msg); - void dump_counters ( void ); - void dump_regs( void ); - int execute ( void ); - int reset ( void ); + void dump_counters(); + void dump_regs(); + int execute(); + int reset(); private: const uInt16* rom; uInt16* ram; - //Int32 copydata; - uInt32 halfadd; - uInt32 cpsr; - //uInt32 reg_usr[16]; //User mode - uInt32 reg_sys[16]; //System mode - uInt32 reg_svc[16]; //Supervisor mode - //uInt32 reg_abt[16]; //Abort mode - //uInt32 reg_und[16]; //Undefined mode - //uInt32 reg_irq[16]; //Interrupt mode - //uInt32 reg_fiq[16]; //Fast Interrupt mode - uInt32 mamcr; + uInt32 reg_norm[16]; // normal execution mode, do not have a thread mode + uInt32 cpsr, mamcr; + bool handler_mode; + uInt32 systick_ctrl, systick_reload, systick_count, systick_calibrate; + uInt64 instructions, fetches, reads, writes, systick_ints; - uInt64 instructions; - uInt64 fetches; - uInt64 reads; - uInt64 writes; + // For emulation of LPC2103's timer 1, used for NTSC/PAL/SECAM detection. + // Register names from documentation: + // http://www.nxp.com/documents/user_manual/UM10161.pdf + uInt32 T1TCR; // Timer 1 Timer Control Register + uInt32 T1TC; // Timer 1 Timer Counter + double timing_factor; ostringstream statusMsg; static bool trapOnFatal; + + ConfigureFor configuration; + + Cartridge* myCartridge; + + private: + // Following constructors and assignment operators not supported + Thumbulator() = delete; + Thumbulator(const Thumbulator&) = delete; + Thumbulator(Thumbulator&&) = delete; + Thumbulator& operator=(const Thumbulator&) = delete; + Thumbulator& operator=(Thumbulator&&) = delete; }; -#endif +#endif // THUMBULATOR_HXX diff --git a/src/emucore/module.mk b/src/emucore/module.mk index 7d754d4fc..67b9cd1dd 100644 --- a/src/emucore/module.mk +++ b/src/emucore/module.mk @@ -35,7 +35,6 @@ MODULE_OBJS := \ src/emucore/CartFA.o \ src/emucore/CartFA2.o \ src/emucore/CartFE.o \ - src/emucore/CartMC.o \ src/emucore/CartSB.o \ src/emucore/CartUA.o \ src/emucore/CartX07.o \ @@ -76,5 +75,5 @@ MODULE_OBJS := \ MODULE_DIRS += \ src/emucore -# Include common rules +# Include common rules include $(srcdir)/common.rules