From 3e4d82472b014a2ed2a53a6dfb1005e93a23a52b Mon Sep 17 00:00:00 2001 From: stephena Date: Sat, 21 Jan 2012 13:11:08 +0000 Subject: [PATCH] Added 'FA2' bankswitch support (from cdw) for upcoming Star Castle ROM. git-svn-id: svn://svn.code.sf.net/p/stella/code/trunk@2361 8b62c5a3-ac7e-4cc8-8f21-d9a121418aba --- Changes.txt | 3 + src/emucore/Cart.cxx | 7 + src/emucore/CartFA2.cxx | 317 ++++++++++++++++++++++++++++++++++++++++ src/emucore/CartFA2.hxx | 153 +++++++++++++++++++ src/emucore/module.mk | 1 + 5 files changed, 481 insertions(+) create mode 100644 src/emucore/CartFA2.cxx create mode 100644 src/emucore/CartFA2.hxx diff --git a/Changes.txt b/Changes.txt index a83115f97..e21aa7edd 100644 --- a/Changes.txt +++ b/Changes.txt @@ -42,6 +42,9 @@ * Updated ROM properties database for all Sega Genesis controller compatible ROMs. + * Added 'FA2' bankswitch scheme, thanks to code from Chris D. + Walton. This scheme will be used in an upcoming 'Star Castle' ROM. + * Fixed compile issues in Irix when using the default compiler instead of gcc. Thanks go to Rainer M. Canavan for this code. diff --git a/src/emucore/Cart.cxx b/src/emucore/Cart.cxx index 9236bef81..0386d72d7 100644 --- a/src/emucore/Cart.cxx +++ b/src/emucore/Cart.cxx @@ -44,6 +44,7 @@ #include "CartF8.hxx" #include "CartF8SC.hxx" #include "CartFA.hxx" +#include "CartFA2.hxx" #include "CartFE.hxx" #include "CartMC.hxx" #include "CartCV.hxx" @@ -175,6 +176,8 @@ Cartridge* Cartridge::create(const uInt8* image, uInt32 size, string& md5, cartridge = new CartridgeF8SC(image, size, settings); else if(type == "FA" || type == "FASC") cartridge = new CartridgeFA(image, size, settings); + else if(type == "FA2") + cartridge = new CartridgeFA2(image, size, settings); else if(type == "FE") cartridge = new CartridgeFE(image, size, settings); else if(type == "MC") @@ -377,6 +380,10 @@ string Cartridge::autodetectType(const uInt8* image, uInt32 size) else type = "F6"; } + else if(size == 24*1024) // 24K + { + type = "FA2"; + } else if(size == 29*1024) // 29K { type = "DPC+"; diff --git a/src/emucore/CartFA2.cxx b/src/emucore/CartFA2.cxx new file mode 100644 index 000000000..3d65af495 --- /dev/null +++ b/src/emucore/CartFA2.cxx @@ -0,0 +1,317 @@ +//============================================================================ +// +// 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-2012 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$ +//============================================================================ + +#include +#include + +#include "System.hxx" +#include "CartFA2.hxx" + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +CartridgeFA2::CartridgeFA2(const uInt8* image, uInt32 size, const Settings& settings) + : Cartridge(settings) +{ + // Copy the ROM image into my buffer + memcpy(myImage, image, BSPF_min(24576u, size)); + createCodeAccessBase(24576); + + // This cart contains 256 bytes extended RAM @ 0x1000 + registerRamArea(0x1000, 256, 0x100, 0x00); + + // Remember startup bank + myStartBank = 5; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +CartridgeFA2::~CartridgeFA2() +{ +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void CartridgeFA2::reset() +{ + // Initialize RAM + if(mySettings.getBool("ramrandom")) + for(uInt32 i = 0; i < 256; ++i) + myRAM[i] = mySystem->randGenerator().next(); + else + memset(myRAM, 0, 256); + + // Upon reset we switch to the startup bank + bank(myStartBank); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void CartridgeFA2::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(((0x1100 & mask) == 0) && ((0x1200 & mask) == 0)); + + System::PageAccess access(0, 0, 0, this, System::PA_READ); + + // Set the page accessing method for the RAM writing pages + access.type = System::PA_WRITE; + for(uInt32 j = 0x1000; j < 0x1100; j += (1 << shift)) + { + access.directPokeBase = &myRAM[j & 0x00FF]; + 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; + for(uInt32 k = 0x1100; k < 0x1200; k += (1 << shift)) + { + access.directPeekBase = &myRAM[k & 0x00FF]; + access.codeAccessBase = &myCodeAccessBase[0x100 + (k & 0x00FF)]; + mySystem->setPageAccess(k >> shift, access); + } + + // Install pages for the startup bank + bank(myStartBank); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +uInt8 CartridgeFA2::peek(uInt16 address) +{ + uInt16 peekAddress = address; + address &= 0x0FFF; + + // Switch banks if necessary + switch(address) + { + case 0x0FF5: + // Set the current bank to the first 4k bank + bank(0); + break; + + case 0x0FF6: + // Set the current bank to the second 4k bank + bank(1); + break; + + case 0x0FF7: + // 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); + break; + + case 0x0FF9: + // Set the current bank to the fifth 4k bank + bank(4); + break; + + case 0x0FFA: + // Set the current bank to the sixth 4k bank + bank(5); + break; + + default: + break; + } + + if(address < 0x0100) // Write port is at 0xF000 - 0xF100 (256 bytes) + { + // Reading from the write port triggers an unwanted write + uInt8 value = mySystem->getDataBusState(0xFF); + + if(bankLocked()) + return value; + else + { + triggerReadFromWritePort(peekAddress); + return myRAM[address] = value; + } + } + else + return myImage[(myCurrentBank << 12) + address]; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +bool CartridgeFA2::poke(uInt16 address, uInt8) +{ + address &= 0x0FFF; + + // Switch banks if necessary + switch(address) + { + case 0x0FF5: + // Set the current bank to the first 4k bank + bank(0); + break; + + case 0x0FF6: + // Set the current bank to the second 4k bank + bank(1); + break; + + case 0x0FF7: + // 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); + break; + + case 0x0FF9: + // Set the current bank to the fifth 4k bank + bank(4); + break; + + case 0x0FFA: + // Set the current bank to the sixth 4k bank + bank(5); + break; + + default: + break; + } + + // NOTE: This does not handle accessing RAM, however, this function + // should never be called for RAM because of the way page accessing + // has been setup + return false; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +bool CartridgeFA2::bank(uInt16 bank) +{ + if(bankLocked()) return false; + + // Remember what bank we're in + myCurrentBank = bank; + uInt16 offset = myCurrentBank << 12; + uInt16 shift = mySystem->pageShift(); + uInt16 mask = mySystem->pageMask(); + + System::PageAccess access(0, 0, 0, this, System::PA_READ); + + // Set the page accessing methods for the hot spots + for(uInt32 i = (0x1FF5 & ~mask); i < 0x2000; i += (1 << shift)) + { + access.codeAccessBase = &myCodeAccessBase[offset + (i & 0x0FFF)]; + mySystem->setPageAccess(i >> shift, access); + } + + // Setup the page access methods for the current bank + for(uInt32 address = 0x1200; address < (0x1FF5U & ~mask); + address += (1 << shift)) + { + access.directPeekBase = &myImage[offset + (address & 0x0FFF)]; + access.codeAccessBase = &myCodeAccessBase[offset + (address & 0x0FFF)]; + mySystem->setPageAccess(address >> shift, access); + } + return myBankChanged = true; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +uInt16 CartridgeFA2::bank() const +{ + return myCurrentBank; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +uInt16 CartridgeFA2::bankCount() const +{ + return 6; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +bool CartridgeFA2::patch(uInt16 address, uInt8 value) +{ + address &= 0x0FFF; + + if(address < 0x0200) + { + // Normally, a write to the read port won't do anything + // However, the patch command is special in that ignores such + // cart restrictions + myRAM[address & 0x00FF] = value; + } + else + myImage[(myCurrentBank << 12) + address] = value; + + return myBankChanged = true; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +const uInt8* CartridgeFA2::getImage(int& size) const +{ + size = 24576; + return myImage; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +bool CartridgeFA2::save(Serializer& out) const +{ + try + { + out.putString(name()); + out.putInt(myCurrentBank); + + // The 256 bytes of RAM + out.putInt(256); + for(uInt32 i = 0; i < 256; ++i) + out.putByte((char)myRAM[i]); + } + catch(const char* msg) + { + cerr << "ERROR: CartridgeFA2::save" << endl << " " << msg << endl; + return false; + } + + return true; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +bool CartridgeFA2::load(Serializer& in) +{ + try + { + if(in.getString() != name()) + return false; + + myCurrentBank = (uInt16) in.getInt(); + + uInt32 limit = (uInt32) in.getInt(); + for(uInt32 i = 0; i < limit; ++i) + myRAM[i] = (uInt8) in.getByte(); + } + catch(const char* msg) + { + cerr << "ERROR: CartridgeFA2::load" << endl << " " << msg << endl; + return false; + } + + // Remember what bank we were in + bank(myCurrentBank); + + return true; +} diff --git a/src/emucore/CartFA2.hxx b/src/emucore/CartFA2.hxx new file mode 100644 index 000000000..9c4bd8eae --- /dev/null +++ b/src/emucore/CartFA2.hxx @@ -0,0 +1,153 @@ +//============================================================================ +// +// 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-2012 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$ +//============================================================================ + +#ifndef CARTRIDGEFA2_HXX +#define CARTRIDGEFA2_HXX + +class System; + +#include "bspf.hxx" +#include "Cart.hxx" + +/** + This is an extended version of the CBS RAM Plus bankswitching scheme + supported by the Harmony cartridge. + + There are six 4K banks and 256 bytes of RAM. + + @author Chris D. Walton + @version $Id$ +*/ +class CartridgeFA2 : public Cartridge +{ + public: + /** + Create a new cartridge using the specified image + + @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) + */ + CartridgeFA2(const uInt8* image, uInt32 size, const Settings& settings); + + /** + Destructor + */ + virtual ~CartridgeFA2(); + + 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 "CartridgeFA2"; } + + 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: + // Indicates which bank is currently active + uInt16 myCurrentBank; + + // The 24K ROM image of the cartridge + uInt8 myImage[24576]; + + // The 256 bytes of RAM on the cartridge + uInt8 myRAM[256]; +}; + +#endif diff --git a/src/emucore/module.mk b/src/emucore/module.mk index b17cff8fb..9ccddb4ff 100644 --- a/src/emucore/module.mk +++ b/src/emucore/module.mk @@ -26,6 +26,7 @@ MODULE_OBJS := \ src/emucore/CartF8.o \ src/emucore/CartF8SC.o \ src/emucore/CartFA.o \ + src/emucore/CartFA2.o \ src/emucore/CartFE.o \ src/emucore/CartMC.o \ src/emucore/CartSB.o \