diff --git a/docs/debugger.html b/docs/debugger.html index 57be1b6ab..11aa69785 100644 --- a/docs/debugger.html +++ b/docs/debugger.html @@ -866,10 +866,9 @@ measure the minimum, maximum and average cycles executed between their two timer points. Start and end points can be set via prompt command or ROM Disassembly settings dialog.

-

Timer points can be defined with or without a bank. With a bank, they -are triggered in the given bank only, using all mirror addresses too. Without -a bank, the timer points are triggered in all bank, using the given adress -only.

+

If the timer point is defined without an address or bank, the current PC +address or bank are used. If a '+' is added, both timer points use address +mirrors too, a '*' triggers both timer points in any bank.

All timers can be shown in detail with "listTimers", a single timer with "printTimer number". "resetTimers" allows resetting all timer statistics, diff --git a/src/debugger/DebuggerParser.cxx b/src/debugger/DebuggerParser.cxx index fa7852f70..d88558c38 100644 --- a/src/debugger/DebuggerParser.cxx +++ b/src/debugger/DebuggerParser.cxx @@ -593,13 +593,16 @@ void DebuggerParser::printTimer(uInt32 idx, bool showHeader) if(!debugger.cartDebug().getLabel(buf, timer.from.addr, true)) buf << " $" << setw(4) << Base::HEX4 << timer.from.addr; string labelFrom = buf.str(); + labelFrom = labelFrom.substr(0, (banked ? 12 : 15) - (timer.mirrors ? 1 : 0)); + labelFrom += (timer.mirrors ? "+" : ""); + labelFrom = (labelFrom + " ").substr(0, banked ? 12 : 15); buf.str(""); if(!debugger.cartDebug().getLabel(buf, timer.to.addr, true)) buf << " $" << setw(4) << Base::HEX4 << timer.to.addr; string labelTo = buf.str(); - - labelFrom = (labelFrom + " ").substr(0, banked ? 12 : 15); + labelTo = labelTo.substr(0, (banked ? 12 : 15) - (timer.mirrors ? 1 : 0)); + labelTo += (timer.mirrors ? "+" : ""); labelTo = (labelTo + " ").substr(0, banked ? 12 : 15); if(showHeader) @@ -613,8 +616,8 @@ void DebuggerParser::printTimer(uInt32 idx, bool showHeader) if(banked) { commandResult << "/" << setw(2) << setfill(' '); - if(timer.from.bank == TimerMap::ANY_BANK) - commandResult << "-"; + if(timer.anyBank) + commandResult << "*"; else commandResult << dec << static_cast(timer.from.bank); } @@ -628,8 +631,8 @@ void DebuggerParser::printTimer(uInt32 idx, bool showHeader) if(banked) { commandResult << "/" << setw(2) << setfill(' '); - if(timer.to.bank == TimerMap::ANY_BANK) - commandResult << "-"; + if(timer.anyBank) + commandResult << "*"; else commandResult << dec << static_cast(timer.to.bank); } @@ -2294,58 +2297,111 @@ void DebuggerParser::executeTia() // "timer" void DebuggerParser::executeTimer() { + // Input variants: + // timer current address @ current bank + // timer + current address + mirrors @ current bank + // timer * current address @ any bank + // timer + * current address + mirrors @ any bank + // timer addr addr @ current bank + // timer addr + addr + mirrors @ current bank + // timer addr * addr @ any bank + // timer addr + * addr + mirrors @ any bank + // timer bank current address @ bank + // timer bank + current address + mirrors @ bank + // timer addr bank addr @ bank + // timer addr bank + addr + mirrors @ bank + // timer addr addr addr, addr @ current bank + // timer addr addr + addr, addr + mirrors @ current bank + // timer addr addr * addr, addr @ any bank + // timer addr addr + * addr, addr + mirrors @ any bank + // timer addr addr bank addr, addr @ bank + // timer addr addr bank bank addr, addr @ bank, bank + // timer addr addr bank bank + addr, addr + mirrors @ bank, bank const uInt32 romBankCount = debugger.cartDebug().romBankCount(); + const bool banked = romBankCount > 1; + bool mirrors = (argCount >= 1 && argStrings[argCount - 1] == "+") + || (argCount >= 2 && argStrings[argCount - 2] == "+"); + bool anyBank = banked + && ((argCount >= 1 && argStrings[argCount - 1] == "*") + || (argCount >= 2 && argStrings[argCount - 2] == "*")); - if(argCount < 2) - { - const uInt16 addr = !argCount ? debugger.cpuDebug().pc() : args[0]; - const uInt8 bank = !argCount ? debugger.cartDebug().getBank(addr) : TimerMap::ANY_BANK; - const uInt32 idx = debugger.m6502().addTimer(addr, bank); - commandResult << "set timer " << dec << idx << " " - << (debugger.m6502().getTimer(idx).isPartial ? "start" : "end"); - if(!argCount) - commandResult << " at $" << Base::HEX4 << (addr & TimerMap::ADDRESS_MASK); - if(romBankCount > 1 && !argCount) - commandResult << " + mirrors in bank #" << std::dec << static_cast(bank); - return; - } - else if(argCount > 4) + argCount -= ((mirrors ? 1 : 0) + (anyBank ? 1 : 0)); + if(argCount > 4) { outputCommandError("too many arguments", myCommand); return; } - // Detect if 2nd parameter is bank - if(argCount == 2 && args[0] >= 0x1000 && args[1] <= TimerMap::ANY_BANK) + uInt32 numAddrs = 0, numBanks = 0; + uInt16 addr[2]; + uInt8 bank[2]; + + // set defaults: + addr[0] = debugger.cpuDebug().pc() ; + bank[0] = debugger.cartDebug().getBank(addr[0]); + + for(uInt32 i = 0; i < argCount; ++i) { - const uInt16 addr = args[0]; - const uInt8 bank = args[1]; - if(bank >= romBankCount && bank != TimerMap::ANY_BANK) + if(static_cast(args[i]) >= std::max(0x80u, romBankCount - 1)) { - commandResult << red("invalid bank"); - return; + if(numAddrs == 2) + { + outputCommandError("too many address arguments", myCommand); + return; + } + addr[numAddrs++] = args[i]; } - const uInt32 idx = debugger.m6502().addTimer(addr, bank); - commandResult << "set timer " << dec << idx << " " - << (debugger.m6502().getTimer(idx).isPartial ? "start" : "end"); - return; + else + { + if(anyBank || (numBanks == 1 && numAddrs < 2) || numBanks == 2) + { + outputCommandError("too many bank arguments", myCommand); + return; + } + if(static_cast(args[i]) >= romBankCount) + { + commandResult << red("invalid bank"); + return; + } + bank[numBanks++] = args[i]; + } + } + + uInt32 idx; + if(numAddrs < 2) + { + idx = debugger.m6502().addTimer(addr[0], bank[0], mirrors, anyBank); } else { - const uInt8 bankFrom = argCount >= 3 ? args[2] : TimerMap::ANY_BANK; - if(bankFrom >= romBankCount && bankFrom != TimerMap::ANY_BANK) + idx = debugger.m6502().addTimer(addr[0], addr[1], + bank[0], numBanks < 2 ? bank[0] : bank[1], + mirrors, anyBank); + } + + const bool isPartial = debugger.m6502().getTimer(idx).isPartial; + if(numAddrs < 2 && !isPartial) + { + mirrors |= debugger.m6502().getTimer(idx).mirrors; + anyBank |= debugger.m6502().getTimer(idx).anyBank; + } + commandResult << "set timer " << Base::toString(idx) + << (numAddrs < 2 ? (isPartial ? " start" : " end") : "") + << " at $" << Base::HEX4 << addr[0]; + if(numAddrs == 2) + commandResult << ", $" << Base::HEX4 << addr[1]; + if(mirrors) + commandResult << " + mirrors"; + if(banked) + { + if(anyBank) + commandResult << " in all banks"; + else { - commandResult << red("invalid bank"); - return; + commandResult << " in bank #" << std::dec << static_cast(bank[0]); + if(numBanks == 2) + commandResult << ", #" << std::dec << static_cast(bank[1]); } - const uInt8 bankTo = argCount == 4 ? args[3] : bankFrom; - if(bankTo >= romBankCount && bankTo != TimerMap::ANY_BANK) - { - commandResult << red("invalid bank"); - return; - } - const uInt32 idx = debugger.m6502().addTimer(args[0], args[1], bankFrom, bankTo); - commandResult << "timer " << idx << " added"; } } @@ -3637,10 +3693,11 @@ DebuggerParser::CommandArray DebuggerParser::commands = { { { "timer", "Set a cycle counting timer from addresses xx to yy [banks aa bb]", - "Example: timer, timer 1000, timer 3000 3100, timer f000 f800 1", + "Example: timer, timer 1000 + *, timer f000 f800 1 +", false, true, - { Parameters::ARG_WORD, Parameters::ARG_MULTI_BYTE }, + { Parameters::ARG_LABEL, Parameters::ARG_LABEL, Parameters::ARG_LABEL, + Parameters::ARG_LABEL, Parameters::ARG_LABEL, Parameters::ARG_MULTI_BYTE }, std::mem_fn(&DebuggerParser::executeTimer) }, diff --git a/src/debugger/TimerMap.cxx b/src/debugger/TimerMap.cxx index d392e227f..9c4d8b022 100644 --- a/src/debugger/TimerMap.cxx +++ b/src/debugger/TimerMap.cxx @@ -21,18 +21,34 @@ TODOs: x unordered_multimap (not required for just a few timers) o 13 vs 16 bit, use ADDRESS_MASK & ANY_BANK, when??? + - never, unless the user defines * for the bank + o any bank + - never unless the user defines * ? timer line display in disassembly? (color, symbol,...?) + - keep original from and to addresses for label display */ // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt32 TimerMap::add(const uInt16 fromAddr, const uInt16 toAddr, - const uInt8 fromBank, const uInt8 toBank) +void TimerMap::toKey(TimerPoint& tp, bool mirrors, bool anyBank) { - const TimerPoint tpFrom(fromAddr, fromBank); - const TimerPoint tpTo(toAddr, toBank); - const Timer complete(tpFrom, tpTo); + if(mirrors) + tp.addr &= ADDRESS_MASK; + if(anyBank) + tp.bank = ANY_BANK; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +uInt32 TimerMap::add(uInt16 fromAddr, uInt16 toAddr, + uInt8 fromBank, uInt8 toBank, + bool mirrors, bool anyBank) +{ + TimerPoint tpFrom(fromAddr, fromBank); + TimerPoint tpTo(toAddr, toBank); + const Timer complete(tpFrom, tpTo, mirrors, anyBank); myList.push_back(complete); + toKey(tpFrom, mirrors, anyBank); + toKey(tpTo, mirrors, anyBank); myFromMap.insert(TimerPair(tpFrom, &myList.back())); myToMap.insert(TimerPair(tpTo, &myList.back())); @@ -40,31 +56,61 @@ uInt32 TimerMap::add(const uInt16 fromAddr, const uInt16 toAddr, } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt32 TimerMap::add(const uInt16 addr, const uInt8 bank) +uInt32 TimerMap::add(uInt16 addr, uInt8 bank, bool mirrors, bool anyBank) { - const uInt32 idx = size() - 1; + uInt32 idx = size() - 1; + uInt32 i = 0; // find first incomplete timer, if any + for(auto it = myList.begin(); it != myList.end(); ++it, ++i) + if(it->isPartial) + { + idx = i; + break; + } const bool isPartialTimer = size() && get(idx).isPartial; - const TimerPoint tp(addr, bank); + TimerPoint tp(addr, bank); if(!isPartialTimer) { - const Timer partial(tp); + // create a new timer: + const Timer tmNew(tp, mirrors, anyBank); - myList.push_back(partial); + myList.push_back(tmNew); + toKey(tp, mirrors, anyBank); myFromMap.insert(TimerPair(tp, &myList.back())); + return size() - 1; } else { - Timer& partial = myList[idx]; + // complete a partial timer: + Timer& tmPartial = myList[idx]; + TimerPoint tpFrom = tmPartial.from; + bool oldMirrors = tmPartial.mirrors; + bool oldAnyBank = tmPartial.anyBank; - partial.setTo(tp); - myToMap.insert(TimerPair(tp, &partial)); + tmPartial.setTo(tp, mirrors, anyBank); + toKey(tp, tmPartial.mirrors, tmPartial.anyBank); + myToMap.insert(TimerPair(tp, &tmPartial)); + + // update tp key in myFromMap for new mirrors & anyBank settings + // 1. find map entry using OLD tp key settings: + toKey(tpFrom, oldMirrors, oldAnyBank); + auto from = myFromMap.equal_range(tpFrom); + for(auto it = from.first; it != from.second; ++it) + if(it->second == &tmPartial) + { + // 2. erase old map entry + myFromMap.erase(it); + // ...and add new map entry with NEW tp key settings: + toKey(tpFrom, tmPartial.mirrors, tmPartial.anyBank); + myFromMap.insert(TimerPair(tpFrom, &tmPartial)); + break; + } + return idx; } - return size() - 1; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool TimerMap::erase(const uInt32 idx) +bool TimerMap::erase(uInt32 idx) { if(size() > idx) { @@ -112,13 +158,12 @@ void TimerMap::reset() } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void TimerMap::update(const uInt16 addr, const uInt8 bank, - const uInt64 cycles) +void TimerMap::update(uInt16 addr, uInt8 bank, const uInt64 cycles) { - // 13 bit timerpoint if((addr & ADDRESS_MASK) != addr) { - TimerPoint tp(addr, bank); // -> addr & ADDRESS_MASK + // 13 bit timerpoint + TimerPoint tp(addr & ADDRESS_MASK, bank); // Find address in from and to maps const auto from = myFromMap.equal_range(tp); @@ -133,7 +178,7 @@ void TimerMap::update(const uInt16 addr, const uInt8 bank, } // 16 bit timerpoint - TimerPoint tp(addr, bank, false); // -> addr + TimerPoint tp(addr, bank); // Find address in from and to maps const auto from = myFromMap.equal_range(tp); diff --git a/src/debugger/TimerMap.hxx b/src/debugger/TimerMap.hxx index 95b6de2ea..23d10aaeb 100644 --- a/src/debugger/TimerMap.hxx +++ b/src/debugger/TimerMap.hxx @@ -32,11 +32,9 @@ */ class TimerMap { - public: - static constexpr uInt8 ANY_BANK = 255; // timer breakpoint valid in any bank - - //private: + private: static constexpr uInt16 ADDRESS_MASK = 0x1fff; // either 0x1fff or 0xffff (not needed then) + static constexpr uInt8 ANY_BANK = 255; // timer point valid in any bank private: struct TimerPoint @@ -44,12 +42,9 @@ class TimerMap uInt16 addr{0}; uInt8 bank{ANY_BANK}; - explicit constexpr TimerPoint(uInt16 c_addr, uInt8 c_bank, bool mask = true) - : addr{c_addr}, bank{c_bank} - { - if(mask && bank != ANY_BANK) - addr = addr & ADDRESS_MASK; - } + explicit constexpr TimerPoint(uInt16 c_addr, uInt8 c_bank) + : addr{c_addr}, bank{c_bank} {} + TimerPoint() : addr{0}, bank(ANY_BANK) {} @@ -81,6 +76,8 @@ class TimerMap { TimerPoint from{}; TimerPoint to{}; + bool mirrors{false}; + bool anyBank{false}; bool isPartial{false}; uInt64 execs{0}; @@ -90,46 +87,28 @@ class TimerMap uInt64 maxCycles{0}; bool isStarted{false}; - explicit constexpr Timer(const TimerPoint& c_from, const TimerPoint& c_to) - : from{c_from}, to{c_to} + explicit constexpr Timer(const TimerPoint& c_from, const TimerPoint& c_to, + bool c_mirrors = false, bool c_anyBank = false) + : from{c_from}, to{c_to}, mirrors{c_mirrors}, anyBank{c_anyBank} {} + + Timer(uInt16 fromAddr, uInt16 toAddr, uInt8 fromBank, uInt8 toBank, + bool mirrors = false, bool anyBank = false) { - //if(to.bank == ANY_BANK) // TODO: check if this is required - //{ - // to.bank = from.bank; - // if(to.bank != ANY_BANK) - // to.addr &= ADDRESS_MASK; - //} + Timer(TimerPoint(fromAddr, fromBank), TimerPoint(fromAddr, fromBank), + mirrors, anyBank); } - Timer(uInt16 fromAddr, uInt16 toAddr, uInt8 fromBank, uInt8 toBank) + Timer(const TimerPoint& tp, bool c_mirrors = false, bool c_anyBank = false) { - Timer(TimerPoint(fromAddr, fromBank), TimerPoint(fromAddr, fromBank)); + from = tp; + mirrors = c_mirrors; + anyBank = c_anyBank; + isPartial = true; } - Timer(const TimerPoint& tp) + Timer(uInt16 addr, uInt8 bank, bool mirrors = false, bool anyBank = false) { - if(!isPartial) - { - from = tp; - isPartial = true; - } - else - { - to = tp; - isPartial = false; - - //if(to.bank == ANY_BANK) // TODO: check if this is required - //{ - // to.bank = from.bank; - // if(to.bank != ANY_BANK) - // to.addr &= ADDRESS_MASK; - //} - } - } - - Timer(uInt16 addr, uInt8 bank) - { - Timer(TimerPoint(addr, bank)); + Timer(TimerPoint(addr, bank), mirrors, anyBank); } #if 0 // unused @@ -159,17 +138,12 @@ class TimerMap } #endif - void setTo(const TimerPoint& tp) + void setTo(const TimerPoint& tp, bool c_mirrors = false, bool c_anyBank = false) { to = tp; + mirrors |= c_mirrors; + anyBank |= c_anyBank; isPartial = false; - - //if(to.bank == ANY_BANK) // TODO: check if this is required - //{ - // to.bank = from.bank; - // if(to.bank != ANY_BANK) - // to.addr &= ADDRESS_MASK; - //} } void reset() @@ -209,9 +183,11 @@ class TimerMap bool isInitialized() const { return myList.size(); } /** Add new timer */ - uInt32 add(const uInt16 fromAddr, const uInt16 toAddr, - const uInt8 fromBank, const uInt8 toBank); - uInt32 add(const uInt16 addr, const uInt8 bank); + uInt32 add(uInt16 fromAddr, uInt16 toAddr, + uInt8 fromBank, uInt8 toBank, + bool mirrors, bool anyBank); + uInt32 add(uInt16 addr, uInt8 bank, + bool mirrors, bool anyBank); /** Erase timer */ bool erase(const uInt32 idx); @@ -223,13 +199,16 @@ class TimerMap void reset(); /** Get timer */ - const Timer& get(const uInt32 idx) const { return myList[idx]; } + const Timer& get(uInt32 idx) const { return myList[idx]; } uInt32 size() const { return static_cast(myList.size()); } /** Update timer */ - void update(const uInt16 addr, const uInt8 bank, + void update(uInt16 addr, uInt8 bank, const uInt64 cycles); + private: + void toKey(TimerPoint& tp, bool mirrors, bool anyBank); + private: using TimerList = std::deque; // makes sure that the element pointers do NOT change using TimerPair = std::pair; diff --git a/src/emucore/M6502.cxx b/src/emucore/M6502.cxx index 5132fea36..bcbe3fc09 100644 --- a/src/emucore/M6502.cxx +++ b/src/emucore/M6502.cxx @@ -698,20 +698,22 @@ void M6502::updateStepStateByInstruction() } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt32 M6502::addTimer(const uInt16 fromAddr, const uInt16 toAddr, - const uInt8 fromBank, const uInt8 toBank) +uInt32 M6502::addTimer(uInt16 fromAddr, uInt16 toAddr, + uInt8 fromBank, uInt8 toBank, + bool mirrors, bool anyBank) { - return myTimer.add(fromAddr, toAddr, fromBank, toBank); + return myTimer.add(fromAddr, toAddr, fromBank, toBank, mirrors, anyBank); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -uInt32 M6502::addTimer(const uInt16 addr, const uInt8 bank) +uInt32 M6502::addTimer(uInt16 addr, uInt8 bank, + bool mirrors, bool anyBank) { - return myTimer.add(addr, bank); + return myTimer.add(addr, bank, mirrors, anyBank); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -bool M6502::delTimer(const uInt32 idx) +bool M6502::delTimer(uInt32 idx) { return myTimer.erase(idx); } diff --git a/src/emucore/M6502.hxx b/src/emucore/M6502.hxx index 3bf9f7a24..2160070cf 100644 --- a/src/emucore/M6502.hxx +++ b/src/emucore/M6502.hxx @@ -254,9 +254,9 @@ class M6502 : public Serializable const StringList& getCondTrapNames() const; // methods for 'timer' handling: - uInt32 addTimer(uInt16 fromAddr, uInt16 toAddr, - uInt8 fromBank, uInt8 toBank); - uInt32 addTimer(uInt16 addr, uInt8 bank); + uInt32 addTimer(uInt16 fromAddr, uInt16 toAddr, uInt8 fromBank, uInt8 toBank, + bool mirrors, bool anyBank); + uInt32 addTimer(uInt16 addr, uInt8 bank, bool mirrors, bool anyBank); bool delTimer(uInt32 idx); void clearTimers(); void resetTimers(); diff --git a/src/gui/RomImageWidget.cxx b/src/gui/RomImageWidget.cxx index 4c0d69779..63626bf0e 100644 --- a/src/gui/RomImageWidget.cxx +++ b/src/gui/RomImageWidget.cxx @@ -386,7 +386,6 @@ void RomImageWidget::drawWidget(bool hilite) // the dialog surface position into account const Common::Rect& s_dst = s.dstRect(); mySurface->setDstPos(x + s_dst.x(), y + s_dst.y()); - } else if(!mySurfaceErrorMsg.empty()) {