diff --git a/src/debugger/gui/CartBUSInfoWidget.cxx b/src/debugger/gui/CartBUSInfoWidget.cxx new file mode 100644 index 000000000..18958c3ef --- /dev/null +++ b/src/debugger/gui/CartBUSInfoWidget.cxx @@ -0,0 +1,82 @@ +//============================================================================ +// +// 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-2022 by Bradford W. Mott, Stephen Anthony +// and the Stella Team +// +// See the file "License.txt" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +//============================================================================ + +#include "CartBUSInfoWidget.hxx" + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +CartridgeBUSInfoWidget::CartridgeBUSInfoWidget( + 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) +{ + constexpr uInt16 size = 8 * 4096; + ostringstream info; + + if (cart.myBUSSubtype == CartridgeBUS::BUSSubtype::BUS0) + { + info << "BUS Stuffing cartridge (EXPERIMENTAL)\n" + << "32K ROM, six 4K banks are accessible to 2600\n" + << "8K BUS RAM\n" + << "BUS registers accessible @ $F000 - $F03F\n" + << "Banks accessible at hotspots $FFF6 to $FFFB\n" + << "Startup bank = " << cart.startBank() << "\n"; + } + else + { + info << "BUS Stuffing cartridge (EXPERIMENTAL)\n" + << "32K ROM, seven 4K banks are accessible to 2600\n" + << "8K BUS RAM\n" + << (cart.myBUSSubtype == CartridgeBUS::BUSSubtype::BUS3 ? + "BUS registers accessible @ $FFEE - $FFF3\n" : // BUS3 + "BUS registers accessible @ $F000 - $F01A\n") // BUS1, BUS2 + << "Banks accessible at hotspots $FFF5 to $FFFB\n" + << "Startup bank = " << cart.startBank() << "\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 + + addBaseInformation(size, "AtariAge", info.str()); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +string CartridgeBUSInfoWidget::describeBUSVersion(CartridgeBUS::BUSSubtype subtype) +{ + switch(subtype) + { + case CartridgeBUS::BUSSubtype::BUS0: + return "BUS (v0)"; + + case CartridgeBUS::BUSSubtype::BUS1: + return "BUS (v1)"; + + case CartridgeBUS::BUSSubtype::BUS2: + return "BUS (v2)"; + + case CartridgeBUS::BUSSubtype::BUS3: + return "BUS (v3)"; + default: + throw runtime_error("unreachable"); + } +} diff --git a/src/debugger/gui/CartBUSInfoWidget.hxx b/src/debugger/gui/CartBUSInfoWidget.hxx new file mode 100644 index 000000000..c9537f2b3 --- /dev/null +++ b/src/debugger/gui/CartBUSInfoWidget.hxx @@ -0,0 +1,43 @@ +//============================================================================ +// +// 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-2022 by Bradford W. Mott, Stephen Anthony +// and the Stella Team +// +// See the file "License.txt" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +//============================================================================ + +#ifndef CARTRIDGEBUS_INFO_WIDGET_HXX +#define CARTRIDGEBUS_INFO_WIDGET_HXX + +#include "CartBUS.hxx" +#include "CartDebugWidget.hxx" + +class CartridgeBUSInfoWidget : public CartDebugWidget +{ + public: + CartridgeBUSInfoWidget(GuiObject* boss, const GUI::Font& lfont, + const GUI::Font& nfont, + int x, int y, int w, int h, + CartridgeBUS& cart); + ~CartridgeBUSInfoWidget() override = default; + + private: + static string describeBUSVersion(CartridgeBUS::BUSSubtype subtype); + + // Following constructors and assignment operators not supported + CartridgeBUSInfoWidget() = delete; + CartridgeBUSInfoWidget(const CartridgeBUSInfoWidget&) = delete; + CartridgeBUSInfoWidget(CartridgeBUSInfoWidget&&) = delete; + CartridgeBUSInfoWidget& operator=(const CartridgeBUSInfoWidget&) = delete; + CartridgeBUSInfoWidget& operator=(CartridgeBUSInfoWidget&&) = delete; +}; +#endif diff --git a/src/debugger/gui/CartBUSWidget.cxx b/src/debugger/gui/CartBUSWidget.cxx index 365f32bb9..456b78899 100644 --- a/src/debugger/gui/CartBUSWidget.cxx +++ b/src/debugger/gui/CartBUSWidget.cxx @@ -28,38 +28,53 @@ CartridgeBUSWidget::CartridgeBUSWidget( : CartridgeARMWidget(boss, lfont, nfont, x, y, w, h, cart), myCart{cart} { - constexpr uInt16 size = 8 * 4096; + constexpr int VBORDER = 8, + HBORDER = 2, + INDENT = 20, + VGAP = 4; - ostringstream info; - info << "BUS Stuffing cartridge (EXPERIMENTAL)\n" - << "32K ROM, seven 4K banks are accessible to 2600\n" - << "8K BUS RAM\n" - << "BUS registers accessible @ $FFEE - $FFF3\n" - << "Banks accessible at hotspots $FFFF to $FFFB\n" - << "Startup bank = " << cart.startBank() << "\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) + int xpos = HBORDER, ypos = VBORDER; + int ds2_rows; + +// if (cart.myBUSSubtype == CartridgeBUS::BUSSubtype::BUS0) +// { +// int lwidth = _font.getStringWidth("Unsupported version of BUS"); // get width of the widest label +// new StaticTextWidget(boss, _font, xpos, ypos, lwidth, +// myFontHeight, "Unsupported version of BUS", TextAlign::Left); +// return; +// } + + if (cart.myBUSSubtype == CartridgeBUS::BUSSubtype::BUS3) { - 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"; + ds2_rows = 2; + myDatastreamCount = 18; } -#endif - - int xpos = 2, - ypos = addBaseInformation(size, "AtariAge", info.str(), 4) + 4; - + else + { + ds2_rows = 4; + myDatastreamCount = 20; + } + VariantList items; - 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)"); + if (cart.myBUSSubtype == CartridgeBUS::BUSSubtype::BUS0) + { + VarList::push_back(items, "0 ($FFF6)"); + VarList::push_back(items, "1 ($FFF7)"); + VarList::push_back(items, "2 ($FFF8)"); + VarList::push_back(items, "3 ($FFF9)"); + VarList::push_back(items, "4 ($FFFA)"); + VarList::push_back(items, "5 ($FFFB)"); + } + else + { + 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 ($FFFx)"), myLineHeight, items, "Set bank ", @@ -72,7 +87,7 @@ CartridgeBUSWidget::CartridgeBUSWidget( // Datastream Pointers #define DS_X 30 xpos = DS_X; - ypos += myLineHeight + 4; + ypos += myLineHeight + VGAP; new StaticTextWidget(boss, _font, xpos, ypos, lwidth, myFontHeight, "Datastream Pointers", TextAlign::Left); @@ -80,7 +95,7 @@ CartridgeBUSWidget::CartridgeBUSWidget( 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::Fmt::_16_3_2); + myDatastreamPointers2 = new DataGridWidget(boss, _nfont, DS_X + myDatastreamPointers->getWidth() * 3 / 4, ypos+myLineHeight-2 + 4*myLineHeight, 1, ds2_rows, 6, 32, Common::Base::Fmt::_16_3_2); myDatastreamPointers2->setTarget(this); myDatastreamPointers2->setEditable(false); @@ -93,18 +108,42 @@ CartridgeBUSWidget::CartridgeBUSWidget( myFontWidth*2, myFontHeight, "", TextAlign::Left); myDatastreamLabels[row]->setLabel(Common::Base::toString(row * 4, Common::Base::Fmt::_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)", TextAlign::Left); - myDatastreamLabels[5] = - new StaticTextWidget(_boss, _font, DS_X - _font.getStringWidth("xx "), - ypos+myLineHeight-2 + 5*myLineHeight + 2, - lwidth, myFontHeight, "Jump Data (stream 17)", TextAlign::Left); - + + if (cart.myBUSSubtype == CartridgeBUS::BUSSubtype::BUS3) + { + 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)", TextAlign::Left); + myDatastreamLabels[5] = + new StaticTextWidget(_boss, _font, DS_X - _font.getStringWidth("xx "), + ypos+myLineHeight-2 + 5*myLineHeight + 2, + lwidth, myFontHeight, "Jump Data (stream 17)", TextAlign::Left); + } + else + { + lwidth = _font.getStringWidth("Write Data 0 (stream 16)"); + myDatastreamLabels[4] = + new StaticTextWidget(_boss, _font, DS_X - _font.getStringWidth("xx "), + ypos+myLineHeight-2 + 4*myLineHeight + 2, + lwidth, myFontHeight, "Write Data 0(stream 16)", TextAlign::Left); + myDatastreamLabels[5] = + new StaticTextWidget(_boss, _font, DS_X - _font.getStringWidth("xx "), + ypos+myLineHeight-2 + 5*myLineHeight + 2, + lwidth, myFontHeight, "Write Data 1(stream 17)", TextAlign::Left); + myDatastreamLabels[6] = + new StaticTextWidget(_boss, _font, DS_X - _font.getStringWidth("xx "), + ypos+myLineHeight-2 + 6*myLineHeight + 2, + lwidth, myFontHeight, "Write Data 2(stream 18)", TextAlign::Left); + myDatastreamLabels[7] = + new StaticTextWidget(_boss, _font, DS_X - _font.getStringWidth("xx "), + ypos+myLineHeight-2 + 7*myLineHeight + 2, + lwidth, myFontHeight, "Write Data 3(stream 19)", TextAlign::Left); + } + // Datastream Increments - xpos = DS_X + myDatastreamPointers->getWidth() + 20; + xpos = DS_X + myDatastreamPointers->getWidth() + INDENT; new StaticTextWidget(boss, _font, xpos, ypos, lwidth, myFontHeight, "Datastream Increments", TextAlign::Left); @@ -112,12 +151,12 @@ CartridgeBUSWidget::CartridgeBUSWidget( myDatastreamIncrements->setTarget(this); myDatastreamIncrements->setEditable(false); - myDatastreamIncrements2 = new DataGridWidget(boss, _nfont, xpos, ypos+myLineHeight-2 + 4*myLineHeight, 1, 2, 5, 32, Common::Base::Fmt::_16_2_2); + myDatastreamIncrements2 = new DataGridWidget(boss, _nfont, xpos, ypos+myLineHeight-2 + 4*myLineHeight, 1, ds2_rows, 5, 32, Common::Base::Fmt::_16_2_2); myDatastreamIncrements2->setTarget(this); myDatastreamIncrements2->setEditable(false); // Datastream Maps - xpos = 0; ypos += myLineHeight*7 + 4; + xpos = 0; ypos += myLineHeight*(5 + ds2_rows) + VGAP; new StaticTextWidget(boss, _font, xpos, ypos, lwidth, myFontHeight, "Address Maps", TextAlign::Left); @@ -126,7 +165,7 @@ CartridgeBUSWidget::CartridgeBUSWidget( myAddressMaps->setEditable(false); // Music counters - xpos = 10; ypos += myLineHeight*6 + 4; + xpos = 10; ypos += myLineHeight*6 + VGAP; new StaticTextWidget(boss, _font, xpos, ypos, lwidth, myFontHeight, "Music Counters", TextAlign::Left); xpos += lwidth; @@ -136,7 +175,7 @@ CartridgeBUSWidget::CartridgeBUSWidget( myMusicCounters->setEditable(false); // Music frequencies - xpos = 10; ypos += myLineHeight + 4; + xpos = 10; ypos += myLineHeight + VGAP; new StaticTextWidget(boss, _font, xpos, ypos, lwidth, myFontHeight, "Music Frequencies", TextAlign::Left); xpos += lwidth; @@ -146,7 +185,7 @@ CartridgeBUSWidget::CartridgeBUSWidget( myMusicFrequencies->setEditable(false); // Music waveforms - xpos = 10; ypos += myLineHeight + 4; + xpos = 10; ypos += myLineHeight + VGAP; new StaticTextWidget(boss, _font, xpos, ypos, lwidth, myFontHeight, "Music Waveforms", TextAlign::Left); xpos += lwidth; @@ -155,17 +194,20 @@ CartridgeBUSWidget::CartridgeBUSWidget( myMusicWaveforms->setTarget(this); myMusicWaveforms->setEditable(false); - const int xpossp = xpos + myMusicWaveforms->getWidth() + 20; - const int lwidth2 = _font.getStringWidth("Sample Pointer "); - new StaticTextWidget(boss, _font, xpossp, ypos, lwidth2, - myFontHeight, "Sample Pointer ", TextAlign::Left); + const int xpossp = xpos + myMusicWaveforms->getWidth() + INDENT; + if (cart.myBUSSubtype == CartridgeBUS::BUSSubtype::BUS3) + { + const int lwidth2 = _font.getStringWidth("Sample Pointer "); + new StaticTextWidget(boss, _font, xpossp, ypos, lwidth2, + myFontHeight, "Sample Pointer ", TextAlign::Left); - mySamplePointer = new DataGridWidget(boss, _nfont, xpossp + lwidth2, ypos-2, 1, 1, 8, 32, Common::Base::Fmt::_16_8); - mySamplePointer->setTarget(this); - mySamplePointer->setEditable(false); + mySamplePointer = new DataGridWidget(boss, _nfont, xpossp + lwidth2, ypos-2, 1, 1, 8, 32, Common::Base::Fmt::_16_8); + mySamplePointer->setTarget(this); + mySamplePointer->setEditable(false); + } // Music waveform sizes - xpos = 10; ypos += myLineHeight + 4; + xpos = 10; ypos += myLineHeight + VGAP; new StaticTextWidget(boss, _font, xpos, ypos, lwidth, myFontHeight, "Music Waveform Sizes", TextAlign::Left); xpos += lwidth; @@ -176,16 +218,18 @@ CartridgeBUSWidget::CartridgeBUSWidget( // BUS stuff and Digital Audio flags - xpos = 10; ypos += myLineHeight + 4; + xpos = 10; ypos += myLineHeight + VGAP; myBusOverdrive = new CheckboxWidget(boss, _font, xpos, ypos, "BUS Overdrive enabled"); myBusOverdrive->setTarget(this); myBusOverdrive->setEditable(false); - myDigitalSample = new CheckboxWidget(boss, _font, xpossp, ypos, "Digital Sample mode"); - myDigitalSample->setTarget(this); - myDigitalSample->setEditable(false); - - xpos = 10; ypos += myLineHeight + 4 * 2; + if (cart.myBUSSubtype == CartridgeBUS::BUSSubtype::BUS3) + { + myDigitalSample = new CheckboxWidget(boss, _font, xpossp, ypos, "Digital Sample mode"); + myDigitalSample->setTarget(this); + myDigitalSample->setEditable(false); + } + xpos = 10; ypos += myLineHeight + VGAP * 2; addCycleWidgets(xpos, ypos); } @@ -204,7 +248,7 @@ void CartridgeBUSWidget::saveOldState() myOldState.internalram.clear(); myOldState.samplepointer.clear(); - for(uInt32 i = 0; i < 18; ++i) + for(int i = 0; i < myDatastreamCount; ++i) { // Pointers are stored as: // PPPFF--- @@ -250,6 +294,9 @@ void CartridgeBUSWidget::saveOldState() // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void CartridgeBUSWidget::loadConfig() { +// if (myCart.myBUSSubtype == CartridgeBUS::BUSSubtype::BUS0) +// return; + myBank->setSelectedIndex(myCart.getBank()); // Get registers, using change tracking @@ -277,7 +324,7 @@ void CartridgeBUSWidget::loadConfig() myDatastreamPointers->setList(alist, vlist, changed); alist.clear(); vlist.clear(); changed.clear(); - for(int i = 16; i < 18; ++i) + for(int i = 16; i < myDatastreamCount; ++i) { const Int32 pointervalue = myCart.getDatastreamPointer(i) >> 12; alist.push_back(0); vlist.push_back(pointervalue); @@ -295,7 +342,7 @@ void CartridgeBUSWidget::loadConfig() myDatastreamIncrements->setList(alist, vlist, changed); alist.clear(); vlist.clear(); changed.clear(); - for(int i = 16; i < 18; ++i) + for(int i = 16; i < myDatastreamCount; ++i) { constexpr Int32 incrementvalue = 0x100; alist.push_back(0); vlist.push_back(incrementvalue); @@ -350,25 +397,32 @@ void CartridgeBUSWidget::loadConfig() } myMusicWaveformSizes->setList(alist, vlist, changed); - 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); + + if (myCart.myBUSSubtype == CartridgeBUS::BUSSubtype::BUS3) + { + 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.myBUSSubtype == CartridgeBUS::BUSSubtype::BUS3) + myDigitalSample->setState((myCart.myMode & 0xf0) == 0); if ((myCart.myMode & 0xf0) == 0) { myMusicWaveforms->setCrossed(true); myMusicWaveformSizes->setCrossed(true); - mySamplePointer->setCrossed(false); + if (myCart.myBUSSubtype == CartridgeBUS::BUSSubtype::BUS3) + mySamplePointer->setCrossed(false); } else { myMusicWaveforms->setCrossed(false); myMusicWaveformSizes->setCrossed(false); - mySamplePointer->setCrossed(true); + if (myCart.myBUSSubtype == CartridgeBUS::BUSSubtype::BUS3) + mySamplePointer->setCrossed(true); } CartridgeARMWidget::loadConfig(); @@ -394,11 +448,22 @@ string CartridgeBUSWidget::bankState() { ostringstream& buf = buffer(); - static constexpr std::array spot = { - "$FFF5", "$FFF6", "$FFF7", "$FFF8", "$FFF9", "$FFFA", "$FFFB" - }; - buf << "Bank = " << std::dec << myCart.getBank() - << ", hotspot = " << spot[myCart.getBank()]; + if (myCart.myBUSSubtype == CartridgeBUS::BUSSubtype::BUS0) + { + static constexpr std::array spot = { + "$FFF6", "$FFF7", "$FFF8", "$FFF9", "$FFFA", "$FFFB" + }; + buf << "Bank = " << std::dec << myCart.getBank() + << ", hotspot = " << spot[myCart.getBank()]; + } + else + { + static constexpr std::array spot = { + "$FFF5", "$FFF6", "$FFF7", "$FFF8", "$FFF9", "$FFFA", "$FFFB" + }; + buf << "Bank = " << std::dec << myCart.getBank() + << ", hotspot = " << spot[myCart.getBank()]; + } return buf.str(); } diff --git a/src/debugger/gui/CartBUSWidget.hxx b/src/debugger/gui/CartBUSWidget.hxx index 8b324a115..83f9e4f2d 100644 --- a/src/debugger/gui/CartBUSWidget.hxx +++ b/src/debugger/gui/CartBUSWidget.hxx @@ -53,6 +53,7 @@ class CartridgeBUSWidget : public CartridgeARMWidget CartridgeBUS& myCart; PopUpWidget* myBank{nullptr}; + int myDatastreamCount; DataGridWidget* myDatastreamPointers{nullptr}; DataGridWidget* myDatastreamIncrements{nullptr}; @@ -66,7 +67,7 @@ class CartridgeBUSWidget : public CartridgeARMWidget DataGridWidget* mySamplePointer{nullptr}; CheckboxWidget* myBusOverdrive{nullptr}; CheckboxWidget* myDigitalSample{nullptr}; - std::array myDatastreamLabels{nullptr}; + std::array myDatastreamLabels{nullptr}; CartState myOldState; enum { kBankChanged = 'bkCH' }; diff --git a/src/emucore/CartBUS.cxx b/src/emucore/CartBUS.cxx index 4489c6547..e0db84cee 100644 --- a/src/emucore/CartBUS.cxx +++ b/src/emucore/CartBUS.cxx @@ -19,6 +19,8 @@ #ifdef DEBUGGER_SUPPORT #include "Debugger.hxx" + #include "CartBUSWidget.hxx" + #include "CartBUSInfoWidget.hxx" #endif #include "System.hxx" #include "M6532.hxx" @@ -28,18 +30,20 @@ // Location of data within the RAM copy of the BUS Driver. static constexpr int - DSxPTR = 0x06D8, - DSxINC = 0x0720, - DSMAPS = 0x0760, - WAVEFORM = 0x07F4, - DSRAM = 0x0800, - COMMSTREAM = 0x10, JUMPSTREAM = 0x11; static constexpr bool BUS_STUFF_ON(uInt8 mode) { return (mode & 0x0F) == 0; } static constexpr bool DIGITAL_AUDIO_ON(uInt8 mode) { return (mode & 0xF0) == 0; } +static constexpr uInt32 getUInt32(const uInt8* _array, size_t _address) { + return static_cast((_array)[(_address) + 0] + + ((_array)[(_address) + 1] << 8) + + ((_array)[(_address) + 2] << 16) + + ((_array)[(_address) + 3] << 24)); +} + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - CartridgeBUS::CartridgeBUS(const ByteBuffer& image, size_t size, const string& md5, const Settings& settings) @@ -49,33 +53,64 @@ CartridgeBUS::CartridgeBUS(const ByteBuffer& image, size_t size, // Copy the ROM image into my buffer std::copy_n(image.get(), std::min(32_KB, size), myImage.get()); - // Even though the ROM is 32K, only 28K is accessible to the 6507 - createRomAccessArrays(28_KB); - - // Pointer to the program ROM (28K @ 0 byte offset) - // which starts after the 2K BUS Driver and 2K C Code - myProgramImage = myImage.get() + 4_KB; - + // Detect cart version + setupVersion(); + // Pointer to BUS driver in RAM myDriverImage = myRAM.data(); - - // Pointer to the display RAM - myDisplayImage = myRAM.data() + DSRAM; - - // Create Thumbulator ARM emulator + bool devSettings = settings.getBool("dev.settings"); - myThumbEmulator = make_unique( - reinterpret_cast(myImage.get()), - reinterpret_cast(myRAM.data()), - static_cast(32_KB), - 0x00000800, - 0x00000808, - 0x40001FFC, - devSettings ? settings.getBool("dev.thumb.trapfatal") : false, - devSettings ? static_cast( - settings.getFloat("dev.thumb.cyclefactor")) : 1.0, - Thumbulator::ConfigureFor::BUS, - this); + + if (myBUSSubtype == BUSSubtype::BUS0) + { + // BUS0 driver is 3K, so configuration is different from others + createRomAccessArrays(24_KB); + + // Pointer to the program ROM (28K @ 0 byte offset) + myProgramImage = myImage.get() + 3_KB; + + // Pointer to the display RAM + myDisplayImage = myRAM.data() + 0x0C00; + + // Create Thumbulator ARM emulator + myThumbEmulator = make_unique( + reinterpret_cast(myImage.get()), + reinterpret_cast(myRAM.data()), + static_cast(32_KB), + 0x00000C00, + 0x00000C08, + 0x40001FFC, + devSettings ? settings.getBool("dev.thumb.trapfatal") : false, + devSettings ? static_cast( + settings.getFloat("dev.thumb.cyclefactor")) : 1.0, + Thumbulator::ConfigureFor::BUS, + this); + } + else + { + // BUS1+ drivers are 2K + createRomAccessArrays(28_KB); + + // Pointer to the program ROM (28K @ 0 byte offset) + myProgramImage = myImage.get() + 4_KB; + + // Pointer to the display RAM + myDisplayImage = myRAM.data() + 0x0800; + + // Create Thumbulator ARM emulator + myThumbEmulator = make_unique( + reinterpret_cast(myImage.get()), + reinterpret_cast(myRAM.data()), + static_cast(32_KB), + 0x00000800, + 0x00000808, + 0x40001FFC, + devSettings ? settings.getBool("dev.thumb.trapfatal") : false, + devSettings ? static_cast( + settings.getFloat("dev.thumb.cyclefactor")) : 1.0, + Thumbulator::ConfigureFor::BUS, + this); + } this->setInitialState(); @@ -88,10 +123,16 @@ CartridgeBUS::CartridgeBUS(const ByteBuffer& image, size_t size, // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void CartridgeBUS::reset() { - initializeRAM(myRAM.data() + 2_KB, 6_KB); - - // BUS always starts in bank 6 - initializeStartBank(6); + if (myBUSSubtype == BUSSubtype::BUS0) + { + initializeRAM(myRAM.data() + 3_KB, 5_KB); + initializeStartBank(5); // BUS0 always starts in bank 5 + } + else + { + initializeRAM(myRAM.data() + 2_KB, 6_KB); + initializeStartBank(6); // BUS1+ always starts in bank 6 + } // Update cycles to the current system cycles myAudioCycles = myARMCycles = 0; @@ -107,7 +148,10 @@ void CartridgeBUS::reset() void CartridgeBUS::setInitialState() { // Copy initial BUS driver to Harmony RAM - std::copy_n(myImage.get(), 2_KB, myDriverImage); + if (myBUSSubtype == BUSSubtype::BUS0) + std::copy_n(myImage.get(), 3_KB, myDriverImage); + else + std::copy_n(myImage.get(), 2_KB, myDriverImage); myMusicWaveformSize.fill(27); @@ -219,125 +263,232 @@ uInt8 CartridgeBUS::peek(uInt16 address) if(hotspotsLocked()) return peekvalue; - // implement JMP FASTJMP which fetches the destination address from stream 17 - if (myFastJumpActive - && myJMPoperandAddress == address) + if (myBUSSubtype == BUSSubtype::BUS3) { - --myFastJumpActive; - ++myJMPoperandAddress; + // Fast Jump only supported in BUS3 + // implement JMP FASTJMP which fetches the destination address from stream 17 + if (myFastJumpActive + && myJMPoperandAddress == address) + { + --myFastJumpActive; + ++myJMPoperandAddress; - uInt32 pointer = getDatastreamPointer(JUMPSTREAM); - const uInt8 value = myDisplayImage[pointer >> 20]; - pointer += 0x100000; // always increment by 1 - setDatastreamPointer(JUMPSTREAM, pointer); + uInt32 pointer = getDatastreamPointer(JUMPSTREAM); + const uInt8 value = myDisplayImage[pointer >> 20]; + pointer += 0x100000; // always increment by 1 + setDatastreamPointer(JUMPSTREAM, pointer); - return value; + return value; + } + + // test for JMP FASTJUMP where FASTJUMP = $0000 + if (BUS_STUFF_ON(myMode) + && peekvalue == 0x4C + && myProgramImage[myBankOffset + address+1] == 0 + && myProgramImage[myBankOffset + address+2] == 0) + { + myFastJumpActive = 2; // return next two peeks from datastream 17 + myJMPoperandAddress = address + 1; + return peekvalue; + } + + myJMPoperandAddress = 0; } - // test for JMP FASTJUMP where FASTJUMP = $0000 - if (BUS_STUFF_ON(myMode) - && peekvalue == 0x4C - && myProgramImage[myBankOffset + address+1] == 0 - && myProgramImage[myBankOffset + address+2] == 0) - { - myFastJumpActive = 2; // return next two peeks from datastream 17 - myJMPoperandAddress = address + 1; - return peekvalue; - } - - myJMPoperandAddress = 0; - // save the STY's zero page address if (BUS_STUFF_ON(myMode) && mySTYZeroPageAddress == address) myBusOverdriveAddress = peekvalue; mySTYZeroPageAddress = 0; - - switch(address) + + if (address < 0x20 && + (myBUSSubtype == BUSSubtype::BUS1 || myBUSSubtype == BUSSubtype::BUS2)) { - case 0xFEE: // AMPLITUDE - // Update the music data fetchers (counter & flag) - updateMusicModeDataFetchers(); - - if (DIGITAL_AUDIO_ON(myMode)) + 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 { - // retrieve packed sample (max size is 2K, or 4K of unpacked data) - const uInt32 sampleaddress = getSample() + (myMusicCounters[0] >> 21); - - // get sample value from ROM or RAM - if (sampleaddress < 0x8000) - peekvalue = myImage[sampleaddress]; - else if (sampleaddress >= 0x40000000 && sampleaddress < 0x40002000) // check for RAM - peekvalue = myRAM[sampleaddress - 0x40000000]; - else - peekvalue = 0; - - // make sure current volume value is in the lower nybble - if ((myMusicCounters[0] & (1<<20)) == 0) - peekvalue >>= 4; - peekvalue &= 0x0f; + result = readFromDatastream(index); + break; } - else + case 0x01: // misc read registers { - // using myDisplayImage[] instead of myProgramImage[] because waveforms - // can be modified during runtime. - const uInt32 i = - myDisplayImage[(getWaveform(0) ) + (myMusicCounters[0] >> myMusicWaveformSize[0])] + + 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])]; - - peekvalue = static_cast(i); + + result = uInt8(i); + break; + } + break; } - break; + } + + return result; + } + else if (address >= 0xFEE && address <= 0xFF3 && myBUSSubtype == BUSSubtype::BUS3) + { + switch(address) + { + case 0xFEE: // AMPLITUDE + // Update the music data fetchers (counter & flag) + updateMusicModeDataFetchers(); - case 0xFEF: // DSREAD - peekvalue = readFromDatastream(COMMSTREAM); - break; + if (DIGITAL_AUDIO_ON(myMode)) + { + // retrieve packed sample (max size is 2K, or 4K of unpacked data) + const uInt32 sampleaddress = getSample() + (myMusicCounters[0] >> 21); - case 0xFF0: // DSWRITE - case 0xFF1: // DSPTR - case 0xFF2: // SETMODE - case 0xFF3: // CALLFN - // these are write-only - break; + // get sample value from ROM or RAM + if (sampleaddress < 0x8000) + peekvalue = myImage[sampleaddress]; + else if (sampleaddress >= 0x40000000 && sampleaddress < 0x40002000) // check for RAM + peekvalue = myRAM[sampleaddress - 0x40000000]; + else + peekvalue = 0; - case 0xFF5: - // Set the current bank to the first 4k bank - bank(0); - break; + // make sure current volume value is in the lower nybble + if ((myMusicCounters[0] & (1<<20)) == 0) + peekvalue >>= 4; + peekvalue &= 0x0f; + } + else + { + // using myDisplayImage[] instead of myProgramImage[] because waveforms + // can be modified during runtime. + const uInt32 i = + myDisplayImage[(getWaveform(0) ) + (myMusicCounters[0] >> myMusicWaveformSize[0])] + + myDisplayImage[(getWaveform(1) ) + (myMusicCounters[1] >> myMusicWaveformSize[1])] + + myDisplayImage[(getWaveform(2) ) + (myMusicCounters[2] >> myMusicWaveformSize[2])]; - case 0x0FF6: - // Set the current bank to the second 4k bank - bank(1); - break; + peekvalue = static_cast(i); + } + break; - case 0x0FF7: - // Set the current bank to the third 4k bank - bank(2); - break; + case 0xFEF: // DSREAD + peekvalue = readFromDatastream(COMMSTREAM); + break; - case 0x0FF8: - // Set the current bank to the fourth 4k bank - bank(3); - break; + case 0xFF0: // DSWRITE + case 0xFF1: // DSPTR + case 0xFF2: // SETMODE + case 0xFF3: // CALLFN + // these are write-only + break; + } + } + else if (myBUSSubtype == BUSSubtype::BUS0) + { + switch(address) + { + case 0x00 ... 0x0f: + peekvalue = readFromDatastream(address); + break; + + case 0xFF6: + // Set the current bank to the first 4k bank + bank(0); + break; - case 0x0FF9: - // Set the current bank to the fifth 4k bank - bank(4); - break; + case 0x0FF7: + // Set the current bank to the second 4k bank + bank(1); + break; - case 0x0FFA: - // Set the current bank to the sixth 4k bank - bank(5); - break; + case 0x0FF8: + // Set the current bank to the third 4k bank + bank(2); + break; - case 0x0FFB: - // Set the current bank to the last 4k bank - bank(6); - break; + case 0x0FF9: + // Set the current bank to the fourth 4k bank + bank(3); + break; - default: - break; + case 0x0FFA: + // Set the current bank to the fifth 4k bank + bank(4); + break; + + case 0x0FFB: + // Set the current bank to the sixth 4k bank + bank(5); + break; + + default: + break; + } +// return result; + } + else + { + 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 @@ -374,73 +525,237 @@ bool CartridgeBUS::poke(uInt16 address, uInt8 value) address &= 0x0FFF; - switch(address) + if (myBUSSubtype == BUSSubtype::BUS0) { - case 0xFEE: // AMPLITUDE - case 0xFEF: // DSREAD - // these are read-only - break; + uInt32 index = address & 0x0f; + uInt32 pointer, increment; + + switch(address) + { + case 0x10: // DS0WRITE + case 0x11: // DS1WRITE + case 0x12: // DS2WRITE + case 0x13: // 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 0x1B: // CALLFN + callFunction(value); + break; + + case 0x1c: // BUSSTUFF + if (value==0) + myMode = 0; // lower nybble 0 = STUFFON in BUS3 + else + myMode = 0x0f; // lower nybble f = STUFFOFF in BUS3 + break; + + case 0x20 ... 0x2f: + // Pointers are stored as: + // PPPFF--- + // + // P = Pointer + // F = Fractional + pointer = getDatastreamPointer(index); + pointer <<=8; + pointer &= 0xf0000000; + pointer |= (value << 20); + setDatastreamPointer(index, pointer); + break; + + case 0x30 ... 0x3f: // DSxINC + // Increments are stored as + // ----IIFF + // + // I = Increment + // F = Fractional + increment = getDatastreamIncrement(index); + index <<= 8; + index |= value; + index &= 0xffff; + setDatastreamIncrement(index, increment); + break; + + + case 0xFF6: + // Set the current bank to the first 4k bank + bank(0); + break; + + case 0x0FF7: + // Set the current bank to the second 4k bank + bank(1); + break; + + case 0x0FF8: + // Set the current bank to the third 4k bank + bank(2); + break; + + case 0x0FF9: + // Set the current bank to the fourth 4k bank + bank(3); + break; + + case 0x0FFA: + // Set the current bank to the fifth 4k bank + bank(4); + break; + + case 0x0FFB: + // Set the current bank to the sixth 4k bank + bank(5); + break; + + default: + break; + } + } + else if (address >= 0xFF5) + { + // bankswitch hotspots same for BUS1+ versions + 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; + } + } + else if (myBUSSubtype == BUSSubtype::BUS3) + { + switch(address) + { + case 0xFEE: // AMPLITUDE + case 0xFEF: // DSREAD + // these are read-only + break; - case 0xFF0: // DSWRITE - pointer = getDatastreamPointer(COMMSTREAM); - myDisplayImage[ pointer >> 20 ] = value; - pointer += 0x100000; // always increment by 1 when writing - setDatastreamPointer(COMMSTREAM, pointer); - break; + case 0xFF0: // DSWRITE + pointer = getDatastreamPointer(COMMSTREAM); + myDisplayImage[ pointer >> 20 ] = value; + pointer += 0x100000; // always increment by 1 when writing + setDatastreamPointer(COMMSTREAM, pointer); + break; - case 0xFF1: // DSPTR - pointer = getDatastreamPointer(COMMSTREAM); - pointer <<=8; - pointer &= 0xf0000000; - pointer |= (value << 20); - setDatastreamPointer(COMMSTREAM, pointer); - break; + case 0xFF1: // DSPTR + pointer = getDatastreamPointer(COMMSTREAM); + pointer <<=8; + pointer &= 0xf0000000; + pointer |= (value << 20); + setDatastreamPointer(COMMSTREAM, pointer); + break; - case 0xFF2: // SETMODE - myMode = value; - break; + case 0xFF2: // SETMODE + myMode = value; + break; - case 0xFF3: // CALLFN - callFunction(value); - break; + case 0xFF3: // CALLFN + callFunction(value); + 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; + default: + break; + } + } + else // BUS1 and BUS2 + { + 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 + if (value==0) + myMode = 0; // lower nybble 0 = STUFFON in BUS3 + else + myMode = 0x0f; // lower nybble f = STUFFOFF in BUS3 + break; + + case 0x0A: // 0x1A CALLFUNCTION + callFunction(value); + break; + } + } } } @@ -663,42 +978,56 @@ bool CartridgeBUS::load(Serializer& in) // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - uInt32 CartridgeBUS::getDatastreamPointer(uInt8 index) const { -// index &= 0x0f; + const uInt16 address = myDatastreamBase + index * 4; - return myRAM[DSxPTR + index*4 + 0] + // low byte - (myRAM[DSxPTR + index*4 + 1] << 8) + - (myRAM[DSxPTR + index*4 + 2] << 16) + - (myRAM[DSxPTR + index*4 + 3] << 24) ; // high byte + return myRAM[address + 0] + // low byte + (myRAM[address + 1] << 8) + + (myRAM[address + 2] << 16) + + (myRAM[address + 3] << 24) ; // high byte } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void CartridgeBUS::setDatastreamPointer(uInt8 index, uInt32 value) { -// index &= 0x0f; - myRAM[DSxPTR + index*4 + 0] = value & 0xff; // low byte - myRAM[DSxPTR + index*4 + 1] = (value >> 8) & 0xff; - myRAM[DSxPTR + index*4 + 2] = (value >> 16) & 0xff; - myRAM[DSxPTR + index*4 + 3] = (value >> 24) & 0xff; // high byte + const uInt16 address = myDatastreamBase + index * 4; + + myRAM[address + 0] = value & 0xff; // low byte + myRAM[address + 1] = (value >> 8) & 0xff; + myRAM[address + 2] = (value >> 16) & 0xff; + myRAM[address + 3] = (value >> 24) & 0xff; // high byte } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - uInt32 CartridgeBUS::getDatastreamIncrement(uInt8 index) const { -// index &= 0x0f; - return myRAM[DSxINC + index*4 + 0] + // low byte - (myRAM[DSxINC + index*4 + 1] << 8) + - (myRAM[DSxINC + index*4 + 2] << 16) + - (myRAM[DSxINC + index*4 + 3] << 24) ; // high byte + const uInt16 address = myDatastreamIncrementBase + index * 4; + + return myRAM[address + 0] + // low byte + (myRAM[address + 1] << 8) + + (myRAM[address + 2] << 16) + + (myRAM[address + 3] << 24) ; // high byte +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void CartridgeBUS::setDatastreamIncrement(uInt8 index, uInt32 value) +{ + const uInt16 address = myDatastreamIncrementBase + index * 4; + + myRAM[address + 0] = value & 0xff; // low byte + myRAM[address + 1] = (value >> 8) & 0xff; + myRAM[address + 2] = (value >> 16) & 0xff; + myRAM[address + 3] = (value >> 24) & 0xff; // high byte } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - uInt32 CartridgeBUS::getAddressMap(uInt8 index) const { - // index &= 0x0f; - return myRAM[DSMAPS + index*4 + 0] + // low byte - (myRAM[DSMAPS + index*4 + 1] << 8) + - (myRAM[DSMAPS + index*4 + 2] << 16) + - (myRAM[DSMAPS + index*4 + 3] << 24) ; // high byte + const uInt16 address = myDatastreamMapBase + index * 4; + + return myRAM[address + 0] + // low byte + (myRAM[address + 1] << 8) + + (myRAM[address + 2] << 16) + + (myRAM[address + 3] << 24) ; // high byte } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -716,12 +1045,12 @@ uInt32 CartridgeBUS::getWaveform(uInt8 index) const // (myBUSRAM[WAVEFORM + index*4 + 3] << 24) - // high byte // 0x40000800; - uInt32 result; + const uInt16 address = myWaveformBase + index * 4; - result = myRAM[WAVEFORM + index*4 + 0] + // low byte - (myRAM[WAVEFORM + index*4 + 1] << 8) + - (myRAM[WAVEFORM + index*4 + 2] << 16) + - (myRAM[WAVEFORM + index*4 + 3] << 24); // high byte + uInt32 result = myRAM[address + 0] + // low byte + (myRAM[address + 1] << 8) + + (myRAM[address + 2] << 16) + + (myRAM[address + 3] << 24); // high byte result -= 0x40000800; @@ -734,13 +1063,12 @@ uInt32 CartridgeBUS::getWaveform(uInt8 index) const // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - uInt32 CartridgeBUS::getSample() { - uInt32 result; - - result = myRAM[WAVEFORM + 0] + // low byte - (myRAM[WAVEFORM + 1] << 8) + - (myRAM[WAVEFORM + 2] << 16) + - (myRAM[WAVEFORM + 3] << 24); // high byte + const uInt16 address = myWaveformBase; + const uInt32 result = myRAM[address + 0] + // low byte + (myRAM[address + 1] << 8) + + (myRAM[address + 2] << 16) + + (myRAM[address + 3] << 24); // high byte return result; } @@ -753,11 +1081,13 @@ uInt32 CartridgeBUS::getWaveformSize(uInt8 index) const // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void CartridgeBUS::setAddressMap(uInt8 index, uInt32 value) { - // index &= 0x0f; - myRAM[DSMAPS + index*4 + 0] = value & 0xff; // low byte - myRAM[DSMAPS + index*4 + 1] = (value >> 8) & 0xff; - myRAM[DSMAPS + index*4 + 2] = (value >> 16) & 0xff; - myRAM[DSMAPS + index*4 + 3] = (value >> 24) & 0xff; // high byte + const uInt16 address = myDatastreamMapBase + index * 4; + + myRAM[address + 0] = value & 0xff; // low byte + myRAM[address + 1] = (value >> 8) & 0xff; + myRAM[address + 2] = (value >> 16) & 0xff; + myRAM[address + 3] = (value >> 24) & 0xff; // high byte + } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -780,3 +1110,99 @@ uInt8 CartridgeBUS::readFromDatastream(uInt8 index) setDatastreamPointer(index, pointer); return value; } + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// params: +// - searchValue: uInt32 value to search for; assumes it is on a DWORD boundary +// +// returns: +// - offset in image where value was found +// - 0xFFFFFFFF if not found +uInt32 CartridgeBUS::scanBUSDriver(uInt32 searchValue) +{ + // original BUS driver is 3K in size. Later BUS drivers are 2K in size. + for (int i = 0; i < 3072; i += 4) + if (getUInt32(myImage.get(), i) == searchValue) + return i; + + return 0xFFFFFFFF; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void CartridgeBUS::setupVersion() +{ + // 3 versions of the BUS driver have been found. Location of the BUS + // strings are in a different location for each. + + + // get offset of BUS ID + uInt32 busOffset = scanBUSDriver(0x00535542); + + switch(busOffset) + { + case 0x7f4: // draconian_20161102.bin + myBUSSubtype = BUSSubtype::BUS1; + myDatastreamBase = 0x06E0; + myDatastreamIncrementBase = 0x0720; + myDatastreamMapBase = 0x0760; + myWaveformBase = 0x07F4; + break; + + case 0x778: // 128bus_20170120.bin, 128chronocolour_20170101.bin, parrot_20161231_NTSC.bin + myBUSSubtype = BUSSubtype::BUS2; + myDatastreamBase = 0x06E0; + myDatastreamIncrementBase = 0x0720; + myDatastreamMapBase = 0x0760; + myWaveformBase = 0x07F4; + break; + + case 0x770: // rpg_20170616_NTSC.bin newest + myBUSSubtype = BUSSubtype::BUS3; + myDatastreamBase = 0x06D8; + myDatastreamIncrementBase = 0x0720; + myDatastreamMapBase = 0x0760; + myWaveformBase = 0x07F4; + break; + + default: // case 0xbf8: // original BUS driver was 3K in size + myBUSSubtype = BUSSubtype::BUS0; +// unsupported + myDatastreamBase = 0x0AE0; + myDatastreamIncrementBase = 0x0B20; + myDatastreamMapBase = 0x0B64; +// myWaveformBase = 0x07F4; + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +string CartridgeBUS::name() const +{ + switch(myBUSSubtype) + { + case BUSSubtype::BUS0: + return "CartridgeBUS0"; + case BUSSubtype::BUS1: + return "CartridgeBUS1"; + case BUSSubtype::BUS2: + return "CartridgeBUS2"; + case BUSSubtype::BUS3: + return "CartridgeBUS3"; + default: + return "Unsupported BUS"; + } +} + +#ifdef DEBUGGER_SUPPORT +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + CartDebugWidget* CartridgeBUS::debugWidget(GuiObject* boss, const GUI::Font& lfont, + const GUI::Font& nfont, int x, int y, int w, int h) + { + return new CartridgeBUSWidget(boss, lfont, nfont, x, y, w, h, *this); + } + + CartDebugWidget* CartridgeBUS::infoWidget(GuiObject* boss, const GUI::Font& lfont, + const GUI::Font& nfont, int x, int y, int w, int h) + { + return new CartridgeBUSInfoWidget(boss, lfont, nfont, x, y, w, h, *this); + } +#endif diff --git a/src/emucore/CartBUS.hxx b/src/emucore/CartBUS.hxx index 917b105b9..ceed187e4 100644 --- a/src/emucore/CartBUS.hxx +++ b/src/emucore/CartBUS.hxx @@ -20,10 +20,6 @@ class System; -#ifdef DEBUGGER_SUPPORT - #include "CartBUSWidget.hxx" -#endif - #include "bspf.hxx" #include "CartARM.hxx" @@ -43,7 +39,16 @@ class System; class CartridgeBUS : public CartridgeARM { friend class CartridgeBUSWidget; + friend class CartridgeBUSInfoWidget; friend class CartridgeRamBUSWidget; + + enum class BUSSubtype { + BUS0, // very old demos when BUS was in flux, not supported in Stella + BUS1, // draconian_20161102.bin + BUS2, // 128bus_20170120.bin, 128chronocolour_20170101.bin, parrot_20161231_NTSC.bin + BUS3 // rpg_20170616_NTSC.bin + }; + public: /** @@ -132,7 +137,7 @@ class CartridgeBUS : public CartridgeARM @return The name of the object */ - string name() const override { return "CartridgeBUS"; } + string name() const override; uInt8 busOverdrive(uInt16 address); @@ -162,12 +167,13 @@ class CartridgeBUS : public CartridgeARM 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 + const GUI::Font& nfont, int x, int y, int w, int h) override; + + CartDebugWidget* infoWidget(GuiObject* boss, const GUI::Font& lfont, + const GUI::Font& nfont, int x, int y, int w, int h) override; + #endif + public: /** Get the byte at the specified address. @@ -213,7 +219,8 @@ class CartridgeBUS : public CartridgeARM void setDatastreamPointer(uInt8 index, uInt32 value); uInt32 getDatastreamIncrement(uInt8 index) const; - + void setDatastreamIncrement(uInt8 index, uInt32 value); + uInt32 getAddressMap(uInt8 index) const; void setAddressMap(uInt8 index, uInt32 value); @@ -222,6 +229,8 @@ class CartridgeBUS : public CartridgeARM uInt32 getWaveform(uInt8 index) const; uInt32 getWaveformSize(uInt8 index) const; uInt32 getSample(); + void setupVersion(); + uInt32 scanBUSDriver(uInt32 value); private: // The 32K ROM image of the cartridge @@ -260,6 +269,18 @@ class CartridgeBUS : public CartridgeARM // ARM cycle count from when the last callFunction() occurred uInt64 myARMCycles{0}; + + // Pointer to the array of datastream pointers + uInt16 myDatastreamBase{0}; // was DSxPTR + + // Pointer to the array of datastream increments + uInt16 myDatastreamIncrementBase{0}; // was DSxINC + + // Pointer to the array of datastream maps + uInt16 myDatastreamMapBase{0}; // was DSMAPS + + // Pointer to the beginning of the waveform data block + uInt16 myWaveformBase{0}; // was WAVEFORM // The music mode counters std::array myMusicCounters{0}; @@ -281,6 +302,9 @@ class CartridgeBUS : public CartridgeARM uInt8 myMode{0}; uInt8 myFastJumpActive{false}; + + // BUS subtype + BUSSubtype myBUSSubtype{BUSSubtype::BUS1}; private: // Following constructors and assignment operators not supported diff --git a/src/macos/stella.xcodeproj/project.pbxproj b/src/macos/stella.xcodeproj/project.pbxproj index 7da45a054..ac72a6491 100644 --- a/src/macos/stella.xcodeproj/project.pbxproj +++ b/src/macos/stella.xcodeproj/project.pbxproj @@ -195,6 +195,8 @@ 2DEFB40C09C3386F00754289 /* Cart.icns in Resources */ = {isa = PBXBuildFile; fileRef = 2DEFB40B09C3386F00754289 /* Cart.icns */; }; 55FE2A321EE4856B00078ADE /* SDL2.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = DCDAF4D818CA9AAB00D3865D /* SDL2.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 55FE2A501EE4880500078ADE /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 55FE2A3C1EE487CA00078ADE /* InfoPlist.strings */; }; + CFB521D72853A2590083B9CE /* CartBUSInfoWidget.cxx in Sources */ = {isa = PBXBuildFile; fileRef = CFB521D52853A2590083B9CE /* CartBUSInfoWidget.cxx */; }; + CFB521D82853A2590083B9CE /* CartBUSInfoWidget.hxx in Headers */ = {isa = PBXBuildFile; fileRef = CFB521D62853A2590083B9CE /* CartBUSInfoWidget.hxx */; }; 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 */; }; @@ -1021,6 +1023,8 @@ 2DF971D70892CEA400F64D23 /* DebuggerSystem.hxx */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; path = DebuggerSystem.hxx; sourceTree = ""; }; 2DF971DF0892CEA400F64D23 /* Expression.hxx */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.h; path = Expression.hxx; sourceTree = ""; }; B2F367C504C7ADC700A80002 /* SDLMain.nib */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; path = SDLMain.nib; sourceTree = ""; }; + CFB521D52853A2590083B9CE /* CartBUSInfoWidget.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CartBUSInfoWidget.cxx; sourceTree = ""; }; + CFB521D62853A2590083B9CE /* CartBUSInfoWidget.hxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = CartBUSInfoWidget.hxx; sourceTree = ""; }; CFE3F6071E84A9A200A8204E /* CartBUSWidget.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CartBUSWidget.cxx; sourceTree = ""; }; CFE3F6081E84A9A200A8204E /* CartBUSWidget.hxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = CartBUSWidget.hxx; sourceTree = ""; }; CFE3F6091E84A9A200A8204E /* CartCDFWidget.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CartCDFWidget.cxx; sourceTree = ""; }; @@ -1751,6 +1755,8 @@ DCAACB07188D636F00A4D282 /* CartBFSCWidget.hxx */, DCAACB08188D636F00A4D282 /* CartBFWidget.cxx */, DCAACB09188D636F00A4D282 /* CartBFWidget.hxx */, + CFB521D52853A2590083B9CE /* CartBUSInfoWidget.cxx */, + CFB521D62853A2590083B9CE /* CartBUSInfoWidget.hxx */, CFE3F6071E84A9A200A8204E /* CartBUSWidget.cxx */, CFE3F6081E84A9A200A8204E /* CartBUSWidget.hxx */, E0A755772244294600101889 /* CartCDFInfoWidget.cxx */, @@ -2734,6 +2740,7 @@ DC816CF72572F92A00FBCCDA /* json_lib.hxx in Headers */, 2D91741B09BA90380026E9FF /* OSystem.hxx in Headers */, DC6A18F919B3E65500DEB242 /* CartMDMWidget.hxx in Headers */, + CFB521D82853A2590083B9CE /* CartBUSInfoWidget.hxx in Headers */, 2D91741F09BA90380026E9FF /* AboutBox.h in Headers */, DC84FC572677C64200E60ADE /* CartARMWidget.hxx in Headers */, 2D91742009BA90380026E9FF /* ConsoleFont.hxx in Headers */, @@ -3420,6 +3427,7 @@ DCA233B423BAB1300032ABF3 /* Lightgun.cxx in Sources */, DCAAE5DE1715887B0080BB82 /* CartEFSCWidget.cxx in Sources */, DCAAE5E01715887B0080BB82 /* CartEFWidget.cxx in Sources */, + CFB521D72853A2590083B9CE /* CartBUSInfoWidget.cxx in Sources */, DCAAE5E21715887B0080BB82 /* CartF0Widget.cxx in Sources */, DCB2ECAF1F0AECA3009738A6 /* CartDetector.cxx in Sources */, DCAAE5E41715887B0080BB82 /* CartF4SCWidget.cxx in Sources */, diff --git a/test/roms/bankswitching/BUS/128bus_20160903.bin b/test/roms/bankswitching/BUS/128bus_20160903.bin new file mode 100644 index 000000000..36609105d Binary files /dev/null and b/test/roms/bankswitching/BUS/128bus_20160903.bin differ diff --git a/test/roms/bankswitching/BUS/parrot_bus_NTSC.bin b/test/roms/bankswitching/BUS/parrot_bus_NTSC.bin new file mode 100644 index 000000000..1f053ef86 Binary files /dev/null and b/test/roms/bankswitching/BUS/parrot_bus_NTSC.bin differ diff --git a/test/roms/bankswitching/BUS/rpg_20161020.bin b/test/roms/bankswitching/BUS/rpg_20161020.bin new file mode 100644 index 000000000..748a4d650 Binary files /dev/null and b/test/roms/bankswitching/BUS/rpg_20161020.bin differ diff --git a/test/roms/bankswitching/BUS/test_bus_NTSC.bin b/test/roms/bankswitching/BUS/test_bus_NTSC.bin new file mode 100644 index 000000000..ffd151dee Binary files /dev/null and b/test/roms/bankswitching/BUS/test_bus_NTSC.bin differ