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())
{