Added 'MDM' bankswitch scheme from Edwin Blink, as documented in the AtariAge thread:

http://atariage.com/forums/topic/56073-cheap-2k4k-x-in-1-menu-driven-multicart-for-atari-2600

Fixed declaration of bankswitch type so it isn't in two places, which necessitated
modiying two files and keeping them in sync.


git-svn-id: svn://svn.code.sf.net/p/stella/code/trunk@2974 8b62c5a3-ac7e-4cc8-8f21-d9a121418aba
This commit is contained in:
stephena 2014-08-14 00:03:56 +00:00
parent a7aece9d36
commit fa6127d53a
9 changed files with 485 additions and 110 deletions

View File

@ -56,6 +56,7 @@
#include "CartFA2.hxx"
#include "CartFE.hxx"
#include "CartMC.hxx"
#include "CartMDM.hxx"
#include "CartSB.hxx"
#include "CartUA.hxx"
#include "CartX07.hxx"
@ -244,6 +245,8 @@ Cartridge* Cartridge::create(const uInt8* image, uInt32 size, string& md5,
cartridge = new CartridgeFE(image, size, settings);
else if(type == "MC")
cartridge = new CartridgeMC(image, size, settings);
else if(type == "MDM")
cartridge = new CartridgeMDM(image, size, settings);
else if(type == "UA")
cartridge = new CartridgeUA(image, size, settings);
else if(type == "SB")
@ -517,9 +520,11 @@ string Cartridge::autodetectType(const uInt8* image, uInt32 size)
type = "4K"; // Most common bankswitching type
}
// Because it's a variable size ROM format, DASH check is independent of image size and comes last
// Variable sized ROM formats are independent of image size and comes last
if(isProbablyDASH(image, size))
type = "DASH";
else if(isProbablyMDM(image, size))
type = "MDM";
return type;
}
@ -881,6 +886,14 @@ bool Cartridge::isProbablyFE(const uInt8* image, uInt32 size)
return false;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool Cartridge::isProbablyMDM(const uInt8* image, uInt32 size)
{
// MDM cart is identified key 'MDMC' in the first 4K of ROM
uInt8 signature[] = { 0x4D, 0x44, 0x4D, 0x43 };
return searchForBytes(image, 4096, signature, 4, 1);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool Cartridge::isProbablySB(const uInt8* image, uInt32 size)
{
@ -947,3 +960,51 @@ Cartridge& Cartridge::operator = (const Cartridge&)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string Cartridge::myAboutString= "";
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Cartridge::BankswitchType Cartridge::ourBSList[] = {
{ "AUTO", "Auto-detect" },
{ "0840", "0840 (8K ECONObank)" },
{ "2IN1", "2IN1 Multicart (4-32K)" },
{ "4IN1", "4IN1 Multicart (8-32K)" },
{ "8IN1", "8IN1 Multicart (16-64K)" },
{ "16IN1", "16IN1 Multicart (32-128K)" },
{ "32IN1", "32IN1 Multicart (64/128K)" },
{ "64IN1", "64IN1 Multicart (128/256K)" },
{ "128IN1", "128IN1 Multicart (256/512K)" },
{ "2K", "2K (64-2048 bytes Atari)" },
{ "3E", "3E (32K Tigervision)" },
{ "3F", "3F (512K Tigervision)" },
{ "4A50", "4A50 (64K 4A50 + ram)" },
{ "4K", "4K (4K Atari)" },
{ "4KSC", "4KSC (CPUWIZ 4K + ram)" },
{ "AR", "AR (Supercharger)" },
{ "BF", "BF (CPUWIZ 256K)" },
{ "BFSC", "BFSC (CPUWIZ 256K + ram)" },
{ "CV", "CV (Commavid extra ram)" },
{ "CM", "CM (SpectraVideo CompuMate)" },
{ "DASH", "Experimental" },
{ "DF", "DF (CPUWIZ 128K)" },
{ "DFSC", "DFSC (CPUWIZ 128K + ram)" },
{ "DPC", "DPC (Pitfall II)" },
{ "DPC+", "DPC+ (Enhanced DPC)" },
{ "E0", "E0 (8K Parker Bros)" },
{ "E7", "E7 (16K M-network)" },
{ "EF", "EF (64K H. Runner)" },
{ "EFSC", "EFSC (64K H. Runner + ram)" },
{ "F0", "F0 (Dynacom Megaboy)" },
{ "F4", "F4 (32K Atari)" },
{ "F4SC", "F4SC (32K Atari + ram)" },
{ "F6", "F6 (16K Atari)" },
{ "F6SC", "F6SC (16K Atari + ram)" },
{ "F8", "F8 (8K Atari)" },
{ "F8SC", "F8SC (8K Atari + ram)" },
{ "FA", "FA (CBS RAM Plus)" },
{ "FA2", "FA2 (CBS RAM Plus 24/28K)" },
{ "FE", "FE (8K Decathlon)" },
{ "MC", "MC (C. Wilkson Megacart)" },
{ "MDM", "MDM (Menu Driven Megacart)" },
{ "SB", "SB (128-256K SUPERbank)" },
{ "UA", "UA (8K UA Ltd.)" },
{ "X07", "X07 (64K AtariAge)" }
};

View File

@ -211,6 +211,15 @@ class Cartridge : public Device
virtual CartDebugWidget* debugWidget(GuiObject* boss, const GUI::Font& lfont,
const GUI::Font& nfont, int x, int y, int w, int h) { return NULL; }
// Info about the various bankswitch schemes, useful for displaying
// in GUI dropdown boxes, etc
struct BankswitchType {
const char* type;
const char* desc;
};
enum { ourNumBSTypes = 44 };
static BankswitchType ourBSList[ourNumBSTypes];
protected:
/**
Indicate that an illegal read from a write port has occurred.
@ -303,6 +312,11 @@ class Cartridge : public Device
*/
static bool isProbably4A50(const uInt8* image, uInt32 size);
/**
Returns true if the image is probably a BF/BFSC bankswitching cartridge
*/
static bool isProbablyBF(const uInt8* image, uInt32 size, const char*& type);
/**
Returns true if the image is probably a CTY bankswitching cartridge
*/
@ -318,6 +332,11 @@ class Cartridge : public Device
*/
static bool isProbablyDASH(const uInt8* image, uInt32 size);
/**
Returns true if the image is probably a DF/DFSC bankswitching cartridge
*/
static bool isProbablyDF(const uInt8* image, uInt32 size, const char*& type);
/**
Returns true if the image is probably a DPC+ bankswitching cartridge
*/
@ -338,15 +357,6 @@ class Cartridge : public Device
*/
static bool isProbablyEF(const uInt8* image, uInt32 size, const char*& type);
/**
Returns true if the image is probably a BF/BFSC bankswitching cartridge
*/
static bool isProbablyBF(const uInt8* image, uInt32 size, const char*& type);
/**
Returns true if the image is probably a DF/DFSC bankswitching cartridge
*/
static bool isProbablyDF(const uInt8* image, uInt32 size, const char*& type);
/**
Returns true if the image is probably an F6 bankswitching cartridge
*/
@ -362,6 +372,11 @@ class Cartridge : public Device
*/
static bool isProbablyFE(const uInt8* image, uInt32 size);
/**
Returns true if the image is probably a MDM bankswitching cartridge
*/
static bool isProbablyMDM(const uInt8* image, uInt32 size);
/**
Returns true if the image is probably a SB bankswitching cartridge
*/

View File

@ -14,7 +14,7 @@
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: Cart0840.cxx,v 1.0 2006/11/17
// $Id$
//============================================================================
#include <cassert>

View File

@ -14,7 +14,7 @@
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: Cart0840.hxx,v 1.0 2006/11/14
// $Id$
//============================================================================
#ifndef CARTRIDGE0840_HXX

207
src/emucore/CartMDM.cxx Normal file
View File

@ -0,0 +1,207 @@
//============================================================================
//
// 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-2014 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 <cassert>
#include <cstring>
#include "System.hxx"
#include "CartMDM.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
CartridgeMDM::CartridgeMDM(const uInt8* image, uInt32 size, const Settings& settings)
: Cartridge(settings),
myImage(NULL),
mySize(size),
myBankingDisabled(false)
{
// Allocate array for the ROM image
myImage = new uInt8[mySize];
// Copy the ROM image into my buffer
memcpy(myImage, image, mySize);
createCodeAccessBase(mySize);
// Remember startup bank
myStartBank = 0;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
CartridgeMDM::~CartridgeMDM()
{
delete[] myImage; myImage = NULL;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void CartridgeMDM::reset()
{
// Upon reset we switch to the startup bank
bank(myStartBank);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void CartridgeMDM::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((0x1000 & mask) == 0);
// Get the page accessing methods for the hot spots since they overlap
// areas within the TIA we'll need to forward requests to the TIA
myHotSpotPageAccess[0] = mySystem->getPageAccess(0x0800 >> shift);
myHotSpotPageAccess[1] = mySystem->getPageAccess(0x0900 >> shift);
myHotSpotPageAccess[2] = mySystem->getPageAccess(0x0A00 >> shift);
myHotSpotPageAccess[3] = mySystem->getPageAccess(0x0B00 >> shift);
myHotSpotPageAccess[4] = mySystem->getPageAccess(0x0C00 >> shift);
myHotSpotPageAccess[5] = mySystem->getPageAccess(0x0D00 >> shift);
myHotSpotPageAccess[6] = mySystem->getPageAccess(0x0E00 >> shift);
myHotSpotPageAccess[7] = mySystem->getPageAccess(0x0F00 >> shift);
// Set the page accessing methods for the hot spots
System::PageAccess access(this, System::PA_READ);
for(uInt32 i = 0x0800; i < 0x0FFF; i += (1 << shift))
mySystem->setPageAccess(i >> shift, access);
// Install pages for bank 0
bank(myStartBank);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt8 CartridgeMDM::peek(uInt16 address)
{
// Because of the way we've set up accessing above, we can only
// get here when the addresses are from 0x800 - 0xFFF
bank(address & 0x0FF);
int hotspot = ((address & 0x0F00) >> 8) - 8;
return myHotSpotPageAccess[hotspot].device->peek(address);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool CartridgeMDM::poke(uInt16 address, uInt8 value)
{
// Currently, writing to the hotspots is disabled, so we don't need to
// worry about bankswitching
// However, all possible addresses can appear here, and we only care
// about those below $1000
if(!(address & 0x1000))
{
int hotspot = ((address & 0x0F00) >> 8) - 8;
myHotSpotPageAccess[hotspot].device->poke(address, value);
}
return false;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool CartridgeMDM::bank(uInt16 bank)
{
if(bankLocked()) return false;
// Accesses above bank 127 disable further bankswitching; we're only
// concerned with the lower byte
myBankingDisabled = myBankingDisabled || bank > 127;
if(myBankingDisabled)
return false;
// Remember what bank we're in
// Wrap around to a valid bank number if necessary
myCurrentBank = bank % bankCount();
uInt16 offset = myCurrentBank << 12;
uInt16 shift = mySystem->pageShift();
// Setup the page access methods for the current bank
System::PageAccess access(this, System::PA_READ);
// Map ROM image into the system
for(uInt32 address = 0x1000; address < 0x2000; address += (1 << shift))
{
access.directPeekBase = &myImage[offset + (address & 0x0FFF)];
access.codeAccessBase = &myCodeAccessBase[offset + (address & 0x0FFF)];
mySystem->setPageAccess(address >> shift, access);
}
return myBankChanged = true;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt16 CartridgeMDM::getBank() const
{
return myCurrentBank;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt16 CartridgeMDM::bankCount() const
{
return mySize >> 12;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool CartridgeMDM::patch(uInt16 address, uInt8 value)
{
myImage[(myCurrentBank << 12) + (address & 0x0FFF)] = value;
return myBankChanged = true;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
const uInt8* CartridgeMDM::getImage(int& size) const
{
size = mySize;
return myImage;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool CartridgeMDM::save(Serializer& out) const
{
try
{
out.putString(name());
out.putShort(myCurrentBank);
}
catch(...)
{
cerr << "ERROR: CartridgeMDM::save" << endl;
return false;
}
return true;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool CartridgeMDM::load(Serializer& in)
{
try
{
if(in.getString() != name())
return false;
myCurrentBank = in.getShort();
}
catch(...)
{
cerr << "ERROR: CartridgeMDM::load" << endl;
return false;
}
// Remember what bank we were in
bank(myCurrentBank);
return true;
}

183
src/emucore/CartMDM.hxx Normal file
View File

@ -0,0 +1,183 @@
//============================================================================
//
// 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-2014 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 CARTRIDGEMDM_HXX
#define CARTRIDGEMDM_HXX
#include "bspf.hxx"
#include "Cart.hxx"
#include "System.hxx"
#ifdef DEBUGGER_SUPPORT
// #include "CartMDMWidget.hxx"
#endif
/**
Cartridge class used for "Menu Driven Megacart" as described at the
following link and developed by Edwin Blink:
http://atariage.com/forums/topic/56073-cheap-2k4k-x-in-1-menu-driven-multicart-for-atari-2600
The hotspots in this scheme read from addresses $800 to $FFF, where the
lower byte determines the actual 4K bank switch to. In the current
implementation, only 128 banks are supported, so selecting bank 128+ results
in further bankswitching being locked. A reset line is used to reset to
bank 0 and re-enable bankswitching.
Therefore, there are 128 banks / 512K possible in total.
@author Stephen Anthony, based on 0840 scheme by Fred X. Quimby
*/
class CartridgeMDM : public Cartridge
{
friend class CartridgeMDMWidget;
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)
*/
CartridgeMDM(const uInt8* image, uInt32 size, const Settings& settings);
/**
Destructor
*/
virtual ~CartridgeMDM();
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 getBank() 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 "CartridgeMDM"; }
#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)
{
return 0;//new CartridgeMDMWidget(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);
/**
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:
// Pointer to a dynamically allocated ROM image of the cartridge
uInt8* myImage;
// Size of the ROM image
uInt32 mySize;
// Indicates which bank is currently active
uInt16 myCurrentBank;
// Previous Device's page access
System::PageAccess myHotSpotPageAccess[8];
// Indicates whether banking has been disabled due to a bankswitch
// above bank 127
bool myBankingDisabled;
};
#endif

View File

@ -37,6 +37,7 @@ MODULE_OBJS := \
src/emucore/CartFA2.o \
src/emucore/CartFE.o \
src/emucore/CartMC.o \
src/emucore/CartMDM.o \
src/emucore/CartSB.o \
src/emucore/CartUA.o \
src/emucore/CartX07.o \

View File

@ -17,6 +17,7 @@
// $Id$
//============================================================================
#include "Cart.hxx"
#include "Console.hxx"
#include "MouseControl.hxx"
#include "Dialog.hxx"
@ -56,12 +57,6 @@ GameInfoDialog::GameInfoDialog(
_w = 52 * fontWidth + 8;
_h = 12 * (lineHeight + 4) + 10;
////////////////////////////////////////////////////////////////////
// Some of the following items are also present in GlobalPropsDialog
// If any changes are ever made here, GlobalPropsDialog should also
// be updated accordingly
////////////////////////////////////////////////////////////////////
// The tab widget
xpos = 2; ypos = vBorder;
myTab = new TabWidget(this, font, xpos, ypos, _w - 2*xpos,
@ -132,49 +127,8 @@ GameInfoDialog::GameInfoDialog(
"Type:", kTextAlignLeft);
pwidth = font.getStringWidth("CM (SpectraVideo CompuMate)");
items.clear();
items.push_back("Auto-detect", "AUTO" );
items.push_back("0840 (8K ECONObank)", "0840" );
items.push_back("2IN1 Multicart (4-32K)", "2IN1" );
items.push_back("4IN1 Multicart (8-32K)", "4IN1" );
items.push_back("8IN1 Multicart (16-64K)", "8IN1" );
items.push_back("16IN1 Multicart (32-128K)", "16IN1" );
items.push_back("32IN1 Multicart (64/128K)", "32IN1" );
items.push_back("64IN1 Multicart (128/256K)", "64IN1" );
items.push_back("128IN1 Multicart (256/512K)", "128IN1");
items.push_back("2K (64-2048 bytes Atari)", "2K" );
items.push_back("3E (32K Tigervision)", "3E" );
items.push_back("3F (512K Tigervision)", "3F" );
items.push_back("4A50 (64K 4A50 + ram)", "4A50" );
items.push_back("4K (4K Atari)", "4K" );
items.push_back("4KSC (CPUWIZ 4K + ram)", "4KSC" );
items.push_back("AR (Supercharger)", "AR" );
items.push_back("BF (CPUWIZ 256K)", "BF" );
items.push_back("BFSC (CPUWIZ 256K + ram)", "BFSC" );
items.push_back("CV (Commavid extra ram)", "CV" );
items.push_back("CM (SpectraVideo CompuMate)", "CM" );
items.push_back("Experimental", "DASH" );
items.push_back("DF (CPUWIZ 128K)", "DF" );
items.push_back("DFSC (CPUWIZ 128K + ram)", "DFSC" );
items.push_back("DPC (Pitfall II)", "DPC" );
items.push_back("DPC+ (Enhanced DPC)", "DPC+" );
items.push_back("E0 (8K Parker Bros)", "E0" );
items.push_back("E7 (16K M-network)", "E7" );
items.push_back("EF (64K H. Runner)", "EF" );
items.push_back("EFSC (64K H. Runner + ram)", "EFSC" );
items.push_back("F0 (Dynacom Megaboy)", "F0" );
items.push_back("F4 (32K Atari)", "F4" );
items.push_back("F4SC (32K Atari + ram)", "F4SC" );
items.push_back("F6 (16K Atari)", "F6" );
items.push_back("F6SC (16K Atari + ram)", "F6SC" );
items.push_back("F8 (8K Atari)", "F8" );
items.push_back("F8SC (8K Atari + ram)", "F8SC" );
items.push_back("FA (CBS RAM Plus)", "FA" );
items.push_back("FA2 (CBS RAM Plus 24/28K)", "FA2" );
items.push_back("FE (8K Decathlon)", "FE" );
items.push_back("MC (C. Wilkson Megacart)", "MC" );
items.push_back("SB (128-256K SUPERbank)", "SB" );
items.push_back("UA (8K UA Ltd.)", "UA" );
items.push_back("X07 (64K AtariAge)", "X07" );
for(int i = 0; i < Cartridge::ourNumBSTypes; ++i)
items.push_back(Cartridge::ourBSList[i].desc, Cartridge::ourBSList[i].type);
myType = new PopUpWidget(myTab, font, xpos+lwidth, ypos,
pwidth, lineHeight, items, "", 0, 0);
wid.push_back(myType);

View File

@ -19,6 +19,7 @@
#include "bspf.hxx"
#include "Cart.hxx"
#include "Control.hxx"
#include "Dialog.hxx"
#include "OSystem.hxx"
@ -53,59 +54,12 @@ GlobalPropsDialog::GlobalPropsDialog(GuiObject* boss, const GUI::Font& font)
xpos = 10; ypos = 10;
////////////////////////////////////////////////////////////////////
// The following items are also present in GameInfoDialog
// If any changes are ever made here, GameInfoDialog should also
// be updated accordingly
////////////////////////////////////////////////////////////////////
// Bankswitch type
new StaticTextWidget(this, font, xpos, ypos+1, lwidth, fontHeight,
"Bankswitch type:", kTextAlignLeft);
items.clear();
items.push_back("Auto-detect", "AUTO" );
items.push_back("0840 (8K ECONObank)", "0840" );
items.push_back("2IN1 Multicart (4-32K)", "2IN1" );
items.push_back("4IN1 Multicart (8-32K)", "4IN1" );
items.push_back("8IN1 Multicart (16-64K)", "8IN1" );
items.push_back("16IN1 Multicart (32-128K)", "16IN1" );
items.push_back("32IN1 Multicart (64/128K)", "32IN1" );
items.push_back("64IN1 Multicart (128/256K)", "64IN1" );
items.push_back("128IN1 Multicart (256/512K)", "128IN1");
items.push_back("2K (64-2048 bytes Atari)", "2K" );
items.push_back("3E (32K Tigervision)", "3E" );
items.push_back("3F (512K Tigervision)", "3F" );
items.push_back("4A50 (64K 4A50 + ram)", "4A50" );
items.push_back("4K (4K Atari)", "4K" );
items.push_back("4KSC (CPUWIZ 4K + ram)", "4KSC" );
items.push_back("AR (Supercharger)", "AR" );
items.push_back("BF (CPUWIZ 256K)", "BF" );
items.push_back("BFSC (CPUWIZ 256K + ram)", "BFSC" );
items.push_back("CV (Commavid extra ram)", "CV" );
items.push_back("CM (SpectraVideo CompuMate)", "CM" );
items.push_back("Experimental", "DASH" );
items.push_back("DF (CPUWIZ 128K)", "DF" );
items.push_back("DFSC (CPUWIZ 128K + ram)", "DFSC" );
items.push_back("DPC (Pitfall II)", "DPC" );
items.push_back("DPC+ (Enhanced DPC)", "DPC+" );
items.push_back("E0 (8K Parker Bros)", "E0" );
items.push_back("E7 (16K M-network)", "E7" );
items.push_back("EF (64K H. Runner)", "EF" );
items.push_back("EFSC (64K H. Runner + ram)", "EFSC" );
items.push_back("F0 (Dynacom Megaboy)", "F0" );
items.push_back("F4 (32K Atari)", "F4" );
items.push_back("F4SC (32K Atari + ram)", "F4SC" );
items.push_back("F6 (16K Atari)", "F6" );
items.push_back("F6SC (16K Atari + ram)", "F6SC" );
items.push_back("F8 (8K Atari)", "F8" );
items.push_back("F8SC (8K Atari + ram)", "F8SC" );
items.push_back("FA (CBS RAM Plus)", "FA" );
items.push_back("FA2 (CBS RAM Plus 24/28K)", "FA2" );
items.push_back("FE (8K Decathlon)", "FE" );
items.push_back("MC (C. Wilkson Megacart)", "MC" );
items.push_back("SB (128-256K SUPERbank)", "SB" );
items.push_back("UA (8K UA Ltd.)", "UA" );
items.push_back("X07 (64K AtariAge)", "X07" );
for(int i = 0; i < Cartridge::ourNumBSTypes; ++i)
items.push_back(Cartridge::ourBSList[i].desc, Cartridge::ourBSList[i].type);
myBSType = new PopUpWidget(this, font, xpos+lwidth, ypos,
pwidth, lineHeight, items, "", 0, 0);
wid.push_back(myBSType);