diff --git a/src/common/main.cxx b/src/common/main.cxx index f5d887eb4..3caca17bb 100644 --- a/src/common/main.cxx +++ b/src/common/main.cxx @@ -282,7 +282,7 @@ int main(int ac, char* av[]) { Debugger& dbg = theOSystem->debugger(); uInt16 bp = uInt16(dbg.stringToValue(localOpts["break"].toString())); - dbg.setBreakPoint(bp, true); + dbg.setBreakPoint(bp); } #endif } diff --git a/src/debugger/Debugger.cxx b/src/debugger/Debugger.cxx index 7456dd0e4..364129e9b 100644 --- a/src/debugger/Debugger.cxx +++ b/src/debugger/Debugger.cxx @@ -49,13 +49,14 @@ #include "RomWidget.hxx" #include "Expression.hxx" -#include "PackedBitArray.hxx" #include "YaccParser.hxx" #include "TIA.hxx" #include "Debugger.hxx" #include "DispatchResult.hxx" +using Common::Base; + Debugger* Debugger::myStaticDebugger = nullptr; // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -190,18 +191,6 @@ string Debugger::autoExec(StringList* history) return buf.str(); } -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -PackedBitArray& Debugger::breakPoints() const -{ - return mySystem.m6502().breakPoints(); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -PackedBitArray& Debugger::breakPointFlags() const -{ - return mySystem.m6502().breakPointFlags(); -} - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - TrapArray& Debugger::readTraps() const { @@ -336,12 +325,11 @@ int Debugger::trace() int targetPC = myCpuDebug->pc() + 3; // return address // set temporary breakpoint at target PC (if not existing already) - breakPoints().initialize(); - if(!breakPoints().isSet(targetPC)) + Int8 bank = myCartDebug->getBank(); + if(checkBreakPoint(targetPC, bank) == NOT_FOUND) { - breakPoints().set(targetPC); - breakPointFlags().initialize(); - breakPointFlags().set(targetPC); + // add temporary breakpoint and remove later + setBreakPoint(targetPC, bank, true, true); } unlockSystem(); @@ -357,26 +345,59 @@ int Debugger::trace() } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void Debugger::toggleBreakPoint(uInt16 bp) +bool Debugger::setBreakPoint(uInt16 addr, Int8 bank, bool set, bool oneShot) { - breakPoints().initialize(); - breakPoints().toggle(bp); - breakPointFlags().initialize(); + Int32 id = checkBreakPoint(addr, bank); + + if(set) + { + if(id != NOT_FOUND) + return false; + + mySystem.m6502().addCondBreak(YaccParser::getResult(), getCondition(addr, bank), oneShot); + } + else + { + if(id == NOT_FOUND) + return false; + + m6502().delCondBreak(id); + } + return true; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void Debugger::setBreakPoint(uInt16 bp, bool set) +Int32 Debugger::checkBreakPoint(uInt16 addr, Int8 bank) { - breakPoints().initialize(); - if(set) breakPoints().set(bp); - else breakPoints().clear(bp); - breakPointFlags().initialize(); + string condition = getCondition(addr, bank); + + for(uInt32 i = 0; i < m6502().getCondBreakNames().size(); ++i) + if(condition == m6502().getCondBreakNames()[i]) + return i; + + return NOT_FOUND; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool Debugger::breakPoint(uInt16 bp) +string Debugger::getCondition(uInt16 addr, Int8 bank) { - return breakPoints().isSet(bp); + stringstream condition; + + condition << "(pc == " << Base::HEX4 << addr << ")"; + if(bank != ANY_BANK && myCartDebug->bankCount() > 1) + condition << " && (_bank == #" << int(bank) << ")"; + + YaccParser::parse(condition.str()); + + return condition.str(); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +bool Debugger::toggleBreakPoint(uInt16 addr, Int8 bank) +{ + setBreakPoint(addr, bank, checkBreakPoint(addr, bank) == NOT_FOUND); + + return checkBreakPoint(addr, bank) != NOT_FOUND; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -604,12 +625,6 @@ uInt16 Debugger::unwindStates(const uInt16 numStates, string& message) return windStates(numStates, true, message); } -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void Debugger::clearAllBreakPoints() -{ - breakPoints().clearAll(); -} - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void Debugger::clearAllTraps() { @@ -688,7 +703,6 @@ void Debugger::setQuitState() // execute one instruction on quit. If we're // sitting at a breakpoint/trap, this will get us past it. // Somehow this feels like a hack to me, but I don't know why - // if(breakPoints().isSet(myCpuDebug->pc())) mySystem.m6502().execute(1); } diff --git a/src/debugger/Debugger.hxx b/src/debugger/Debugger.hxx index c71311ddf..cc1e2ccd7 100644 --- a/src/debugger/Debugger.hxx +++ b/src/debugger/Debugger.hxx @@ -27,7 +27,6 @@ class TiaZoomWidget; class EditTextWidget; class RomWidget; class Expression; -class PackedBitArray; class TrapArray; class PromptWidget; class ButtonWidget; @@ -74,6 +73,9 @@ class Debugger : public DialogContainer Debugger(OSystem& osystem, Console& console); virtual ~Debugger(); + private: + static const Int8 ANY_BANK = -1; + public: /** Initialize the debugger dialog container. @@ -149,11 +151,24 @@ class Debugger : public DialogContainer RomWidget& rom() const { return myDialog->rom(); } TiaOutputWidget& tiaOutput() const { return myDialog->tiaOutput(); } - PackedBitArray& breakPoints() const; - PackedBitArray& breakPointFlags() const; TrapArray& readTraps() const; TrapArray& writeTraps() const; + /** + Sets or clears a breakpoint. + + Returns true if successfully set or cleared + */ + bool setBreakPoint(uInt16 addr, Int8 bank = ANY_BANK, + bool set = true, bool oneShot = false); + + /** + Checks for a breakpoint. + + Returns -1 if not existing, else the Id + */ + Int32 checkBreakPoint(uInt16 addr, Int8 bank); + /** Run the debugger command and return the result. */ @@ -224,7 +239,6 @@ class Debugger : public DialogContainer int getAccessFlags(uInt16 addr) const; void setAccessFlags(uInt16 addr, uInt8 flags); - void setBreakPoint(uInt16 bp, bool set); uInt32 getBaseAddress(uInt32 addr, bool read); bool patchROM(uInt16 addr, uInt8 value); @@ -250,6 +264,8 @@ class Debugger : public DialogContainer */ Dialog* baseDialog() override { return myDialog; } + static const Int32 NOT_FOUND = -1; + private: /** Save state of each debugger subsystem and, by default, mark all @@ -279,9 +295,9 @@ class Debugger : public DialogContainer uInt16 rewindStates(const uInt16 numStates, string& message); uInt16 unwindStates(const uInt16 numStates, string& message); - void toggleBreakPoint(uInt16 bp); + bool toggleBreakPoint(uInt16 addr, Int8 bank); + string getCondition(uInt16 addr, Int8 bank); - bool breakPoint(uInt16 bp); void addReadTrap(uInt16 t); void addWriteTrap(uInt16 t); void addTrap(uInt16 t); @@ -296,7 +312,6 @@ class Debugger : public DialogContainer string setRAM(IntArray& args); void reset(); - void clearAllBreakPoints(); void saveState(int state); void saveAllStates(); diff --git a/src/debugger/DebuggerParser.cxx b/src/debugger/DebuggerParser.cxx index 05a0e29e3..698105ac2 100644 --- a/src/debugger/DebuggerParser.cxx +++ b/src/debugger/DebuggerParser.cxx @@ -34,7 +34,6 @@ #include "PromptWidget.hxx" #include "RomWidget.hxx" #include "ProgressDialog.hxx" -#include "PackedBitArray.hxx" #include "TimerManager.hxx" #include "Vec.hxx" @@ -648,10 +647,6 @@ string DebuggerParser::saveScriptFile(string file) for(const auto& w: myWatches) out << "watch " << w << endl; - for(uInt32 i = 0; i < 0x10000; ++i) - if(debugger.breakPoint(i)) - out << "break " << Base::toString(i) << endl; - StringList conds = debugger.m6502().getCondBreakNames(); for(const auto& cond : conds) out << "breakif {" << cond << "}" << endl; @@ -730,20 +725,35 @@ void DebuggerParser::executeBase() // "break" void DebuggerParser::executeBreak() { - uInt16 bp; + uInt16 addr; + Int8 bank; + if(argCount == 0) - bp = debugger.cpuDebug().pc(); + addr = debugger.cpuDebug().pc(); else - bp = args[0]; - debugger.toggleBreakPoint(bp); + addr = args[0]; + + if(argCount < 2) + bank = debugger.cartDebug().getBank(); + else + { + bank = args[1]; + if(bank >= debugger.cartDebug().bankCount()) + { + commandResult << red("invalid bank"); + return; + } + } + + bool set = debugger.toggleBreakPoint(addr, bank); debugger.rom().invalidate(); - if(debugger.breakPoint(bp)) + if(set) commandResult << "set"; else commandResult << "cleared"; - commandResult << " breakpoint at " << Base::toString(bp); + commandResult << " breakpoint at " << Base::toString(addr) << " in bank " << int(bank); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -764,7 +774,7 @@ void DebuggerParser::executeBreakif() } } uInt32 ret = debugger.m6502().addCondBreak( - YaccParser::getResult(), argStrings[0] ); + YaccParser::getResult(), argStrings[0]); commandResult << "added breakif " << Base::toString(ret); } else @@ -810,7 +820,6 @@ void DebuggerParser::executeCheat() // "clearbreaks" void DebuggerParser::executeClearbreaks() { - debugger.clearAllBreakPoints(); debugger.m6502().clearCondBreaks(); commandResult << "all breakpoints cleared"; } @@ -1403,21 +1412,9 @@ void DebuggerParser::executeJump() // "listbreaks" void DebuggerParser::executeListbreaks() { - ostringstream buf; int count = 0; - - for(uInt32 i = 0; i <= 0xffff; ++i) - { - if(debugger.breakPoints().isSet(i)) - { - buf << debugger.cartDebug().getLabel(i, true, 4) << " "; - if(! (++count % 8) ) buf << endl; - } - } - if(count) - commandResult << "breaks:" << endl << buf.str(); - StringList conds = debugger.m6502().getCondBreakNames(); + if(conds.size() > 0) { if(count) @@ -2256,12 +2253,12 @@ DebuggerParser::Command DebuggerParser::commands[NumCommands] = { { "break", - "Set/clear breakpoint at
", - "Command is a toggle, default is current PC\nValid address is 0 - ffff\n" - "Example: break, break f000", + "Set/clear breakpoint at
and ", + "Command is a toggle, default is current PC and bank\nValid address is 0 - ffff\n" + "Example: break, break f000, break f000 0", false, true, - { Parameters::ARG_WORD, Parameters::ARG_END_ARGS }, + { Parameters::ARG_WORD, Parameters::ARG_BYTE, Parameters::ARG_END_ARGS }, std::mem_fn(&DebuggerParser::executeBreak) }, diff --git a/src/debugger/PackedBitArray.hxx b/src/debugger/PackedBitArray.hxx deleted file mode 100644 index 9804067e6..000000000 --- a/src/debugger/PackedBitArray.hxx +++ /dev/null @@ -1,57 +0,0 @@ -//============================================================================ -// -// 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-2019 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 PACKED_BIT_ARRAY_HXX -#define PACKED_BIT_ARRAY_HXX - -#include - -#include "bspf.hxx" - -class PackedBitArray -{ - public: - PackedBitArray() : myInitialized(false) { } - - bool isSet(uInt16 bit) const { return myBits[bit]; } - bool isClear(uInt16 bit) const { return !myBits[bit]; } - - void set(uInt16 bit) { myBits[bit] = true; } - void clear(uInt16 bit) { myBits[bit] = false; } - void toggle(uInt16 bit) { myBits.flip(bit); } - - void initialize() { myInitialized = true; } - void clearAll() { myInitialized = false; myBits.reset(); } - - bool isInitialized() const { return myInitialized; } - - private: - // The actual bits - std::bitset<0x10000> myBits; - - // Indicates whether we should treat this bitset as initialized - bool myInitialized; - - private: - // Following constructors and assignment operators not supported - PackedBitArray(const PackedBitArray&) = delete; - PackedBitArray(PackedBitArray&&) = delete; - PackedBitArray& operator=(const PackedBitArray&) = delete; - PackedBitArray& operator=(PackedBitArray&&) = delete; -}; - -#endif diff --git a/src/debugger/gui/RomListWidget.cxx b/src/debugger/gui/RomListWidget.cxx index 48e6f35d3..b686f658e 100644 --- a/src/debugger/gui/RomListWidget.cxx +++ b/src/debugger/gui/RomListWidget.cxx @@ -18,7 +18,6 @@ #include "bspf.hxx" #include "Debugger.hxx" #include "DiStella.hxx" -#include "PackedBitArray.hxx" #include "Widget.hxx" #include "StellaKeys.hxx" #include "FBSurface.hxx" @@ -40,8 +39,7 @@ RomListWidget::RomListWidget(GuiObject* boss, const GUI::Font& lfont, _editMode(false), _currentKeyDown(KBDK_UNKNOWN), _base(Common::Base::F_DEFAULT), - myDisasm(nullptr), - myBPState(nullptr) + myDisasm(nullptr)//, { _flags = Widget::FLAG_ENABLED | Widget::FLAG_CLEARBG | Widget::FLAG_RETAIN_FOCUS; _bgcolor = kWidColor; @@ -122,11 +120,9 @@ RomListWidget::RomListWidget(GuiObject* boss, const GUI::Font& lfont, } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void RomListWidget::setList(const CartDebug::Disassembly& disasm, - const PackedBitArray& state) +void RomListWidget::setList(const CartDebug::Disassembly& disasm) { myDisasm = &disasm; - myBPState = &state; // Enable all checkboxes for(int i = 0; i < _rows; ++i) @@ -496,7 +492,9 @@ void RomListWidget::drawWidget(bool hilite) ColorId bytesColor = textColor; // Draw checkboxes for correct lines (takes scrolling into account) - myCheckList[i]->setState(myBPState->isSet(dlist[pos].address)); + myCheckList[i]->setState(instance().debugger(). + checkBreakPoint(dlist[pos].address, instance().debugger().cartDebug().getBank()) != Debugger::NOT_FOUND); + myCheckList[i]->setDirty(); myCheckList[i]->draw(); diff --git a/src/debugger/gui/RomListWidget.hxx b/src/debugger/gui/RomListWidget.hxx index ac15b2ddd..a3b642c2c 100644 --- a/src/debugger/gui/RomListWidget.hxx +++ b/src/debugger/gui/RomListWidget.hxx @@ -19,7 +19,6 @@ #define ROM_LIST_WIDGET_HXX class ScrollBarWidget; -class PackedBitArray; class CheckListWidget; class RomListSettings; @@ -50,7 +49,7 @@ class RomListWidget : public EditableWidget int x, int y, int w, int h); virtual ~RomListWidget() = default; - void setList(const CartDebug::Disassembly& disasm, const PackedBitArray& state); + void setList(const CartDebug::Disassembly& disasm); int getSelected() const { return _selectedItem; } int getHighlighted() const { return _highlightedItem; } @@ -102,7 +101,6 @@ class RomListWidget : public EditableWidget Common::Base::Format _base; // base used during editing const CartDebug::Disassembly* myDisasm; - const PackedBitArray* myBPState; vector myCheckList; private: diff --git a/src/debugger/gui/RomWidget.cxx b/src/debugger/gui/RomWidget.cxx index a7c143982..fdab6d591 100644 --- a/src/debugger/gui/RomWidget.cxx +++ b/src/debugger/gui/RomWidget.cxx @@ -72,7 +72,7 @@ void RomWidget::loadConfig() myListIsDirty |= cart.disassemble(myListIsDirty); if(myListIsDirty) { - myRomList->setList(cart.disassembly(), dbg.breakPoints()); + myRomList->setList(cart.disassembly()); myListIsDirty = false; } @@ -171,7 +171,8 @@ void RomWidget::setBreak(int disasm_line, bool state) if(disasm_line >= int(list.size())) return; if(list[disasm_line].address != 0 && list[disasm_line].bytes != "") - instance().debugger().setBreakPoint(list[disasm_line].address, state); + instance().debugger().setBreakPoint(list[disasm_line].address, + instance().debugger().cartDebug().getBank(), state); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/emucore/M6502.cxx b/src/emucore/M6502.cxx index f6f468715..e3b154343 100644 --- a/src/emucore/M6502.cxx +++ b/src/emucore/M6502.cxx @@ -19,7 +19,6 @@ #include "Debugger.hxx" #include "Expression.hxx" #include "CartDebug.hxx" - #include "PackedBitArray.hxx" #include "Base.hxx" // Flags for disassembly types @@ -280,24 +279,18 @@ inline void M6502::_execute(uInt64 cycles, DispatchResult& result) return; } - if(myBreakPoints.isInitialized() && myBreakPoints.isSet(PC)) { - myLastBreakCycle = mySystem->cycles(); - // disable a one-shot breakpoint - if(myBreakPoints.isInitialized() && myBreakPointFlags.isSet(PC)) - { - myBreakPoints.clear(PC); - myBreakPointFlags.clear(PC); - } - else - result.setDebugger(currentCycles, "BP: ", PC); - return; - } - int cond = evalCondBreaks(); if(cond > -1) { ostringstream msg; - msg << "CBP[" << Common::Base::HEX2 << cond << "]: " << myCondBreakNames[cond]; + + // one shot break (trace)? + if(myCondBreakFlags[cond]) + { + delCondBreak(cond); + } + else + msg << "BP[" << Common::Base::HEX2 << cond << "]: " << myCondBreakNames[cond]; myLastBreakCycle = mySystem->cycles(); result.setDebugger(currentCycles, msg.str()); @@ -543,10 +536,11 @@ void M6502::attach(Debugger& debugger) } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt32 M6502::addCondBreak(Expression* e, const string& name) +uInt32 M6502::addCondBreak(Expression* e, const string& name, bool oneShot) { myCondBreaks.emplace_back(e); myCondBreakNames.push_back(name); + myCondBreakFlags.push_back(oneShot); updateStepStateByInstruction(); @@ -560,6 +554,7 @@ bool M6502::delCondBreak(uInt32 idx) { Vec::removeAt(myCondBreaks, idx); Vec::removeAt(myCondBreakNames, idx); + Vec::removeAt(myCondBreakFlags, idx); updateStepStateByInstruction(); diff --git a/src/emucore/M6502.hxx b/src/emucore/M6502.hxx index 84672c378..49d829841 100644 --- a/src/emucore/M6502.hxx +++ b/src/emucore/M6502.hxx @@ -29,7 +29,6 @@ class DispatchResult; class CpuDebug; #include "Expression.hxx" - #include "PackedBitArray.hxx" #include "TrapArray.hxx" #endif @@ -212,14 +211,11 @@ class M6502 : public Serializable // Attach the specified debugger. void attach(Debugger& debugger); - PackedBitArray& breakPoints() { return myBreakPoints; } - // flags used for one-shot breakpoints - PackedBitArray& breakPointFlags() { return myBreakPointFlags; } TrapArray& readTraps() { return myReadTraps; } TrapArray& writeTraps() { return myWriteTraps; } // methods for 'breakif' handling - uInt32 addCondBreak(Expression* e, const string& name); + uInt32 addCondBreak(Expression* e, const string& name, bool oneShot = false); bool delCondBreak(uInt32 idx); void clearCondBreaks(); const StringList& getCondBreakNames() const; @@ -399,7 +395,7 @@ class M6502 : public Serializable #ifdef DEBUGGER_SUPPORT Int32 evalCondBreaks() { - for(uInt32 i = 0; i < myCondBreaks.size(); i++) + for(Int32 i = Int32(myCondBreaks.size()) - 1; i >= 0; --i) if(myCondBreaks[i]->evaluate()) return i; @@ -408,7 +404,7 @@ class M6502 : public Serializable Int32 evalCondSaveStates() { - for(uInt32 i = 0; i < myCondSaveStates.size(); i++) + for(Int32 i = Int32(myCondSaveStates.size()) - 1; i >= 0; --i) if(myCondSaveStates[i]->evaluate()) return i; @@ -417,7 +413,7 @@ class M6502 : public Serializable Int32 evalCondTraps() { - for(uInt32 i = 0; i < myTrapConds.size(); i++) + for(Int32 i = Int32(myTrapConds.size()) - 1; i >= 0; --i) if(myTrapConds[i]->evaluate()) return i; @@ -428,8 +424,6 @@ class M6502 : public Serializable Debugger* myDebugger; // Addresses for which the specified action should occur - PackedBitArray myBreakPoints; - PackedBitArray myBreakPointFlags; TrapArray myReadTraps, myWriteTraps; // Did we just now hit a trap? @@ -443,6 +437,7 @@ class M6502 : public Serializable vector> myCondBreaks; StringList myCondBreakNames; + BoolArray myCondBreakFlags; vector> myCondSaveStates; StringList myCondSaveStateNames; vector> myTrapConds; diff --git a/src/windows/Stella.vcxproj b/src/windows/Stella.vcxproj index 39972b1dd..a508ff8fd 100644 --- a/src/windows/Stella.vcxproj +++ b/src/windows/Stella.vcxproj @@ -1324,7 +1324,6 @@ - diff --git a/src/windows/Stella.vcxproj.filters b/src/windows/Stella.vcxproj.filters index a879ed272..6cbd09c82 100644 --- a/src/windows/Stella.vcxproj.filters +++ b/src/windows/Stella.vcxproj.filters @@ -1226,9 +1226,6 @@ Header Files\debugger - - Header Files\debugger - Header Files\debugger