Merge pull request #110 from SpiceWare/master

Preliminary support for BUS and CDF
This commit is contained in:
sa666666 2017-03-24 16:09:59 -02:30 committed by GitHub
commit 7c6821dfcb
16 changed files with 3136 additions and 13 deletions

View File

@ -74,6 +74,12 @@ string Base::toString(int value, Common::Base::Format outputBase)
case Base::F_16_2: // base 16: 2 bytes wide
std::snprintf(vToS_buf, 3, myFmt[1], value);
break;
case Base::F_16_2_2:
std::snprintf(vToS_buf, 6, "%02X.%02X", value >> 8, value & 0xff );
break;
case Base::F_16_3_2:
std::snprintf(vToS_buf, 7, "%03X.%02X", value >> 8, value & 0xff );
break;
case Base::F_16_4: // base 16: 4 bytes wide
std::snprintf(vToS_buf, 5, myFmt[2], value);
break;

View File

@ -42,6 +42,8 @@ class Base
F_16, // base 16: 2, 4, 8 bytes (depending on value)
F_16_1, // base 16: 1 byte wide
F_16_2, // base 16: 2 bytes wide
F_16_2_2, // base 16: fractional value shown as xx.xx
F_16_3_2, // base 16: fractional value shown as xxx.xx
F_16_4, // base 16: 4 bytes wide
F_16_8, // base 16: 8 bytes wide
F_10, // base 10: 3 or 5 bytes (depending on value)

View File

