mirror of https://github.com/stella-emu/stella.git
Updated BUS to new spec
The Harmony/Melody BUS driver is undergoing a major revision to add support for the new JMP FASTJUMP feature. This update implements the changes, as well as the fast jump feature which isn't yet in the HMdriver. It also implements the load/save state support as well as the BUS debugger widget. A few minor updates to CDF were also done. Note that this update breaks compatibility with the previous BUS demos.
This commit is contained in:
parent
e3514a9f80
commit
490724c079
|
@ -33,8 +33,8 @@ CartridgeBUSWidget::CartridgeBUSWidget(
|
|||
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"
|
||||
<< "BUS registers accessible @ $FFEE - $FFF3\n"
|
||||
<< "Banks accessible at hotspots $FFFF to $FFFB\n"
|
||||
<< "Startup bank = " << cart.myStartBank << "\n";
|
||||
|
||||
#if 0
|
||||
|
@ -49,49 +49,78 @@ CartridgeBUSWidget::CartridgeBUSWidget(
|
|||
#endif
|
||||
|
||||
int xpos = 10,
|
||||
ypos = addBaseInformation(size, "AtariAge", info.str()) +
|
||||
ypos = addBaseInformation(size, "AtariAge", info.str(), 4) +
|
||||
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)");
|
||||
VarList::push_back(items, "0 ($FFF5)");
|
||||
VarList::push_back(items, "1 ($FFF6)");
|
||||
VarList::push_back(items, "2 ($FFF7)");
|
||||
VarList::push_back(items, "3 ($FFF8)");
|
||||
VarList::push_back(items, "4 ($FFF9)");
|
||||
VarList::push_back(items, "5 ($FFFA)");
|
||||
VarList::push_back(items, "6 ($FFFB)");
|
||||
myBank =
|
||||
new PopUpWidget(boss, _font, xpos, ypos-2, _font.getStringWidth("0 ($FFx) "),
|
||||
myLineHeight, items, "Set bank: ",
|
||||
_font.getStringWidth("Set bank: "), kBankChanged);
|
||||
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
|
||||
int lwidth = _font.getStringWidth("Datastream Increments "); // get width of the widest label
|
||||
|
||||
// Datastream Pointers
|
||||
xpos = 0; ypos += myLineHeight + 4;
|
||||
#define DS_X 30
|
||||
xpos = DS_X;
|
||||
ypos += myLineHeight + 4;
|
||||
new StaticTextWidget(boss, _font, xpos, ypos, lwidth,
|
||||
myFontHeight, "Datastream Pointers: ", kTextAlignLeft);
|
||||
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 = new DataGridWidget(boss, _nfont, DS_X, ypos+myLineHeight-2, 4, 4, 6, 32, Common::Base::F_16_3_2);
|
||||
myDatastreamPointers->setTarget(this);
|
||||
myDatastreamPointers->setEditable(false);
|
||||
|
||||
myDatastreamPointers2 = new DataGridWidget(boss, _nfont, DS_X + myDatastreamPointers->getWidth() * 3 / 4, ypos+myLineHeight-2 + 4*myLineHeight, 1, 2, 6, 32, Common::Base::F_16_3_2);
|
||||
myDatastreamPointers2->setTarget(this);
|
||||
myDatastreamPointers2->setEditable(false);
|
||||
|
||||
uInt32 row;
|
||||
for(row = 0; row < 4; ++row)
|
||||
{
|
||||
myDatastreamLabels[row] =
|
||||
new StaticTextWidget(_boss, _font, DS_X - _font.getStringWidth("xx "),
|
||||
ypos+myLineHeight-2 + row*myLineHeight + 2,
|
||||
myFontWidth*2, myFontHeight, "", kTextAlignLeft);
|
||||
myDatastreamLabels[row]->setLabel(Common::Base::toString(row * 4, Common::Base::F_16_2));
|
||||
}
|
||||
lwidth = _font.getStringWidth("Write Data (stream 16)");
|
||||
myDatastreamLabels[4] =
|
||||
new StaticTextWidget(_boss, _font, DS_X - _font.getStringWidth("xx "),
|
||||
ypos+myLineHeight-2 + 4*myLineHeight + 2,
|
||||
lwidth, myFontHeight, "Write Data (stream 16)", kTextAlignLeft);
|
||||
myDatastreamLabels[5] =
|
||||
new StaticTextWidget(_boss, _font, DS_X - _font.getStringWidth("xx "),
|
||||
ypos+myLineHeight-2 + 5*myLineHeight + 2,
|
||||
lwidth, myFontHeight, "Jump Data (stream 17)", kTextAlignLeft);
|
||||
|
||||
// Datastream Increments
|
||||
xpos = 0 + myDatastreamPointers->getWidth();
|
||||
xpos = DS_X + myDatastreamPointers->getWidth() + 20;
|
||||
new StaticTextWidget(boss, _font, xpos, ypos, lwidth,
|
||||
myFontHeight, "Datastream Increments: ", kTextAlignLeft);
|
||||
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);
|
||||
|
||||
myDatastreamIncrements2 = new DataGridWidget(boss, _nfont, xpos, ypos+myLineHeight-2 + 4*myLineHeight, 1, 2, 5, 32, Common::Base::F_16_2_2);
|
||||
myDatastreamIncrements2->setTarget(this);
|
||||
myDatastreamIncrements2->setEditable(false);
|
||||
|
||||
// Datastream Maps
|
||||
xpos = 0; ypos += myLineHeight*5 + 4;
|
||||
xpos = 0; ypos += myLineHeight*7 + 4;
|
||||
new StaticTextWidget(boss, _font, xpos, ypos, lwidth,
|
||||
myFontHeight, "Address Maps: ", kTextAlignLeft);
|
||||
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);
|
||||
|
@ -100,7 +129,7 @@ CartridgeBUSWidget::CartridgeBUSWidget(
|
|||
// Music counters
|
||||
xpos = 10; ypos += myLineHeight*6 + 4;
|
||||
new StaticTextWidget(boss, _font, xpos, ypos, lwidth,
|
||||
myFontHeight, "Music Counters: ", kTextAlignLeft);
|
||||
myFontHeight, "Music Counters", kTextAlignLeft);
|
||||
xpos += lwidth;
|
||||
|
||||
myMusicCounters = new DataGridWidget(boss, _nfont, xpos, ypos-2, 3, 1, 8, 32, Common::Base::F_16_8);
|
||||
|
@ -110,7 +139,7 @@ CartridgeBUSWidget::CartridgeBUSWidget(
|
|||
// Music frequencies
|
||||
xpos = 10; ypos += myLineHeight + 4;
|
||||
new StaticTextWidget(boss, _font, xpos, ypos, lwidth,
|
||||
myFontHeight, "Music Frequencies: ", kTextAlignLeft);
|
||||
myFontHeight, "Music Frequencies", kTextAlignLeft);
|
||||
xpos += lwidth;
|
||||
|
||||
myMusicFrequencies = new DataGridWidget(boss, _nfont, xpos, ypos-2, 3, 1, 8, 32, Common::Base::F_16_8);
|
||||
|
@ -120,17 +149,26 @@ CartridgeBUSWidget::CartridgeBUSWidget(
|
|||
// Music waveforms
|
||||
xpos = 10; ypos += myLineHeight + 4;
|
||||
new StaticTextWidget(boss, _font, xpos, ypos, lwidth,
|
||||
myFontHeight, "Music Waveforms: ", kTextAlignLeft);
|
||||
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);
|
||||
|
||||
int xpossp = xpos + myMusicWaveforms->getWidth() + 20;
|
||||
int lwidth2 = _font.getStringWidth("Sample Pointer ");
|
||||
new StaticTextWidget(boss, _font, xpossp, ypos, lwidth2,
|
||||
myFontHeight, "Sample Pointer ", kTextAlignLeft);
|
||||
|
||||
mySamplePointer = new DataGridWidget(boss, _nfont, xpossp + lwidth2, ypos-2, 1, 1, 8, 32, Common::Base::F_16_8);
|
||||
mySamplePointer->setTarget(this);
|
||||
mySamplePointer->setEditable(false);
|
||||
|
||||
// Music waveform sizes
|
||||
xpos = 10; ypos += myLineHeight + 4;
|
||||
new StaticTextWidget(boss, _font, xpos, ypos, lwidth,
|
||||
myFontHeight, "Music Waveform Sizes: ", kTextAlignLeft);
|
||||
myFontHeight, "Music Waveform Sizes", kTextAlignLeft);
|
||||
xpos += lwidth;
|
||||
|
||||
myMusicWaveformSizes = new DataGridWidget(boss, _nfont, xpos, ypos-2, 3, 1, 4, 16, Common::Base::F_16_2);
|
||||
|
@ -138,15 +176,15 @@ CartridgeBUSWidget::CartridgeBUSWidget(
|
|||
myMusicWaveformSizes->setEditable(false);
|
||||
|
||||
|
||||
// BUS stuff and ZP STY flags
|
||||
// BUS stuff and Digital Audio 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);
|
||||
|
||||
myDigitalSample = new CheckboxWidget(boss, _font, xpossp, ypos, "Digital Sample mode");
|
||||
myDigitalSample->setTarget(this);
|
||||
myDigitalSample->setEditable(false);
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
@ -162,8 +200,9 @@ void CartridgeBUSWidget::saveOldState()
|
|||
myOldState.mwaves.clear();
|
||||
myOldState.mwavesizes.clear();
|
||||
myOldState.internalram.clear();
|
||||
myOldState.samplepointer.clear();
|
||||
|
||||
for(uInt32 i = 0; i < 16; i++)
|
||||
for(uInt32 i = 0; i < 18; i++)
|
||||
{
|
||||
// Pointers are stored as:
|
||||
// PPPFF---
|
||||
|
@ -176,14 +215,22 @@ void CartridgeBUSWidget::saveOldState()
|
|||
// F = Fractional
|
||||
|
||||
myOldState.datastreampointers.push_back(myCart.getDatastreamPointer(i)>>12);
|
||||
myOldState.datastreamincrements.push_back(myCart.getDatastreamIncrement(i));
|
||||
if (i < 16)
|
||||
myOldState.datastreamincrements.push_back(myCart.getDatastreamIncrement(i));
|
||||
else
|
||||
myOldState.datastreamincrements.push_back(0x100);
|
||||
}
|
||||
|
||||
for(uInt32 i = 0; i < 40; i++)
|
||||
for(uInt32 i = 0; i < 37; i++) // only 37 map values
|
||||
{
|
||||
myOldState.addressmaps.push_back(myCart.getAddressMap(i));
|
||||
}
|
||||
|
||||
for(uInt32 i = 37; i < 40; i++) // but need 40 for the grid
|
||||
{
|
||||
myOldState.addressmaps.push_back(0);
|
||||
}
|
||||
|
||||
for(uInt32 i = 0; i < 3; ++i)
|
||||
{
|
||||
myOldState.mcounters.push_back(myCart.myMusicCounters[i]);
|
||||
|
@ -198,6 +245,8 @@ void CartridgeBUSWidget::saveOldState()
|
|||
|
||||
for(uInt32 i = 0; i < internalRamSize(); ++i)
|
||||
myOldState.internalram.push_back(myCart.myBUSRAM[i]);
|
||||
|
||||
myOldState.samplepointer.push_back(myCart.getSample());
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
@ -229,6 +278,15 @@ void CartridgeBUSWidget::loadConfig()
|
|||
}
|
||||
myDatastreamPointers->setList(alist, vlist, changed);
|
||||
|
||||
alist.clear(); vlist.clear(); changed.clear();
|
||||
for(int i = 16; i < 18; ++i)
|
||||
{
|
||||
Int32 pointervalue = myCart.getDatastreamPointer(i) >> 12;
|
||||
alist.push_back(0); vlist.push_back(pointervalue);
|
||||
changed.push_back(pointervalue != myOldState.datastreampointers[i]);
|
||||
}
|
||||
myDatastreamPointers2->setList(alist, vlist, changed);
|
||||
|
||||
alist.clear(); vlist.clear(); changed.clear();
|
||||
for(int i = 0; i < 16; ++i)
|
||||
{
|
||||
|
@ -239,12 +297,27 @@ void CartridgeBUSWidget::loadConfig()
|
|||
myDatastreamIncrements->setList(alist, vlist, changed);
|
||||
|
||||
alist.clear(); vlist.clear(); changed.clear();
|
||||
for(int i = 0; i < 40; ++i)
|
||||
for(int i = 16; i < 18; ++i)
|
||||
{
|
||||
Int32 incrementvalue = 0x100;
|
||||
alist.push_back(0); vlist.push_back(incrementvalue);
|
||||
changed.push_back(incrementvalue != myOldState.datastreamincrements[i]);
|
||||
}
|
||||
myDatastreamIncrements2->setList(alist, vlist, changed);
|
||||
|
||||
alist.clear(); vlist.clear(); changed.clear();
|
||||
for(int i = 0; i < 37; ++i) // only 37 map values
|
||||
{
|
||||
Int32 mapvalue = myCart.getAddressMap(i);
|
||||
alist.push_back(0); vlist.push_back(mapvalue);
|
||||
changed.push_back(mapvalue != myOldState.addressmaps[i]);
|
||||
}
|
||||
for(int i = 37; i < 40; ++i) // but need 40 for the grid
|
||||
{
|
||||
Int32 mapvalue = 0;
|
||||
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();
|
||||
|
@ -279,8 +352,26 @@ void CartridgeBUSWidget::loadConfig()
|
|||
}
|
||||
myMusicWaveformSizes->setList(alist, vlist, changed);
|
||||
|
||||
// myBusOverdrive->setState(myCart.getBusStuffFlag());
|
||||
// myZPSTY->setState(myCart.mySTYZeroPage);
|
||||
alist.clear(); vlist.clear(); changed.clear();
|
||||
alist.push_back(0); vlist.push_back(myCart.getSample());
|
||||
changed.push_back((myCart.getSample()) != uInt32(myOldState.samplepointer[0]));
|
||||
mySamplePointer->setList(alist, vlist, changed);
|
||||
|
||||
myBusOverdrive->setState((myCart.myMode & 0x0f) == 0);
|
||||
myDigitalSample->setState((myCart.myMode & 0xf0) == 0);
|
||||
|
||||
if ((myCart.myMode & 0xf0) == 0)
|
||||
{
|
||||
myMusicWaveforms->setCrossed(true);
|
||||
myMusicWaveformSizes->setCrossed(true);
|
||||
mySamplePointer->setCrossed(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
myMusicWaveforms->setCrossed(false);
|
||||
myMusicWaveformSizes->setCrossed(false);
|
||||
mySamplePointer->setCrossed(true);
|
||||
}
|
||||
|
||||
CartDebugWidget::loadConfig();
|
||||
}
|
||||
|
|
|
@ -45,6 +45,7 @@ class CartridgeBUSWidget : public CartDebugWidget
|
|||
IntArray mfreqs;
|
||||
IntArray mwaves;
|
||||
IntArray mwavesizes;
|
||||
IntArray samplepointer;
|
||||
uInt32 random;
|
||||
ByteArray internalram;
|
||||
};
|
||||
|
@ -54,13 +55,17 @@ class CartridgeBUSWidget : public CartDebugWidget
|
|||
|
||||
DataGridWidget* myDatastreamPointers;
|
||||
DataGridWidget* myDatastreamIncrements;
|
||||
DataGridWidget* myDatastreamPointers2;
|
||||
DataGridWidget* myDatastreamIncrements2;
|
||||
DataGridWidget* myAddressMaps;
|
||||
DataGridWidget* myMusicCounters;
|
||||
DataGridWidget* myMusicFrequencies;
|
||||
DataGridWidget* myMusicWaveforms;
|
||||
DataGridWidget* myMusicWaveformSizes;
|
||||
DataGridWidget* mySamplePointer;
|
||||
StaticTextWidget* myDatastreamLabels[6];
|
||||
CheckboxWidget* myBusOverdrive;
|
||||
CheckboxWidget* myZPSTY;
|
||||
CheckboxWidget* myDigitalSample;
|
||||
CartState myOldState;
|
||||
|
||||
enum { kBankChanged = 'bkCH' };
|
||||
|
|
|
@ -96,16 +96,14 @@ CartridgeCDFWidget::CartridgeCDFWidget(
|
|||
myDatastreamLabels[row]->setLabel(Common::Base::toString(row * 4, Common::Base::F_16_2));
|
||||
}
|
||||
lwidth = _font.getStringWidth("Write Data (stream 20)");
|
||||
myDatastreamLabels[row] =
|
||||
myDatastreamLabels[8] =
|
||||
new StaticTextWidget(_boss, _font, DS_X - _font.getStringWidth("xx "),
|
||||
ypos+myLineHeight-2 + 8*myLineHeight + 2,
|
||||
lwidth, myFontHeight, "Write Data (stream 20)", kTextAlignLeft);
|
||||
// myDatastreamLabels[row]->setLabel(Common::Base::toString(row * 4, Common::Base::F_16_2));
|
||||
myDatastreamLabels[row] =
|
||||
myDatastreamLabels[9] =
|
||||
new StaticTextWidget(_boss, _font, DS_X - _font.getStringWidth("xx "),
|
||||
ypos+myLineHeight-2 + 9*myLineHeight + 2,
|
||||
lwidth, myFontHeight, "Jump Data (stream 21)", kTextAlignLeft);
|
||||
// myDatastreamLabels[row]->setLabel(Common::Base::toString(row * 4, Common::Base::F_16_2));
|
||||
|
||||
// Datastream Increments
|
||||
xpos = DS_X + myDatastreamPointers->getWidth() + 20;
|
||||
|
|
|
@ -65,7 +65,6 @@ class CartridgeCDFWidget : public CartDebugWidget
|
|||
DataGridWidget* mySamplePointer;
|
||||
StaticTextWidget* myDatastreamLabels[10];
|
||||
|
||||
// done differently than in DPC+, need to rethink debugger support
|
||||
CheckboxWidget* myFastFetch;
|
||||
CheckboxWidget* myDigitalSample;
|
||||
CartState myOldState;
|
||||
|
|
|
@ -27,12 +27,15 @@
|
|||
#include "CartBUS.hxx"
|
||||
|
||||
// Location of data within the RAM copy of the BUS Driver.
|
||||
#define DSxPTR 0x06E0
|
||||
#define DSxPTR 0x06D8
|
||||
#define DSxINC 0x0720
|
||||
#define DSMAPS 0x0760
|
||||
#define WAVEFORM 0x07F4
|
||||
#define DSRAM 0x0800
|
||||
|
||||
#define COMMSTREAM 0x10
|
||||
#define JUMPSTREAM 0x11
|
||||
|
||||
#define BUS_STUFF_ON ((myMode & 0x0F) == 0)
|
||||
#define DIGITAL_AUDIO_ON ((myMode & 0xF0) == 0)
|
||||
|
||||
|
@ -41,6 +44,7 @@ CartridgeBUS::CartridgeBUS(const uInt8* image, uInt32 size,
|
|||
const Settings& settings)
|
||||
: Cartridge(settings),
|
||||
mySystemCycles(0),
|
||||
myARMCycles(0),
|
||||
myFractionalClocks(0.0)
|
||||
{
|
||||
// Copy the ROM image into my buffer
|
||||
|
@ -78,6 +82,7 @@ void CartridgeBUS::reset()
|
|||
|
||||
// Update cycles to the current system cycles
|
||||
mySystemCycles = mySystem->cycles();
|
||||
myARMCycles = mySystem->cycles();
|
||||
myFractionalClocks = 0.0;
|
||||
|
||||
setInitialState();
|
||||
|
@ -116,6 +121,7 @@ void CartridgeBUS::systemCyclesReset()
|
|||
{
|
||||
// Adjust the cycle counter so that it reflects the new value
|
||||
mySystemCycles -= mySystem->cycles();
|
||||
myARMCycles -= mySystem->cycles();
|
||||
}
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
@ -172,7 +178,10 @@ inline void CartridgeBUS::callFunction(uInt8 value)
|
|||
// time for Stella as ARM code "runs in zero 6507 cycles".
|
||||
case 255: // call without IRQ driven audio
|
||||
try {
|
||||
myThumbEmulator->run();
|
||||
Int32 cycles = mySystem->cycles() - myARMCycles;
|
||||
myARMCycles = mySystem->cycles();
|
||||
|
||||
myThumbEmulator->run(cycles);
|
||||
}
|
||||
catch(const runtime_error& e) {
|
||||
if(!mySystem->autodetectMode())
|
||||
|
@ -212,126 +221,122 @@ uInt8 CartridgeBUS::peek(uInt16 address)
|
|||
if(bankLocked())
|
||||
return peekvalue;
|
||||
|
||||
// implement JMP FASTJMP which fetches the destination address from stream 33
|
||||
if (myFastJumpActive)
|
||||
{
|
||||
uInt32 pointer;
|
||||
uInt8 value;
|
||||
|
||||
myFastJumpActive--;
|
||||
|
||||
pointer = getDatastreamPointer(JUMPSTREAM);
|
||||
value = myDisplayImage[ pointer >> 20 ];
|
||||
pointer += 0x100000; // always increment by 1
|
||||
setDatastreamPointer(JUMPSTREAM, pointer);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
// test for JMP FASTJUMP where FASTJUMP = $0000
|
||||
if (BUS_STUFF_ON
|
||||
&& peekvalue == 0x4C
|
||||
&& myProgramImage[(myCurrentBank << 12) + address+1] == 0
|
||||
&& myProgramImage[(myCurrentBank << 12) + address+2] == 0)
|
||||
{
|
||||
myFastJumpActive = 2; // return next two peeks from datastream 31
|
||||
return peekvalue;
|
||||
}
|
||||
|
||||
// save the STY's zero page address
|
||||
if (BUS_STUFF_ON && mySTYZeroPageAddress == address)
|
||||
myBusOverdriveAddress = peekvalue;
|
||||
|
||||
mySTYZeroPageAddress = 0;
|
||||
|
||||
if(address < 0x20)
|
||||
switch(address)
|
||||
{
|
||||
uInt8 result = 0;
|
||||
case 0xFEE: // AMPLITUDE
|
||||
|
||||
// Get the index of the data fetcher that's being accessed
|
||||
uInt32 index = address & 0x0f;
|
||||
uInt32 function = (address >> 4) & 0x01;
|
||||
// Update the music data fetchers (counter & flag)
|
||||
updateMusicModeDataFetchers();
|
||||
|
||||
switch(function)
|
||||
{
|
||||
case 0x00: // read from a datastream
|
||||
if DIGITAL_AUDIO_ON
|
||||
{
|
||||
result = readFromDatastream(index);
|
||||
break;
|
||||
// retrieve packed sample (max size is 2K, or 4K of unpacked data)
|
||||
peekvalue = myImage[getSample() + (myMusicCounters[0] >> 21)];
|
||||
|
||||
//
|
||||
if ((myMusicCounters[0] & (1<<20)) == 0)
|
||||
peekvalue >>= 4;
|
||||
peekvalue &= 0x0f;
|
||||
}
|
||||
case 0x01: // misc read registers
|
||||
else
|
||||
{
|
||||
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;
|
||||
// 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])];
|
||||
|
||||
case 0x08: // 0x18 = AMPLITUDE
|
||||
// Update the music data fetchers (counter & flag)
|
||||
updateMusicModeDataFetchers();
|
||||
|
||||
if DIGITAL_AUDIO_ON
|
||||
{
|
||||
// retrieve packed sample (max size is 2K, or 4K of unpacked data)
|
||||
result = myImage[getSample() + (myMusicCounters[0] >> 21)];
|
||||
|
||||
//
|
||||
if ((myMusicCounters[0] & (1<<20)) == 0)
|
||||
result >>= 4;
|
||||
result &= 0x0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 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;
|
||||
peekvalue = uInt8(i);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
return result;
|
||||
case 0xFEF: // DSREAD
|
||||
peekvalue = readFromDatastream(COMMSTREAM);
|
||||
break;
|
||||
|
||||
case 0xFF0: // DSWRITE
|
||||
case 0xFF1: // DSPTR
|
||||
case 0xFF2: // SETMODE
|
||||
case 0xFF3: // CALLFN
|
||||
// these are write-only
|
||||
break;
|
||||
|
||||
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;
|
||||
}
|
||||
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;
|
||||
// this might not work right for STY $84
|
||||
if (BUS_STUFF_ON && peekvalue == 0x84)
|
||||
mySTYZeroPageAddress = address + 1;
|
||||
|
||||
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 (BUS_STUFF_ON && peekvalue == 0x84)
|
||||
mySTYZeroPageAddress = address + 1;
|
||||
|
||||
return peekvalue;
|
||||
}
|
||||
return peekvalue;
|
||||
}
|
||||
|
||||
return 0; // make compiler happy
|
||||
|
@ -353,102 +358,77 @@ bool CartridgeBUS::poke(uInt16 address, uInt8 value)
|
|||
}
|
||||
else
|
||||
{
|
||||
uInt32 pointer;
|
||||
|
||||
address &= 0x0FFF;
|
||||
|
||||
if ((address >= 0x10) && (address <= 0x1F))
|
||||
switch(address)
|
||||
{
|
||||
// Get the index of the data fetcher that's being accessed
|
||||
uInt32 index = address & 0x0f;
|
||||
uInt32 pointer;
|
||||
case 0xFEE: // AMPLITUDE
|
||||
case 0xFEF: // DSREAD
|
||||
// these are read-only
|
||||
break;
|
||||
|
||||
switch (index)
|
||||
{
|
||||
case 0x00: // DS0WRITE
|
||||
case 0x01: // DS1WRITE
|
||||
case 0x02: // DS2WRITE
|
||||
case 0x03: // DS3WRITE
|
||||
// Pointers are stored as:
|
||||
// PPPFF---
|
||||
//
|
||||
// P = Pointer
|
||||
// F = Fractional
|
||||
case 0xFF0: // DSWRITE
|
||||
pointer = getDatastreamPointer(COMMSTREAM);
|
||||
myDisplayImage[ pointer >> 20 ] = value;
|
||||
pointer += 0x100000; // always increment by 1 when writing
|
||||
setDatastreamPointer(COMMSTREAM, pointer);
|
||||
break;
|
||||
|
||||
pointer = getDatastreamPointer(index);
|
||||
myDisplayImage[ pointer >> 20 ] = value;
|
||||
pointer += 0x100000; // always increment by 1 when writing
|
||||
setDatastreamPointer(index, pointer);
|
||||
break;
|
||||
case 0xFF1: // DSPTR
|
||||
pointer = getDatastreamPointer(COMMSTREAM);
|
||||
pointer <<=8;
|
||||
pointer &= 0xf0000000;
|
||||
pointer |= (value << 20);
|
||||
setDatastreamPointer(COMMSTREAM, 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
|
||||
case 0xFF2: // SETMODE
|
||||
myMode = value;
|
||||
break;
|
||||
|
||||
index &= 0x03;
|
||||
pointer = getDatastreamPointer(index);
|
||||
pointer <<=8;
|
||||
pointer &= 0xf0000000;
|
||||
pointer |= (value << 20);
|
||||
setDatastreamPointer(index, pointer);
|
||||
break;
|
||||
case 0xFF3: // CALLFN
|
||||
callFunction(value);
|
||||
break;
|
||||
|
||||
case 0x09: // 0x19 SETMODE
|
||||
myMode = value;
|
||||
break;
|
||||
case 0xFF5:
|
||||
// Set the current bank to the first 4k bank
|
||||
bank(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 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 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 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 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 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;
|
||||
|
||||
case 0x0FFB:
|
||||
// Set the current bank to the last 4k bank
|
||||
bank(6);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -516,12 +496,6 @@ 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)
|
||||
{
|
||||
|
@ -536,8 +510,6 @@ uInt8 CartridgeBUS::busOverdrive(uInt16 address)
|
|||
alldatastreams >>= 4;
|
||||
alldatastreams |= (datastream << 28);
|
||||
setAddressMap(map, alldatastreams);
|
||||
|
||||
// overdrive |= 0x7c; // breaks bus stuffing to match hobo's system
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -589,8 +561,25 @@ bool CartridgeBUS::save(Serializer& out) const
|
|||
// Harmony RAM
|
||||
out.putByteArray(myBUSRAM, 8192);
|
||||
|
||||
// Addresses for bus override logic
|
||||
out.putShort(myBusOverdriveAddress);
|
||||
out.putShort(mySTYZeroPageAddress);
|
||||
|
||||
// Save cycles and clocks
|
||||
out.putInt(mySystemCycles);
|
||||
out.putInt((uInt32)(myFractionalClocks * 100000000.0));
|
||||
out.putInt(myARMCycles);
|
||||
|
||||
// Audio info
|
||||
out.putIntArray(myMusicCounters, 3);
|
||||
out.putIntArray(myMusicFrequencies, 3);
|
||||
out.putByteArray(myMusicWaveformSize, 3);
|
||||
|
||||
// Indicates current mode
|
||||
out.putByte(myMode);
|
||||
|
||||
// Indicates if in the middle of a fast jump
|
||||
out.putByte(myFastJumpActive);
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
|
@ -615,9 +604,25 @@ bool CartridgeBUS::load(Serializer& in)
|
|||
// Harmony RAM
|
||||
in.getByteArray(myBUSRAM, 8192);
|
||||
|
||||
// Addresses for bus override logic
|
||||
myBusOverdriveAddress = in.getShort();
|
||||
mySTYZeroPageAddress = in.getShort();
|
||||
|
||||
// Get system cycles and fractional clocks
|
||||
mySystemCycles = (Int32)in.getInt();
|
||||
myFractionalClocks = (double)in.getInt() / 100000000.0;
|
||||
myARMCycles = (Int32)in.getInt();
|
||||
|
||||
// Audio info
|
||||
in.getIntArray(myMusicCounters, 3);
|
||||
in.getIntArray(myMusicFrequencies, 3);
|
||||
in.getByteArray(myMusicWaveformSize, 3);
|
||||
|
||||
// Indicates current mode
|
||||
myMode = in.getByte();
|
||||
|
||||
// Indicates if in the middle of a fast jump
|
||||
myFastJumpActive = in.getByte();
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
|
|
|
@ -250,7 +250,8 @@ class CartridgeBUS : public Cartridge
|
|||
// System cycle count when the last update to music data fetchers occurred
|
||||
Int32 mySystemCycles;
|
||||
|
||||
uInt8 mySetAddress;
|
||||
// ARM cycle count from when the last callFunction() occurred
|
||||
Int32 myARMCycles;
|
||||
|
||||
// The music mode counters
|
||||
uInt32 myMusicCounters[3];
|
||||
|
@ -271,6 +272,8 @@ class CartridgeBUS : public Cartridge
|
|||
// F- = 3 Voice Music
|
||||
uInt8 myMode;
|
||||
|
||||
uInt8 myFastJumpActive;
|
||||
|
||||
private:
|
||||
// Following constructors and assignment operators not supported
|
||||
CartridgeBUS() = delete;
|
||||
|
|
|
@ -32,7 +32,7 @@
|
|||
#define WAVEFORM 0x07F0
|
||||
#define DSRAM 0x0800
|
||||
|
||||
#define WRITESTREAM 0x20
|
||||
#define COMMSTREAM 0x20
|
||||
#define JUMPSTREAM 0x21
|
||||
#define AMPLITUDE 0x22
|
||||
|
||||
|
@ -228,11 +228,15 @@ uInt8 CartridgeCDF::peek(uInt16 address)
|
|||
return peekvalue;
|
||||
}
|
||||
|
||||
// Check if we're in Fast Fetch mode and the prior byte was an A9 (LDA #value)
|
||||
// Do a FAST FETCH LDA# if:
|
||||
// 1) in Fast Fetch mode
|
||||
// 2) peeking the operand of an LDA # instruction
|
||||
// 3) peek value is 0-34
|
||||
if(FAST_FETCH_ON
|
||||
&& myLDAimmediateOperandAddress == address
|
||||
&& peekvalue <= AMPLITUDE)
|
||||
{
|
||||
myLDAimmediateOperandAddress = 0;
|
||||
if (peekvalue == AMPLITUDE)
|
||||
{
|
||||
updateMusicModeDataFetchers();
|
||||
|
@ -318,22 +322,21 @@ bool CartridgeCDF::poke(uInt16 address, uInt8 value)
|
|||
|
||||
address &= 0x0FFF;
|
||||
|
||||
// Switch banks if necessary
|
||||
switch(address)
|
||||
{
|
||||
case 0xFF0: // DSWRITE
|
||||
pointer = getDatastreamPointer(WRITESTREAM);
|
||||
pointer = getDatastreamPointer(COMMSTREAM);
|
||||
myDisplayImage[ pointer >> 20 ] = value;
|
||||
pointer += 0x100000; // always increment by 1 when writing
|
||||
setDatastreamPointer(WRITESTREAM, pointer);
|
||||
setDatastreamPointer(COMMSTREAM, pointer);
|
||||
break;
|
||||
|
||||
case 0xFF1: // DSPTR
|
||||
pointer = getDatastreamPointer(WRITESTREAM);
|
||||
pointer = getDatastreamPointer(COMMSTREAM);
|
||||
pointer <<=8;
|
||||
pointer &= 0xf0000000;
|
||||
pointer |= (value << 20);
|
||||
setDatastreamPointer(WRITESTREAM, pointer);
|
||||
setDatastreamPointer(COMMSTREAM, pointer);
|
||||
break;
|
||||
|
||||
case 0xFF2: // SETMODE
|
||||
|
|
|
@ -1076,28 +1076,27 @@ int Thumbulator::execute()
|
|||
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.
|
||||
// this subroutine interface is used in the BUS driver,
|
||||
// it starts at address 0x000006d8
|
||||
// _SetNote:
|
||||
// ldr r4, =NoteStore
|
||||
// bx r4 // bx instruction at 0x000006e2
|
||||
// bx r4 // bx instruction at 0x000006da
|
||||
// _ResetWave:
|
||||
// ldr r4, =ResetWaveStore
|
||||
// bx r4 // bx instruction at 0x000006e6
|
||||
// bx r4 // bx instruction at 0x000006de
|
||||
// _GetWavePtr:
|
||||
// ldr r4, =WavePtrFetch
|
||||
// bx r4 // bx instruction at 0x000006ea
|
||||
// bx r4 // bx instruction at 0x000006e2
|
||||
// _SetWaveSize:
|
||||
// ldr r4, =WaveSizeStore
|
||||
// bx r4 // bx instruction at 0x000006ee
|
||||
// bx r4 // bx instruction at 0x000006e6
|
||||
|
||||
// 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)
|
||||
#define BUS_SetNote (0x000006da + 4)
|
||||
#define BUS_ResetWave (0x000006de + 4)
|
||||
#define BUS_GetWavePtr (0x000006e2 + 4)
|
||||
#define BUS_SetWaveSize (0x000006e6 + 4)
|
||||
|
||||
if (pc == BUS_SetNote)
|
||||
{
|
||||
|
@ -1125,6 +1124,67 @@ int Thumbulator::execute()
|
|||
}
|
||||
else
|
||||
{
|
||||
#if 0 // uncomment this 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);
|
||||
#endif
|
||||
myCartridge->thumbCallback(255, 0, 0);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case ConfigureFor::CDF:
|
||||
// this subroutine interface is used in the CDF driver,
|
||||
// it starts at address 0x000006e0
|
||||
// _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 CDF_SetNote (0x000006e2 + 4)
|
||||
#define CDF_ResetWave (0x000006e6 + 4)
|
||||
#define CDF_GetWavePtr (0x000006ea + 4)
|
||||
#define CDF_SetWaveSize (0x000006ee + 4)
|
||||
|
||||
if (pc == CDF_SetNote)
|
||||
{
|
||||
myCartridge->thumbCallback(0, read_register(2), read_register(3));
|
||||
handled = true;
|
||||
}
|
||||
else if (pc == CDF_ResetWave)
|
||||
{
|
||||
myCartridge->thumbCallback(1, read_register(2), 0);
|
||||
handled = true;
|
||||
}
|
||||
else if (pc == CDF_GetWavePtr)
|
||||
{
|
||||
write_register(2, myCartridge->thumbCallback(2, read_register(2), 0));
|
||||
handled = true;
|
||||
}
|
||||
else if (pc == CDF_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
|
||||
{
|
||||
#if 0 // uncomment this for testing
|
||||
uInt32 r0 = read_register(0);
|
||||
uInt32 r1 = read_register(1);
|
||||
|
|
Loading…
Reference in New Issue