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