Added 3E+ bankswitching scheme by Thomas Jentzsch.

git-svn-id: svn://svn.code.sf.net/p/stella/code/trunk@3311 8b62c5a3-ac7e-4cc8-8f21-d9a121418aba
This commit is contained in:
stephena 2016-08-21 21:37:06 +00:00
parent 1ecfee71e7
commit 087d382280
22 changed files with 1178 additions and 170 deletions

View File

@ -14,6 +14,9 @@
4.7.2 to 4.8: (xxx. xx, 2016)
* Added preliminary support for the 3E+ bankswitching scheme, developed
by Thomas Jentzsch.
* Fixed HMOVE positioning bug that occurred under certain circumstances.
Thanks to Omegamatrix of AtariAge for the bug report and patch to fix
the issue.

View File

@ -92,7 +92,7 @@ class PNGLibrary
// The following data remains between invocations of allocateStorage,
// and is only changed when absolutely necessary.
struct ReadInfoType {
unique_ptr<uInt8[]> buffer;
BytePtr buffer;
unique_ptr<png_bytep[]> row_pointers;
png_uint_32 width, height, pitch;
uInt32 buffer_size, row_size;

View File

@ -0,0 +1,336 @@
//============================================================================
//
// 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-2016 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 "Cart3EPlus.hxx"
#include "PopUpWidget.hxx"
#include "Cart3EPlusWidget.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Cartridge3EPlusWidget::Cartridge3EPlusWidget(
GuiObject* boss, const GUI::Font& lfont, const GUI::Font& nfont,
int x, int y, int w, int h, Cartridge3EPlus& cart)
: CartDebugWidget(boss, lfont, nfont, x, y, w, h),
myCart(cart)
{
uInt32 size = cart.mySize;
ostringstream info;
info << "3EPlus cartridge - (64K ROM + RAM)\n"
<< " 4-64K ROM (1K banks), 32K RAM (512b banks)\n"
<< "Each 1K ROM selected by writing to $3F\n"
"Each 512b RAM selected by writing to $3E\n"
" Lower 512B of bank x (R)\n"
" Upper 512B of bank x (+$200) (W)\n"
<< "Startup bank = 0/-1/-1/0 (ROM)\n";
// 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" << " = $" << Common::Base::HEX4 << start << "\n";
int xpos = 10,
ypos = addBaseInformation(size, "T. Jentzsch", info.str()) +
myLineHeight;
VariantList bankno;
for(uInt32 i = 0; i < myCart.ROM_BANK_COUNT; ++i)
VarList::push_back(bankno, i, i);
VariantList banktype;
VarList::push_back(banktype, "ROM", "ROM");
VarList::push_back(banktype, "RAM", "RAM");
for(uInt32 i = 0; i < 4; ++i)
{
int xpos_s, ypos_s = ypos;
ostringstream label;
label << "Set segment " << i << " as: ";
new StaticTextWidget(boss, _font, xpos, ypos, _font.getStringWidth(label.str()),
myFontHeight, label.str(), kTextAlignLeft);
ypos += myLineHeight + 8;
xpos += 20;
myBankNumber[i] =
new PopUpWidget(boss, _font, xpos, ypos-2, _font.getStringWidth("Slot "),
myLineHeight, bankno, "Slot ",
6*_font.getMaxCharWidth());
addFocusWidget(myBankNumber[i]);
xpos += myBankNumber[i]->getWidth();
myBankType[i] =
new PopUpWidget(boss, _font, xpos, ypos-2, 5*_font.getMaxCharWidth(),
myLineHeight, banktype, " of ", _font.getStringWidth(" of "));
addFocusWidget(myBankType[i]);
xpos += myBankType[i]->getWidth() + 10;
myBankCommit[i] = new ButtonWidget(boss, _font, xpos, ypos-4,
_font.getStringWidth(" Commit "), myButtonHeight,
"Commit", bankEnum[i]);
myBankCommit[i]->setTarget(this);
addFocusWidget(myBankCommit[i]);
xpos_s = xpos + myBankCommit[i]->getWidth() + 20;
StaticTextWidget* t;
int addr1 = start + (i*0x400), addr2 = addr1 + 0x1FF;
label.str("");
label << Common::Base::HEX4 << addr1 << "-" << Common::Base::HEX4 << addr2;
t = new StaticTextWidget(boss, _font, xpos_s, ypos_s+2,
_font.getStringWidth(label.str()), myFontHeight, label.str(), kTextAlignLeft);
int xoffset = xpos_s+t->getWidth() + 10;
myBankState[2*i] = new EditTextWidget(boss, _font, xoffset, ypos_s,
w - xoffset - 10, myLineHeight, "");
myBankState[2*i]->setEditable(false);
ypos_s += myLineHeight + 4;
label.str("");
label << Common::Base::HEX4 << (addr2 + 1) << "-" << Common::Base::HEX4 << (addr2 + 1 + 0x1FF);
new StaticTextWidget(boss, _font, xpos_s, ypos_s+2,
_font.getStringWidth(label.str()), myFontHeight, label.str(), kTextAlignLeft);
myBankState[2*i+1] = new EditTextWidget(boss, _font, xoffset, ypos_s,
w - xoffset - 10, myLineHeight, "");
myBankState[2*i+1]->setEditable(false);
xpos = 10;
ypos+= 2 * myLineHeight;
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Cartridge3EPlusWidget::saveOldState()
{
myOldState.internalram.clear();
for(uInt32 i = 0; i < this->internalRamSize();i++)
myOldState.internalram.push_back(myCart.myRAM[i]);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Cartridge3EPlusWidget::loadConfig()
{
updateUIState();
CartDebugWidget::loadConfig();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Cartridge3EPlusWidget::handleCommand(CommandSender* sender,
int cmd, int data, int id)
{
uInt16 segment = 0;
switch(cmd)
{
case kBank0Changed:
segment = 0;
break;
case kBank1Changed:
segment = 1;
break;
case kBank2Changed:
segment = 2;
break;
case kBank3Changed:
segment = 3;
break;
}
// Ignore bank if either number or type hasn't been selected
if(myBankNumber[segment]->getSelected() < 0 ||
myBankType[segment]->getSelected() < 0)
return;
uInt8 bank = (segment << myCart.BANK_BITS) |
(myBankNumber[segment]->getSelected() & myCart.BIT_BANK_MASK);
myCart.unlockBank();
if(myBankType[segment]->getSelectedTag() == "ROM")
myCart.bankROM(bank);
else
myCart.bankRAM(bank);
myCart.lockBank();
invalidate();
updateUIState();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string Cartridge3EPlusWidget::bankState()
{
ostringstream& buf = buffer();
// In this scheme, consecutive 512b segments are either both ROM or both RAM;
// we only need to look at the lower segment to determine what the 1K bank is
for(int i = 0; i < 4; ++i)
{
uInt16 bank = myCart.bankInUse[i*2];
if(bank == myCart.BANK_UNDEFINED) // never accessed
{
buf << " U!";
}
else
{
int bankno = bank & myCart.BIT_BANK_MASK;
if(bank & myCart.BITMASK_ROMRAM) // was RAM mapped here?
buf << " RAM " << bankno;
else
buf << " ROM " << bankno;
}
if(i < 3)
buf << " /";
}
return buf.str();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Cartridge3EPlusWidget::updateUIState()
{
// Set contents for actual banks number and type
for(int i = 0; i < 4; ++i)
{
uInt16 segment = myCart.segmentInUse[i];
if(segment == myCart.BANK_UNDEFINED)
{
myBankNumber[i]->clearSelection();
myBankType[i]->clearSelection();
}
else
{
int bankno = segment & myCart.BIT_BANK_MASK;
const char* banktype = segment & myCart.BITMASK_ROMRAM ? "RAM" : "ROM";
myBankNumber[i]->setSelected(bankno);
myBankType[i]->setSelected(banktype);
}
}
// Set description for each 512b bank state
for(int i = 0; i < 8; ++i)
{
uInt16 bank = myCart.bankInUse[i];
if(bank == myCart.BANK_UNDEFINED) // never accessed
{
myBankState[i]->setText("Undefined");
}
else
{
ostringstream buf;
int bankno = bank & myCart.BIT_BANK_MASK;
if(bank & myCart.BITMASK_ROMRAM) // was RAM mapped here?
{
if(bank & myCart.BITMASK_LOWERUPPER) // upper is write port
{
buf << "RAM " << bankno << " @ $" << Common::Base::HEX4
<< (bankno << myCart.RAM_BANK_TO_POWER) << " (W)";
myBankState[i]->setText(buf.str());
}
else
{
buf << "RAM " << bankno << " @ $" << Common::Base::HEX4
<< (bankno << myCart.RAM_BANK_TO_POWER) << " (R)";
myBankState[i]->setText(buf.str());
}
}
else
{
if(bank & myCart.BITMASK_LOWERUPPER) // upper is high 512b
{
buf << "ROM " << bankno << " @ $" << Common::Base::HEX4
<< ((bankno << myCart.RAM_BANK_TO_POWER) + myCart.RAM_BANK_SIZE);
myBankState[i]->setText(buf.str());
}
else
{
buf << "ROM " << bankno << " @ $" << Common::Base::HEX4
<< (bankno << myCart.RAM_BANK_TO_POWER);
myBankState[i]->setText(buf.str());
}
}
}
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt32 Cartridge3EPlusWidget::internalRamSize()
{
return 32*1024;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt32 Cartridge3EPlusWidget::internalRamRPort(int start)
{
return 0x0000 + start;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string Cartridge3EPlusWidget::internalRamDescription()
{
ostringstream desc;
desc << "Accessible 512b at a time via:\n"
<< " $F000/$F400/$F800/etc used for Read Access\n"
<< " $F200/$F600/$FA00/etc used for Write Access (+$200)";
return desc.str();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
const ByteArray& Cartridge3EPlusWidget::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& Cartridge3EPlusWidget::internalRamCurrent(int start, int count)
{
myRamCurrent.clear();
for(int i = 0; i < count; i++)
myRamCurrent.push_back(myCart.myRAM[start + i]);
return myRamCurrent;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Cartridge3EPlusWidget::internalRamSetValue(int addr, uInt8 value)
{
myCart.myRAM[addr] = value;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt8 Cartridge3EPlusWidget::internalRamGetValue(int addr)
{
return myCart.myRAM[addr];
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
const Cartridge3EPlusWidget::BankID Cartridge3EPlusWidget::bankEnum[4] = {
kBank0Changed, kBank1Changed, kBank2Changed, kBank3Changed
};

View File

@ -0,0 +1,87 @@
//============================================================================
//
// 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-2016 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 CARTRIDGE3EPLUS_WIDGET_HXX
#define CARTRIDGE3EPLUS_WIDGET_HXX
class Cartridge3EPlus;
class ButtonWidget;
class PopUpWidget;
#include "CartDebugWidget.hxx"
class Cartridge3EPlusWidget : public CartDebugWidget
{
public:
Cartridge3EPlusWidget(GuiObject* boss, const GUI::Font& lfont,
const GUI::Font& nfont,
int x, int y, int w, int h,
Cartridge3EPlus& cart);
virtual ~Cartridge3EPlusWidget() = default;
private:
void updateUIState();
private:
Cartridge3EPlus& myCart;
PopUpWidget* myBankNumber[4];
PopUpWidget* myBankType[4];
ButtonWidget* myBankCommit[4];
EditTextWidget* myBankState[8];
struct CartState {
ByteArray internalram;
};
CartState myOldState;
enum BankID {
kBank0Changed = 'b0CH',
kBank1Changed = 'b1CH',
kBank2Changed = 'b2CH',
kBank3Changed = 'b3CH'
};
static const BankID bankEnum[4];
private:
void saveOldState() override;
void loadConfig() override;
void handleCommand(CommandSender* sender, int cmd, int data, int id) override;
string bankState() override;
// start of functions for Cartridge RAM tab
uInt32 internalRamSize() override;
uInt32 internalRamRPort(int start) override;
string internalRamDescription() override;
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
// Following constructors and assignment operators not supported
Cartridge3EPlusWidget() = delete;
Cartridge3EPlusWidget(const Cartridge3EPlusWidget&) = delete;
Cartridge3EPlusWidget(Cartridge3EPlusWidget&&) = delete;
Cartridge3EPlusWidget& operator=(const Cartridge3EPlusWidget&) = delete;
Cartridge3EPlusWidget& operator=(Cartridge3EPlusWidget&&) = delete;
};
#endif

View File

@ -25,6 +25,7 @@ MODULE_OBJS := \
src/debugger/gui/Cart0840Widget.o \
src/debugger/gui/Cart2KWidget.o \
src/debugger/gui/Cart3EWidget.o \
src/debugger/gui/Cart3EPlusWidget.o \
src/debugger/gui/Cart3FWidget.o \
src/debugger/gui/Cart4A50Widget.o \
src/debugger/gui/Cart4KWidget.o \

View File

@ -25,6 +25,7 @@
#include "Cart0840.hxx"
#include "Cart2K.hxx"
#include "Cart3E.hxx"
#include "Cart3EPlus.hxx"
#include "Cart3F.hxx"
#include "Cart4A50.hxx"
#include "Cart4K.hxx"
@ -190,6 +191,8 @@ unique_ptr<Cartridge> Cartridge::create(const BytePtr& img, uInt32 size,
cartridge = make_ptr<Cartridge2K>(image, size, settings);
else if(type == "3E")
cartridge = make_ptr<Cartridge3E>(image, size, settings);
else if(type == "3E+")
cartridge = make_ptr<Cartridge3EPlus>(image, size, settings);
else if(type == "3F")
cartridge = make_ptr<Cartridge3F>(image, size, settings);
else if(type == "4A50")
@ -527,6 +530,8 @@ string Cartridge::autodetectType(const uInt8* image, uInt32 size)
// Variable sized ROM formats are independent of image size and come last
if(isProbablyDASH(image, size))
type = "DASH";
else if(isProbably3EPlus(image, size))
type = "3E+";
else if(isProbablyMDM(image, size))
type = "MDM";
@ -647,6 +652,14 @@ bool Cartridge::isProbably3E(const uInt8* image, uInt32 size)
return searchForBytes(image, size, signature, 4, 1);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool Cartridge::isProbably3EPlus(const uInt8* image, uInt32 size)
{
// 3E+ cart is identified key 'TJ3E' in the ROM
uInt8 signature[] = { 'T', 'J', '3', 'E' };
return searchForBytes(image, size, signature, 4, 1);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool Cartridge::isProbably3F(const uInt8* image, uInt32 size)
{
@ -974,6 +987,7 @@ Cartridge::BankswitchType Cartridge::ourBSList[ourNumBSTypes] = {
{ "128IN1", "128IN1 Multicart (256/512K)" },
{ "2K", "2K (64-2048 bytes Atari)" },
{ "3E", "3E (32K Tigervision)" },
{ "3E+", "3E+ (TJ modified DASH)" },
{ "3F", "3F (512K Tigervision)" },
{ "4A50", "4A50 (64K 4A50 + ram)" },
{ "4K", "4K (4K Atari)" },

View File

@ -190,7 +190,7 @@ class Cartridge : public Device
const char* type;
const char* desc;
};
enum { ourNumBSTypes = 47 };
enum { ourNumBSTypes = 48 };
static BankswitchType ourBSList[ourNumBSTypes];
protected:
@ -276,6 +276,11 @@ class Cartridge : public Device
*/
static bool isProbably3E(const uInt8* image, uInt32 size);
/**
Returns true if the image is probably a 3E+ bankswitching cartridge
*/
static bool isProbably3EPlus(const uInt8* image, uInt32 size);
/**
Returns true if the image is probably a 3F bankswitching cartridge
*/
@ -383,7 +388,7 @@ class Cartridge : public Device
// The array containing information about every byte of ROM indicating
// whether it is used as code.
unique_ptr<uInt8[]> myCodeAccessBase;
BytePtr myCodeAccessBase;
private:
// If myBankLocked is true, ignore attempts at bankswitching. This is used

View File

@ -136,7 +136,7 @@ class Cartridge2K : public Cartridge
private:
// Pointer to a dynamically allocated ROM image of the cartridge
unique_ptr<uInt8[]> myImage;
BytePtr myImage;
// Size of the ROM image
uInt32 mySize;

View File

@ -181,7 +181,7 @@ class Cartridge3E : public Cartridge
private:
// Pointer to a dynamically allocated ROM image of the cartridge
unique_ptr<uInt8[]> myImage;
BytePtr myImage;
// RAM contents. For now every ROM gets all 32K of potential RAM
uInt8 myRAM[32 * 1024];

359
src/emucore/Cart3EPlus.cxx Normal file
View File

@ -0,0 +1,359 @@
//============================================================================
//
// 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-2016 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 <cstring>
#include "System.hxx"
#include "TIA.hxx"
#include "Cart3EPlus.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Cartridge3EPlus::Cartridge3EPlus(const uInt8* image, uInt32 size, const Settings& settings)
: Cartridge(settings),
mySize(size)
{
// Allocate array for the ROM image
myImage = make_ptr<uInt8[]>(mySize);
// Copy the ROM image into my buffer
memcpy(myImage.get(), image, mySize);
createCodeAccessBase(mySize + RAM_TOTAL_SIZE);
// Remember startup bank (0 per spec, rather than last per 3E scheme).
// Set this to go to 3rd 1K Bank.
myStartBank = 0;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Cartridge3EPlus::reset()
{
// Initialize RAM
if (mySettings.getBool("ramrandom"))
for (uInt32 i = 0; i < RAM_TOTAL_SIZE; ++i)
myRAM[i] = mySystem->randGenerator().next();
else
memset(myRAM, 0, RAM_TOTAL_SIZE);
// Initialise bank values for all ROM/RAM access
// This is used to reverse-lookup from address to bank location
for (uInt32 b = 0; b < 8; b++)
{
bankInUse[b] = BANK_UNDEFINED; // bank is undefined and inaccessible!
segmentInUse[b/2] = BANK_UNDEFINED;
}
initializeBankState();
// We'll map the startup banks 0 and 3 from the image into the third 1K bank upon reset
bankROM((0 << BANK_BITS) | 0);
bankROM((3 << BANK_BITS) | 0);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Cartridge3EPlus::install(System& system)
{
mySystem = &system;
System::PageAccess access(this, System::PA_READWRITE);
// Set the page accessing methods for the hot spots (for 100% emulation
// we need to chain any accesses below 0x40 to the TIA. Our poke() method
// does this via mySystem->tiaPoke(...), at least until we come up with a
// cleaner way to do it).
for (uInt32 i = 0x00; i < 0x40; i += (1 << System::PAGE_SHIFT))
mySystem->setPageAccess(i >> System::PAGE_SHIFT, access);
// Initialise bank values for all ROM/RAM access
// This is used to reverse-lookup from address to bank location
for (uInt32 b = 0; b < 8; b++)
{
bankInUse[b] = BANK_UNDEFINED; // bank is undefined and inaccessible!
segmentInUse[b/2] = BANK_UNDEFINED;
}
initializeBankState();
// Setup the last segment (of 4, each 1K) to point to the first ROM slice
// Actually we DO NOT want "always". It's just on bootup, and can be out switched later
bankROM((0 << BANK_BITS) | 0);
bankROM((3 << BANK_BITS) | 0);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt8 Cartridge3EPlus::peek(uInt16 address)
{
uInt16 peekAddress = address;
address &= 0x0FFF; // restrict to 4K address range
uInt8 value = 0;
uInt32 bank = (address >> (ROM_BANK_TO_POWER - 1)) & 7; // convert to 512 byte bank index (0-7)
uInt16 imageBank = bankInUse[bank]; // the ROM/RAM bank that's here
if (imageBank == BANK_UNDEFINED) // an uninitialised bank?
{
// accessing invalid bank, so return should be... random?
value = mySystem->randGenerator().next();
}
else if (imageBank & BITMASK_ROMRAM) // a RAM bank
{
// Reading from the write port triggers an unwanted write
value = mySystem->getDataBusState(0xFF);
if(bankLocked())
return value;
else
{
triggerReadFromWritePort(peekAddress);
Int32 ramBank = imageBank & BIT_BANK_MASK; // discard irrelevant bits
Int32 offset = ramBank << RAM_BANK_TO_POWER; // base bank address in RAM
offset += (address & BITMASK_RAM_BANK); // + byte offset in RAM bank
return myRAM[offset] = value;
}
}
return value;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool Cartridge3EPlus::poke(uInt16 address, uInt8 value)
{
bool changed = false;
// Check for write to the bank switch address. RAM/ROM and bank # are encoded in 'value'
// There are NO mirrored hotspots.
if (address == BANK_SWITCH_HOTSPOT_RAM)
changed = bankRAM(value);
else if (address == BANK_SWITCH_HOTSPOT_ROM)
changed = bankROM(value);
// Pass the poke through to the TIA. In a real Atari, both the cart and the
// TIA see the address lines, and both react accordingly. In Stella, each
// 64-byte chunk of address space is "owned" by only one device. If we
// don't chain the poke to the TIA, then the TIA can't see it...
mySystem->tia().poke(address, value);
return changed;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool Cartridge3EPlus::bankRAM(uInt8 bank)
{
if (bankLocked()) // debugger can lock RAM
return false;
// Each RAM bank uses two slots, separated by 0x200 in memory -- one read, one write.
bankRAMSlot(bank | BITMASK_ROMRAM | 0);
bankRAMSlot(bank | BITMASK_ROMRAM | BITMASK_LOWERUPPER);
// Remember that this hotspot was accessed for RAM
segmentInUse[(bank >> BANK_BITS) & 3] = bank | BITMASK_ROMRAM;
return myBankChanged = true;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Cartridge3EPlus::bankRAMSlot(uInt16 bank)
{
uInt16 bankNumber = (bank >> BANK_BITS) & 3; // which bank # we are switching TO (BITS D6,D7) to 512 byte block
uInt16 currentBank = bank & BIT_BANK_MASK; // Wrap around/restrict to valid range
bool upper = bank & BITMASK_LOWERUPPER; // is this the read or write port
uInt32 startCurrentBank = currentBank << RAM_BANK_TO_POWER; // Effectively * 512 bytes
cerr << "raw bank=" << std::dec << currentBank << endl
<< "startCurrentBank=$" << std::hex << startCurrentBank << endl;
// Setup the page access methods for the current bank
System::PageAccess access(this, System::PA_READ);
if(upper) // We're mapping the write port
{
bankInUse[bankNumber * 2 + 1] = Int16(bank);
access.type = System::PA_WRITE;
}
else // We're mapping the read port
{
bankInUse[bankNumber * 2] = Int16(bank);
access.type = System::PA_READ;
}
uInt32 start = 0x1000 + (bankNumber << (RAM_BANK_TO_POWER+1)) + (upper ? RAM_WRITE_OFFSET : 0);
uInt32 end = start + RAM_BANK_SIZE - 1;
cerr << "bank RAM: " << bankNumber << " -> " << (bankNumber * 2 + (upper ? 1 : 0)) << (upper ? " (W)" : " (R)") << endl
<< "start=" << std::hex << start << ", end=" << end << endl << endl;
for (uInt32 address = start; address <= end; address += (1 << System::PAGE_SHIFT))
{
if(upper)
access.directPokeBase = &myRAM[startCurrentBank + (address & (RAM_BANK_SIZE - 1))];
else
access.directPeekBase = &myRAM[startCurrentBank + (address & (RAM_BANK_SIZE - 1))];
access.codeAccessBase = &myCodeAccessBase[mySize + startCurrentBank + (address & (RAM_BANK_SIZE - 1))];
mySystem->setPageAccess(address >> System::PAGE_SHIFT, access);
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool Cartridge3EPlus::bankROM(uInt8 bank)
{
if (bankLocked()) // debugger can lock ROM
return false;
// Map ROM bank image into the system into the correct slot
// Memory map is 1K slots at 0x1000, 0x1400, 0x1800, 0x1C00
// Each ROM uses 2 consecutive 512 byte slots
bankROMSlot(bank | 0);
bankROMSlot(bank | BITMASK_LOWERUPPER);
// Remember that this hotspot was accessed for ROM
segmentInUse[(bank >> BANK_BITS) & 3] = bank;
return myBankChanged = true;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Cartridge3EPlus::bankROMSlot(uInt16 bank)
{
uInt16 bankNumber = (bank >> BANK_BITS) & 3; // which bank # we are switching TO (BITS D6,D7)
uInt16 currentBank = bank & BIT_BANK_MASK; // Wrap around/restrict to valid range
bool upper = bank & BITMASK_LOWERUPPER; // is this the lower or upper 512b
bankInUse[bankNumber * 2 + (upper ? 1 : 0)] = Int16(bank); // Record which bank switched in (as ROM)
uInt32 startCurrentBank = currentBank << ROM_BANK_TO_POWER; // Effectively *1K
// Setup the page access methods for the current bank
System::PageAccess access(this, System::PA_READ);
uInt32 start = 0x1000 + (bankNumber << ROM_BANK_TO_POWER) + (upper ? ROM_BANK_SIZE / 2 : 0);
uInt32 end = start + ROM_BANK_SIZE / 2 - 1;
for (uInt32 address = start; address <= end; address += (1 << System::PAGE_SHIFT))
{
access.directPeekBase = &myImage[startCurrentBank + (address & (ROM_BANK_SIZE - 1))];
access.codeAccessBase = &myCodeAccessBase[startCurrentBank + (address & (ROM_BANK_SIZE - 1))];
mySystem->setPageAccess(address >> System::PAGE_SHIFT, access);
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Cartridge3EPlus::initializeBankState()
{
// Switch in each 512b slot
for(uInt32 b = 0; b < 8; b++)
{
if(bankInUse[b] == BANK_UNDEFINED)
{
// All accesses point to peek/poke above
System::PageAccess access(this, System::PA_READ);
uInt32 start = 0x1000 + (b << RAM_BANK_TO_POWER);
uInt32 end = start + RAM_BANK_SIZE - 1;
for (uInt32 address = start; address <= end; address += (1 << System::PAGE_SHIFT))
mySystem->setPageAccess(address >> System::PAGE_SHIFT, access);
}
else if (bankInUse[b] & BITMASK_ROMRAM)
bankRAMSlot(bankInUse[b]);
else
bankROMSlot(bankInUse[b]);
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool Cartridge3EPlus::patch(uInt16 address, uInt8 value)
{
#if 0
// Patch the cartridge ROM (for debugger)
myBankChanged = true;
uInt32 bankNumber = (address >> RAM_BANK_TO_POWER) & 7; // now 512 byte bank # (ie: 0-7)
Int16 whichBankIsThere = bankInUse[bankNumber]; // ROM or RAM bank reference
if (whichBankIsThere == BANK_UNDEFINED) {
// We're trying to access undefined memory (no bank here yet). Fail!
myBankChanged = false;
} else if (whichBankIsThere & BITMASK_ROMRAM) { // patching RAM (512 byte banks)
uInt32 byteOffset = address & BITMASK_RAM_BANK;
uInt32 baseAddress = ((whichBankIsThere & BIT_BANK_MASK) << RAM_BANK_TO_POWER) + byteOffset;
myRAM[baseAddress] = value; // write to RAM
// TODO: Stephen -- should we set 'myBankChanged' true when there's a RAM write?
} else { // patching ROM (1K banks)
uInt32 byteOffset = address & BITMASK_ROM_BANK;
uInt32 baseAddress = (whichBankIsThere << ROM_BANK_TO_POWER) + byteOffset;
myImage[baseAddress] = value; // write to the image
}
return myBankChanged;
#else
return false;
#endif
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
const uInt8* Cartridge3EPlus::getImage(int& size) const
{
size = mySize;
return myImage.get();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool Cartridge3EPlus::save(Serializer& out) const
{
try
{
out.putString(name());
out.putShortArray(bankInUse, 8);
out.putShortArray(segmentInUse, 4);
out.putByteArray(myRAM, RAM_TOTAL_SIZE);
}
catch (...)
{
cerr << "ERROR: Cartridge3EPlus::save" << endl;
return false;
}
return true;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool Cartridge3EPlus::load(Serializer& in)
{
try
{
if (in.getString() != name())
return false;
in.getShortArray(bankInUse, 8);
in.getShortArray(segmentInUse, 4);
in.getByteArray(myRAM, RAM_TOTAL_SIZE);
}
catch (...)
{
cerr << "ERROR: Cartridge3EPlus::load" << endl;
return false;
}
initializeBankState();
return true;
}

195
src/emucore/Cart3EPlus.hxx Normal file
View File

@ -0,0 +1,195 @@
//============================================================================
//
// 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-2016 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 CARTRIDGE_3EPLUS_HXX
#define CARTRIDGE_3EPLUS_HXX
class System;
#include "bspf.hxx"
#include "Cart.hxx"
#ifdef DEBUGGER_SUPPORT
class Cartridge3EPlusWidget;
#include "Cart3EPlusWidget.hxx"
#endif
/**
Cartridge class from Thomas Jentzsch, mostly based on the 'DASH' scheme
with the following changes:
RAM areas:
- read $x000, write $x200
- read $x400, write $x600
- read $x800, write $xa00
- read $xc00, write $xe00
@author Thomas Jentzsch and Stephen Anthony
*/
class Cartridge3EPlus: public Cartridge
{
friend class Cartridge3EPlusWidget;
public:
/**
Create a new cartridge using the specified image and size
@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)
*/
Cartridge3EPlus(const uInt8* image, uInt32 size, const Settings& settings);
virtual ~Cartridge3EPlus() = 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;
/**
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(int& 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;
/**
Get a descriptor for the device name (used in error checking).
@return The name of the object
*/
string name() const override { return "Cartridge3E+"; }
#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 Cartridge3EPlusWidget(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;
/**
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;
private:
bool bankRAM(uInt8 bank); // switch a RAM bank
bool bankROM(uInt8 bank); // switch a ROM bank
void bankRAMSlot(uInt16 bank); // switch in a 512b RAM slot (lower or upper 1/2 bank)
void bankROMSlot(uInt16 bank); // switch in a 512b RAM slot (read or write port)
void initializeBankState(); // set all banks according to current bankInUse state
// We have an array that indicates for each of the 8 512 byte areas of the address space, which ROM/RAM
// bank is used in that area. ROM switches 1K so occupies 2 successive entries for each switch. RAM occupies
// two as well, one 512 byte for read and one for write. The RAM locations are +0x800 apart, and the ROM
// are consecutive. This allows us to determine on a read/write exactly where the data is.
static constexpr uInt16 BANK_UNDEFINED = 0x8000; // bank is undefined and inaccessible
uInt16 bankInUse[8]; // bank being used for ROM/RAM (eight 512 byte areas)
uInt16 segmentInUse[4]; // set by bank methods, to know which hotspot was accessed
static constexpr uInt16 BANK_SWITCH_HOTSPOT_RAM = 0x3E; // writes to this address cause bankswitching
static constexpr uInt16 BANK_SWITCH_HOTSPOT_ROM = 0x3F; // writes to this address cause bankswitching
static constexpr uInt8 BANK_BITS = 6; // # bits for bank
static constexpr uInt8 BIT_BANK_MASK = (1 << BANK_BITS) - 1; // mask for those bits
static constexpr uInt16 BITMASK_LOWERUPPER = 0x100; // flags lower or upper section of bank (1==upper)
static constexpr uInt16 BITMASK_ROMRAM = 0x200; // flags ROM or RAM bank switching (1==RAM)
static constexpr uInt16 MAXIMUM_BANK_COUNT = (1 << BANK_BITS);
static constexpr uInt16 RAM_BANK_TO_POWER = 9; // 2^n = 512
static constexpr uInt16 RAM_BANK_SIZE = (1 << RAM_BANK_TO_POWER);
static constexpr uInt16 BITMASK_RAM_BANK = (RAM_BANK_SIZE - 1);
static constexpr uInt32 RAM_TOTAL_SIZE = MAXIMUM_BANK_COUNT * RAM_BANK_SIZE;
static constexpr uInt16 ROM_BANK_TO_POWER = 10; // 2^n = 1024
static constexpr uInt16 ROM_BANK_SIZE = (1 << ROM_BANK_TO_POWER);
static constexpr uInt16 BITMASK_ROM_BANK = (ROM_BANK_SIZE - 1);
static constexpr uInt16 ROM_BANK_COUNT = 64;
static constexpr uInt16 RAM_WRITE_OFFSET = 0x200;
BytePtr myImage; // Pointer to a dynamically allocated ROM image of the cartridge
uInt32 mySize; // Size of the ROM image
uInt8 myRAM[RAM_TOTAL_SIZE];
private:
// Following constructors and assignment operators not supported
Cartridge3EPlus() = delete;
Cartridge3EPlus(const Cartridge3EPlus&) = delete;
Cartridge3EPlus(Cartridge3EPlus&&) = delete;
Cartridge3EPlus& operator=(const Cartridge3EPlus&) = delete;
Cartridge3EPlus& operator=(Cartridge3EPlus&&) = delete;
};
#endif

View File

@ -158,7 +158,7 @@ class Cartridge3F : public Cartridge
private:
// Pointer to a dynamically allocated ROM image of the cartridge
unique_ptr<uInt8[]> myImage;
BytePtr myImage;
// Size of the ROM image
uInt32 mySize;

View File

@ -198,7 +198,7 @@ class CartridgeAR : public Cartridge
uInt32 mySize;
// All of the 8448 byte loads associated with the game
unique_ptr<uInt8[]> myLoadImages;
BytePtr myLoadImages;
// Indicates how many 8448 loads there are
uInt8 myNumberOfLoadImages;

View File

@ -139,7 +139,7 @@ class CartridgeCV : public Cartridge
private:
// Pointer to the initial RAM data from the cart
// This doesn't always exist, so we don't pre-allocate it
unique_ptr<uInt8[]> myInitialRAM;
BytePtr myInitialRAM;
// Initial size of the cart data
uInt32 mySize;

View File

@ -165,7 +165,7 @@ class CartridgeCVPlus : public Cartridge
private:
// Pointer to a dynamically allocated ROM image of the cartridge
unique_ptr<uInt8[]> myImage;
BytePtr myImage;
// The 1024 bytes of RAM
uInt8 myRAM[1024];

View File

@ -26,8 +26,7 @@
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
CartridgeDASH::CartridgeDASH(const uInt8* image, uInt32 size, const Settings& settings)
: Cartridge(settings),
mySize(size),
myImage(nullptr)
mySize(size)
{
// Allocate array for the ROM image
myImage = make_ptr<uInt8[]>(mySize);
@ -42,8 +41,8 @@ CartridgeDASH::CartridgeDASH(const uInt8* image, uInt32 size, const Settings& se
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void CartridgeDASH::reset() {
void CartridgeDASH::reset()
{
// Initialize RAM
if (mySettings.getBool("ramrandom"))
for (uInt32 i = 0; i < RAM_TOTAL_SIZE; ++i)
@ -66,8 +65,8 @@ void CartridgeDASH::reset() {
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void CartridgeDASH::install(System& system) {
void CartridgeDASH::install(System& system)
{
mySystem = &system;
System::PageAccess access(this, System::PA_READWRITE);
@ -95,22 +94,23 @@ void CartridgeDASH::install(System& system) {
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt8 CartridgeDASH::peek(uInt16 address) {
uInt8 CartridgeDASH::peek(uInt16 address)
{
uInt16 peekAddress = address;
address &= 0x0FFF; // restrict to 4K address range
uInt8 value = 0;
uInt32 bank = (address >> (ROM_BANK_TO_POWER - 1)) & 7; // convert to 512 byte bank index (0-7)
uInt16 imageBank = bankInUse[bank]; // the ROM/RAM bank that's here
if (imageBank == BANK_UNDEFINED) { // an uninitialised bank?
uInt16 imageBank = bankInUse[bank]; // the ROM/RAM bank that's here
if (imageBank == BANK_UNDEFINED) // an uninitialised bank?
{
// accessing invalid bank, so return should be... random?
value = mySystem->randGenerator().next();
} else if (imageBank & BITMASK_ROMRAM) { // a RAM bank
}
else if (imageBank & BITMASK_ROMRAM) // a RAM bank
{
// Reading from the write port triggers an unwanted write
value = mySystem->getDataBusState(0xFF);
@ -131,8 +131,8 @@ uInt8 CartridgeDASH::peek(uInt16 address) {
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool CartridgeDASH::poke(uInt16 address, uInt8 value) {
bool CartridgeDASH::poke(uInt16 address, uInt8 value)
{
bool changed = false;
// Check for write to the bank switch address. RAM/ROM and bank # are encoded in 'value'
@ -154,8 +154,8 @@ bool CartridgeDASH::poke(uInt16 address, uInt8 value) {
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool CartridgeDASH::bankRAM(uInt8 bank) {
bool CartridgeDASH::bankRAM(uInt8 bank)
{
if (bankLocked()) // debugger can lock RAM
return false;
@ -170,8 +170,8 @@ bool CartridgeDASH::bankRAM(uInt8 bank) {
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void CartridgeDASH::bankRAMSlot(uInt16 bank) {
void CartridgeDASH::bankRAMSlot(uInt16 bank)
{
uInt16 bankNumber = (bank >> BANK_BITS) & 3; // which bank # we are switching TO (BITS D6,D7) to 512 byte block
uInt16 currentBank = bank & BIT_BANK_MASK; // Wrap around/restrict to valid range
bool upper = bank & BITMASK_LOWERUPPER; // is this the read or write port
@ -181,35 +181,35 @@ void CartridgeDASH::bankRAMSlot(uInt16 bank) {
// Setup the page access methods for the current bank
System::PageAccess access(this, System::PA_READ);
if (upper) { // We're mapping the write port
if(upper) // We're mapping the write port
{
bankInUse[bankNumber + 4] = Int16(bank);
access.type = System::PA_WRITE;
} else { // We're mapping the read port
}
else // We're mapping the read port
{
bankInUse[bankNumber] = Int16(bank);
access.type = System::PA_READ;
}
uInt32 start = 0x1000 + (bankNumber << RAM_BANK_TO_POWER) + (upper ? RAM_WRITE_OFFSET : 0);
uInt32 end = start + RAM_BANK_SIZE - 1;
for (uInt32 address = start; address <= end; address += (1 << System::PAGE_SHIFT)) {
for (uInt32 address = start; address <= end; address += (1 << System::PAGE_SHIFT))
{
if(upper)
access.directPokeBase = &myRAM[startCurrentBank + (address & (RAM_BANK_SIZE - 1))];
else
access.directPeekBase = &myRAM[startCurrentBank + (address & (RAM_BANK_SIZE - 1))];
access.codeAccessBase = &myCodeAccessBase[mySize + startCurrentBank + (address & (RAM_BANK_SIZE - 1))];
mySystem->setPageAccess(address >> System::PAGE_SHIFT, access);
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool CartridgeDASH::bankROM(uInt8 bank) {
bool CartridgeDASH::bankROM(uInt8 bank)
{
if (bankLocked()) // debugger can lock ROM
return false;
@ -226,8 +226,8 @@ bool CartridgeDASH::bankROM(uInt8 bank) {
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void CartridgeDASH::bankROMSlot(uInt16 bank) {
void CartridgeDASH::bankROMSlot(uInt16 bank)
{
uInt16 bankNumber = (bank >> BANK_BITS) & 3; // which bank # we are switching TO (BITS D6,D7)
uInt16 currentBank = bank & BIT_BANK_MASK; // Wrap around/restrict to valid range
bool upper = bank & BITMASK_LOWERUPPER; // is this the lower or upper 512b
@ -242,7 +242,8 @@ void CartridgeDASH::bankROMSlot(uInt16 bank) {
uInt32 start = 0x1000 + (bankNumber << ROM_BANK_TO_POWER) + (upper ? ROM_BANK_SIZE / 2 : 0);
uInt32 end = start + ROM_BANK_SIZE / 2 - 1;
for (uInt32 address = start; address <= end; address += (1 << System::PAGE_SHIFT)) {
for (uInt32 address = start; address <= end; address += (1 << System::PAGE_SHIFT))
{
access.directPeekBase = &myImage[startCurrentBank + (address & (ROM_BANK_SIZE - 1))];
access.codeAccessBase = &myCodeAccessBase[startCurrentBank + (address & (ROM_BANK_SIZE - 1))];
mySystem->setPageAccess(address >> System::PAGE_SHIFT, access);
@ -250,19 +251,19 @@ void CartridgeDASH::bankROMSlot(uInt16 bank) {
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void CartridgeDASH::initializeBankState() {
void CartridgeDASH::initializeBankState()
{
// Switch in each 512b slot
for (uInt32 b = 0; b < 8; b++) {
if (bankInUse[b] == BANK_UNDEFINED) {
for(uInt32 b = 0; b < 8; b++)
{
if(bankInUse[b] == BANK_UNDEFINED)
{
// All accesses point to peek/poke above
System::PageAccess access(this, System::PA_READ);
uInt32 start = 0x1000 + (b << RAM_BANK_TO_POWER);
uInt32 end = start + RAM_BANK_SIZE - 1;
for (uInt32 address = start; address <= end; address += (1 << System::PAGE_SHIFT))
mySystem->setPageAccess(address >> System::PAGE_SHIFT, access);
}
else if (bankInUse[b] & BITMASK_ROMRAM)
bankRAMSlot(bankInUse[b]);
@ -272,7 +273,8 @@ void CartridgeDASH::initializeBankState() {
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool CartridgeDASH::patch(uInt16 address, uInt8 value) {
bool CartridgeDASH::patch(uInt16 address, uInt8 value)
{
#if 0
// Patch the cartridge ROM (for debugger)
@ -308,20 +310,24 @@ bool CartridgeDASH::patch(uInt16 address, uInt8 value) {
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
const uInt8* CartridgeDASH::getImage(int& size) const {
const uInt8* CartridgeDASH::getImage(int& size) const
{
size = mySize;
return myImage.get();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool CartridgeDASH::save(Serializer& out) const {
try {
bool CartridgeDASH::save(Serializer& out) const
{
try
{
out.putString(name());
out.putShortArray(bankInUse, 8);
out.putShortArray(segmentInUse, 4);
out.putByteArray(myRAM, RAM_TOTAL_SIZE);
} catch (...) {
}
catch (...)
{
cerr << "ERROR: CartridgeDASH::save" << endl;
return false;
}
@ -329,15 +335,18 @@ bool CartridgeDASH::save(Serializer& out) const {
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool CartridgeDASH::load(Serializer& in) {
try {
bool CartridgeDASH::load(Serializer& in)
{
try
{
if (in.getString() != name())
return false;
in.getShortArray(bankInUse, 8);
in.getShortArray(segmentInUse, 4);
in.getByteArray(myRAM, RAM_TOTAL_SIZE);
} catch (...) {
}
catch (...)
{
cerr << "ERROR: CartridgeDASH::load" << endl;
return false;
}

View File

@ -125,147 +125,145 @@ class CartridgeDASHWidget;
@author Andrew Davie
*/
class CartridgeDASH: public Cartridge {
class CartridgeDASH: public Cartridge
{
friend class CartridgeDASHWidget;
public:
/**
Create a new cartridge using the specified image and size
public:
/**
Create a new cartridge using the specified image and size
@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)
*/
CartridgeDASH(const uInt8* image, uInt32 size, const Settings& settings);
virtual ~CartridgeDASH() = default;
@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)
*/
CartridgeDASH(const uInt8* image, uInt32 size, const Settings& settings);
virtual ~CartridgeDASH() = default;
public:
/**
Reset device to its power-on state
*/
void reset() override;
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.
/**
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;
@param system The system the device should install itself in
*/
void install(System& system) override;
/**
Patch the cartridge ROM.
/**
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;
@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.
/**
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 override;
@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 override;
/**
Save the current state of this cart to the given Serializer.
/**
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;
@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.
/**
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;
@param in The Serializer object to use
@return False on any errors, else true
*/
bool load(Serializer& in) override;
/**
Get a descriptor for the device name (used in error checking).
/**
Get a descriptor for the device name (used in error checking).
@return The name of the object
*/
string name() const override { return "CartridgeDASH"; }
@return The name of the object
*/
string name() const override { return "CartridgeDASH"; }
#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 CartridgeDASHWidget(boss, lfont, nfont, x, y, w, h, *this);
}
#endif
#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 CartridgeDASHWidget(boss, lfont, nfont, x, y, w, h, *this);
}
#endif
public:
/**
Get the byte at the specified address
public:
/**
Get the byte at the specified address
@return The byte at the specified address
*/
uInt8 peek(uInt16 address) override;
@return The byte at the specified address
*/
uInt8 peek(uInt16 address) override;
/**
Change the byte at the specified address to the given value
/**
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;
@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;
private:
private:
bool bankRAM(uInt8 bank); // switch a RAM bank
bool bankROM(uInt8 bank); // switch a ROM bank
bool bankRAM(uInt8 bank); // switch a RAM bank
bool bankROM(uInt8 bank); // switch a ROM bank
void bankRAMSlot(uInt16 bank); // switch in a 512b RAM slot (lower or upper 1/2 bank)
void bankROMSlot(uInt16 bank); // switch in a 512b RAM slot (read or write port)
void bankRAMSlot(uInt16 bank); // switch in a 512b RAM slot (lower or upper 1/2 bank)
void bankROMSlot(uInt16 bank); // switch in a 512b RAM slot (read or write port)
void initializeBankState(); // set all banks according to current bankInUse state
void initializeBankState(); // set all banks according to current bankInUse state
// We have an array that indicates for each of the 8 512 byte areas of the address space, which ROM/RAM
// bank is used in that area. ROM switches 1K so occupies 2 successive entries for each switch. RAM occupies
// two as well, one 512 byte for read and one for write. The RAM locations are +0x800 apart, and the ROM
// are consecutive. This allows us to determine on a read/write exactly where the data is.
// We have an array that indicates for each of the 8 512 byte areas of the address space, which ROM/RAM
// bank is used in that area. ROM switches 1K so occupies 2 successive entries for each switch. RAM occupies
// two as well, one 512 byte for read and one for write. The RAM locations are +0x800 apart, and the ROM
// are consecutive. This allows us to determine on a read/write exactly where the data is.
static constexpr uInt16 BANK_UNDEFINED = 0x8000; // bank is undefined and inaccessible
uInt16 bankInUse[8]; // bank being used for ROM/RAM (eight 512 byte areas)
uInt16 segmentInUse[4]; // set by bank methods, to know which hotspot was accessed
static constexpr uInt16 BANK_UNDEFINED = 0x8000; // bank is undefined and inaccessible
uInt16 bankInUse[8]; // bank being used for ROM/RAM (eight 512 byte areas)
uInt16 segmentInUse[4]; // set by bank methods, to know which hotspot was accessed
static constexpr uInt16 BANK_SWITCH_HOTSPOT_RAM = 0x3E; // writes to this address cause bankswitching
static constexpr uInt16 BANK_SWITCH_HOTSPOT_ROM = 0x3F; // writes to this address cause bankswitching
static constexpr uInt16 BANK_SWITCH_HOTSPOT_RAM = 0x3E; // writes to this address cause bankswitching
static constexpr uInt16 BANK_SWITCH_HOTSPOT_ROM = 0x3F; // writes to this address cause bankswitching
static constexpr uInt8 BANK_BITS = 6; // # bits for bank
static constexpr uInt8 BIT_BANK_MASK = (1 << BANK_BITS) - 1; // mask for those bits
static constexpr uInt16 BITMASK_LOWERUPPER = 0x100; // flags lower or upper section of bank (1==upper)
static constexpr uInt16 BITMASK_ROMRAM = 0x200; // flags ROM or RAM bank switching (1==RAM)
static constexpr uInt8 BANK_BITS = 6; // # bits for bank
static constexpr uInt8 BIT_BANK_MASK = (1 << BANK_BITS) - 1; // mask for those bits
static constexpr uInt16 BITMASK_LOWERUPPER = 0x100; // flags lower or upper section of bank (1==upper)
static constexpr uInt16 BITMASK_ROMRAM = 0x200; // flags ROM or RAM bank switching (1==RAM)
static constexpr uInt16 MAXIMUM_BANK_COUNT = (1 << BANK_BITS);
static constexpr uInt16 RAM_BANK_TO_POWER = 9; // 2^n = 512
static constexpr uInt16 RAM_BANK_SIZE = (1 << RAM_BANK_TO_POWER);
static constexpr uInt16 BITMASK_RAM_BANK = (RAM_BANK_SIZE - 1);
static constexpr uInt32 RAM_TOTAL_SIZE = MAXIMUM_BANK_COUNT * RAM_BANK_SIZE;
static constexpr uInt16 MAXIMUM_BANK_COUNT = (1<<BANK_BITS);
static constexpr uInt16 RAM_BANK_TO_POWER = 9; // 2^n = 512
static constexpr uInt16 RAM_BANK_SIZE = (1 << RAM_BANK_TO_POWER);
static constexpr uInt16 BITMASK_RAM_BANK = (RAM_BANK_SIZE - 1);
static constexpr uInt32 RAM_TOTAL_SIZE = MAXIMUM_BANK_COUNT * RAM_BANK_SIZE;
static constexpr uInt16 ROM_BANK_TO_POWER = 10; // 2^n = 1024
static constexpr uInt16 ROM_BANK_SIZE = (1 << ROM_BANK_TO_POWER);
static constexpr uInt16 BITMASK_ROM_BANK = (ROM_BANK_SIZE - 1);
static constexpr uInt16 ROM_BANK_TO_POWER = 10; // 2^n = 1024
static constexpr uInt16 ROM_BANK_SIZE = (1 << ROM_BANK_TO_POWER);
static constexpr uInt16 BITMASK_ROM_BANK = (ROM_BANK_SIZE -1);
static constexpr uInt16 ROM_BANK_COUNT = 64;
static constexpr uInt16 ROM_BANK_COUNT = 64;
static constexpr uInt16 RAM_WRITE_OFFSET = 0x800;
static constexpr uInt16 RAM_WRITE_OFFSET = 0x800;
uInt32 mySize; // Size of the ROM image
unique_ptr<uInt8[]> myImage; // Pointer to a dynamically allocated ROM image of the cartridge
uInt8 myRAM[RAM_TOTAL_SIZE];
BytePtr myImage; // Pointer to a dynamically allocated ROM image of the cartridge
uInt32 mySize; // Size of the ROM image
uInt8 myRAM[RAM_TOTAL_SIZE];
private:
// Following constructors and assignment operators not supported

View File

@ -195,7 +195,7 @@ class CartridgeDPCPlus : public Cartridge
private:
// The ROM image and size
unique_ptr<uInt8[]> myImage;
BytePtr myImage;
uInt32 mySize;
// Pointer to the 24K program ROM image of the cartridge

View File

@ -163,7 +163,7 @@ class CartridgeMDM : public Cartridge
private:
// Pointer to a dynamically allocated ROM image of the cartridge
unique_ptr<uInt8[]> myImage;
BytePtr myImage;
// Size of the ROM image
uInt32 mySize;

View File

@ -152,7 +152,7 @@ class CartridgeSB : public Cartridge
private:
// The 128-256K ROM image and size of the cartridge
unique_ptr<uInt8[]> myImage;
BytePtr myImage;
uInt32 mySize;
// Indicates which bank is currently active

View File

@ -408,10 +408,10 @@ class TIA : public Device
Settings& mySettings;
// Pointer to the current frame buffer
unique_ptr<uInt8[]> myCurrentFrameBuffer;
BytePtr myCurrentFrameBuffer;
// Pointer to the previous frame buffer
unique_ptr<uInt8[]> myPreviousFrameBuffer;
BytePtr myPreviousFrameBuffer;
// Pointer to the next pixel that will be drawn in the current frame buffer
uInt8* myFramePointer;

View File

@ -7,6 +7,7 @@ MODULE_OBJS := \
src/emucore/Cart0840.o \
src/emucore/Cart2K.o \
src/emucore/Cart3E.o \
src/emucore/Cart3EPlus.o \
src/emucore/Cart3F.o \
src/emucore/Cart4A50.o \
src/emucore/Cart4K.o \