diff --git a/docs/index.html b/docs/index.html index 09a5d0b2e..9653c8de6 100644 --- a/docs/index.html +++ b/docs/index.html @@ -3453,6 +3453,7 @@ Ms Pac-Man (Stella extended codes): DPC+Enhanced DPC E0 8K Parker Bros E7 16K M-network + E78K 8K M-network EF 64K Homestar Runner EFSC 64K Homestar Runner + ram F0 Dynacom Megaboy diff --git a/src/debugger/gui/CartE78KWidget.cxx b/src/debugger/gui/CartE78KWidget.cxx new file mode 100644 index 000000000..b5bd94390 --- /dev/null +++ b/src/debugger/gui/CartE78KWidget.cxx @@ -0,0 +1,70 @@ +//============================================================================ +// +// 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-2017 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. +//============================================================================ + +#include "CartMNetwork.hxx" +#include "PopUpWidget.hxx" +#include "CartE78KWidget.hxx" + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +CartridgeE78KWidget::CartridgeE78KWidget( + GuiObject* boss, const GUI::Font& lfont, const GUI::Font& nfont, + int x, int y, int w, int h, + CartridgeMNetwork& cart) + : CartridgeMNetworkWidget(boss, lfont, nfont, x, y, w, h, cart) +{ + ostringstream info; + info << "E78K cartridge, 4 2K slices ROM + 2 1K RAM\n" + << "Lower 2K accessible @ $F000 - $F7FF\n" + << " Slice 0 - 2 of ROM (hotspots $FE4 to $FE6)\n" + << " Slice 0 (1K) of RAM (hotspot $FE7)\n" + << " $F400 - $F7FF (R), $F000 - $F3FF (W)\n" + << "256B RAM accessible @ $F800 - $F9FF\n" + << " Hotspots $FE8 - $FEB (256B of RAM slice 1)\n" + << " $F900 - $F9FF (R), $F800 - $F8FF (W)\n" + << "Upper 1.5K ROM accessible @ $FA00 - $FFFF\n" + << " Always points to last 1.5K of ROM\n" + << "Startup slices = " << cart.myStartBank << " / 0\n"; + +#if 0 + // Eventually, we should query this from the debugger/disassembler + uInt16 start = (cart.myImage[size - 3] << 8) | cart.myImage[size - 4]; + start -= start % 0x1000; + info << "Bank RORG" << " = $" << HEX4 << start << "\n"; +#endif + + initialize(boss, cart, info); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +const char* CartridgeE78KWidget::getSpotLower(int idx) +{ + static const char* const spot_lower[] = { + "0 - ROM ($FFE4)", "1 - ROM ($FFE5)", "2 - ROM ($FFE6)", "3 - RAM ($FFE7)" + }; + + return spot_lower[idx]; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +const char* CartridgeE78KWidget::getSpotUpper(int idx) +{ + static const char* const spot_upper[] = { + "0 - RAM ($FFE8)", "1 - RAM ($FFE9)", "2 - RAM ($FFEA)", "3 - RAM ($FFEB)" + }; + + return spot_upper[idx]; +} diff --git a/src/debugger/gui/CartE78KWidget.hxx b/src/debugger/gui/CartE78KWidget.hxx new file mode 100644 index 000000000..c844bb30f --- /dev/null +++ b/src/debugger/gui/CartE78KWidget.hxx @@ -0,0 +1,45 @@ +//============================================================================ +// +// 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-2017 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 CARTRIDGEE78K_WIDGET_HXX +#define CARTRIDGEE78K_WIDGET_HXX + +#include "CartMNetworkWidget.hxx" + +class CartridgeE78KWidget : public CartridgeMNetworkWidget +{ + public: + CartridgeE78KWidget(GuiObject* boss, const GUI::Font& lfont, + const GUI::Font& nfont, + int x, int y, int w, int h, + CartridgeMNetwork& cart); + virtual ~CartridgeE78KWidget() = default; + + protected: + const char* getSpotLower(int idx); + const char* getSpotUpper(int idx); + + private: + // Following constructors and assignment operators not supported + CartridgeE78KWidget() = delete; + CartridgeE78KWidget(const CartridgeE78KWidget&) = delete; + CartridgeE78KWidget(CartridgeE78KWidget&&) = delete; + CartridgeE78KWidget& operator=(const CartridgeE78KWidget&) = delete; + CartridgeE78KWidget& operator=(CartridgeE78KWidget&&) = delete; +}; + +#endif diff --git a/src/debugger/gui/CartMNetworkWidget.cxx b/src/debugger/gui/CartMNetworkWidget.cxx new file mode 100644 index 000000000..f14f1ead3 --- /dev/null +++ b/src/debugger/gui/CartMNetworkWidget.cxx @@ -0,0 +1,169 @@ +//============================================================================ +// +// 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-2017 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. +//============================================================================ + +//#include "CartE7.hxx" +#include "CartMNetwork.hxx" +#include "PopUpWidget.hxx" +#include "CartMNetworkWidget.hxx" + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +CartridgeMNetworkWidget::CartridgeMNetworkWidget( + GuiObject* boss, const GUI::Font& lfont, const GUI::Font& nfont, + int x, int y, int w, int h, + CartridgeMNetwork& cart) + : CartDebugWidget(boss, lfont, nfont, x, y, w, h), + myCart(cart) +{} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void CartridgeMNetworkWidget::initialize(GuiObject* boss, CartridgeMNetwork& cart, ostringstream& info) +{ + uInt32 size = cart.bankCount() * cart.BANK_SIZE; + + int xpos = 10, + ypos = addBaseInformation(size, "M-Network", info.str(), 15) + + myLineHeight; + + VariantList items0, items1; + for(int i = 0; i < cart.bankCount(); ++i) + VarList::push_back(items0, getSpotLower(i)); + for(int i = 0; i < 4; ++i) + VarList::push_back(items1, getSpotUpper(i)); + + const int lwidth = _font.getStringWidth("Set slice for upper 256B "), + fwidth = _font.getStringWidth("3 - RAM ($FFEB)"); + myLower2K = + new PopUpWidget(boss, _font, xpos, ypos - 2, fwidth, myLineHeight, items0, + "Set slice for lower 2K ", lwidth, kLowerChanged); + myLower2K->setTarget(this); + addFocusWidget(myLower2K); + ypos += myLower2K->getHeight() + 4; + + myUpper256B = + new PopUpWidget(boss, _font, xpos, ypos - 2, fwidth, myLineHeight, items1, + "Set slice for upper 256B ", lwidth, kUpperChanged); + myUpper256B->setTarget(this); + addFocusWidget(myUpper256B); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void CartridgeMNetworkWidget::saveOldState() +{ + myOldState.internalram.clear(); + + for(uInt32 i = 0; i < this->internalRamSize(); i++) + { + myOldState.internalram.push_back(myCart.myRAM[i]); + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void CartridgeMNetworkWidget::loadConfig() +{ + myLower2K->setSelectedIndex(myCart.myCurrentSlice[0]); + myUpper256B->setSelectedIndex(myCart.myCurrentRAM); + + CartDebugWidget::loadConfig(); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void CartridgeMNetworkWidget::handleCommand(CommandSender* sender, + int cmd, int data, int id) +{ + myCart.unlockBank(); + + switch(cmd) + { + case kLowerChanged: + myCart.bank(myLower2K->getSelected()); + break; + case kUpperChanged: + myCart.bankRAM(myUpper256B->getSelected()); + break; + } + + myCart.lockBank(); + invalidate(); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +string CartridgeMNetworkWidget::bankState() +{ + ostringstream& buf = buffer(); + + buf << "Slices: " << std::dec + << getSpotLower(myCart.myCurrentSlice[0]) << " / " + << getSpotUpper(myCart.myCurrentRAM); + + return buf.str(); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +uInt32 CartridgeMNetworkWidget::internalRamSize() +{ + return 2048; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +uInt32 CartridgeMNetworkWidget::internalRamRPort(int start) +{ + return 0x0000 + start; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +string CartridgeMNetworkWidget::internalRamDescription() +{ + ostringstream desc; + desc << "First 1K accessible via:\n" + << " $F000 - $F3FF used for Write Access\n" + << " $F400 - $F7FF used for Read Access\n" + << "256K of second 1K accessible via:\n" + << " $F800 - $F8FF used for Write Access\n" + << " $F900 - $F9FF used for Read Access"; + + return desc.str(); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +const ByteArray& CartridgeMNetworkWidget::internalRamOld(int start, int count) +{ + myRamOld.clear(); + for(int i = 0; i < count; i++) + myRamOld.push_back(myOldState.internalram[start + i]); + return myRamOld; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +const ByteArray& CartridgeMNetworkWidget::internalRamCurrent(int start, int count) +{ + myRamCurrent.clear(); + for(int i = 0; i < count; i++) + myRamCurrent.push_back(myCart.myRAM[start + i]); + return myRamCurrent; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void CartridgeMNetworkWidget::internalRamSetValue(int addr, uInt8 value) +{ + myCart.myRAM[addr] = value; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +uInt8 CartridgeMNetworkWidget::internalRamGetValue(int addr) +{ + return myCart.myRAM[addr]; +} diff --git a/src/debugger/gui/CartMNetworkWidget.hxx b/src/debugger/gui/CartMNetworkWidget.hxx new file mode 100644 index 000000000..08eb6fe52 --- /dev/null +++ b/src/debugger/gui/CartMNetworkWidget.hxx @@ -0,0 +1,83 @@ +//============================================================================ +// +// 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-2017 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 CARTRIDGE_MNETWORK_WIDGET_HXX +#define CARTRIDGE_MNETWORK_WIDGET_HXX + +class CartridgeMNetwork; +class PopUpWidget; + +#include "CartDebugWidget.hxx" + +class CartridgeMNetworkWidget : public CartDebugWidget +{ + public: + CartridgeMNetworkWidget(GuiObject* boss, const GUI::Font& lfont, + const GUI::Font& nfont, + int x, int y, int w, int h, + //CartridgeE7& cart); + CartridgeMNetwork& cart); + virtual ~CartridgeMNetworkWidget() = default; + + protected: + PopUpWidget *myLower2K, *myUpper256B; + + //CartridgeE7& myCart; + CartridgeMNetwork& myCart; + + struct CartState + { + ByteArray internalram; + }; + CartState myOldState; + + enum + { + kLowerChanged = 'lwCH', + kUpperChanged = 'upCH' + }; + + protected: + void initialize(GuiObject* boss, CartridgeMNetwork& cart, ostringstream& info); + virtual const char* getSpotLower(int idx) = 0; + virtual const char* getSpotUpper(int idx) = 0; + + private: + void saveOldState() override; + void loadConfig() override; + void handleCommand(CommandSender* sender, int cmd, int data, int id) override; + string bankState(); + // start of functions for Cartridge RAM tab + uInt32 internalRamSize() override; + uInt32 internalRamRPort(int start) override; + string internalRamDescription(); + const ByteArray& internalRamOld(int start, int count) override; + const ByteArray& internalRamCurrent(int start, int count) override; + void internalRamSetValue(int addr, uInt8 value) override; + uInt8 internalRamGetValue(int addr) override; + // end of functions for Cartridge RAM tab + + private: + // Following constructors and assignment operators not supported + CartridgeMNetworkWidget() = delete; + CartridgeMNetworkWidget(const CartridgeMNetworkWidget&) = delete; + CartridgeMNetworkWidget(CartridgeMNetworkWidget&&) = delete; + CartridgeMNetworkWidget& operator=(const CartridgeMNetworkWidget&) = delete; + CartridgeMNetworkWidget& operator=(CartridgeMNetworkWidget&&) = delete; +}; + +#endif diff --git a/src/emucore/CartE78K.cxx b/src/emucore/CartE78K.cxx new file mode 100644 index 000000000..f1660389b --- /dev/null +++ b/src/emucore/CartE78K.cxx @@ -0,0 +1,47 @@ +//============================================================================ +// +// 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-2017 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. +//============================================================================ + +#include "System.hxx" +#include "CartE78K.hxx" + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +CartridgeE78K::CartridgeE78K(const BytePtr& image, uInt32 size, + const Settings& settings) + : CartridgeMNetwork(image, size, settings) +{ + initialize(image, size); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void CartridgeE78K::checkSwitchBank(uInt16 address) +{ + // Switch banks if necessary + if((address >= 0x0FE4) && (address <= 0x0FE7)) + { + bank(address & 0x0003); + } + else if((address >= 0x0FE8) && (address <= 0x0FEB)) + { + bankRAM(address & 0x0003); + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +uInt16 CartridgeE78K::bankCount() const +{ + return 4; +} diff --git a/src/emucore/CartE78K.hxx b/src/emucore/CartE78K.hxx new file mode 100644 index 000000000..1383d8909 --- /dev/null +++ b/src/emucore/CartE78K.hxx @@ -0,0 +1,86 @@ +//============================================================================ +// +// 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-2017 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 CARTRIDGE_E78K_HXX +#define CARTRIDGE_E78K_HXX + +#include "bspf.hxx" +#include "Cart.hxx" +#ifdef DEBUGGER_SUPPORT + #include "CartE78KWidget.hxx" +#endif +#include "CartMNetwork.hxx" + +/** + This is the cartridge class for 8K M-Network bankswitched games. + + @author Bradford W. Mott, Thomas Jentzsch +*/ +class CartridgeE78K : public CartridgeMNetwork +{ + 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) + */ + CartridgeE78K(const BytePtr& image, uInt32 size, const Settings& settings); + virtual ~CartridgeE78K() = default; + + public: + /** + Query the number of banks supported by the cartridge. + */ + uInt16 bankCount() const override; + + /** + Get a descriptor for the device name (used in error checking). + + @return The name of the object + */ + string name() const override { return "CartridgeE78K"; } + + #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) override + { + return new CartridgeE78KWidget(boss, lfont, nfont, x, y, w, h, *this); + } + #endif + + private: + /** + Check hotspots and switch bank if triggered. + */ + void checkSwitchBank(uInt16 address) override; + + private: + // Following constructors and assignment operators not supported + CartridgeE78K() = delete; + CartridgeE78K(const CartridgeE78K&) = delete; + CartridgeE78K(CartridgeE78K&&) = delete; + CartridgeE78K& operator=(const CartridgeE78K&) = delete; + CartridgeE78K& operator=(CartridgeE78K&&) = delete; +}; + +#endif \ No newline at end of file diff --git a/src/emucore/CartMNetwork.cxx b/src/emucore/CartMNetwork.cxx new file mode 100644 index 000000000..d8509254c --- /dev/null +++ b/src/emucore/CartMNetwork.cxx @@ -0,0 +1,296 @@ +//============================================================================ +// +// 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-2017 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. +//============================================================================ + +#include "System.hxx" +#include "CartMNetwork.hxx" + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +CartridgeMNetwork::CartridgeMNetwork(const BytePtr& image, uInt32 size, + const Settings& settings) + : Cartridge(settings), + myCurrentRAM(0) +{} + +void CartridgeMNetwork::initialize(const BytePtr& image, uInt32 size) +{ + // Copy the ROM image into my buffer + memcpy(myImage, image.get(), std::min(bankCount() * BANK_SIZE, size)); + createCodeAccessBase(bankCount() * BANK_SIZE + RAM_SIZE); + + // Remember startup bank + myStartBank = 0; + myFixedSlice = bankCount() - 1; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void CartridgeMNetwork::reset() +{ + initializeRAM(myRAM, RAM_SIZE); + + // Install some default banks for the RAM and first segment + bankRAM(0); + bank(myStartBank); + + myBankChanged = true; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void CartridgeMNetwork::install(System& system) +{ + mySystem = &system; + + System::PageAccess access(this, System::PA_READ); + + // Set the page accessing methods for the hot spots + for(uInt16 addr = (0x1FE0 & ~System::PAGE_MASK); addr < 0x2000; + addr += System::PAGE_SIZE) + { + access.codeAccessBase = &myCodeAccessBase[0x1fc0]; + mySystem->setPageAccess(addr, access); + } + + // Setup the second segment to always point to the last ROM slice + for(uInt16 addr = 0x1A00; addr < (0x1FE0U & ~System::PAGE_MASK); + addr += System::PAGE_SIZE) + { + access.directPeekBase = &myImage[myFixedSlice * BANK_SIZE + (addr & (BANK_SIZE-1))]; + access.codeAccessBase = &myCodeAccessBase[myFixedSlice * BANK_SIZE + (addr & (BANK_SIZE-1))]; + mySystem->setPageAccess(addr, access); + } + myCurrentSlice[1] = myFixedSlice; + + // Install some default banks for the RAM and first segment + bankRAM(0); + bank(myStartBank); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +uInt8 CartridgeMNetwork::peek(uInt16 address) +{ + uInt16 peekAddress = address; + address &= 0x0FFF; + + // Switch banks if necessary + checkSwitchBank(address); + + if((myCurrentSlice[0] == myFixedSlice) && (address < 0x0400)) + { + // Reading from the 1K write port @ $1000 triggers an unwanted write + uInt8 value = mySystem->getDataBusState(0xFF); + + if(bankLocked()) + return value; + else + { + triggerReadFromWritePort(peekAddress); + return myRAM[address & 0x03FF] = value; + } + } + else if((address >= 0x0800) && (address <= 0x08FF)) + { + // Reading from the 256B write port @ $1800 triggers an unwanted write + uInt8 value = mySystem->getDataBusState(0xFF); + + if(bankLocked()) + return value; + else + { + triggerReadFromWritePort(peekAddress); + return myRAM[1024 + (myCurrentRAM << 8) + (address & 0x00FF)] = value; + } + } + else + return myImage[(myCurrentSlice[address >> 11] << 11) + (address & (BANK_SIZE-1))]; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +bool CartridgeMNetwork::poke(uInt16 address, uInt8) +{ + address &= 0x0FFF; + + // Switch banks if necessary + checkSwitchBank(address); + + // NOTE: This does not handle writing to RAM, however, this + // method should never be called for RAM because of the + // way page accessing has been setup + return false; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void CartridgeMNetwork::bankRAM(uInt16 bank) +{ + if(bankLocked()) return; + + // Remember what bank we're in + myCurrentRAM = bank; + uInt16 offset = bank << 8; + + // Setup the page access methods for the current bank + System::PageAccess access(this, System::PA_WRITE); + + // Set the page accessing method for the 256 bytes of RAM writing pages + for(uInt16 addr = 0x1800; addr < 0x1900; addr += System::PAGE_SIZE) + { + access.directPokeBase = &myRAM[1024 + offset + (addr & 0x00FF)]; + access.codeAccessBase = &myCodeAccessBase[0x2000 + 1024 + offset + (addr & 0x00FF)]; + mySystem->setPageAccess(addr, access); + } + + // Set the page accessing method for the 256 bytes of RAM reading pages + access.directPokeBase = nullptr; + access.type = System::PA_READ; + for(uInt16 addr = 0x1900; addr < 0x1A00; addr += System::PAGE_SIZE) + { + access.directPeekBase = &myRAM[1024 + offset + (addr & 0x00FF)]; + access.codeAccessBase = &myCodeAccessBase[0x2000 + 1024 + offset + (addr & 0x00FF)]; + mySystem->setPageAccess(addr, access); + } + myBankChanged = true; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +bool CartridgeMNetwork::bank(uInt16 slice) +{ + if(bankLocked()) return false; + + // Remember what bank we're in + myCurrentSlice[0] = slice; + uInt16 offset = slice << 11; + + // Setup the page access methods for the current bank + if(slice != myFixedSlice) + { + System::PageAccess access(this, System::PA_READ); + + // Map ROM image into first segment + for(uInt16 addr = 0x1000; addr < 0x1800; addr += System::PAGE_SIZE) + { + access.directPeekBase = &myImage[offset + (addr & (BANK_SIZE-1))]; + access.codeAccessBase = &myCodeAccessBase[offset + (addr & (BANK_SIZE-1))]; + mySystem->setPageAccess(addr, access); + } + } + else + { + System::PageAccess access(this, System::PA_WRITE); + + // Set the page accessing method for the 1K slice of RAM writing pages + for(uInt16 addr = 0x1000; addr < 0x1400; addr += System::PAGE_SIZE) + { + access.directPokeBase = &myRAM[addr & 0x03FF]; + access.codeAccessBase = &myCodeAccessBase[0x2000 + (addr & 0x03FF)]; + mySystem->setPageAccess(addr, access); + } + + // Set the page accessing method for the 1K slice of RAM reading pages + access.directPokeBase = nullptr; + access.type = System::PA_READ; + for(uInt16 addr = 0x1400; addr < 0x1800; addr += System::PAGE_SIZE) + { + access.directPeekBase = &myRAM[addr & 0x03FF]; + access.codeAccessBase = &myCodeAccessBase[0x2000 + (addr & 0x03FF)]; + mySystem->setPageAccess(addr, access); + } + } + return myBankChanged = true; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +uInt16 CartridgeMNetwork::getBank() const +{ + return myCurrentSlice[0]; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +bool CartridgeMNetwork::patch(uInt16 address, uInt8 value) +{ + address = address & 0x0FFF; + + if(address < 0x0800) + { + if(myCurrentSlice[0] == myFixedSlice) + { + // 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 & 0x03FF] = value; + } + else + myImage[(myCurrentSlice[0] << 11) + (address & (BANK_SIZE-1))] = value; + } + else if(address < 0x0900) + { + // Normally, a write to the read port won't do anything + // However, the patch command is special in that ignores such + // cart restrictions + myRAM[1024 + (myCurrentRAM << 8) + (address & 0x00FF)] = value; + } + else + myImage[(myCurrentSlice[address >> 11] << 11) + (address & (BANK_SIZE-1))] = value; + + return myBankChanged = true; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +const uInt8* CartridgeMNetwork::getImage(uInt32& size) const +{ + size = bankCount() * BANK_SIZE; + return myImage; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +bool CartridgeMNetwork::save(Serializer& out) const +{ + try + { + out.putString(name()); + out.putShortArray(myCurrentSlice, 2); + out.putShort(myCurrentRAM); + out.putByteArray(myRAM, RAM_SIZE); + } catch(...) + { + cerr << "ERROR: " << name() << "::save" << endl; + return false; + } + + return true; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +bool CartridgeMNetwork::load(Serializer& in) +{ + try + { + if(in.getString() != name()) + return false; + + in.getShortArray(myCurrentSlice, 2); + myCurrentRAM = in.getShort(); + in.getByteArray(myRAM, RAM_SIZE); + } catch(...) + { + cerr << "ERROR: " << name() << "::load" << endl; + return false; + } + + // Set up the previously used banks for the RAM and segment + bankRAM(myCurrentRAM); + bank(myCurrentSlice[0]); + + return true; +} diff --git a/src/emucore/CartMNetwork.hxx b/src/emucore/CartMNetwork.hxx new file mode 100644 index 000000000..deedc1b9e --- /dev/null +++ b/src/emucore/CartMNetwork.hxx @@ -0,0 +1,202 @@ + +//============================================================================ +// +// 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-2017 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 CARTRIDGE_MNETWORK_HXX +#define CARTRIDGE_MNETWORK_HXX + +class System; + +#include "bspf.hxx" +#include "Cart.hxx" + +/** + This is the abstract cartridge class for M-Network + bankswitched games. + In this bankswitching scheme the 2600's 4K cartridge address + space is broken into two 2K segments. + + Kevin Horton describes E7 as follows: + + "Only M-Network used this scheme. This has to be the + most complex method used in any cart! :-) It allows + for the capability of 2K of RAM; although it doesn't + have to be used (in fact, only one cart used it). + There are now 8 2K banks, instead of 4. The last 2K + in the cart always points to the last 2K of the ROM + image, while the first 2K is selectable. You access + 1FE0 to 1FE6 to select which 2K bank. Note that you + cannot select the last 2K of the ROM image into the + lower 2K of the cart! Accessing 1FE7 selects 1K of + RAM at 1000-17FF instead of ROM! The 2K of RAM is + broken up into two 1K sections. One 1K section is + mapped in at 1000-17FF if 1FE7 has been accessed. + 1000-13FF is the write port, while 1400-17FF is the + read port. The second 1K of RAM appears at 1800-19FF. + 1800-18FF is the write port while 1900-19FF is the + read port. You select which 256 byte block appears + here by accessing 1FE8 to 1FEB. + + This cart reports having 8 banks; 1 for each of the possible 7 + slices in the lower 2K area, and the last for RAM in the lower + 2K area." + + There are 8K and 16K variations. + + @author Bradford W. Mott, Thomas Jentzsch +*/ +class CartridgeMNetwork : public Cartridge +{ + friend class CartridgeMNetworkWidget; + friend class CartridgeE7Widget; + friend class CartridgeE78KWidget; + + 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) + */ + CartridgeMNetwork(const BytePtr& image, uInt32 size, const Settings& settings); + virtual ~CartridgeMNetwork() = default; + + public: + /** + Reset device to its power-on state + */ + 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; + + /** + Install pages for the specified bank in the system. + + @param bank The bank that should be installed in the system + */ + bool bank(uInt16 bank) override; + + /** + Get the current bank. + */ + uInt16 getBank() const 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(uInt32& 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; + + public: + /** + Get the byte at the specified address. + + @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: + /** + Class initialization + */ + void initialize(const BytePtr& image, uInt32 size); + + /** + Install pages for the specified 256 byte bank of RAM + + @param bank The bank that should be installed in the system + */ + void bankRAM(uInt16 bank); + + private: + const uInt32 BANK_SIZE = 0x800; + const uInt32 RAM_SIZE = 0x800; + + /** + Check hotspots and switch bank if triggered. + */ + virtual void checkSwitchBank(uInt16 address) = 0; + + private: + // The 16K ROM image of the cartridge + uInt8 myImage[16384]; + + // The 2048 bytes of RAM + uInt8 myRAM[2048]; + + // Indicates which slice is in the segment + uInt16 myCurrentSlice[2]; + + // Indicates which 256 byte bank of RAM is being used + uInt16 myCurrentRAM; + + // The number of the fixed slice (bankCount() - 1) + uInt32 myFixedSlice; + + private: + // Following constructors and assignment operators not supported + CartridgeMNetwork() = delete; + CartridgeMNetwork(const CartridgeMNetwork&) = delete; + CartridgeMNetwork(CartridgeMNetwork&&) = delete; + CartridgeMNetwork& operator=(const CartridgeMNetwork&) = delete; + CartridgeMNetwork& operator=(CartridgeMNetwork&&) = delete; +}; + +#endif