diff --git a/Changes.txt b/Changes.txt
index 2d44ec96e..eb605d984 100644
--- a/Changes.txt
+++ b/Changes.txt
@@ -34,6 +34,8 @@
* Acclerated emulation up to ~15% (ARM).
+ * Added limited GameLine Master Module bankswitching support.
+
* Added BUS bankswitching support for some older demos.
* Fixed broken 7800 pause key support.
diff --git a/docs/index.html b/docs/index.html
index 51d20a55a..b95740a3c 100644
--- a/docs/index.html
+++ b/docs/index.html
@@ -4947,6 +4947,7 @@ Ms Pac-Man (Stella extended codes):
FA2 | CBS RAM Plus 24/28K | .FA2 |
FC | Amiga Power Play Aracde 16/32K | .FC |
FE | 8K Activision (aka SCABS) | .FE |
+ GL | GameLine Master Module | .GL |
MDM | Menu Driven Megacart | .MDM |
MVC | Movie Cart | .MVC |
SB | 128-256K SUPERbanking | .SB |
diff --git a/src/debugger/gui/Cart4KWidget.cxx b/src/debugger/gui/Cart4KWidget.cxx
index 4d9c75b69..bfd9bba00 100644
--- a/src/debugger/gui/Cart4KWidget.cxx
+++ b/src/debugger/gui/Cart4KWidget.cxx
@@ -25,16 +25,6 @@ Cartridge4KWidget::Cartridge4KWidget(
: CartridgeEnhancedWidget(boss, lfont, nfont, x, y, w, h, cart)
{
initialize();
-
- //// Eventually, we should query this from the debugger/disassembler
- //uInt16 start = (cart.myImage[0xFFD] << 8) | cart.myImage[0xFFC];
- //start -= start % 0x1000;
-
- //ostringstream info;
- //info << "Standard 4K cartridge, non-bankswitched\n"
- // << "Accessible @ $" << Common::Base::HEX4 << start << " - "
- // << "$" << (start + 0xFFF);
- //addBaseInformation(4096, "Atari", info.str());
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
diff --git a/src/debugger/gui/CartEnhancedWidget.cxx b/src/debugger/gui/CartEnhancedWidget.cxx
index 4b017a984..3cd486d5f 100644
--- a/src/debugger/gui/CartEnhancedWidget.cxx
+++ b/src/debugger/gui/CartEnhancedWidget.cxx
@@ -181,11 +181,20 @@ void CartridgeEnhancedWidget::bankList(uInt16 bankCount, int seg, VariantList& i
{
width = 0;
+ const bool hasRamBanks = myCart.myRamBankCount > 0;
+
for(int bank = 0; bank < bankCount; ++bank)
{
ostringstream buf;
+ const bool isRamBank = (bank >= myCart.romBankCount());
+ const int bankNum = (bank - (isRamBank ? myCart.romBankCount() : 0));
+
+ buf << std::setw(bankNum < 10 ? 2 : 1) << "#" << std::dec << bankNum;
+ if(isRamBank) // was RAM mapped here?
+ buf << " RAM";
+ else if (hasRamBanks)
+ buf << " ROM";
- buf << std::setw(bank < 10 ? 2 : 1) << "#" << std::dec << bank;
if(myCart.hotspot() != 0 && myHotspotDelta > 0)
buf << " " << hotspotStr(bank, seg);
VarList::push_back(items, buf.str());
@@ -196,7 +205,7 @@ void CartridgeEnhancedWidget::bankList(uInt16 bankCount, int seg, VariantList& i
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void CartridgeEnhancedWidget::bankSelect(int& ypos)
{
- if(myCart.romBankCount() > 1)
+ if(myCart.romBankCount() + myCart.ramBankCount() > 1)
{
constexpr int xpos = 2;
@@ -208,7 +217,7 @@ void CartridgeEnhancedWidget::bankSelect(int& ypos)
VariantList items;
int pw = 0;
- bankList(myCart.romBankCount(), seg, items, pw);
+ bankList(myCart.romBankCount() + myCart.ramBankCount(), seg, items, pw);
// create widgets
ostringstream buf;
@@ -249,7 +258,6 @@ string CartridgeEnhancedWidget::bankState()
const int bank = myCart.getSegmentBank(seg);
const bool isRamBank = (bank >= myCart.romBankCount());
-
if(seg > 0)
buf << " / ";
diff --git a/src/debugger/gui/CartGLWidget.cxx b/src/debugger/gui/CartGLWidget.cxx
new file mode 100644
index 000000000..b4b6668ce
--- /dev/null
+++ b/src/debugger/gui/CartGLWidget.cxx
@@ -0,0 +1,50 @@
+//============================================================================
+//
+// 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-2023 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 "CartGL.hxx"
+#include "CartGLWidget.hxx"
+
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+CartridgeGLWidget::CartridgeGLWidget(
+ GuiObject* boss, const GUI::Font& lfont, const GUI::Font& nfont,
+ int x, int y, int w, int h, CartridgeGL& cart)
+ : CartridgeEnhancedWidget(boss, lfont, nfont, x, y, w, h, cart)
+{
+ initialize();
+}
+
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+string CartridgeGLWidget::description()
+{
+ ostringstream info;
+
+ info << "GameLine Master Module cartridge, 4K ROM, 10/12K RAM\n"
+ << "mapped into four 1K segments\n"
+ << "THIS SCHEME IS NOT FULLY IMPLEMENTED OR TESTED";
+
+ return info.str();
+}
+
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+string CartridgeGLWidget::internalRamDescription()
+{
+ ostringstream desc;
+
+ desc << "Accessible 1K" << " at a time";
+
+ return desc.str();
+}
diff --git a/src/debugger/gui/CartGLWidget.hxx b/src/debugger/gui/CartGLWidget.hxx
new file mode 100644
index 000000000..70dd87fe2
--- /dev/null
+++ b/src/debugger/gui/CartGLWidget.hxx
@@ -0,0 +1,50 @@
+//============================================================================
+//
+// 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-2023 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 CARTRIDGEGL_WIDGET_HXX
+#define CARTRIDGEGL_WIDGET_HXX
+
+class CartridgeGL;
+
+#include "CartEnhanced.hxx"
+
+class CartridgeGLWidget : public CartridgeEnhancedWidget
+{
+ public:
+ CartridgeGLWidget(GuiObject* boss, const GUI::Font& lfont,
+ const GUI::Font& nfont,
+ int x, int y, int w, int h,
+ CartridgeGL& cart);
+ ~CartridgeGLWidget() override = default;
+
+ private:
+ string manufacturer() override { return "Control Video Corporation (CVC)"; }
+
+ string description() override;
+
+ string internalRamDescription() override;
+
+ private:
+ // Following constructors and assignment operators not supported
+ CartridgeGLWidget() = delete;
+ CartridgeGLWidget(const CartridgeGLWidget&) = delete;
+ CartridgeGLWidget(CartridgeGLWidget&&) = delete;
+ CartridgeGLWidget& operator=(const CartridgeGLWidget&) = delete;
+ CartridgeGLWidget& operator=(CartridgeGLWidget&&) = delete;
+};
+
+#endif
diff --git a/src/debugger/gui/module.mk b/src/debugger/gui/module.mk
index f0c64410c..45ea9b5ae 100644
--- a/src/debugger/gui/module.mk
+++ b/src/debugger/gui/module.mk
@@ -46,6 +46,7 @@ MODULE_OBJS := \
src/debugger/gui/CartFAWidget.o \
src/debugger/gui/CartFCWidget.o \
src/debugger/gui/CartFEWidget.o \
+ src/debugger/gui/CartGLWidget.o \
src/debugger/gui/CartMDMWidget.o \
src/debugger/gui/CartRamWidget.o \
src/debugger/gui/CartSBWidget.o \
diff --git a/src/emucore/Bankswitch.cxx b/src/emucore/Bankswitch.cxx
index 7f9ceae81..afabf2575 100644
--- a/src/emucore/Bankswitch.cxx
+++ b/src/emucore/Bankswitch.cxx
@@ -119,6 +119,7 @@ Bankswitch::BSList = {{
{ "FA2" , "FA2 (CBS RAM Plus 24-32K)" },
{ "FC" , "FC (32K Amiga)" },
{ "FE" , "FE (8K Activision)" },
+ { "GL" , "GL (GameLine Master Module)" },
{ "MDM" , "MDM (Menu Driven Megacart)" },
{ "MVC" , "MVC (Movie Cart)" },
{ "SB" , "SB (128-256K SUPERbank)" },
@@ -129,7 +130,7 @@ Bankswitch::BSList = {{
{ "WDSW" , "WDSW (Pink Panther, bad)" },
{ "X07" , "X07 (64K AtariAge)" },
#if defined(CUSTOM_ARM)
- { "CUSTOM" , "CUSTOM (ARM)" }
+ { "CUSTOM" , "CUSTOM (ARM)" }
#endif
}};
@@ -181,6 +182,7 @@ Bankswitch::Sizes = {{
{ 24_KB, 32_KB }, // _FA2
{ 32_KB, 32_KB }, // _FC
{ 8_KB, 8_KB }, // _FE
+ { 4_KB, 4_KB }, // _GL
{ 8_KB, Bankswitch::any_KB }, // _MDM
{ 1024_KB, Bankswitch::any_KB }, // _MVC
{ 128_KB, 256_KB }, // _SB
@@ -270,6 +272,7 @@ Bankswitch::ExtensionMap Bankswitch::ourExtensions = {
{ "FA2" , Bankswitch::Type::_FA2 },
{ "FC" , Bankswitch::Type::_FC },
{ "FE" , Bankswitch::Type::_FE },
+ { "GL" , Bankswitch::Type::_GL },
{ "MDM" , Bankswitch::Type::_MDM },
{ "MVC" , Bankswitch::Type::_MVC },
{ "SB" , Bankswitch::Type::_SB },
@@ -329,6 +332,7 @@ Bankswitch::NameToTypeMap Bankswitch::ourNameToTypes = {
{ "FA2" , Bankswitch::Type::_FA2 },
{ "FC" , Bankswitch::Type::_FC },
{ "FE" , Bankswitch::Type::_FE },
+ { "GL" , Bankswitch::Type::_GL },
{ "MDM" , Bankswitch::Type::_MDM },
{ "MVC" , Bankswitch::Type::_MVC },
{ "SB" , Bankswitch::Type::_SB },
diff --git a/src/emucore/Bankswitch.hxx b/src/emucore/Bankswitch.hxx
index 229f30e08..d7eebd9a4 100644
--- a/src/emucore/Bankswitch.hxx
+++ b/src/emucore/Bankswitch.hxx
@@ -43,8 +43,8 @@ class Bankswitch
_4K, _4KSC, _AR, _BF, _BFSC, _BUS, _CDF, _CM,
_CTY, _CV, _DF, _DFSC, _DPC, _DPCP, _E0, _E7,
_EF, _EFSC, _F0, _F4, _F4SC, _F6, _F6SC, _F8,
- _F8SC, _FA, _FA2, _FC, _FE, _MDM, _MVC, _SB,
- _TVBOY, _UA, _UASW, _WD, _WDSW, _X07,
+ _F8SC, _FA, _FA2, _FC, _FE, _GL, _MDM, _MVC,
+ _SB, _TVBOY, _UA, _UASW, _WD, _WDSW, _X07,
#ifdef CUSTOM_ARM
_CUSTOM,
#endif
diff --git a/src/emucore/Cart.hxx b/src/emucore/Cart.hxx
index be56e4d10..b5f6555ca 100644
--- a/src/emucore/Cart.hxx
+++ b/src/emucore/Cart.hxx
@@ -434,6 +434,9 @@ class Cartridge : public Device
// Callback to output messages
messageCallback myMsgCallback{nullptr};
+ // Semi-random values to use when a read from write port occurs
+ std::array myRWPRandomValues;
+
private:
// The startup bank to use (where to look for the reset vector address)
uInt16 myStartBank{0};
@@ -442,9 +445,6 @@ class Cartridge : public Device
// by the debugger, when disassembling/dumping ROM.
bool myHotspotsLocked{false};
- // Semi-random values to use when a read from write port occurs
- std::array myRWPRandomValues;
-
// Contains various info about this cartridge
// This needs to be stored separately from child classes, since
// sometimes the information in both do not match
diff --git a/src/emucore/CartCreator.cxx b/src/emucore/CartCreator.cxx
index 04e742310..111695afe 100644
--- a/src/emucore/CartCreator.cxx
+++ b/src/emucore/CartCreator.cxx
@@ -54,6 +54,7 @@
#include "CartFA2.hxx"
#include "CartFC.hxx"
#include "CartFE.hxx"
+#include "CartGL.hxx"
#include "CartMDM.hxx"
#include "CartMVC.hxx"
#include "CartSB.hxx"
@@ -305,6 +306,8 @@ CartCreator::createFromImage(const ByteBuffer& image, size_t size,
return make_unique(image, size, md5, settings);
case Bankswitch::Type::_FE:
return make_unique(image, size, md5, settings);
+ case Bankswitch::Type::_GL:
+ return make_unique(image, size, md5, settings);
case Bankswitch::Type::_MDM:
return make_unique(image, size, md5, settings);
case Bankswitch::Type::_UA:
diff --git a/src/emucore/CartDetector.cxx b/src/emucore/CartDetector.cxx
index ced6c349d..2d611290e 100644
--- a/src/emucore/CartDetector.cxx
+++ b/src/emucore/CartDetector.cxx
@@ -27,9 +27,12 @@ Bankswitch::Type CartDetector::autodetectType(const ByteBuffer& image, size_t si
// Guess type based on size
Bankswitch::Type type = Bankswitch::Type::_AUTO;
- if((size % 8448) == 0 || size == 6144)
+ if((size % 8448) == 0 || size == 6_KB)
{
- type = Bankswitch::Type::_AR;
+ if(size == 6_KB && isProbablyGL(image, size))
+ type = Bankswitch::Type::_GL;
+ else
+ type = Bankswitch::Type::_AR;
}
else if(size < 2_KB) // Sub2K images
{
@@ -48,6 +51,8 @@ Bankswitch::Type CartDetector::autodetectType(const ByteBuffer& image, size_t si
type = Bankswitch::Type::_4KSC;
else if (isProbablyFC(image, size))
type = Bankswitch::Type::_FC;
+ else if (isProbablyGL(image, size))
+ type = Bankswitch::Type::_GL;
else
type = Bankswitch::Type::_4K;
}
@@ -706,6 +711,14 @@ bool CartDetector::isProbablyFE(const ByteBuffer& image, size_t size)
return false;
}
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+bool CartDetector::isProbablyGL(const ByteBuffer& image, size_t size)
+{
+ static constexpr uInt8 signature[] = { 0xad, 0xb8, 0x0c }; // LDA $0CB8
+
+ return searchForBytes(image, size, signature, 3);
+}
+
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool CartDetector::isProbablyMDM(const ByteBuffer& image, size_t size)
{
diff --git a/src/emucore/CartDetector.hxx b/src/emucore/CartDetector.hxx
index 9e5716888..e2d346f0a 100644
--- a/src/emucore/CartDetector.hxx
+++ b/src/emucore/CartDetector.hxx
@@ -201,6 +201,11 @@ class CartDetector
*/
static bool isProbablyFE(const ByteBuffer& image, size_t size);
+ /**
+ Returns true if the image is probably a GameLine cartridge
+ */
+ static bool isProbablyGL(const ByteBuffer& image, size_t size);
+
/**
Returns true if the image is probably a MDM bankswitching cartridge
*/
diff --git a/src/emucore/CartEnhanced.cxx b/src/emucore/CartEnhanced.cxx
index c15727f6c..fa983f0fb 100644
--- a/src/emucore/CartEnhanced.cxx
+++ b/src/emucore/CartEnhanced.cxx
@@ -65,8 +65,11 @@ CartridgeEnhanced::CartridgeEnhanced(const ByteBuffer& image, size_t size,
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void CartridgeEnhanced::install(System& system)
{
+ if(!myRamBankShift)
+ myRamBankShift = myBankShift - 1;
+
// limit banked RAM size to the size of one RAM bank
- const uInt16 ramSize = myRamBankCount > 0 ? 1 << (myBankShift - 1) :
+ const uInt16 ramSize = myRamBankCount > 0 ? 1 << myRamBankShift :
static_cast(myRamSize);
// calculate bank switching and RAM sizes and masks
@@ -200,7 +203,8 @@ bool CartridgeEnhanced::poke(uInt16 address, uInt8 value)
if(isRamBank(address))
{
- if(static_cast(address & (myBankSize >> 1)) == myRamWpHigh)
+ if(static_cast(address & (myBankSize >> 1)) == myRamWpHigh
+ || myBankShift == myRamBankShift)
{
address &= myRamMask;
// The RAM banks follow the ROM banks and are half the size of a ROM bank
@@ -271,7 +275,7 @@ bool CartridgeEnhanced::bank(uInt16 bank, uInt16 segment)
const uInt16 ramBank = (bank - romBankCount()) % myRamBankCount;
// The RAM banks follow the ROM banks and are half the size of a ROM bank
const uInt32 bankOffset = static_cast(mySize) +
- (ramBank << (myBankShift - 1));
+ (ramBank << myRamBankShift);
// Remember what bank is in this segment
myCurrentSegOffset[segment] = static_cast(mySize) +
@@ -280,7 +284,8 @@ bool CartridgeEnhanced::bank(uInt16 bank, uInt16 segment)
// Set the page accessing method for the RAM writing pages
// Note: Writes are mapped to poke() (NOT using directPokeBase) to check for read from write port (RWP)
uInt16 fromAddr = (ROM_OFFSET + segmentOffset + myWriteOffset) & ~System::PAGE_MASK;
- uInt16 toAddr = (ROM_OFFSET + segmentOffset + myWriteOffset + (myBankSize >> 1)) & ~System::PAGE_MASK;
+ uInt16 toAddr = (ROM_OFFSET + segmentOffset + myWriteOffset
+ + (myBankSize >> (myBankShift - myRamBankShift))) & ~System::PAGE_MASK;
System::PageAccess access(this, System::PageAccessType::WRITE);
for(uInt16 addr = fromAddr; addr < toAddr; addr += System::PAGE_SIZE)
@@ -295,7 +300,8 @@ bool CartridgeEnhanced::bank(uInt16 bank, uInt16 segment)
// Set the page accessing method for the RAM reading pages
fromAddr = (ROM_OFFSET + segmentOffset + myReadOffset) & ~System::PAGE_MASK;
- toAddr = (ROM_OFFSET + segmentOffset + myReadOffset + (myBankSize >> 1)) & ~System::PAGE_MASK;
+ toAddr = (ROM_OFFSET + segmentOffset + myReadOffset
+ + (myBankSize >> (myBankShift - myRamBankShift))) & ~System::PAGE_MASK;
access.type = System::PageAccessType::READ;
for(uInt16 addr = fromAddr; addr < toAddr; addr += System::PAGE_SIZE)
diff --git a/src/emucore/CartEnhanced.hxx b/src/emucore/CartEnhanced.hxx
index 4d47411f2..d45c0c758 100644
--- a/src/emucore/CartEnhanced.hxx
+++ b/src/emucore/CartEnhanced.hxx
@@ -214,6 +214,9 @@ class CartridgeEnhanced : public Cartridge
// The mask for a bank segment
uInt16 myBankMask{ROM_MASK};
+ // Usually myBankShift - 1
+ uInt16 myRamBankShift{0};
+
protected:
// The extra RAM size
size_t myRamSize{RAM_SIZE}; // default 0
@@ -318,7 +321,7 @@ class CartridgeEnhanced : public Cartridge
virtual uInt16 getStartBank() const { return 0; }
/**
- Get the ROM offset of the segment of the given address
+ Get the ROM offset of the segment of the given address.
@param address The address to get the offset for
@return The calculated offset
@@ -328,14 +331,16 @@ class CartridgeEnhanced : public Cartridge
}
/**
- Get the RAM offset of the segment of the given address
+ Get the RAM offset of the segment of the given address.
+ The RAM banks are half the size of a ROM bank.
@param address The address to get the offset for
@return The calculated offset
*/
uInt16 ramAddressSegmentOffset(uInt16 address) const {
- return static_cast((myCurrentSegOffset[
- ((address & ROM_MASK) >> myBankShift) % myBankSegs] - mySize) >> 1);
+ return static_cast(
+ (myCurrentSegOffset[((address & ROM_MASK) >> myBankShift) % myBankSegs] - mySize)
+ >> (myBankShift - myRamBankShift));
}
private:
diff --git a/src/emucore/CartGL.cxx b/src/emucore/CartGL.cxx
new file mode 100644
index 000000000..bf9d9c496
--- /dev/null
+++ b/src/emucore/CartGL.cxx
@@ -0,0 +1,133 @@
+//============================================================================
+//
+// 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-2023 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 "CartGL.hxx"
+
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+CartridgeGL::CartridgeGL(const ByteBuffer& image, size_t size,
+ string_view md5, const Settings& settings,
+ size_t bsSize)
+ : CartridgeEnhanced(image, size, md5, settings, bsSize)
+{
+ myBankShift = myRamBankShift = BANK_SHIFT;
+ myRamSize = RAM_SIZE;
+ myRamBankCount = RAM_BANKS;
+
+ if(size == 4_KB + 2_KB) // ROM containing RAM data?
+ {
+ myInitialRAM = make_unique(2_KB);
+ // Copy the RAM image into a buffer for use in reset()
+ std::copy_n(image.get() + 4_KB, 2_KB, myInitialRAM.get());
+ }
+}
+
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+void CartridgeGL::reset()
+{
+ CartridgeEnhanced::reset();
+
+ // Initially bank 0 is mapped into all four segments
+ bank(0, 0);
+ bank(0, 1);
+ bank(0, 2);
+ bank(0, 3);
+ myBankChanged = true;
+
+ myOrgAccess = mySystem->getPageAccess(0x1fc0);
+
+ initializeRAM(myRAM.get(), myRamSize);
+ if(myInitialRAM != nullptr)
+ {
+ // Copy the RAM image into my RAM buffer
+ std::copy_n(myInitialRAM.get(), 2_KB, myRAM.get());
+ }
+}
+
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+void CartridgeGL::install(System& system)
+{
+ CartridgeEnhanced::install(system);
+
+ System::PageAccess access(this, System::PageAccessType::READ);
+
+ mySystem->setPageAccess(0x480, access);
+ mySystem->setPageAccess(0x580, access);
+ mySystem->setPageAccess(0x680, access);
+ mySystem->setPageAccess(0x880, access);
+ mySystem->setPageAccess(0x980, access);
+ mySystem->setPageAccess(0xc80, access);
+ mySystem->setPageAccess(0xd80, access);
+
+ myReadOffset = myWriteOffset = 0;
+}
+
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+bool CartridgeGL::checkSwitchBank(uInt16 address, uInt8)
+{
+ int slice = -1;
+ bool control = false;
+
+ // Switch banks if necessary
+ switch(address & 0x1f80)
+ {
+ case 0x480:
+ slice = 0;
+ break;
+ case 0x580:
+ slice = 1;
+ break;
+ case 0x880:
+ slice = 2;
+ break;
+ case 0x980:
+ slice = 3;
+ break;
+ case 0xc80:
+ control = true;
+ break;
+ }
+ if(slice >= 0)
+ {
+ //const bool isWrite = address & 0x20; // could be checked, but not necessary for known GL ROMs
+ bank(address & 0xf, slice);
+ return true;
+ }
+ if(control)
+ {
+ myEnablePROM = (address & 0x30) == 0x30;
+ if(myEnablePROM)
+ mySystem->setPageAccess(0x1fc0, System::PageAccess(this, System::PageAccessType::READ));
+ else
+ mySystem->setPageAccess(0x1fc0, myOrgAccess);
+ return true;
+ }
+ return false;
+}
+
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+uInt8 CartridgeGL::peek(uInt16 address)
+{
+ if(myEnablePROM && ((address & ADDR_MASK) >= 0x1fc0) && ((address & ADDR_MASK) <= 0x1fdf))
+ {
+ return 0; // sufficient for PROM check
+ }
+
+ checkSwitchBank(address, 0);
+
+ return myRWPRandomValues[address & 0xFF];
+}
diff --git a/src/emucore/CartGL.hxx b/src/emucore/CartGL.hxx
new file mode 100644
index 000000000..77f34d1b7
--- /dev/null
+++ b/src/emucore/CartGL.hxx
@@ -0,0 +1,134 @@
+//============================================================================
+//
+// 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-2023 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 CARTRIDGEGL_HXX
+#define CARTRIDGEGL_HXX
+
+#include "CartEnhanced.hxx"
+#ifdef DEBUGGER_SUPPORT
+ #include "CartGLWidget.hxx"
+#endif
+#include "System.hxx"
+
+/**
+ Cartridge class used for the GameLine Master module. In this bankswitching
+ scheme the 2600's 4K cartridge address space is broken into four 1K segments.
+ The desired 1K bank of the ROM or RAM is selected as follows:
+ - $0480 + x: 1st 1K segment
+ - $0580 + x: 2nd 1K segment
+ - $0880 + x: 3rd 1K segment
+ - $0980 + x: 4th 1K segment
+ Where x is defined as follows:
+ - bits 0..3: mapped 1K bank (0..3 = ROM bank, 4..f = RAM bank)
+ - bit 5: 0 = read, 1 = write (RAM only)
+ Initially bank 0 is mapped to all four segments.
+ The scheme supports 4K ROM and 2K RAM.
+
+ $0c80.. and $0d80.. control the modem (not implemented, except for PROM access).
+
+ @author Thomas Jentzsch
+*/
+class CartridgeGL : public CartridgeEnhanced
+{
+ friend class CartridgeGLWidget;
+
+ 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 md5 The md5sum of the ROM image
+ @param settings A reference to the various settings (read-only)
+ @param bsSize The size specified by the bankswitching scheme
+ */
+ CartridgeGL(const ByteBuffer& image, size_t size, string_view md5,
+ const Settings& settings, size_t bsSize = 4_KB);
+ ~CartridgeGL() override = 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;
+
+ /**
+ Get a descriptor for the device name (used in error checking).
+
+ @return The name of the object
+ */
+ string name() const override { return "CartridgeGL"; }
+
+ #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 CartridgeGLWidget(boss, lfont, nfont, x, y, w, h, *this);
+ }
+ #endif
+
+ public:
+ /**
+ Get the byte at the specified address.
+
+ @return The byte at the specified address
+ */
+ uInt8 peek(uInt16 address) override;
+
+ private:
+ bool checkSwitchBank(uInt16 address, uInt8) override;
+
+ protected:
+ // log(ROM bank segment size) / log(2)
+ static constexpr uInt16 BANK_SHIFT = 10; // = 1K = 0x0400
+
+ // The number of RAM banks
+ static constexpr uInt16 RAM_BANKS = 12;
+
+ // RAM size
+ static constexpr size_t RAM_SIZE = RAM_BANKS << BANK_SHIFT; // = 12K;
+
+ private:
+ // Initial RAM data from the cart (doesn't always exist)
+ ByteBuffer myInitialRAM{nullptr};
+
+ bool myEnablePROM{false};
+
+ System::PageAccess myOrgAccess;
+
+ private:
+ // Following constructors and assignment operators not supported
+ CartridgeGL() = delete;
+ CartridgeGL(const CartridgeGL&) = delete;
+ CartridgeGL(CartridgeGL&&) = delete;
+ CartridgeGL& operator=(const CartridgeGL&) = delete;
+ CartridgeGL& operator=(CartridgeGL&&) = delete;
+};
+
+#endif
diff --git a/src/emucore/module.mk b/src/emucore/module.mk
index c85875e8b..be23f42ee 100644
--- a/src/emucore/module.mk
+++ b/src/emucore/module.mk
@@ -46,6 +46,7 @@ MODULE_OBJS := \
src/emucore/CartFA2.o \
src/emucore/CartFC.o \
src/emucore/CartFE.o \
+ src/emucore/CartGL.o \
src/emucore/CartMDM.o \
src/emucore/CartMVC.o \
src/emucore/CartSB.o \
diff --git a/src/os/windows/Stella.vcxproj b/src/os/windows/Stella.vcxproj
index 1b88ac41f..05ab4683f 100755
--- a/src/os/windows/Stella.vcxproj
+++ b/src/os/windows/Stella.vcxproj
@@ -911,6 +911,7 @@
true
+
true
@@ -996,6 +997,7 @@
+
@@ -2223,6 +2225,7 @@
true
+
true
@@ -2319,6 +2322,7 @@
+
diff --git a/src/os/windows/Stella.vcxproj.filters b/src/os/windows/Stella.vcxproj.filters
index 071b641e8..7d3849df5 100644
--- a/src/os/windows/Stella.vcxproj.filters
+++ b/src/os/windows/Stella.vcxproj.filters
@@ -1197,6 +1197,12 @@
Source Files\debugger
+
+ Source Files\emucore
+
+
+ Source Files\debugger\gui
+
@@ -2441,6 +2447,12 @@
Header Files\debugger
+
+ Header Files\emucore
+
+
+ Header Files\debugger\gui
+
diff --git a/test/roms/bankswitching/GL/GameLine Master Module ROM (1983) (Control Video Corporation) (fixed V2).bin b/test/roms/bankswitching/GL/GameLine Master Module ROM (1983) (Control Video Corporation) (fixed V2).bin
new file mode 100644
index 000000000..d9ad832be
Binary files /dev/null and b/test/roms/bankswitching/GL/GameLine Master Module ROM (1983) (Control Video Corporation) (fixed V2).bin differ
diff --git a/test/roms/bankswitching/GL/download_rom.bin b/test/roms/bankswitching/GL/download_rom.bin
new file mode 100644
index 000000000..e5b1c3c7d
Binary files /dev/null and b/test/roms/bankswitching/GL/download_rom.bin differ
diff --git a/test/roms/bankswitching/GL/ramdata_rom.bin b/test/roms/bankswitching/GL/ramdata_rom.bin
new file mode 100644
index 000000000..73b1e6262
Binary files /dev/null and b/test/roms/bankswitching/GL/ramdata_rom.bin differ