reworked timer syntax and code and fixed some bugs

This commit is contained in:
Thomas Jentzsch 2022-10-08 11:54:38 +02:00
parent a8142633ce
commit aadb28a618
7 changed files with 216 additions and 135 deletions

View File

@ -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.</p>
<p>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.</p>
<p>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.</p>
<p>All timers can be shown in detail with "listTimers", a single timer
with "printTimer number". "resetTimers" allows resetting all timer statistics,

View File

@ -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<uInt16>(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<uInt16>(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<int>(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<uInt32>(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<uInt32>(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<int>(bank[0]);
if(numBanks == 2)
commandResult << ", #" << std::dec << static_cast<int>(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)
},

View File

@ -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);

View File

@ -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<uInt32>(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<Timer>; // makes sure that the element pointers do NOT change
using TimerPair = std::pair<TimerPoint, Timer*>;

View File

@ -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);
}

View File

@ -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();

View File

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