diff --git a/src/emucore/CartCV.cxx b/src/emucore/CartCV.cxx index 42c74a808..136e817c3 100644 --- a/src/emucore/CartCV.cxx +++ b/src/emucore/CartCV.cxx @@ -21,164 +21,42 @@ // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - CartridgeCV::CartridgeCV(const ByteBuffer& image, size_t size, const string& md5, const Settings& settings) - : Cartridge(settings, md5), - mySize(size) + : CartridgeEnhanced(image, size, md5, settings) { - if(mySize == myImage.size()) - { - // Copy the ROM data into my buffer - std::copy_n(image.get(), myImage.size(), myImage.begin()); - } - else if(mySize == 4_KB) + myBankShift = BANK_SHIFT; + myRamSize = RAM_SIZE; + myRamWpHigh = RAM_HIGH_WP; + + + if(mySize == 4_KB) { // The game has something saved in the RAM // Useful for MagiCard program listings - // Copy the ROM data into my buffer - std::copy_n(image.get() + myImage.size(), myImage.size(), myImage.begin()); + // Allocate array for the ROM image + mySize = 2_KB; + myImage = make_unique(mySize); + // Copy the ROM image into my buffer + std::copy_n(image.get() + mySize, mySize, myImage.get()); + + + myInitialRAM = make_unique(1_KB); // Copy the RAM image into a buffer for use in reset() - std::copy_n(image.get(), myInitialRAM.size(), myInitialRAM.begin()); + std::copy_n(image.get(), 1_KB, myInitialRAM.get()); } - createRomAccessArrays(myImage.size() + myRAM.size()); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void CartridgeCV::reset() { - if(mySize == 4_KB) + if(myInitialRAM != nullptr) { // Copy the RAM image into my buffer - myRAM = myInitialRAM; + std::copy_n(myInitialRAM.get(), 1_KB, myRAM.get()); } else - initializeRAM(myRAM.data(), myRAM.size()); + initializeRAM(myRAM.get(), myRamSize); myBankChanged = true; } - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void CartridgeCV::install(System& system) -{ - mySystem = &system; - - System::PageAccess access(this, System::PageAccessType::READ); - - // Map ROM image into the system - for(uInt16 addr = 0x1800; addr < 0x2000; addr += System::PAGE_SIZE) - { - access.directPeekBase = &myImage[addr & 0x07FF]; - access.romAccessBase = &myRomAccessBase[addr & 0x07FF]; - access.romPeekCounter = &myRomAccessCounter[addr & 0x07FF]; - access.romPokeCounter = &myRomAccessCounter[(addr & 0x07FF) + myAccessSize]; - mySystem->setPageAccess(addr, access); - } - - // Set the page accessing method for the RAM writing pages - // Map access to this class, since we need to inspect all accesses to - // check if RWP happens - access.directPeekBase = nullptr; - access.romAccessBase = nullptr; - access.type = System::PageAccessType::WRITE; - for(uInt16 addr = 0x1400; addr < 0x1800; addr += System::PAGE_SIZE) - mySystem->setPageAccess(addr, access); - - // Set the page accessing method for the RAM reading pages - access.directPokeBase = nullptr; - access.type = System::PageAccessType::READ; - for(uInt16 addr = 0x1000; addr < 0x1400; addr += System::PAGE_SIZE) - { - access.directPeekBase = &myRAM[addr & 0x03FF]; - access.romAccessBase = &myRomAccessBase[2048 + (addr & 0x03FF)]; - access.romPeekCounter = &myRomAccessCounter[2048 + (addr & 0x03FF)]; - access.romPokeCounter = &myRomAccessCounter[2048 + (addr & 0x03FF) + myAccessSize]; - mySystem->setPageAccess(addr, access); - } -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt8 CartridgeCV::peek(uInt16 address) -{ - // The only way we can get to this method is if we attempt to read from - // the write port (0xF400 - 0xF7FF, 1024 bytes), in which case an - // unwanted write is potentially triggered - return peekRAM(myRAM[address & 0x03FF], address); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeCV::poke(uInt16 address, uInt8 value) -{ - - if(address & 0x0400) - { - pokeRAM(myRAM[address & 0x03FF], address, value); - return true; - } - else - { - // Writing to the read port should be ignored, but trigger a break if option enabled - uInt8 dummy; - - pokeRAM(dummy, address, value); - myRamWriteAccess = address; - return false; - } -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeCV::patch(uInt16 address, uInt8 value) -{ - address &= 0x0FFF; - - if(address < 0x0800) - { - // Normally, a write to the read port won't do anything - // However, the patch command is special in that ignores such - // cart restrictions - // The following will work for both reads and writes - myRAM[address & 0x03FF] = value; - } - else - myImage[address & 0x07FF] = value; - - return myBankChanged = true; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -const uInt8* CartridgeCV::getImage(size_t& size) const -{ - size = myImage.size(); - return myImage.data(); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeCV::save(Serializer& out) const -{ - try - { - out.putByteArray(myRAM.data(), myRAM.size()); - } - catch(...) - { - cerr << "ERROR: CartridgeCV::save" << endl; - return false; - } - - return true; -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool CartridgeCV::load(Serializer& in) -{ - try - { - in.getByteArray(myRAM.data(), myRAM.size()); - } - catch(...) - { - cerr << "ERROR: CartridgeCV::load" << endl; - return false; - } - - return true; -} diff --git a/src/emucore/CartCV.hxx b/src/emucore/CartCV.hxx index 3b872ce9d..4e18040a6 100644 --- a/src/emucore/CartCV.hxx +++ b/src/emucore/CartCV.hxx @@ -21,7 +21,7 @@ class System; #include "bspf.hxx" -#include "Cart.hxx" +#include "CartEnhanced.hxx" #ifdef DEBUGGER_SUPPORT #include "CartCVWidget.hxx" #endif @@ -33,9 +33,9 @@ class System; $F400-$F7FF write to RAM $F800-$FFFF ROM - @author Eckhard Stolberg + @author Eckhard Stolberg, Thomas Jentzsch */ -class CartridgeCV : public Cartridge +class CartridgeCV : public CartridgeEnhanced { friend class CartridgeCVWidget; @@ -58,47 +58,6 @@ class CartridgeCV : public Cartridge */ void reset() override; - /** - 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) override; - - /** - 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) override; - - /** - 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(size_t& size) const override; - - /** - 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 override; - - /** - 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) override; - /** Get a descriptor for the device name (used in error checking). @@ -118,35 +77,22 @@ class CartridgeCV : public Cartridge } #endif - public: - /** - Get the byte at the specified address + private: + bool checkSwitchBank(uInt16, uInt8 = 0) override { return false; }; - @return The byte at the specified address - */ - uInt8 peek(uInt16 address) override; - - /** - 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) override; + protected: + // Initial RAM data from the cart (doesn't always exist) + ByteBuffer myInitialRAM{nullptr}; private: - // The 2k ROM image for the cartridge - std::array myImage; + // Calculated as: log(ROM bank segment size) / log(2) + static constexpr uInt16 BANK_SHIFT = 11; // 2K - // Initial size of the cart data - size_t mySize{0}; + // RAM size + static constexpr uInt16 RAM_SIZE = 0x400; // 1K - // The 1024 bytes of RAM - std::array myRAM; - - // Initial RAM data from the cart (doesn't always exist) - std::array myInitialRAM; + // Write port for extra RAM is at high address + static constexpr bool RAM_HIGH_WP = true; private: // Following constructors and assignment operators not supported diff --git a/src/emucore/CartEnhanced.cxx b/src/emucore/CartEnhanced.cxx index 6752d3153..c479ddd92 100644 --- a/src/emucore/CartEnhanced.cxx +++ b/src/emucore/CartEnhanced.cxx @@ -42,6 +42,8 @@ void CartridgeEnhanced::install(System& system) myBankMask = myBankSize - 1; // e.g. = 0x0FFF myBankSegs = 1 << (12 - myBankShift); // e.g. = 1 myRamMask = myRamSize - 1; // e.g. = 0xFFFF (doesn't matter for RAM size 0) + myWriteOffset = myRamWpHigh ? myRamSize : 0; + myReadOffset = myRamWpHigh ? 0 : myRamSize; // Allocate array for the current bank segments slices myCurrentSegOffset = make_unique(myBankSegs); @@ -58,24 +60,24 @@ void CartridgeEnhanced::install(System& system) // Map access to this class, since we need to inspect all accesses to // check if RWP happens access.type = System::PageAccessType::WRITE; - for(uInt16 addr = 0x1000; addr < 0x1000 + myRamSize; addr += System::PAGE_SIZE) + for(uInt16 addr = 0x1000 + myWriteOffset; addr < 0x1000 + myWriteOffset + myRamSize; addr += System::PAGE_SIZE) { uInt16 offset = addr & myRamMask; - access.romAccessBase = &myRomAccessBase[offset]; - access.romPeekCounter = &myRomAccessCounter[offset]; - access.romPokeCounter = &myRomAccessCounter[offset + myAccessSize]; + access.romAccessBase = &myRomAccessBase[myWriteOffset + offset]; + access.romPeekCounter = &myRomAccessCounter[myWriteOffset + offset]; + access.romPokeCounter = &myRomAccessCounter[myWriteOffset + offset + myAccessSize]; mySystem->setPageAccess(addr, access); } // Set the page accessing method for the RAM reading pages access.type = System::PageAccessType::READ; - for(uInt16 addr = 0x1000 + myRamSize; addr < 0x1000 + myRamSize * 2; addr += System::PAGE_SIZE) + for(uInt16 addr = 0x1000 + myReadOffset; addr < 0x1000 + myReadOffset + myRamSize; addr += System::PAGE_SIZE) { uInt16 offset = addr & myRamMask; access.directPeekBase = &myRAM[offset]; - access.romAccessBase = &myRomAccessBase[myRamSize + offset]; - access.romPeekCounter = &myRomAccessCounter[myRamSize + offset]; - access.romPokeCounter = &myRomAccessCounter[myRamSize + offset + myAccessSize]; + access.romAccessBase = &myRomAccessBase[myReadOffset + offset]; + access.romPeekCounter = &myRomAccessCounter[myReadOffset + offset]; + access.romPokeCounter = &myRomAccessCounter[myReadOffset + offset + myAccessSize]; mySystem->setPageAccess(addr, access); } @@ -106,7 +108,9 @@ uInt8 CartridgeEnhanced::peek(uInt16 address) checkSwitchBank(address & 0x0FFF); address &= myBankMask; - if(address < myRamSize) // Write port is at 0xF000 - 0xF07F (128 bytes) + // Write port is e.g. at 0xF000 - 0xF07F (128 bytes) + if(address < myReadOffset + myRamSize && address >= myReadOffset) + // This is a read accees to a write port! return peekRAM(myRAM[address], peekAddress); else return myImage[myCurrentSegOffset[(peekAddress & 0xFFF) >> myBankShift] + address]; @@ -115,15 +119,18 @@ uInt8 CartridgeEnhanced::peek(uInt16 address) // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - bool CartridgeEnhanced::poke(uInt16 address, uInt8 value) { + uInt16 pokeAddress = address; + // Switch banks if necessary if (checkSwitchBank(address & 0x0FFF, value)) return false; + address &= myBankMask; if(myRamSize) { - if(!(address & myRamSize)) + if(bool(address & myRamSize) == myRamWpHigh) { - pokeRAM(myRAM[address & myRamMask], address, value); + pokeRAM(myRAM[address & myRamMask], pokeAddress, value); return true; } else @@ -131,8 +138,8 @@ bool CartridgeEnhanced::poke(uInt16 address, uInt8 value) // Writing to the read port should be ignored, but trigger a break if option enabled uInt8 dummy; - pokeRAM(dummy, address, value); - myRamWriteAccess = address; + pokeRAM(dummy, pokeAddress, value); + myRamWriteAccess = pokeAddress; } } return false; diff --git a/src/emucore/CartEnhanced.hxx b/src/emucore/CartEnhanced.hxx index 6437c362e..6c67b6eaf 100644 --- a/src/emucore/CartEnhanced.hxx +++ b/src/emucore/CartEnhanced.hxx @@ -40,7 +40,7 @@ class CartridgeEnhanced : public Cartridge @param settings A reference to the various settings (read-only) */ CartridgeEnhanced(const ByteBuffer& image, size_t size, const string& md5, - const Settings& settings); + const Settings& settings); virtual ~CartridgeEnhanced() = default; public: @@ -160,6 +160,21 @@ class CartridgeEnhanced : public Cartridge // The mask for the extra RAM uInt16 myRamMask{0}; // RAM_SIZE - 1, but doesn't matter when RAM_SIZE is 0 + // The offset into ROM space for writing to RAM + // - xxSC = 0x0000 + // - FA(2) = 0x0000 + // - CV = 0x0400 + uInt16 myWriteOffset{0}; + + // The offset into ROM space for reading from RAM + // - xxSC = 0x0080 + // - FA(2) = 0x0100 + // - CV = 0x0000 + uInt16 myReadOffset{0}; + + // Flag, true if write port is at high and read port is at low address + bool myRamWpHigh{RAM_HIGH_WP}; + // Pointer to a dynamically allocated ROM image of the cartridge ByteBuffer myImage{nullptr}; @@ -182,6 +197,9 @@ class CartridgeEnhanced : public Cartridge // The size of extra RAM in ROM address space static constexpr uInt16 RAM_SIZE = 0; // default = none + // Write port for extra RAM is at low address by default + static constexpr bool RAM_HIGH_WP = false; + protected: /** Check hotspots and switch bank if triggered. diff --git a/test/roms/bankswitching/CV/ColorGotcha2600.bin b/test/roms/bankswitching/CV/ColorGotcha2600.bin new file mode 100644 index 000000000..318a2d41d Binary files /dev/null and b/test/roms/bankswitching/CV/ColorGotcha2600.bin differ diff --git a/test/roms/bankswitching/CV/Magicard Sample Program 1 - Display of Character Set $f100 (1982) (CommaVid).a26 b/test/roms/bankswitching/CV/Magicard Sample Program 1 - Display of Character Set $f100 (1982) (CommaVid).a26 new file mode 100644 index 000000000..6eafcbe51 Binary files /dev/null and b/test/roms/bankswitching/CV/Magicard Sample Program 1 - Display of Character Set $f100 (1982) (CommaVid).a26 differ diff --git a/test/roms/bankswitching/CV/Magicard Sample Program 2 - Memo Pad $f100 (1982) (CommaVid).a26 b/test/roms/bankswitching/CV/Magicard Sample Program 2 - Memo Pad $f100 (1982) (CommaVid).a26 new file mode 100644 index 000000000..14bfe5df1 Binary files /dev/null and b/test/roms/bankswitching/CV/Magicard Sample Program 2 - Memo Pad $f100 (1982) (CommaVid).a26 differ diff --git a/test/roms/bankswitching/CV/Magicard Sample Program 3 - Target Practice $f100 (1982) (CommaVid).a26 b/test/roms/bankswitching/CV/Magicard Sample Program 3 - Target Practice $f100 (1982) (CommaVid).a26 new file mode 100644 index 000000000..e650f2ce4 Binary files /dev/null and b/test/roms/bankswitching/CV/Magicard Sample Program 3 - Target Practice $f100 (1982) (CommaVid).a26 differ diff --git a/test/roms/bankswitching/CV/Magicard Sample Program 4 - Generating Your Own Display $f200 (1982) (CommaVid).a26 b/test/roms/bankswitching/CV/Magicard Sample Program 4 - Generating Your Own Display $f200 (1982) (CommaVid).a26 new file mode 100644 index 000000000..25a2264d5 Binary files /dev/null and b/test/roms/bankswitching/CV/Magicard Sample Program 4 - Generating Your Own Display $f200 (1982) (CommaVid).a26 differ diff --git a/test/roms/bankswitching/CV/Magicard Sample Program 5 - Life $f15e (1982) (CommaVid).a26 b/test/roms/bankswitching/CV/Magicard Sample Program 5 - Life $f15e (1982) (CommaVid).a26 new file mode 100644 index 000000000..3cf466119 Binary files /dev/null and b/test/roms/bankswitching/CV/Magicard Sample Program 5 - Life $f15e (1982) (CommaVid).a26 differ diff --git a/test/roms/bankswitching/CV/Video Life (CommaVid) [h1].a26 b/test/roms/bankswitching/CV/Video Life (CommaVid) [h1].a26 deleted file mode 100644 index a38a8e7b1..000000000 Binary files a/test/roms/bankswitching/CV/Video Life (CommaVid) [h1].a26 and /dev/null differ