@ -0,0 +1,373 @@
//============================================================================
//
// 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-2015 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: CartBUSWidget.cxx 3131 2015-01-01 03:49:32Z stephena $
//============================================================================
#include "CartBUS.hxx"
#include "DataGridWidget.hxx"
#include "PopUpWidget.hxx"
#include "CartBUSWidget.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
CartridgeBUSWidget::CartridgeBUSWidget(
GuiObject* boss, const GUI::Font& lfont, const GUI::Font& nfont,
int x, int y, int w, int h, CartridgeBUS& cart)
: CartDebugWidget(boss, lfont, nfont, x, y, w, h),
myCart(cart)
{
uInt16 size = 8 * 4096;
ostringstream info;
info << "BUS Stuffing cartridge\n"
<< "32K ROM, seven 4K banks are accessible to 2600\n"
<< "8K BUS RAM\n"
<< "BUS registers accessible @ $F000 - $F03F\n"
<< "Banks accessible at hotspots $FF5 to $FFB\n"
<< "Startup bank = " << cart.myStartBank << "\n";
#if 0
// Eventually, we should query this from the debugger/disassembler
for(uInt32 i = 0, offset = 0xFFC, spot = 0xFF5; i < 7; ++i, offset += 0x1000)
{
uInt16 start = (cart.myImage[offset+1] << 8) | cart.myImage[offset];
start -= start % 0x1000;
info << "Bank " << i << " @ $" << HEX4 << (start + 0x80) << " - "
<< "$" << (start + 0xFFF) << " (hotspot = $" << (spot+i) << ")\n";
}
#endif
int xpos = 10,
ypos = addBaseInformation(size, "AtariAge", info.str()) +
myLineHeight;
VariantList items;
VarList::push_back(items, "0 ($FF5)");
VarList::push_back(items, "1 ($FF6)");
VarList::push_back(items, "2 ($FF7)");
VarList::push_back(items, "3 ($FF8)");
VarList::push_back(items, "4 ($FF9)");
VarList::push_back(items, "5 ($FFA)");
VarList::push_back(items, "6 ($FFB)");
myBank =
new PopUpWidget(boss, _font, xpos, ypos-2, _font.getStringWidth("0 ($FFx) "),
myLineHeight, items, "Set bank: ",
_font.getStringWidth("Set bank: "), kBankChanged);
myBank->setTarget(this);
addFocusWidget(myBank);
int lwidth = _font.getStringWidth("Datastream Increments: "); // get width of the widest label
// Datastream Pointers
xpos = 0; ypos += myLineHeight + 4;
new StaticTextWidget(boss, _font, xpos, ypos, lwidth,
myFontHeight, "Datastream Pointers: ", kTextAlignLeft);
xpos += lwidth;
myDatastreamPointers = new DataGridWidget(boss, _nfont, 0, ypos+myLineHeight-2, 4, 4, 6, 32, Common::Base::F_16_3_2);
myDatastreamPointers->setTarget(this);
myDatastreamPointers->setEditable(false);
// Datastream Increments
xpos = 0 + myDatastreamPointers->getWidth();
new StaticTextWidget(boss, _font, xpos, ypos, lwidth,
myFontHeight, "Datastream Increments: ", kTextAlignLeft);
myDatastreamIncrements = new DataGridWidget(boss, _nfont, xpos, ypos+myLineHeight-2, 4, 4, 5, 32, Common::Base::F_16_2_2);
myDatastreamIncrements->setTarget(this);
myDatastreamIncrements->setEditable(false);
// Datastream Maps
xpos = 0; ypos += myLineHeight*5 + 4;
new StaticTextWidget(boss, _font, xpos, ypos, lwidth,
myFontHeight, "Address Maps: ", kTextAlignLeft);
myAddressMaps = new DataGridWidget(boss, _nfont, 0, ypos+myLineHeight-2, 8, 5, 8, 32, Common::Base::F_16_8);
myAddressMaps->setTarget(this);
myAddressMaps->setEditable(false);
// Music counters
xpos = 10; ypos += myLineHeight*6 + 4;
new StaticTextWidget(boss, _font, xpos, ypos, lwidth,
myFontHeight, "Music Counters: ", kTextAlignLeft);
xpos += lwidth;
myMusicCounters = new DataGridWidget(boss, _nfont, xpos, ypos-2, 3, 1, 8, 32, Common::Base::F_16_8);
myMusicCounters->setTarget(this);
myMusicCounters->setEditable(false);
// Music frequencies
xpos = 10; ypos += myLineHeight + 4;
new StaticTextWidget(boss, _font, xpos, ypos, lwidth,
myFontHeight, "Music Frequencies: ", kTextAlignLeft);
xpos += lwidth;
myMusicFrequencies = new DataGridWidget(boss, _nfont, xpos, ypos-2, 3, 1, 8, 32, Common::Base::F_16_8);
myMusicFrequencies->setTarget(this);
myMusicFrequencies->setEditable(false);
// Music waveforms
xpos = 10; ypos += myLineHeight + 4;
new StaticTextWidget(boss, _font, xpos, ypos, lwidth,
myFontHeight, "Music Waveforms: ", kTextAlignLeft);
xpos += lwidth;
myMusicWaveforms = new DataGridWidget(boss, _nfont, xpos, ypos-2, 3, 1, 4, 16, Common::Base::F_16_2);
myMusicWaveforms->setTarget(this);
myMusicWaveforms->setEditable(false);
// Music waveform sizes
xpos = 10; ypos += myLineHeight + 4;
new StaticTextWidget(boss, _font, xpos, ypos, lwidth,
myFontHeight, "Music Waveform Sizes: ", kTextAlignLeft);
xpos += lwidth;
myMusicWaveformSizes = new DataGridWidget(boss, _nfont, xpos, ypos-2, 3, 1, 4, 16, Common::Base::F_16_2);
myMusicWaveformSizes->setTarget(this);
myMusicWaveformSizes->setEditable(false);
// BUS stuff and ZP STY flags
xpos = 10; ypos += myLineHeight + 4;
myBusOverdrive = new CheckboxWidget(boss, _font, xpos, ypos, "BUS Overdrive enabled");
myBusOverdrive->setTarget(this);
myBusOverdrive->setEditable(false);
xpos = _font.getStringWidth("CHECKBOX BUS Overdrive enabled");
myZPSTY = new CheckboxWidget(boss, _font, xpos, ypos, "Zero Page STY");
myZPSTY->setTarget(this);
myZPSTY->setEditable(false);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void CartridgeBUSWidget::saveOldState()
{
myOldState.tops.clear();
myOldState.bottoms.clear();
myOldState.datastreampointers.clear();
myOldState.datastreamincrements.clear();
myOldState.addressmaps.clear();
myOldState.mcounters.clear();
myOldState.mfreqs.clear();
myOldState.mwaves.clear();
myOldState.mwavesizes.clear();
myOldState.internalram.clear();
for(uInt32 i = 0; i < 16; i++)
{
// Pointers are stored as:
// PPPFF---
//
// Increments are stored as
// ----IIFF
//
// P = Pointer
// I = Increment
// F = Fractional
myOldState.datastreampointers.push_back(myCart.getDatastreamPointer(i)>>12);
myOldState.datastreamincrements.push_back(myCart.getDatastreamIncrement(i));
}
for(uInt32 i = 0; i < 40; i++)
{
myOldState.addressmaps.push_back(myCart.getAddressMap(i));
}
for(uInt32 i = 0; i < 3; ++i)
{
myOldState.mcounters.push_back(myCart.myMusicCounters[i]);
}
for(uInt32 i = 0; i < 3; ++i)
{
myOldState.mfreqs.push_back(myCart.myMusicFrequencies[i]);
myOldState.mwaves.push_back(myCart.getWaveform(i) >> 5);
myOldState.mwavesizes.push_back(myCart.getWaveformSize((i)));
}
for(uInt32 i = 0; i < internalRamSize(); ++i)
myOldState.internalram.push_back(myCart.myBUSRAM[i]);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void CartridgeBUSWidget::loadConfig()
{
myBank->setSelectedIndex(myCart.myCurrentBank);
// Get registers, using change tracking
IntArray alist;
IntArray vlist;
BoolArray changed;
alist.clear(); vlist.clear(); changed.clear();
for(int i = 0; i < 16; ++i)
{
// Pointers are stored as:
// PPPFF---
//
// Increments are stored as
// ----IIFF
//
// P = Pointer
// I = Increment
// F = Fractional
uInt32 pointervalue = myCart.getDatastreamPointer(i) >> 12;
alist.push_back(0); vlist.push_back(pointervalue);
changed.push_back(pointervalue != myOldState.datastreampointers[i]);
}
myDatastreamPointers->setList(alist, vlist, changed);
alist.clear(); vlist.clear(); changed.clear();
for(int i = 0; i < 16; ++i)
{
uInt32 incrementvalue = myCart.getDatastreamIncrement(i);
alist.push_back(0); vlist.push_back(incrementvalue);
changed.push_back(incrementvalue != myOldState.datastreamincrements[i]);
}
myDatastreamIncrements->setList(alist, vlist, changed);
alist.clear(); vlist.clear(); changed.clear();
for(int i = 0; i < 40; ++i)
{
uInt32 mapvalue = myCart.getAddressMap(i);
alist.push_back(0); vlist.push_back(mapvalue);
changed.push_back(mapvalue != myOldState.addressmaps[i]);
}
myAddressMaps->setList(alist, vlist, changed);
alist.clear(); vlist.clear(); changed.clear();
for(int i = 0; i < 3; ++i)
{
alist.push_back(0); vlist.push_back(myCart.myMusicCounters[i]);
changed.push_back(myCart.myMusicCounters[i] != (uInt32)myOldState.mcounters[i]);
}
myMusicCounters->setList(alist, vlist, changed);
alist.clear(); vlist.clear(); changed.clear();
for(int i = 0; i < 3; ++i)
{
alist.push_back(0); vlist.push_back(myCart.myMusicFrequencies[i]);
changed.push_back(myCart.myMusicFrequencies[i] != (uInt32)myOldState.mfreqs[i]);
}
myMusicFrequencies->setList(alist, vlist, changed);
alist.clear(); vlist.clear(); changed.clear();
for(int i = 0; i < 3; ++i)
{
alist.push_back(0); vlist.push_back(myCart.getWaveform(i) >> 5);
changed.push_back((myCart.getWaveform(i) >> 5) != myOldState.mwaves[i]);
}
myMusicWaveforms->setList(alist, vlist, changed);
alist.clear(); vlist.clear(); changed.clear();
for(int i = 0; i < 3; ++i)
{
alist.push_back(0); vlist.push_back(myCart.getWaveformSize(i));
changed.push_back((myCart.getWaveformSize(i)) != myOldState.mwavesizes[i]);
}
myMusicWaveformSizes->setList(alist, vlist, changed);
myBusOverdrive->setState(myCart.getBusStuffFlag());
myZPSTY->setState(myCart.mySTYZeroPage);
CartDebugWidget::loadConfig();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void CartridgeBUSWidget::handleCommand(CommandSender* sender,
int cmd, int data, int id)
{
if(cmd == kBankChanged)
{
myCart.unlockBank();
myCart.bank(myBank->getSelected());
myCart.lockBank();
invalidate();
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string CartridgeBUSWidget::bankState()
{
ostringstream& buf = buffer();
static const char* spot[] = {
"$FF5", "$FF6", "$FF7", "$FF8", "$FF9", "$FFA", "$FFB"
};
buf << "Bank = " << std::dec << myCart.myCurrentBank
<< ", hotspot = " << spot[myCart.myCurrentBank];
return buf.str();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt32 CartridgeBUSWidget::internalRamSize()
{
return 8*1024;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt32 CartridgeBUSWidget::internalRamRPort(int start)
{
return 0x0000 + start;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string CartridgeBUSWidget::internalRamDescription()
{
ostringstream desc;
desc << "$0000 - $07FF - BUS driver\n"
<< " not accessible to 6507\n"
<< "$0800 - $17FF - 4K Data Stream storage\n"
<< " indirectly accessible to 6507\n"
<< " via BUS's Data Stream registers\n"
<< "$1800 - $1FFF - 2K C variable storage and stack\n"
<< " not accessible to 6507";
return desc.str();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
const ByteArray& CartridgeBUSWidget::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& CartridgeBUSWidget::internalRamCurrent(int start, int count)
{
myRamCurrent.clear();
for(int i = 0; i < count; i++)
myRamCurrent.push_back(myCart.myBUSRAM[start + i]);
return myRamCurrent;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void CartridgeBUSWidget::internalRamSetValue(int addr, uInt8 value)
{
myCart.myBUSRAM[addr] = value;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt8 CartridgeBUSWidget::internalRamGetValue(int addr)
{
return myCart.myBUSRAM[addr];
}

View File

@ -0,0 +1,96 @@
//============================================================================
//
// 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-2015 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: CartBUSWidget.hxx 3131 2015-01-01 03:49:32Z stephena $
//============================================================================
#ifndef CARTRIDGEBUS_WIDGET_HXX
#define CARTRIDGEBUS_WIDGET_HXX
class CartridgeBUS;
class PopUpWidget;
class CheckboxWidget;
class DataGridWidget;
#include "CartDebugWidget.hxx"
class CartridgeBUSWidget : public CartDebugWidget
{
public:
CartridgeBUSWidget(GuiObject* boss, const GUI::Font& lfont,
const GUI::Font& nfont,
int x, int y, int w, int h,
CartridgeBUS& cart);
virtual ~CartridgeBUSWidget() { }
private:
struct CartState {
ByteArray tops;
ByteArray bottoms;
IntArray datastreampointers;
IntArray datastreamincrements;
IntArray addressmaps;
IntArray mcounters;
IntArray mfreqs;
IntArray mwaves;
IntArray mwavesizes;
uInt32 random;
ByteArray internalram;
};
CartridgeBUS& myCart;
PopUpWidget* myBank;
DataGridWidget* myDatastreamPointers;
DataGridWidget* myDatastreamIncrements;
DataGridWidget* myAddressMaps;
DataGridWidget* myMusicCounters;
DataGridWidget* myMusicFrequencies;
DataGridWidget* myMusicWaveforms;
DataGridWidget* myMusicWaveformSizes;
CheckboxWidget* myBusOverdrive;
CheckboxWidget* myZPSTY;
CartState myOldState;
enum { kBankChanged = 'bkCH' };
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
CartridgeBUSWidget() = delete;
CartridgeBUSWidget(const CartridgeBUSWidget&) = delete;
CartridgeBUSWidget(CartridgeBUSWidget&&) = delete;
CartridgeBUSWidget& operator=(const CartridgeBUSWidget&) = delete;
CartridgeBUSWidget& operator=(CartridgeBUSWidget&&) = delete;
};
#endif

View File

@ -0,0 +1,350 @@
//============================================================================
//
// 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-2015 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: CartCDFWidget.cxx 3131 2015-01-01 03:49:32Z stephena $
//============================================================================
#include "CartCDF.hxx"
#include "DataGridWidget.hxx"
#include "PopUpWidget.hxx"
#include "CartCDFWidget.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
CartridgeCDFWidget::CartridgeCDFWidget(
GuiObject* boss, const GUI::Font& lfont, const GUI::Font& nfont,
int x, int y, int w, int h, CartridgeCDF& cart)
: CartDebugWidget(boss, lfont, nfont, x, y, w, h),
myCart(cart)
{
uInt16 size = 8 * 4096;
ostringstream info;
info << "CDF Stuffing cartridge\n"
<< "32K ROM, seven 4K banks are accessible to 2600\n"
<< "8K CDF RAM\n"
<< "CDF registers accessible @ $F000 - $F03F\n"
<< "Banks accessible at hotspots $FF5 to $FFB\n"
<< "Startup bank = " << cart.myStartBank << "\n";
#if 0
// Eventually, we should query this from the debugger/disassembler
for(uInt32 i = 0, offset = 0xFFC, spot = 0xFF5; i < 7; ++i, offset += 0x1000)
{
uInt16 start = (cart.myImage[offset+1] << 8) | cart.myImage[offset];
start -= start % 0x1000;
info << "Bank " << i << " @ $" << HEX4 << (start + 0x80) << " - "
<< "$" << (start + 0xFFF) << " (hotspot = $" << (spot+i) << ")\n";
}
#endif
int xpos = 10,
ypos = addBaseInformation(size, "AtariAge", info.str()) +
myLineHeight;
VariantList items;
VarList::push_back(items, "0 ($FF5)");
VarList::push_back(items, "1 ($FF6)");
VarList::push_back(items, "2 ($FF7)");
VarList::push_back(items, "3 ($FF8)");
VarList::push_back(items, "4 ($FF9)");
VarList::push_back(items, "5 ($FFA)");
VarList::push_back(items, "6 ($FFB)");
myBank =
new PopUpWidget(boss, _font, xpos, ypos-2, _font.getStringWidth("0 ($FFx) "),
myLineHeight, items, "Set bank: ",
_font.getStringWidth("Set bank: "), kBankChanged);
myBank->setTarget(this);
addFocusWidget(myBank);
int lwidth = _font.getStringWidth("Datastream Increments: "); // get width of the widest label
// Datastream Pointers
xpos = 0; ypos += myLineHeight + 4;
new StaticTextWidget(boss, _font, xpos, ypos, lwidth,
myFontHeight, "Datastream Pointers: ", kTextAlignLeft);
xpos += lwidth;
myDatastreamPointers = new DataGridWidget(boss, _nfont, 0, ypos+myLineHeight-2, 4, 8, 6, 32, Common::Base::F_16_3_2);
myDatastreamPointers->setTarget(this);
myDatastreamPointers->setEditable(false);
// Datastream Increments
xpos = 0 + myDatastreamPointers->getWidth();
new StaticTextWidget(boss, _font, xpos, ypos, lwidth,
myFontHeight, "Datastream Increments: ", kTextAlignLeft);
myDatastreamIncrements = new DataGridWidget(boss, _nfont, xpos, ypos+myLineHeight-2, 4, 8, 5, 32, Common::Base::F_16_2_2);
myDatastreamIncrements->setTarget(this);
myDatastreamIncrements->setEditable(false);
// Music counters
xpos = 10; ypos += myLineHeight*10 + 4;
new StaticTextWidget(boss, _font, xpos, ypos, lwidth,
myFontHeight, "Music Counters: ", kTextAlignLeft);
xpos += lwidth;
myMusicCounters = new DataGridWidget(boss, _nfont, xpos, ypos-2, 3, 1, 8, 32, Common::Base::F_16_8);
myMusicCounters->setTarget(this);
myMusicCounters->setEditable(false);
// Music frequencies
xpos = 10; ypos += myLineHeight + 4;
new StaticTextWidget(boss, _font, xpos, ypos, lwidth,
myFontHeight, "Music Frequencies: ", kTextAlignLeft);
xpos += lwidth;
myMusicFrequencies = new DataGridWidget(boss, _nfont, xpos, ypos-2, 3, 1, 8, 32, Common::Base::F_16_8);
myMusicFrequencies->setTarget(this);
myMusicFrequencies->setEditable(false);
// Music waveforms
xpos = 10; ypos += myLineHeight + 4;
new StaticTextWidget(boss, _font, xpos, ypos, lwidth,
myFontHeight, "Music Waveforms: ", kTextAlignLeft);
xpos += lwidth;
myMusicWaveforms = new DataGridWidget(boss, _nfont, xpos, ypos-2, 3, 1, 4, 16, Common::Base::F_16_2);
myMusicWaveforms->setTarget(this);
myMusicWaveforms->setEditable(false);
// Music waveform sizes
xpos = 10; ypos += myLineHeight + 4;
new StaticTextWidget(boss, _font, xpos, ypos, lwidth,
myFontHeight, "Music Waveform Sizes: ", kTextAlignLeft);
xpos += lwidth;
myMusicWaveformSizes = new DataGridWidget(boss, _nfont, xpos, ypos-2, 3, 1, 4, 16, Common::Base::F_16_2);
myMusicWaveformSizes->setTarget(this);
myMusicWaveformSizes->setEditable(false);
// done differently than in DPC+, need to rethink debugger support
// // Fast fetch and immediate mode LDA flags
// xpos = 10; ypos += myLineHeight + 4;
// myFastFetch = new CheckboxWidget(boss, _font, xpos, ypos, "Fast Fetcher enabled");
// myFastFetch->setTarget(this);
// myFastFetch->setEditable(false);
// ypos += myLineHeight + 4;
// myIMLDA = new CheckboxWidget(boss, _font, xpos, ypos, "Immediate mode LDA");
// myIMLDA->setTarget(this);
// myIMLDA->setEditable(false);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void CartridgeCDFWidget::saveOldState()
{
myOldState.tops.clear();
myOldState.bottoms.clear();
myOldState.datastreampointers.clear();
myOldState.datastreamincrements.clear();
myOldState.addressmaps.clear();
myOldState.mcounters.clear();
myOldState.mfreqs.clear();
myOldState.mwaves.clear();
myOldState.mwavesizes.clear();
myOldState.internalram.clear();
for(uInt32 i = 0; i < 32; i++)
{
// Pointers are stored as:
// PPPFF---
//
// Increments are stored as
// ----IIFF
//
// P = Pointer
// I = Increment
// F = Fractional
myOldState.datastreampointers.push_back(myCart.getDatastreamPointer(i)>>12);
myOldState.datastreamincrements.push_back(myCart.getDatastreamIncrement(i));
}
for(uInt32 i = 0; i < 3; ++i)
{
myOldState.mcounters.push_back(myCart.myMusicCounters[i]);
}
for(uInt32 i = 0; i < 3; ++i)
{
myOldState.mfreqs.push_back(myCart.myMusicFrequencies[i]);
myOldState.mwaves.push_back(myCart.getWaveform(i) >> 5);
myOldState.mwavesizes.push_back(myCart.getWaveformSize((i)));
}
for(uInt32 i = 0; i < internalRamSize(); ++i)
myOldState.internalram.push_back(myCart.myCDFRAM[i]);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void CartridgeCDFWidget::loadConfig()
{
myBank->setSelectedIndex(myCart.myCurrentBank);
// Get registers, using change tracking
IntArray alist;
IntArray vlist;
BoolArray changed;
alist.clear(); vlist.clear(); changed.clear();
for(int i = 0; i < 32; ++i)
{
// Pointers are stored as:
// PPPFF---
//
// Increments are stored as
// ----IIFF
//
// P = Pointer
// I = Increment
// F = Fractional
uInt32 pointervalue = myCart.getDatastreamPointer(i) >> 12;
alist.push_back(0); vlist.push_back(pointervalue);
changed.push_back(pointervalue != myOldState.datastreampointers[i]);
}
myDatastreamPointers->setList(alist, vlist, changed);
alist.clear(); vlist.clear(); changed.clear();
for(int i = 0; i < 32; ++i)
{
uInt32 incrementvalue = myCart.getDatastreamIncrement(i);
alist.push_back(0); vlist.push_back(incrementvalue);
changed.push_back(incrementvalue != myOldState.datastreamincrements[i]);
}
myDatastreamIncrements->setList(alist, vlist, changed);
alist.clear(); vlist.clear(); changed.clear();
for(int i = 0; i < 3; ++i)
{
alist.push_back(0); vlist.push_back(myCart.myMusicCounters[i]);
changed.push_back(myCart.myMusicCounters[i] != (uInt32)myOldState.mcounters[i]);
}
myMusicCounters->setList(alist, vlist, changed);
alist.clear(); vlist.clear(); changed.clear();
for(int i = 0; i < 3; ++i)
{
alist.push_back(0); vlist.push_back(myCart.myMusicFrequencies[i]);
changed.push_back(myCart.myMusicFrequencies[i] != (uInt32)myOldState.mfreqs[i]);
}
myMusicFrequencies->setList(alist, vlist, changed);
alist.clear(); vlist.clear(); changed.clear();
for(int i = 0; i < 3; ++i)
{
alist.push_back(0); vlist.push_back(myCart.getWaveform(i) >> 5);
changed.push_back((myCart.getWaveform(i) >> 5) != myOldState.mwaves[i]);
}
myMusicWaveforms->setList(alist, vlist, changed);
alist.clear(); vlist.clear(); changed.clear();
for(int i = 0; i < 3; ++i)
{
alist.push_back(0); vlist.push_back(myCart.getWaveformSize(i));
changed.push_back((myCart.getWaveformSize(i)) != myOldState.mwavesizes[i]);
}
myMusicWaveformSizes->setList(alist, vlist, changed);
// done differently than in DPC+, need to rethink debugger support
// myFastFetch->setState(myCart.myFastFetch);
// myIMLDA->setState(myCart.myLDAimmediate);
CartDebugWidget::loadConfig();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void CartridgeCDFWidget::handleCommand(CommandSender* sender,
int cmd, int data, int id)
{
if(cmd == kBankChanged)
{
myCart.unlockBank();
myCart.bank(myBank->getSelected());
myCart.lockBank();
invalidate();
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string CartridgeCDFWidget::bankState()
{
ostringstream& buf = buffer();
static const char* spot[] = {
"$FF5", "$FF6", "$FF7", "$FF8", "$FF9", "$FFA", "$FFB"
};
buf << "Bank = " << std::dec << myCart.myCurrentBank
<< ", hotspot = " << spot[myCart.myCurrentBank];
return buf.str();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt32 CartridgeCDFWidget::internalRamSize()
{
return 8*1024;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt32 CartridgeCDFWidget::internalRamRPort(int start)
{
return 0x0000 + start;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
string CartridgeCDFWidget::internalRamDescription()
{
ostringstream desc;
desc << "$0000 - $07FF - CDF driver\n"
<< " not accessible to 6507\n"
<< "$0800 - $17FF - 4K Data Stream storage\n"
<< " indirectly accessible to 6507\n"
<< " via CDF's Data Stream registers\n"
<< "$1800 - $1FFF - 2K C variable storage and stack\n"
<< " not accessible to 6507";
return desc.str();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
const ByteArray& CartridgeCDFWidget::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& CartridgeCDFWidget::internalRamCurrent(int start, int count)
{
myRamCurrent.clear();
for(int i = 0; i < count; i++)
myRamCurrent.push_back(myCart.myCDFRAM[start + i]);
return myRamCurrent;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void CartridgeCDFWidget::internalRamSetValue(int addr, uInt8 value)
{
myCart.myCDFRAM[addr] = value;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt8 CartridgeCDFWidget::internalRamGetValue(int addr)
{
return myCart.myCDFRAM[addr];
}

View File

@ -0,0 +1,96 @@
//============================================================================
//
// 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-2015 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: CartCDFWidget.hxx 3131 2015-01-01 03:49:32Z stephena $
//============================================================================
#ifndef CARTRIDGECDF_WIDGET_HXX
#define CARTRIDGECDF_WIDGET_HXX
class CartridgeCDF;
class PopUpWidget;
class CheckboxWidget;
class DataGridWidget;
#include "CartDebugWidget.hxx"
class CartridgeCDFWidget : public CartDebugWidget
{
public:
CartridgeCDFWidget(GuiObject* boss, const GUI::Font& lfont,
const GUI::Font& nfont,
int x, int y, int w, int h,
CartridgeCDF& cart);
virtual ~CartridgeCDFWidget() { }
private:
struct CartState {
ByteArray tops;
ByteArray bottoms;
IntArray datastreampointers;
IntArray datastreamincrements;
IntArray addressmaps;
IntArray mcounters;
IntArray mfreqs;
IntArray mwaves;
IntArray mwavesizes;
uInt32 random;
ByteArray internalram;
};
CartridgeCDF& myCart;
PopUpWidget* myBank;
DataGridWidget* myDatastreamPointers;
DataGridWidget* myDatastreamIncrements;
DataGridWidget* myMusicCounters;
DataGridWidget* myMusicFrequencies;
DataGridWidget* myMusicWaveforms;
DataGridWidget* myMusicWaveformSizes;
// done differently than in DPC+, need to rethink debugger support
// CheckboxWidget* myFastFetch;
// CheckboxWidget* myIMLDA;
CartState myOldState;
enum { kBankChanged = 'bkCH' };
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
CartridgeCDFWidget() = delete;
CartridgeCDFWidget(const CartridgeCDFWidget&) = delete;
CartridgeCDFWidget(CartridgeCDFWidget&&) = delete;
CartridgeCDFWidget& operator=(const CartridgeCDFWidget&) = delete;
CartridgeCDFWidget& operator=(CartridgeCDFWidget&&) = delete;
};
#endif

View File

@ -26,6 +26,8 @@
#include "Cart4K.hxx"
#include "Cart4KSC.hxx"
#include "CartAR.hxx"
#include "CartBUS.hxx"
#include "CartCDF.hxx"
#include "CartCM.hxx"
#include "CartCTY.hxx"
#include "CartCV.hxx"
@ -198,6 +200,10 @@ unique_ptr<Cartridge> Cartridge::create(const BytePtr& img, uInt32 size,
cartridge = make_ptr<Cartridge4KSC>(image, size, settings);
else if(type == "AR")
cartridge = make_ptr<CartridgeAR>(image, size, settings);
else if(type == "BUS")
cartridge = make_ptr<CartridgeBUS>(image, size, settings);
else if(type == "CDF")
cartridge = make_ptr<CartridgeCDF>(image, size, settings);
else if(type == "CM")
cartridge = make_ptr<CartridgeCM>(image, size, settings);
else if(type == "CTY")
@ -472,6 +478,10 @@ string Cartridge::autodetectType(const uInt8* image, uInt32 size)
type = "3E";
else if(isProbably3F(image, size))
type = "3F";
else if (isProbablyBUS(image, size))
type = "BUS";
else if (isProbablyCDF(image, size))
type = "CDF";
else if(isProbablyDPCplus(image, size))
type = "DPC+";
else if(isProbablyCTY(image, size))
@ -734,6 +744,8 @@ bool Cartridge::isProbablyDASH(const uInt8* image, uInt32 size)
bool Cartridge::isProbablyDPCplus(const uInt8* image, uInt32 size)
{
// DPC+ ARM code has 2 occurrences of the string DPC+
// Note: all Harmony/Melody custom drivers also contain the value
// 0x10adab1e (LOADABLE) if needed for future improvement
uInt8 signature[] = { 'D', 'P', 'C', '+' };
return searchForBytes(image, size, signature, 4, 2);
}
@ -858,6 +870,26 @@ bool Cartridge::isProbablyBF(const uInt8* image, uInt32 size, const char*& type)
return false;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool Cartridge::isProbablyBUS(const uInt8* image, uInt32 size)
{
// BUS ARM code has 2 occurrences of the string BUS
// Note: all Harmony/Melody custom drivers also contain the value
// 0x10adab1e (LOADABLE) if needed for future improvement
uInt8 bus[] = { 'B', 'U', 'S'};
return searchForBytes(image, size, bus, 3, 2);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool Cartridge::isProbablyCDF(const uInt8* image, uInt32 size)
{
// CDF ARM code has 3 occurrences of the string DPC+
// Note: all Harmony/Melody custom drivers also contain the value
// 0x10adab1e (LOADABLE) if needed for future improvement
uInt8 signature[] = { 'C', 'D', 'F' };
return searchForBytes(image, size, signature, 3, 3);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool Cartridge::isProbablyDF(const uInt8* image, uInt32 size, const char*& type)
{
@ -997,6 +1029,8 @@ Cartridge::BankswitchType Cartridge::ourBSList[ourNumBSTypes] = {
{ "AR", "AR (Supercharger)" },
{ "BF", "BF (CPUWIZ 256K)" },
{ "BFSC", "BFSC (CPUWIZ 256K + ram)" },
{ "BUS", "BUS (Bus Stuffing)" },
{ "CDF", "CDF (Chris, Darrell, Fred)" },
{ "CM", "CM (SpectraVideo CompuMate)" },
{ "CTY", "CTY (CDW - Chetiry)" },
{ "CV", "CV (Commavid extra ram)" },

View File

@ -172,6 +172,13 @@ class Cartridge : public Device
*/
virtual void setRomName(const string& name) { }
/**
Thumbulator only supports 16-bit ARM code. Some Harmony/Melody drivers,
such as BUS and CDF, feature 32-bit ARM code subroutines. This is used
to pass values back to the cartridge class to emulate those subroutines.
*/
virtual uInt32 thumbCallback(uInt8 function, uInt32 value1, uInt32 value2) { return 0; }
/**
Get debugger widget responsible for accessing the inner workings
of the cart. This will need to be overridden and implemented by
@ -187,7 +194,7 @@ class Cartridge : public Device
const char* type;
const char* desc;
};
enum { ourNumBSTypes = 48 };
enum { ourNumBSTypes = 50 };
static BankswitchType ourBSList[ourNumBSTypes];
protected:
@ -303,6 +310,16 @@ class Cartridge : public Device
*/
static bool isProbablyBF(const uInt8* image, uInt32 size, const char*& type);
/**
Returns true if the image is probably a BUS bankswitching cartridge
*/
static bool isProbablyBUS(const uInt8* image, uInt32 size);
/**
Returns true if the image is probably a CDF bankswitching cartridge
*/
static bool isProbablyCDF(const uInt8* image, uInt32 size);
/**
Returns true if the image is probably a CTY bankswitching cartridge
*/

750
src/emucore/CartBUS.cxx Normal file
View File

@ -0,0 +1,750 @@
//============================================================================
//
// 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-2015 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: CartBUS.cxx 3131 2015-01-01 03:49:32Z stephena $
//============================================================================
#include <cstring>
#ifdef DEBUGGER_SUPPORT
#include "Debugger.hxx"
#endif
#include "System.hxx"
#include "M6532.hxx"
#include "TIA.hxx"
#include "Thumbulator.hxx"
#include "CartBUS.hxx"
// Location of data within the RAM copy of the BUS Driver.
#define DSxPTR 0x06E0
#define DSxINC 0x0720
#define DSMAPS 0x0760
#define WAVEFORM 0x07F4
#define DSRAM 0x0800
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
CartridgeBUS::CartridgeBUS(const uInt8* image, uInt32 size,
const Settings& settings)
: Cartridge(settings),
mySystemCycles(0),
myFractionalClocks(0.0)
{
// Copy the ROM image into my buffer
memcpy(myImage, image, std::min(32768u, size));
// even though the ROM is 32K, only 28K is accessible to the 6507
createCodeAccessBase(4096 * 7);
// Pointer to the program ROM (28K @ 0 byte offset)
// which starts after the 2K BUS Driver and 2K C Code
myProgramImage = myImage + 4096;
// Pointer to BUS driver in RAM
myBusDriverImage = myBUSRAM;
// Pointer to the display RAM
myDisplayImage = myBUSRAM + DSRAM;
#ifdef THUMB_SUPPORT
// Create Thumbulator ARM emulator
myThumbEmulator = make_ptr<Thumbulator>
((uInt16*)myImage, (uInt16*)myBUSRAM,
settings.getBool("thumb.trapfatal"),
Thumbulator::ConfigureFor::BUS,
this);
#endif
setInitialState();
// BUS always starts in bank 6
myStartBank = 6;
// bus stuffing is off by default
myBusStuff = false;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
CartridgeBUS::~CartridgeBUS()
{
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void CartridgeBUS::reset()
{
// Initialize RAM
if(mySettings.getBool("ramrandom"))
for(uInt32 t = 2048; t < 8192; ++t)
myBUSRAM[t] = mySystem->randGenerator().next();
else
memset(myBUSRAM+2048, 0, 8192-2048);
// Update cycles to the current system cycles
mySystemCycles = mySystem->cycles();
myFractionalClocks = 0.0;
setInitialState();
// Upon reset we switch to the startup bank
bank(myStartBank);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void CartridgeBUS::setInitialState()
{
// Copy initial BUS driver to Harmony RAM
memcpy(myBusDriverImage, myImage, 0x0800);
for (int i=0; i < 3; ++i)
myMusicWaveformSize[i] = 27;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void CartridgeBUS::consoleChanged(ConsoleTiming timing)
{
#ifdef THUMB_SUPPORT
myThumbEmulator->setConsoleTiming(timing);
#endif
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void CartridgeBUS::systemCyclesReset()
{
// Adjust the cycle counter so that it reflects the new value
mySystemCycles -= mySystem->cycles();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void CartridgeBUS::install(System& system)
{
mySystem = &system;
// Map all of the accesses to call peek and poke
System::PageAccess access(this, System::PA_READ);
for(uInt32 i = 0x1000; i < 0x1040; i += (1 << System::PAGE_SHIFT))
mySystem->setPageAccess(i >> System::PAGE_SHIFT, access);
// Mirror all access in TIA and RIOT; by doing so we're taking responsibility
// for that address space in peek and poke below.
mySystem->tia().installDelegate(system, *this);
mySystem->m6532().installDelegate(system, *this);
// Install pages for the startup bank
bank(myStartBank);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
inline void CartridgeBUS::updateMusicModeDataFetchers()
{
// Calculate the number of cycles since the last update
Int32 cycles = mySystem->cycles() - mySystemCycles;
mySystemCycles = mySystem->cycles();
// Calculate the number of BUS OSC clocks since the last update
double clocks = ((20000.0 * cycles) / 1193191.66666667) + myFractionalClocks;
Int32 wholeClocks = Int32(clocks);
myFractionalClocks = clocks - double(wholeClocks);
if(wholeClocks <= 0)
{
return;
}
// Let's update counters and flags of the music mode data fetchers
for(int x = 0; x <= 2; ++x)
{
myMusicCounters[x] += myMusicFrequencies[x];
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
inline void CartridgeBUS::callFunction(uInt8 value)
{
switch (value)
{
#ifdef THUMB_SUPPORT
// Call user written ARM code (will most likely be C compiled for ARM)
case 254: // call with IRQ driven audio, no special handling needed at this
// time for Stella as ARM code "runs in zero 6507 cycles".
case 255: // call without IRQ driven audio
try {
myThumbEmulator->run();
}
catch(const runtime_error& e) {
if(!mySystem->autodetectMode())
{
#ifdef DEBUGGER_SUPPORT
Debugger::debugger().startWithFatalError(e.what());
#else
cout << e.what() << endl;
#endif
}
}
break;
#endif
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt8 CartridgeBUS::peek(uInt16 address)
{
if(!(address & 0x1000)) // Hotspots below 0x1000
{
// Check for RAM or TIA mirroring
uInt16 lowAddress = address & 0x3ff;
if(lowAddress & 0x80)
return mySystem->m6532().peek(address);
else if(!(lowAddress & 0x200))
return mySystem->tia().peek(address);
}
else
{
address &= 0x0FFF;
uInt8 peekvalue = myProgramImage[(myCurrentBank << 12) + address];
// In debugger/bank-locked mode, we ignore all hotspots and in general
// anything that can change the internal state of the cart
if(bankLocked())
return peekvalue;
// save the STY's zero page address
if (getBusStuffFlag() && mySTYZeroPage)
myBusOverdriveAddress = peekvalue;
mySTYZeroPage = false;
if(address < 0x20)
{
uInt8 result = 0;
// Get the index of the data fetcher that's being accessed
uInt32 index = address & 0x0f;
uInt32 function = (address >> 4) & 0x01;
switch(function)
{
case 0x00: // read from a datastream
{
result = readFromDatastream(index);
break;
}
case 0x01: // misc read registers
{
switch(index)
{
// the following are POKE ONLY
case 0x00: // 0x10 DF0WRITE
case 0x01: // 0x11 DF1WRITE
case 0x02: // 0x12 DF2WRITE
case 0x03: // 0x13 DF3WRITE
case 0x04: // 0x14 DF0PTR
case 0x05: // 0x15 DF1PTR
case 0x06: // 0x16 DF2PTR
case 0x07: // 0x17 DF3PTR
case 0x09: // 0x19 STUFFMODE
case 0x0a: // 0x1A CALLFN
break;
case 0x08: // 0x18 = AMPLITUDE
// Update the music data fetchers (counter & flag)
updateMusicModeDataFetchers();
// using myDisplayImage[] instead of myProgramImage[] because waveforms
// can be modified during runtime.
uInt32 i = myDisplayImage[(getWaveform(0) ) + (myMusicCounters[0] >> myMusicWaveformSize[0])] +
myDisplayImage[(getWaveform(1) ) + (myMusicCounters[1] >> myMusicWaveformSize[1])] +
myDisplayImage[(getWaveform(2) ) + (myMusicCounters[2] >> myMusicWaveformSize[2])];
result = uInt8(i);
break;
}
break;
}
}
return result;
}
else
{
// Switch banks if necessary
switch(address)
{
case 0xFF5:
// Set the current bank to the first 4k bank
bank(0);
break;
case 0x0FF6:
// Set the current bank to the second 4k bank
bank(1);
break;
case 0x0FF7:
// Set the current bank to the third 4k bank
bank(2);
break;
case 0x0FF8:
// Set the current bank to the fourth 4k bank
bank(3);
break;
case 0x0FF9:
// Set the current bank to the fifth 4k bank
bank(4);
break;
case 0x0FFA:
// Set the current bank to the sixth 4k bank
bank(5);
break;
case 0x0FFB:
// Set the current bank to the last 4k bank
bank(6);
break;
default:
break;
}
// this might not work right for STY $84
if (getBusStuffFlag())
mySTYZeroPage = (peekvalue == 0x84);
return peekvalue;
}
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool CartridgeBUS::poke(uInt16 address, uInt8 value)
{
if (!(address & 0x1000))
{
value &= busOverdrive(address);
// Check for RAM or TIA mirroring
uInt16 lowAddress = address & 0x3ff;
if(lowAddress & 0x80)
mySystem->m6532().poke(address, value);
else if(!(lowAddress & 0x200))
mySystem->tia().poke(address, value);
}
else
{
address &= 0x0FFF;
if ((address >= 0x10) && (address <= 0x1F))
{
// Get the index of the data fetcher that's being accessed
uInt32 index = address & 0x0f;
uInt32 pointer;
switch (index)
{
case 0x00: // DS0WRITE
case 0x01: // DS1WRITE
case 0x02: // DS2WRITE
case 0x03: // DS3WRITE
// Pointers are stored as:
// PPPFF---
//
// P = Pointer
// F = Fractional
pointer = getDatastreamPointer(index);
myDisplayImage[ pointer >> 20 ] = value;
pointer += 0x100000; // always increment by 1 when writing
setDatastreamPointer(index, pointer);
break;
case 0x04: // 0x14 DS0PTR
case 0x05: // 0x15 DS1PTR
case 0x06: // 0x16 DS2PTR
case 0x07: // 0x17 DS3PTR
// Pointers are stored as:
// PPPFF---
//
// P = Pointer
// F = Fractional
index &= 0x03;
pointer = getDatastreamPointer(index);
pointer <<=8;
pointer &= 0xf0000000;
pointer |= (value << 20);
setDatastreamPointer(index, pointer);
break;
case 0x09: // 0x19 turn on STY ZP bus stuffing if value is 0
setBusStuffFlag(value==0);
break;
case 0x0A: // 0x1A CALLFUNCTION
callFunction(value);
break;
}
}
else
{
// Switch banks if necessary
switch(address)
{
case 0xFF5:
// Set the current bank to the first 4k bank
bank(0);
break;
case 0x0FF6:
// Set the current bank to the second 4k bank
bank(1);
break;
case 0x0FF7:
// Set the current bank to the third 4k bank
bank(2);
break;
case 0x0FF8:
// Set the current bank to the fourth 4k bank
bank(3);
break;
case 0x0FF9:
// Set the current bank to the fifth 4k bank
bank(4);
break;
case 0x0FFA:
// Set the current bank to the sixth 4k bank
bank(5);
break;
case 0x0FFB:
// Set the current bank to the last 4k bank
bank(6);
break;
default:
break;
}
}
}
return false;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool CartridgeBUS::bank(uInt16 bank)
{
if(bankLocked()) return false;
// Remember what bank we're in
myCurrentBank = bank;
uInt16 offset = myCurrentBank << 12;
// Setup the page access methods for the current bank
System::PageAccess access(this, System::PA_READ);
// Map Program ROM image into the system
for(uInt32 address = 0x1040; address < 0x2000;
address += (1 << System::PAGE_SHIFT))
{
access.codeAccessBase = &myCodeAccessBase[offset + (address & 0x0FFF)];
mySystem->setPageAccess(address >> System::PAGE_SHIFT, access);
}
return myBankChanged = true;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt16 CartridgeBUS::getBank() const
{
return myCurrentBank;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt16 CartridgeBUS::bankCount() const
{
return 7;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool CartridgeBUS::patch(uInt16 address, uInt8 value)
{
address &= 0x0FFF;
// For now, we ignore attempts to patch the BUS address space
if(address >= 0x0040)
{
myProgramImage[(myCurrentBank << 12) + (address & 0x0FFF)] = value;
return myBankChanged = true;
}
else
return false;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
const uInt8* CartridgeBUS::getImage(int& size) const
{
size = 32768;
return myImage;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt8 CartridgeBUS::busOverdrive(uInt16 address)
{
uInt8 overdrive = 0xff;
// Not sure how to do this, check with stephena.
//
// Per discussion with cd-w, have this routine check that the Y register has a
// value of 0xFF. Of it doesn't then "crash the emulation".
// only overdrive if the address matches
if (address == myBusOverdriveAddress)
{
uInt8 map = address & 0x7f;
if (map <= 0x24) // map TIA registers VSYNC thru HMBL inclusive
{
uInt32 alldatastreams = getAddressMap(map);
uInt8 datastream = alldatastreams & 0x0f; // lowest nybble has the current datastream to use
overdrive = readFromDatastream(datastream);
// rotate map nybbles for next time
alldatastreams >>= 4;
alldatastreams |= (datastream << 28);
setAddressMap(map, alldatastreams);
// overdrive |= 0x7c; // breaks bus stuffing to match hobo's system
}
}
myBusOverdriveAddress = 0xff; // turns off overdrive for next poke event
return overdrive;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt32 CartridgeBUS::thumbCallback(uInt8 function, uInt32 value1, uInt32 value2)
{
switch (function)
{
case 0:
// _SetNote - set the note/frequency
myMusicFrequencies[value1] = value2;
break;
// _ResetWave - reset counter,
// used to make sure digital samples start from the beginning
case 1:
myMusicCounters[value1] = 0;
break;
// _GetWavePtr - return the counter
case 2:
return myMusicCounters[value1];
break;
// _SetWaveSize - set size of waveform buffer
case 3:
myMusicWaveformSize[value1] = value2;
break;
}
return 0;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool CartridgeBUS::save(Serializer& out) const
{
try
{
out.putString(name());
// Indicates which bank is currently active
out.putShort(myCurrentBank);
// Harmony RAM
out.putByteArray(myBUSRAM, 8192);
out.putInt(mySystemCycles);
out.putInt((uInt32)(myFractionalClocks * 100000000.0));
}
catch(...)
{
cerr << "ERROR: CartridgeBUS::save" << endl;
return false;
}
return true;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool CartridgeBUS::load(Serializer& in)
{
try
{
if(in.getString() != name())
return false;
// Indicates which bank is currently active
myCurrentBank = in.getShort();
// Harmony RAM
in.getByteArray(myBUSRAM, 8192);
// Get system cycles and fractional clocks
mySystemCycles = (Int32)in.getInt();
myFractionalClocks = (double)in.getInt() / 100000000.0;
}
catch(...)
{
cerr << "ERROR: CartridgeBUS::load" << endl;
return false;
}
// Now, go to the current bank
bank(myCurrentBank);
return true;
}
uInt32 CartridgeBUS::getDatastreamPointer(uInt8 index)
{
// index &= 0x0f;
return myBUSRAM[DSxPTR + index*4 + 0] + // low byte
(myBUSRAM[DSxPTR + index*4 + 1] << 8) +
(myBUSRAM[DSxPTR + index*4 + 2] << 16) +
(myBUSRAM[DSxPTR + index*4 + 3] << 24) ; // high byte
}
void CartridgeBUS::setDatastreamPointer(uInt8 index, uInt32 value)
{
// index &= 0x0f;
myBUSRAM[DSxPTR + index*4 + 0] = value & 0xff; // low byte
myBUSRAM[DSxPTR + index*4 + 1] = (value >> 8) & 0xff;
myBUSRAM[DSxPTR + index*4 + 2] = (value >> 16) & 0xff;
myBUSRAM[DSxPTR + index*4 + 3] = (value >> 24) & 0xff; // high byte
}
uInt32 CartridgeBUS::getDatastreamIncrement(uInt8 index)
{
// index &= 0x0f;
return myBUSRAM[DSxINC + index*4 + 0] + // low byte
(myBUSRAM[DSxINC + index*4 + 1] << 8) +
(myBUSRAM[DSxINC + index*4 + 2] << 16) +
(myBUSRAM[DSxINC + index*4 + 3] << 24) ; // high byte
}
void CartridgeBUS::setDatastreamIncrement(uInt8 index, uInt32 value)
{
// index &= 0x0f;
myBUSRAM[DSxINC + index*4 + 0] = value & 0xff; // low byte
myBUSRAM[DSxINC + index*4 + 1] = (value >> 8) & 0xff;
myBUSRAM[DSxINC + index*4 + 2] = (value >> 16) & 0xff;
myBUSRAM[DSxINC + index*4 + 3] = (value >> 24) & 0xff; // high byte
}
uInt32 CartridgeBUS::getAddressMap(uInt8 index)
{
// index &= 0x0f;
return myBUSRAM[DSMAPS + index*4 + 0] + // low byte
(myBUSRAM[DSMAPS + index*4 + 1] << 8) +
(myBUSRAM[DSMAPS + index*4 + 2] << 16) +
(myBUSRAM[DSMAPS + index*4 + 3] << 24) ; // high byte
}
uInt32 CartridgeBUS::getWaveform(uInt8 index)
{
// instead of 0, 1, 2, etc. this returned
// 0x40000800 for 0
// 0x40000820 for 1
// 0x40000840 for 2
// ...
// return myBUSRAM[WAVEFORM + index*4 + 0] + // low byte
// (myBUSRAM[WAVEFORM + index*4 + 1] << 8) +
// (myBUSRAM[WAVEFORM + index*4 + 2] << 16) +
// (myBUSRAM[WAVEFORM + index*4 + 3] << 24) - // high byte
// 0x40000800;
uInt32 result;
result = myBUSRAM[WAVEFORM + index*4 + 0] + // low byte
(myBUSRAM[WAVEFORM + index*4 + 1] << 8) +
(myBUSRAM[WAVEFORM + index*4 + 2] << 16) +
(myBUSRAM[WAVEFORM + index*4 + 3] << 24);
result -= 0x40000800;
if (result >= 4096)
result = 0;
return result;
}
uInt32 CartridgeBUS::getWaveformSize(uInt8 index)
{
return myMusicWaveformSize[index];
}
void CartridgeBUS::setAddressMap(uInt8 index, uInt32 value)
{
// index &= 0x0f;
myBUSRAM[DSMAPS + index*4 + 0] = value & 0xff; // low byte
myBUSRAM[DSMAPS + index*4 + 1] = (value >> 8) & 0xff;
myBUSRAM[DSMAPS + index*4 + 2] = (value >> 16) & 0xff;
myBUSRAM[DSMAPS + index*4 + 3] = (value >> 24) & 0xff; // high byte
}
bool CartridgeBUS::getBusStuffFlag(void)
{
return myBusStuff;
}
void CartridgeBUS::setBusStuffFlag(bool value)
{
myBusStuff = value;
}
uInt8 CartridgeBUS::readFromDatastream(uInt8 index)
{
// Pointers are stored as:
// PPPFF---
//
// Increments are stored as
// ----IIFF
//
// P = Pointer
// I = Increment
// F = Fractional
uInt32 pointer = getDatastreamPointer(index);
uInt16 increment = getDatastreamIncrement(index);
uInt8 value = myDisplayImage[ pointer >> 20 ];
pointer += (increment << 12);
setDatastreamPointer(index, pointer);
return value;
}

287
src/emucore/CartBUS.hxx Normal file
View File

@ -0,0 +1,287 @@
//============================================================================
//
// 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-2015 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: CartBUS.hxx 3131 2015-01-01 03:49:32Z stephena $
//============================================================================
#ifndef CARTRIDGE_BUS_HXX
#define CARTRIDGE_BUS_HXX
class System;
#ifdef THUMB_SUPPORT
class Thumbulator;
#endif
#ifdef DEBUGGER_SUPPORT
#include "CartBUSWidget.hxx"
#endif
#include "bspf.hxx"
#include "Cart.hxx"
/**
Cartridge class used for BUS.
THIS NEEDS TO BE UPDATED
There are seven 4K program banks, a 4K Display Data RAM,
1K C Varaible and Stack, and the BUS chip.
BUS chip access is mapped to $1000 - $103F.
@author Darrell Spice Jr, Chris Walton, Fred Quimby, Stephen Anthony, Bradford W. Mott
@version $Id: CartBUS.hxx 3131 2015-01-01 03:49:32Z stephena $
*/
class CartridgeBUS : public Cartridge
{
friend class CartridgeBUSWidget;
friend class CartridgeRamBUSWidget;
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)
*/
CartridgeBUS(const uInt8* image, uInt32 size, const Settings& settings);
/**
Destructor
*/
virtual ~CartridgeBUS();
public:
/**
Reset device to its power-on state
*/
void reset() override;
/**
Notification method invoked by the system when the console type
has changed. We need this to inform the Thumbulator that the
timing has changed.
@param timing Enum representing the new console type
*/
void consoleChanged(ConsoleTiming timing) override;
/**
Notification method invoked by the system right before the
system resets its cycle counter to zero. It may be necessary
to override this method for devices that remember cycle counts.
*/
void systemCyclesReset() override;
/**
Install cartridge in the specified system. Invoked by the system
when the cartridge is attached to it.
@param system The system the device should install itself in
*/
void install(System& system) override;
/**
Install pages for the specified bank in the system.
@param bank The bank that should be installed in the system
*/
bool bank(uInt16 bank) override;
/**
Get the current bank.
*/
uInt16 getBank() const override;
/**
Query the number of banks supported by the cartridge.
*/
uInt16 bankCount() const override;
/**
Patch the cartridge ROM.
@param address The ROM address to patch
@param value The value to place into the address
@return Success or failure of the patch operation
*/
bool patch(uInt16 address, uInt8 value) override;
/**
Access the internal ROM image for this cartridge.
@param size Set to the size of the internal ROM image data
@return A pointer to the internal ROM image data
*/
const uInt8* getImage(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 "CartridgeBUS"; }
uInt8 busOverdrive(uInt16 address);
/**
Used for Thumbulator to pass values back to the cartridge
*/
uInt32 thumbCallback(uInt8 function, uInt32 value1, uInt32 value2) override;
#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 CartridgeBUSWidget(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:
/**
Sets the initial state of the DPC pointers and RAM
*/
void setInitialState();
/**
Updates any data fetchers in music mode based on the number of
CPU cycles which have passed since the last update.
*/
void updateMusicModeDataFetchers();
/**
Call Special Functions
*/
void callFunction(uInt8 value);
uInt32 getDatastreamPointer(uInt8 index);
void setDatastreamPointer(uInt8 index, uInt32 value);
uInt32 getDatastreamIncrement(uInt8 index);
void setDatastreamIncrement(uInt8 index, uInt32 value);
uInt32 getAddressMap(uInt8 index);
void setAddressMap(uInt8 index, uInt32 value);
bool getBusStuffFlag(void);
void setBusStuffFlag(bool value);
uInt8 readFromDatastream(uInt8 index);
uInt32 getWaveform(uInt8 index);
uInt32 getWaveformSize(uInt8 index);
private:
// The 32K ROM image of the cartridge
uInt8 myImage[32768];
// Pointer to the 28K program ROM image of the cartridge
uInt8* myProgramImage;
// Pointer to the 4K display ROM image of the cartridge
uInt8* myDisplayImage;
// Pointer to the 2K BUS driver image in RAM
uInt8* myBusDriverImage;
// The BUS 8k RAM image, used as:
// $0000 - 2K BUS driver
// $0800 - 4K Display Data
// $1800 - 2K C Variable & Stack
uInt8 myBUSRAM[8192];
#ifdef THUMB_SUPPORT
// Pointer to the Thumb ARM emulator object
unique_ptr<Thumbulator> myThumbEmulator;
#endif
// Indicates which bank is currently active
uInt16 myCurrentBank;
// Address to override the bus for
uInt16 myBusOverdriveAddress;
// Flags that last byte peeked was 84 (STY ZP)
bool mySTYZeroPage;
// System cycle count when the last update to music data fetchers occurred
Int32 mySystemCycles;
uInt8 mySetAddress;
// The music mode counters
uInt32 myMusicCounters[3];
// The music frequency
uInt32 myMusicFrequencies[3];
// The music waveform sizes
uInt8 myMusicWaveformSize[3];
// Fractional DPC music OSC clocks unused during the last update
double myFractionalClocks;
// Flags that Bus Stuffing is active
bool myBusStuff;
private:
// Following constructors and assignment operators not supported
CartridgeBUS() = delete;
CartridgeBUS(const CartridgeBUS&) = delete;
CartridgeBUS(CartridgeBUS&&) = delete;
CartridgeBUS& operator=(const CartridgeBUS&) = delete;
CartridgeBUS& operator=(CartridgeBUS&&) = delete;
};
#endif

659
src/emucore/CartCDF.cxx Executable file
View File

@ -0,0 +1,659 @@
//============================================================================
//
// 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-2015 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: CartCDF.cxx 3131 2015-01-01 03:49:32Z stephena $
//============================================================================
#include <cstring>
#ifdef DEBUGGER_SUPPORT
#include "Debugger.hxx"
#endif
#include "System.hxx"
#include "Thumbulator.hxx"
#include "CartCDF.hxx"
#include "TIA.hxx"
// Location of data within the RAM copy of the CDF Driver.
#define DSxPTR 0x06E0
#define DSxINC 0x0760
#define WAVEFORM 0x07E0
#define DSRAM 0x0800
#define FAST_FETCH_ON ((myMode & 0x0F) == 0)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
CartridgeCDF::CartridgeCDF(const uInt8* image, uInt32 size,
const Settings& settings)
: Cartridge(settings),
mySystemCycles(0),
myARMCycles(0),
myFractionalClocks(0.0)
{
// Copy the ROM image into my buffer
memcpy(myImage, image, std::min(32768u, size));
// even though the ROM is 32K, only 28K is accessible to the 6507
createCodeAccessBase(4096 * 7);
// Pointer to the program ROM (28K @ 0 byte offset)
// which starts after the 2K CDF Driver and 2K C Code
myProgramImage = myImage + 4096;
// Pointer to CDF driver in RAM
myBusDriverImage = myCDFRAM;
// Pointer to the display RAM
myDisplayImage = myCDFRAM + DSRAM;
#ifdef THUMB_SUPPORT
// Create Thumbulator ARM emulator
myThumbEmulator = make_ptr<Thumbulator>
((uInt16*)myImage, (uInt16*)myCDFRAM,
settings.getBool("thumb.trapfatal"),
Thumbulator::ConfigureFor::CDF,
this);
#endif
setInitialState();
// CDF always starts in bank 6
myStartBank = 6;
// Assuming mode starts out with Fast Fetch off and 3-Voice music,
// need to confirm with Chris
myMode = 0xFF;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
CartridgeCDF::~CartridgeCDF()
{
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void CartridgeCDF::reset()
{
// Initialize RAM
if(mySettings.getBool("ramrandom"))
for(uInt32 t = 2048; t < 8192; ++t)
myCDFRAM[t] = mySystem->randGenerator().next();
else
memset(myCDFRAM+2048, 0, 8192-2048);
// Update cycles to the current system cycles
mySystemCycles = mySystem->cycles();
myARMCycles = mySystem->cycles();
myFractionalClocks = 0.0;
setInitialState();
// Upon reset we switch to the startup bank
bank(myStartBank);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void CartridgeCDF::setInitialState()
{
// Copy initial CDF driver to Harmony RAM
memcpy(myBusDriverImage, myImage, 0x0800);
for (int i=0; i < 3; ++i)
myMusicWaveformSize[i] = 27;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void CartridgeCDF::consoleChanged(ConsoleTiming timing)
{
#ifdef THUMB_SUPPORT
myThumbEmulator->setConsoleTiming(timing);
#endif
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void CartridgeCDF::systemCyclesReset()
{
// Adjust the cycle counter so that it reflects the new value
mySystemCycles -= mySystem->cycles();
myARMCycles -= mySystem->cycles();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void CartridgeCDF::install(System& system)
{
mySystem = &system;
// Map all of the accesses to call peek and poke
System::PageAccess access(this, System::PA_READ);
for(uInt32 i = 0x1000; i < 0x1040; i += (1 << System::PAGE_SHIFT))
mySystem->setPageAccess(i >> System::PAGE_SHIFT, access);
// Install pages for the startup bank
bank(myStartBank);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
inline void CartridgeCDF::updateMusicModeDataFetchers()
{
// Calculate the number of cycles since the last update
Int32 cycles = mySystem->cycles() - mySystemCycles;
mySystemCycles = mySystem->cycles();
// Calculate the number of CDF OSC clocks since the last update
double clocks = ((20000.0 * cycles) / 1193191.66666667) + myFractionalClocks;
Int32 wholeClocks = Int32(clocks);
myFractionalClocks = clocks - double(wholeClocks);
if(wholeClocks <= 0)
{
return;
}
// Let's update counters and flags of the music mode data fetchers
for(int x = 0; x <= 2; ++x)
{
myMusicCounters[x] += myMusicFrequencies[x];
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
inline void CartridgeCDF::callFunction(uInt8 value)
{
switch (value)
{
#ifdef THUMB_SUPPORT
// Call user written ARM code (will most likely be C compiled for ARM)
case 254: // call with IRQ driven audio, no special handling needed at this
// time for Stella as ARM code "runs in zero 6507 cycles".
case 255: // call without IRQ driven audio
try {
Int32 cycles = mySystem->cycles() - myARMCycles;
myARMCycles = mySystem->cycles();
myThumbEmulator->run(cycles);
}
catch(const runtime_error& e) {
if(!mySystem->autodetectMode())
{
#ifdef DEBUGGER_SUPPORT
Debugger::debugger().startWithFatalError(e.what());
#else
cout << e.what() << endl;
#endif
}
}
break;
#endif
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt8 CartridgeCDF::peek(uInt16 address)
{
address &= 0x0FFF;
uInt8 peekvalue = myProgramImage[(myCurrentBank << 12) + address];
// In debugger/bank-locked mode, we ignore all hotspots and in general
// anything that can change the internal state of the cart
if(bankLocked())
return peekvalue;
// Check if we're in Fast Fetch mode and the prior byte was an A9 (LDA #value)
if(FAST_FETCH_ON && myLDAimmediateOperandAddress == address)
{
if(peekvalue < 0x0028)
// if #value is a read-register then we want to use that as the address
address = peekvalue;
}
myLDAimmediateOperandAddress = 0;
if(address <= 0x20)
{
uInt8 result = 0;
// Get the index of the data fetcher that's being accessed
uInt32 index = address & 0x1f;
uInt32 function = (address >> 5) & 0x01;
switch(function)
{
case 0x00: // read from a datastream
{
result = readFromDatastream(index);
break;
}
case 0x02: // misc read registers
{
// index will be 0 for address 0x20 = AMPLITUDE
// Update the music data fetchers (counter & flag)
updateMusicModeDataFetchers();
// using myDisplayImage[] instead of myProgramImage[] because waveforms
// can be modified during runtime.
uInt32 i = myDisplayImage[(getWaveform(0) ) + (myMusicCounters[0] >> myMusicWaveformSize[0])] +
myDisplayImage[(getWaveform(1) ) + (myMusicCounters[1] >> myMusicWaveformSize[1])] +
myDisplayImage[(getWaveform(2) ) + (myMusicCounters[2] >> myMusicWaveformSize[2])];
result = uInt8(i);
break;
}
}
return result;
}
else
{
// Switch banks if necessary
switch(address)
{
case 0xFF5:
// Set the current bank to the first 4k bank
bank(0);
break;
case 0x0FF6:
// Set the current bank to the second 4k bank
bank(1);
break;
case 0x0FF7:
// Set the current bank to the third 4k bank
bank(2);
break;
case 0x0FF8:
// Set the current bank to the fourth 4k bank
bank(3);
break;
case 0x0FF9:
// Set the current bank to the fifth 4k bank
bank(4);
break;
case 0x0FFA:
// Set the current bank to the sixth 4k bank
bank(5);
break;
case 0x0FFB:
// Set the current bank to the last 4k bank
bank(6);
break;
default:
break;
}
if(FAST_FETCH_ON && peekvalue == 0xA9)
myLDAimmediateOperandAddress = address + 1;
return peekvalue;
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool CartridgeCDF::poke(uInt16 address, uInt8 value)
{
address &= 0x0FFF;
if ((address >= 0x21) && (address <= 0x2B))
{
// Get the index of the data fetcher that's being accessed
uInt32 index = address & 0x0f;
uInt32 pointer;
uInt32 stream = address & 0x03;
switch (index)
{
case 0x00: // 0x20 AMPLITUDE - read register
break;
case 0x01: // 0x21 SETMODE
myMode = value;
break;
case 0x02: // 0x22 CALLFN
callFunction(value);
break;
case 0x03: // 0x23 RESERVED
break;
case 0x04: // 0x24 DS0WRITE
case 0x05: // 0x25 DS1WRITE
case 0x06: // 0x26 DS2WRITE
case 0x07: // 0x27 DS3WRITE
// Pointers are stored as:
// PPPFF---
//
// P = Pointer
// F = Fractional
pointer = getDatastreamPointer(stream);
myDisplayImage[ pointer >> 20 ] = value;
pointer += 0x100000; // always increment by 1 when writing
setDatastreamPointer(stream, pointer);
break;
case 0x08: // 0x28 DS0PTR
case 0x09: // 0x29 DS1PTR
case 0x0A: // 0x2A DS2PTR
case 0x0B: // 0x2B DS3PTR
// Pointers are stored as:
// PPPFF---
//
// P = Pointer
// F = Fractional
pointer = getDatastreamPointer(stream);
pointer <<=8;
pointer &= 0xf0000000;
pointer |= (value << 20);
setDatastreamPointer(stream, pointer);
break;
}
}
else
{
// Switch banks if necessary
switch(address)
{
case 0xFF5:
// Set the current bank to the first 4k bank
bank(0);
break;
case 0x0FF6:
// Set the current bank to the second 4k bank
bank(1);
break;
case 0x0FF7:
// Set the current bank to the third 4k bank
bank(2);
break;
case 0x0FF8:
// Set the current bank to the fourth 4k bank
bank(3);
break;
case 0x0FF9:
// Set the current bank to the fifth 4k bank
bank(4);
break;
case 0x0FFA:
// Set the current bank to the sixth 4k bank
bank(5);
break;
case 0x0FFB:
// Set the current bank to the last 4k bank
bank(6);
break;
default:
break;
}
}
return false;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool CartridgeCDF::bank(uInt16 bank)
{
if(bankLocked()) return false;
// Remember what bank we're in
myCurrentBank = bank;
uInt16 offset = myCurrentBank << 12;
// Setup the page access methods for the current bank
System::PageAccess access(this, System::PA_READ);
// Map Program ROM image into the system
for(uInt32 address = 0x1040; address < 0x2000;
address += (1 << System::PAGE_SHIFT))
{
access.codeAccessBase = &myCodeAccessBase[offset + (address & 0x0FFF)];
mySystem->setPageAccess(address >> System::PAGE_SHIFT, access);
}
return myBankChanged = true;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt16 CartridgeCDF::getBank() const
{
return myCurrentBank;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt16 CartridgeCDF::bankCount() const
{
return 7;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool CartridgeCDF::patch(uInt16 address, uInt8 value)
{
address &= 0x0FFF;
// For now, we ignore attempts to patch the CDF address space
if(address >= 0x0040)
{
myProgramImage[(myCurrentBank << 12) + (address & 0x0FFF)] = value;
return myBankChanged = true;
}
else
return false;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
const uInt8* CartridgeCDF::getImage(int& size) const
{
size = 32768;
return myImage;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt32 CartridgeCDF::thumbCallback(uInt8 function, uInt32 value1, uInt32 value2)
{
switch (function)
{
case 0:
// _SetNote - set the note/frequency
myMusicFrequencies[value1] = value2;
break;
// _ResetWave - reset counter,
// used to make sure digital samples start from the beginning
case 1:
myMusicCounters[value1] = 0;
break;
// _GetWavePtr - return the counter
case 2:
return myMusicCounters[value1];
break;
// _SetWaveSize - set size of waveform buffer
case 3:
myMusicWaveformSize[value1] = value2;
break;
}
return 0;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool CartridgeCDF::save(Serializer& out) const
{
try
{
out.putString(name());
// Indicates which bank is currently active
out.putShort(myCurrentBank);
// Harmony RAM
out.putByteArray(myCDFRAM, 8192);
out.putInt(mySystemCycles);
out.putInt((uInt32)(myFractionalClocks * 100000000.0));
out.putInt(myARMCycles);
}
catch(...)
{
cerr << "ERROR: CartridgeCDF::save" << endl;
return false;
}
return true;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool CartridgeCDF::load(Serializer& in)
{
try
{
if(in.getString() != name())
return false;
// Indicates which bank is currently active
myCurrentBank = in.getShort();
// Harmony RAM
in.getByteArray(myCDFRAM, 8192);
// Get system cycles and fractional clocks
mySystemCycles = (Int32)in.getInt();
myFractionalClocks = (double)in.getInt() / 100000000.0;
myARMCycles = (Int32)in.getInt();
}
catch(...)
{
cerr << "ERROR: CartridgeCDF::load" << endl;
return false;
}
// Now, go to the current bank
bank(myCurrentBank);
return true;
}
uInt32 CartridgeCDF::getDatastreamPointer(uInt8 index)
{
// index &= 0x0f;
return myCDFRAM[DSxPTR + index*4 + 0] + // low byte
(myCDFRAM[DSxPTR + index*4 + 1] << 8) +
(myCDFRAM[DSxPTR + index*4 + 2] << 16) +
(myCDFRAM[DSxPTR + index*4 + 3] << 24) ; // high byte
}
void CartridgeCDF::setDatastreamPointer(uInt8 index, uInt32 value)
{
// index &= 0x1f;
myCDFRAM[DSxPTR + index*4 + 0] = value & 0xff; // low byte
myCDFRAM[DSxPTR + index*4 + 1] = (value >> 8) & 0xff;
myCDFRAM[DSxPTR + index*4 + 2] = (value >> 16) & 0xff;
myCDFRAM[DSxPTR + index*4 + 3] = (value >> 24) & 0xff; // high byte
}
uInt32 CartridgeCDF::getDatastreamIncrement(uInt8 index)
{
// index &= 0x1f;
return myCDFRAM[DSxINC + index*4 + 0] + // low byte
(myCDFRAM[DSxINC + index*4 + 1] << 8) +
(myCDFRAM[DSxINC + index*4 + 2] << 16) +
(myCDFRAM[DSxINC + index*4 + 3] << 24) ; // high byte
}
void CartridgeCDF::setDatastreamIncrement(uInt8 index, uInt32 value)
{
// index &= 0x1f;
myCDFRAM[DSxINC + index*4 + 0] = value & 0xff; // low byte
myCDFRAM[DSxINC + index*4 + 1] = (value >> 8) & 0xff;
myCDFRAM[DSxINC + index*4 + 2] = (value >> 16) & 0xff;
myCDFRAM[DSxINC + index*4 + 3] = (value >> 24) & 0xff; // high byte
}
uInt32 CartridgeCDF::getWaveform(uInt8 index)
{
// instead of 0, 1, 2, etc. this returned
// 0x40000800 for 0
// 0x40000820 for 1
// 0x40000840 for 2
// ...
// return myCDFRAM[WAVEFORM + index*4 + 0] + // low byte
// (myCDFRAM[WAVEFORM + index*4 + 1] << 8) +
// (myCDFRAM[WAVEFORM + index*4 + 2] << 16) +
// (myCDFRAM[WAVEFORM + index*4 + 3] << 24) - // high byte
// 0x40000800;
uInt32 result;
result = myCDFRAM[WAVEFORM + index*4 + 0] + // low byte
(myCDFRAM[WAVEFORM + index*4 + 1] << 8) +
(myCDFRAM[WAVEFORM + index*4 + 2] << 16) +
(myCDFRAM[WAVEFORM + index*4 + 3] << 24);
result -= 0x40000800;
if (result >= 4096)
result = 0;
return result;
}
uInt32 CartridgeCDF::getWaveformSize(uInt8 index)
{
return myMusicWaveformSize[index];
}
uInt8 CartridgeCDF::readFromDatastream(uInt8 index)
{
// Pointers are stored as:
// PPPFF---
//
// Increments are stored as
// ----IIFF
//
// P = Pointer
// I = Increment
// F = Fractional
uInt32 pointer = getDatastreamPointer(index);
uInt16 increment = getDatastreamIncrement(index);
uInt8 value = myDisplayImage[ pointer >> 20 ];
pointer += (increment << 12);
setDatastreamPointer(index, pointer);
return value;
}

285
src/emucore/CartCDF.hxx Executable file
View File

@ -0,0 +1,285 @@
//============================================================================
//
// 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-2015 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: CartCDF.hxx 3131 2015-01-01 03:49:32Z stephena $
//============================================================================
#ifndef CARTRIDGE_CDF_HXX
#define CARTRIDGE_CDF_HXX
class System;
#ifdef THUMB_SUPPORT
class Thumbulator;
#endif
#ifdef DEBUGGER_SUPPORT
#include "CartCDFWidget.hxx"
#endif
#include "bspf.hxx"
#include "Cart.hxx"
/**
Cartridge class used for CDF.
THIS NEEDS TO BE UPDATED
There are seven 4K program banks, a 4K Display Data RAM,
1K C Varaible and Stack, and the CDF chip.
CDF chip access is mapped to $1000 - $103F.
@author Darrell Spice Jr, Chris Walton, Fred Quimby, Stephen Anthony, Bradford W. Mott
@version $Id: CartCDF.hxx 3131 2015-01-01 03:49:32Z stephena $
*/
class CartridgeCDF : public Cartridge
{
friend class CartridgeCDFWidget;
friend class CartridgeRamCDFWidget;
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)
*/
CartridgeCDF(const uInt8* image, uInt32 size, const Settings& settings);
/**
Destructor
*/
virtual ~CartridgeCDF();
public:
/**
Reset device to its power-on state
*/
void reset() override;
/**
Notification method invoked by the system when the console type
has changed. We need this to inform the Thumbulator that the
timing has changed.
@param timing Enum representing the new console type
*/
void consoleChanged(ConsoleTiming timing) override;
/**
Notification method invoked by the system right before the
system resets its cycle counter to zero. It may be necessary
to override this method for devices that remember cycle counts.
*/
void systemCyclesReset() override;
/**
Install cartridge in the specified system. Invoked by the system
when the cartridge is attached to it.
@param system The system the device should install itself in
*/
void install(System& system) override;
/**
Install pages for the specified bank in the system.
@param bank The bank that should be installed in the system
*/
bool bank(uInt16 bank) override;
/**
Get the current bank.
*/
uInt16 getBank() const override;
/**
Query the number of banks supported by the cartridge.
*/
uInt16 bankCount() const override;
/**
Patch the cartridge ROM.
@param address The ROM address to patch
@param value The value to place into the address
@return Success or failure of the patch operation
*/
bool patch(uInt16 address, uInt8 value) override;
/**
Access the internal ROM image for this cartridge.
@param size Set to the size of the internal ROM image data
@return A pointer to the internal ROM image data
*/
const uInt8* getImage(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 "CartridgeCDF"; }
// uInt8 busOverdrive(uInt16 address) override;
/**
Used for Thumbulator to pass values back to the cartridge
*/
uInt32 thumbCallback(uInt8 function, uInt32 value1, uInt32 value2) override;
#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 CartridgeCDFWidget(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:
/**
Sets the initial state of the DPC pointers and RAM
*/
void setInitialState();
/**
Updates any data fetchers in music mode based on the number of
CPU cycles which have passed since the last update.
*/
void updateMusicModeDataFetchers();
/**
Call Special Functions
*/
void callFunction(uInt8 value);
uInt32 getDatastreamPointer(uInt8 index);
void setDatastreamPointer(uInt8 index, uInt32 value);
uInt32 getDatastreamIncrement(uInt8 index);
void setDatastreamIncrement(uInt8 index, uInt32 value);
uInt8 readFromDatastream(uInt8 index);
uInt32 getWaveform(uInt8 index);
uInt32 getWaveformSize(uInt8 index);
private:
// The 32K ROM image of the cartridge
uInt8 myImage[32768];
// Pointer to the 28K program ROM image of the cartridge
uInt8* myProgramImage;
// Pointer to the 4K display ROM image of the cartridge
uInt8* myDisplayImage;
// Pointer to the 2K CDF driver image in RAM
uInt8* myBusDriverImage;
// The CDF 8k RAM image, used as:
// $0000 - 2K CDF driver
// $0800 - 4K Display Data
// $1800 - 2K C Variable & Stack
uInt8 myCDFRAM[8192];
#ifdef THUMB_SUPPORT
// Pointer to the Thumb ARM emulator object
unique_ptr<Thumbulator> myThumbEmulator;
#endif
// Indicates which bank is currently active
uInt16 myCurrentBank;
// System cycle count when the last update to music data fetchers occurred
Int32 mySystemCycles;
Int32 myARMCycles;
uInt8 mySetAddress;
// The music mode counters
uInt32 myMusicCounters[3];
// The music frequency
uInt32 myMusicFrequencies[3];
// The music waveform sizes
uInt8 myMusicWaveformSize[3];
// Fractional DPC music OSC clocks unused during the last update
double myFractionalClocks;
// Controls mode, lower nybble sets Fast Fetch, upper nybble sets audio
// -0 = Fast Fetch ON
// -F = Fast Fetch OFF
// 0- = Packed Digital Sample
// F- = 3 Voice Music
uInt8 myMode;
// set to address of #value if last byte peeked was A9 (LDA #)
uInt16 myLDAimmediateOperandAddress;
TIA* myTIA;
private:
// Following constructors and assignment operators not supported
CartridgeCDF() = delete;
CartridgeCDF(const CartridgeCDF&) = delete;
CartridgeCDF(CartridgeCDF&&) = delete;
CartridgeCDF& operator=(const CartridgeCDF&) = delete;
CartridgeCDF& operator=(CartridgeCDF&&) = delete;
};
#endif

View File

@ -61,7 +61,9 @@ CartridgeDPCPlus::CartridgeDPCPlus(const uInt8* image, uInt32 size,
myThumbEmulator = make_ptr<Thumbulator>
(reinterpret_cast<uInt16*>(myProgramImage-0xC00),
reinterpret_cast<uInt16*>(myDPCRAM),
settings.getBool("thumb.trapfatal"));
settings.getBool("thumb.trapfatal"),
Thumbulator::ConfigureFor::DPCplus,
this);
#endif
setInitialState();
@ -196,9 +198,10 @@ inline void CartridgeDPCPlus::callFunction(uInt8 value)
myParameterPointer = 0;
break;
#ifdef THUMB_SUPPORT
case 254:
case 255:
// Call user written ARM code (most likely be C compiled for ARM)
case 254: // call with IRQ driven audio, no special handling needed at this
// time for Stella as ARM code "runs in zero 6507 cycles".
case 255: // call without IRQ driven audio
try {
Int32 cycles = mySystem->cycles() - myARMCycles;
myARMCycles = mySystem->cycles();

View File

@ -54,11 +54,14 @@ using Common::Base;
#endif
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Thumbulator::Thumbulator(const uInt16* rom_ptr, uInt16* ram_ptr, bool traponfatal)
Thumbulator::Thumbulator(const uInt16* rom_ptr, uInt16* ram_ptr, bool traponfatal,
Thumbulator::ConfigureFor configurefor, Cartridge* cartridge)
: rom(rom_ptr),
ram(ram_ptr),
T1TCR(0),
T1TC(0)
T1TC(0),
configuration(configurefor),
myCartridge(cartridge)
{
setConsoleTiming(ConsoleTiming::ntsc);
trapFatalErrors(traponfatal);
@ -227,8 +230,29 @@ void Thumbulator::write16(uInt32 addr, uInt32 data)
{
if((addr > 0x40001fff) && (addr < 0x50000000))
fatalError("write16", addr, "abort - out of range");
else if((addr > 0x40000028) && (addr < 0x40000c00))
fatalError("write16", addr, "to bankswitch code area");
switch(configuration)
{
// this protects 2K Harmony/Melody Drivers
// Initial section of driver is the bootstrap which copies the driver
// from ROM to RAM, so it can safely be used by the custom ARM code
// as additional RAM
case ConfigureFor::BUS:
case ConfigureFor::CDF:
if((addr > 0x40000028) && (addr < 0x40000800))
fatalError("write16", addr, "to bankswitch code area");
break;
// this protects 3K Harmony/Melody Drivers
// Initial section of driver is the bootstrap which copies the driver
// from ROM to RAM, so it can safely be used by the custom ARM code
// as additional RAM
case ConfigureFor::DPCplus:
if((addr > 0x40000028) && (addr < 0x40000c00))
fatalError("write16", addr, "to bankswitch code area");
break;
}
if(addr & 1)
fatalError("write16", addr, "abort - misaligned");
@ -1042,14 +1066,95 @@ int Thumbulator::execute()
//fprintf(stderr,"bx r%u 0x%X 0x%X\n",rm,rc,pc);
if(rc & 1)
{
// branch to odd address denotes 16 bit ARM code
rc &= ~1;
write_register(15, rc);
return 0;
}
else
{
//fprintf(stderr,"cannot branch to arm 0x%08X 0x%04X\n",pc,inst);
// fxq: or maybe this one??
// branch to even address denotes 32 bit ARM code, which the Thumbulator
// class does not support. So capture relavent information and hand it
// off to the Cartridge class for it to handle.
bool handled = false;
switch(configuration)
{
case ConfigureFor::BUS:
case ConfigureFor::CDF:
// this subroutine interface is used in BUS and CDF drivers,
// and starts at address 0x000006e0 in both.
// _SetNote:
// ldr r4, =NoteStore
// bx r4 // bx instruction at 0x000006e2
// _ResetWave:
// ldr r4, =ResetWaveStore
// bx r4 // bx instruction at 0x000006e6
// _GetWavePtr:
// ldr r4, =WavePtrFetch
// bx r4 // bx instruction at 0x000006ea
// _SetWaveSize:
// ldr r4, =WaveSizeStore
// bx r4 // bx instruction at 0x000006ee
// address to test for is + 4 due to pipelining
#define BUS_SetNote (0x000006e2 + 4)
#define BUS_ResetWave (0x000006e6 + 4)
#define BUS_GetWavePtr (0x000006ea + 4)
#define BUS_SetWaveSize (0x000006ee + 4)
if (pc == BUS_SetNote)
{
myCartridge->thumbCallback(0, read_register(2), read_register(3));
handled = true;
}
else if (pc == BUS_ResetWave)
{
myCartridge->thumbCallback(1, read_register(2), 0);
handled = true;
}
else if (pc == BUS_GetWavePtr)
{
write_register(2, myCartridge->thumbCallback(2, read_register(2), 0));
handled = true;
}
else if (pc == BUS_SetWaveSize)
{
myCartridge->thumbCallback(3, read_register(2), read_register(3));
handled = true;
}
else if (pc == 0x0000083a)
{
// exiting Custom ARM code, returning to BUS Driver control
}
else
{
// just for testing
uInt32 r0 = read_register(0);
uInt32 r1 = read_register(1);
uInt32 r2 = read_register(2);
uInt32 r3 = read_register(3);
uInt32 r4 = read_register(4);
myCartridge->thumbCallback(255,0,0);
}
break;
case ConfigureFor::DPCplus:
// no 32-bit subroutines in DPC+
break;
}
if (handled)
{
rc = read_register(14); // lr
rc += 2;
rc &= ~1;
write_register(15, rc);
return 0;
}
return 1;
}
}
@ -2098,8 +2203,22 @@ int Thumbulator::reset()
{
std::fill(reg_norm, reg_norm+12, 0);
reg_norm[13] = 0x40001FB4;
reg_norm[14] = 0x00000C00;
reg_norm[15] = 0x00000C0B;
switch(configuration)
{
// future 2K Harmony/Melody drivers will most likely use these settings
case ConfigureFor::BUS:
case ConfigureFor::CDF:
reg_norm[14] = 0x00000800; // Link Register
reg_norm[15] = 0x0000080B; // Program Counter
break;
// future 3K Harmony/Melody drivers will most likely use these settings
case ConfigureFor::DPCplus:
reg_norm[14] = 0x00000C00; // Link Register
reg_norm[15] = 0x00000C0B; // Program Counter
break;
}
cpsr = mamcr = 0;
handler_mode = false;

View File

@ -28,6 +28,7 @@
#define THUMBULATOR_HXX
#include "bspf.hxx"
#include "Cart.hxx"
#include "Console.hxx"
#define ROMADDMASK 0x7FFF
@ -44,7 +45,16 @@
class Thumbulator
{
public:
Thumbulator(const uInt16* rom, uInt16* ram, bool traponfatal);
// control cartridge specific features of the Thumbulator class,
// such as the start location for calling custom code
enum ConfigureFor {
BUS, // cartridges of type BUS
CDF, // cartridges of type CDF
DPCplus // cartridges of type DPC+
};
Thumbulator(const uInt16* rom, uInt16* ram, bool traponfatal,
Thumbulator::ConfigureFor configurefor, Cartridge* cartridge);
/**
Run the ARM code, and return when finished. A runtime_error exception is
@ -123,6 +133,10 @@ class Thumbulator
static bool trapOnFatal;
ConfigureFor configuration;
Cartridge* myCartridge;
private:
// Following constructors and assignment operators not supported
Thumbulator() = delete;

View File

@ -214,6 +214,14 @@
2D91751009BA90380026E9FF /* OpenGL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2DEB3D4C0629BD24007EBBD3 /* OpenGL.framework */; };
2D91751209BA90380026E9FF /* ApplicationServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2D17D98E08BC398400E47F69 /* ApplicationServices.framework */; };
2DEFB40C09C3386F00754289 /* Cart.icns in Resources */ = {isa = PBXBuildFile; fileRef = 2DEFB40B09C3386F00754289 /* Cart.icns */; };
CFE3F60B1E84A9A200A8204E /* CartBUSWidget.cxx in Sources */ = {isa = PBXBuildFile; fileRef = CFE3F6071E84A9A200A8204E /* CartBUSWidget.cxx */; };
CFE3F60C1E84A9A200A8204E /* CartBUSWidget.hxx in Headers */ = {isa = PBXBuildFile; fileRef = CFE3F6081E84A9A200A8204E /* CartBUSWidget.hxx */; };
CFE3F60D1E84A9A200A8204E /* CartCDFWidget.cxx in Sources */ = {isa = PBXBuildFile; fileRef = CFE3F6091E84A9A200A8204E /* CartCDFWidget.cxx */; };
CFE3F60E1E84A9A200A8204E /* CartCDFWidget.hxx in Headers */ = {isa = PBXBuildFile; fileRef = CFE3F60A1E84A9A200A8204E /* CartCDFWidget.hxx */; };
CFE3F6131E84A9CE00A8204E /* CartBUS.cxx in Sources */ = {isa = PBXBuildFile; fileRef = CFE3F60F1E84A9CE00A8204E /* CartBUS.cxx */; };
CFE3F6141E84A9CE00A8204E /* CartBUS.hxx in Headers */ = {isa = PBXBuildFile; fileRef = CFE3F6101E84A9CE00A8204E /* CartBUS.hxx */; };
CFE3F6151E84A9CE00A8204E /* CartCDF.cxx in Sources */ = {isa = PBXBuildFile; fileRef = CFE3F6111E84A9CE00A8204E /* CartCDF.cxx */; };
CFE3F6161E84A9CE00A8204E /* CartCDF.hxx in Headers */ = {isa = PBXBuildFile; fileRef = CFE3F6121E84A9CE00A8204E /* CartCDF.hxx */; };
DC047FEE1A4A6F3600348F0F /* JoystickDialog.cxx in Sources */ = {isa = PBXBuildFile; fileRef = DC047FEC1A4A6F3600348F0F /* JoystickDialog.cxx */; };
DC047FEF1A4A6F3600348F0F /* JoystickDialog.hxx in Headers */ = {isa = PBXBuildFile; fileRef = DC047FED1A4A6F3600348F0F /* JoystickDialog.hxx */; };
DC07A3C80CAD738A009B4BC9 /* StateManager.cxx in Sources */ = {isa = PBXBuildFile; fileRef = DC07A3C60CAD738A009B4BC9 /* StateManager.cxx */; };
@ -830,6 +838,14 @@
2DF971D70892CEA400F64D23 /* DebuggerSystem.hxx */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; name = DebuggerSystem.hxx; path = ../debugger/DebuggerSystem.hxx; sourceTree = SOURCE_ROOT; };
2DF971DF0892CEA400F64D23 /* Expression.hxx */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; name = Expression.hxx; path = ../debugger/Expression.hxx; sourceTree = SOURCE_ROOT; };
B2F367C504C7ADC700A80002 /* SDLMain.nib */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; path = SDLMain.nib; sourceTree = "<group>"; };
CFE3F6071E84A9A200A8204E /* CartBUSWidget.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CartBUSWidget.cxx; path = ../debugger/gui/CartBUSWidget.cxx; sourceTree = "<group>"; };
CFE3F6081E84A9A200A8204E /* CartBUSWidget.hxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = CartBUSWidget.hxx; path = ../debugger/gui/CartBUSWidget.hxx; sourceTree = "<group>"; };
CFE3F6091E84A9A200A8204E /* CartCDFWidget.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CartCDFWidget.cxx; path = ../debugger/gui/CartCDFWidget.cxx; sourceTree = "<group>"; };
CFE3F60A1E84A9A200A8204E /* CartCDFWidget.hxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = CartCDFWidget.hxx; path = ../debugger/gui/CartCDFWidget.hxx; sourceTree = "<group>"; };
CFE3F60F1E84A9CE00A8204E /* CartBUS.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CartBUS.cxx; path = ../emucore/CartBUS.cxx; sourceTree = "<group>"; };
CFE3F6101E84A9CE00A8204E /* CartBUS.hxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = CartBUS.hxx; path = ../emucore/CartBUS.hxx; sourceTree = "<group>"; };
CFE3F6111E84A9CE00A8204E /* CartCDF.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CartCDF.cxx; path = ../emucore/CartCDF.cxx; sourceTree = "<group>"; };
CFE3F6121E84A9CE00A8204E /* CartCDF.hxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = CartCDF.hxx; path = ../emucore/CartCDF.hxx; sourceTree = "<group>"; };
DC047FEC1A4A6F3600348F0F /* JoystickDialog.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = JoystickDialog.cxx; path = ../gui/JoystickDialog.cxx; sourceTree = "<group>"; };
DC047FED1A4A6F3600348F0F /* JoystickDialog.hxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = JoystickDialog.hxx; path = ../gui/JoystickDialog.hxx; sourceTree = "<group>"; };
DC07A3C60CAD738A009B4BC9 /* StateManager.cxx */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = StateManager.cxx; path = ../emucore/StateManager.cxx; sourceTree = SOURCE_ROOT; };
@ -1335,6 +1351,10 @@
DCAACB07188D636F00A4D282 /* CartBFSCWidget.hxx */,
DCAACB08188D636F00A4D282 /* CartBFWidget.cxx */,
DCAACB09188D636F00A4D282 /* CartBFWidget.hxx */,
CFE3F6071E84A9A200A8204E /* CartBUSWidget.cxx */,
CFE3F6081E84A9A200A8204E /* CartBUSWidget.hxx */,
CFE3F6091E84A9A200A8204E /* CartCDFWidget.cxx */,
CFE3F60A1E84A9A200A8204E /* CartCDFWidget.hxx */,
DC676A2B1729A0B000E4E73D /* CartCMWidget.cxx */,
DC676A2C1729A0B000E4E73D /* CartCMWidget.hxx */,
DC676A2D1729A0B000E4E73D /* CartCTYWidget.cxx */,
@ -1545,6 +1565,10 @@
DCAACAEF188D631500A4D282 /* CartBF.hxx */,
DCAACAF0188D631500A4D282 /* CartBFSC.cxx */,
DCAACAF1188D631500A4D282 /* CartBFSC.hxx */,
CFE3F60F1E84A9CE00A8204E /* CartBUS.cxx */,
CFE3F6101E84A9CE00A8204E /* CartBUS.hxx */,
CFE3F6111E84A9CE00A8204E /* CartCDF.cxx */,
CFE3F6121E84A9CE00A8204E /* CartCDF.hxx */,
DC8C1BA714B25DE7006440EE /* CartCM.cxx */,
DC8C1BA814B25DE7006440EE /* CartCM.hxx */,
DC6727081556F4860023653B /* CartCTY.cxx */,
@ -2134,6 +2158,7 @@
DC3EE8661E2C0E6D00905161 /* inflate.h in Headers */,
DCC527D510B9DA19005E1287 /* NullDev.hxx in Headers */,
DCC527D710B9DA19005E1287 /* System.hxx in Headers */,
CFE3F6161E84A9CE00A8204E /* CartCDF.hxx in Headers */,
DCC527DB10B9DA6A005E1287 /* bspf.hxx in Headers */,
DC6B2BA511037FF200F199A7 /* CartDebug.hxx in Headers */,
DC6B2BA711037FF200F199A7 /* DiStella.hxx in Headers */,
@ -2144,6 +2169,7 @@
DCF3A6F41DFC75E3008A8AF3 /* LatchedInput.hxx in Headers */,
DC74E5C7198AF12700F37E36 /* CartDASHWidget.hxx in Headers */,
DCD6FC7711C281ED005DA767 /* pngpriv.h in Headers */,
CFE3F60C1E84A9A200A8204E /* CartBUSWidget.hxx in Headers */,
DCD6FC9411C28C6F005DA767 /* PNGLibrary.hxx in Headers */,
DC98F35711F5B56200AA520F /* MessageBox.hxx in Headers */,
DCFFE59E12100E1400DFA000 /* ComboDialog.hxx in Headers */,
@ -2200,6 +2226,7 @@
DCAAE5F31715887B0080BB82 /* CartUAWidget.hxx in Headers */,
DC676A421729A0B000E4E73D /* Cart3EWidget.hxx in Headers */,
DC676A441729A0B000E4E73D /* Cart4A50Widget.hxx in Headers */,
CFE3F6141E84A9CE00A8204E /* CartBUS.hxx in Headers */,
DC676A461729A0B000E4E73D /* CartARWidget.hxx in Headers */,
DC676A481729A0B000E4E73D /* CartCMWidget.hxx in Headers */,
DC676A4A1729A0B000E4E73D /* CartCTYWidget.hxx in Headers */,
@ -2241,6 +2268,7 @@
DCF3A6F91DFC75E3008A8AF3 /* PaddleReader.hxx in Headers */,
DCFF14CE18B0260300A20364 /* EventHandlerSDL2.hxx in Headers */,
DC047FEF1A4A6F3600348F0F /* JoystickDialog.hxx in Headers */,
CFE3F60E1E84A9A200A8204E /* CartCDFWidget.hxx in Headers */,
DCF3A6F01DFC75E3008A8AF3 /* DrawCounterDecodes.hxx in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
@ -2391,6 +2419,7 @@
2D9174B509BA90380026E9FF /* ListWidget.cxx in Sources */,
2D9174B609BA90380026E9FF /* Menu.cxx in Sources */,
DC5E473B19EC9A14000E45DF /* EventJoyHandler.cxx in Sources */,
CFE3F60D1E84A9A200A8204E /* CartCDFWidget.cxx in Sources */,
DC1B2EC71E50036100F62837 /* TrakBall.cxx in Sources */,
2D9174B709BA90380026E9FF /* OptionsDialog.cxx in Sources */,
2D9174B809BA90380026E9FF /* PopUpWidget.cxx in Sources */,
@ -2404,6 +2433,7 @@
2D9174BD09BA90380026E9FF /* Widget.cxx in Sources */,
2D9174BE09BA90380026E9FF /* CartUA.cxx in Sources */,
DC3EE86E1E2C0E6D00905161 /* zutil.c in Sources */,
CFE3F60B1E84A9A200A8204E /* CartBUSWidget.cxx in Sources */,
DC1B2EC51E50036100F62837 /* AtariMouse.cxx in Sources */,
2D9174BF09BA90380026E9FF /* FSNode.cxx in Sources */,
DCF3A6FE1DFC75E3008A8AF3 /* TIA.cxx in Sources */,
@ -2448,6 +2478,7 @@
2D91750309BA90380026E9FF /* TogglePixelWidget.cxx in Sources */,
2D91750409BA90380026E9FF /* ToggleWidget.cxx in Sources */,
2D91750609BA90380026E9FF /* TiaZoomWidget.cxx in Sources */,
CFE3F6131E84A9CE00A8204E /* CartBUS.cxx in Sources */,
DC73BD851915E5B1003FAFAD /* FBSurfaceSDL2.cxx in Sources */,
2D91750709BA90380026E9FF /* TIASnd.cxx in Sources */,
2D91750809BA90380026E9FF /* AudioWidget.cxx in Sources */,
@ -2496,6 +2527,7 @@
DC6B2BA411037FF200F199A7 /* CartDebug.cxx in Sources */,
DCB20EC71A0C506C0048F595 /* main.cxx in Sources */,
DC6B2BA611037FF200F199A7 /* DiStella.cxx in Sources */,
CFE3F6151E84A9CE00A8204E /* CartCDF.cxx in Sources */,
DCD3F7C511340AAF00DBA3AE /* Genesis.cxx in Sources */,
DCAD60A81152F8BD00BC4184 /* CartDPCPlus.cxx in Sources */,
DCD6FC7011C281ED005DA767 /* png.c in Sources */,