fully resolved #165 (added timer read cycles count)

refined TiaInfoWidget again
added doc
This commit is contained in:
thrust26 2020-10-13 15:11:35 +02:00
parent f6d78e57b1
commit 7eece4e994
18 changed files with 125 additions and 44 deletions

View File

@ -753,6 +753,8 @@ that holds 'number of scanlines' on an actual console).</p>
<tr><td> _cycleslo</td><td> Lower 32 bits of number of cycles since emulation started</td></tr>
<tr><td> _fcount</td><td> Number of frames since emulation started</td></tr>
<tr><td> _fcycles</td><td> Number of cycles since frame started</td></tr>
<tr><td> _ftimreadcycles</td><td>Number of cycles used by timer reads since frame started</td></tr>
<tr><td> _fwsynccycles</td><td>Number of cycles skipped by WSYNC since frame started</td></tr>
<tr><td> _icycles</td><td> Number of cycles of last instruction</td></tr>
<tr><td> _scan</td><td> Current scanline count</td></tr>
<tr><td> _scanend</td><td> Scanline count at end of last frame</td></tr>
@ -1137,12 +1139,16 @@ as illustrated:</p>
<p><img src="graphics/debugger_tiainfo.png"></p>
<p>The indicators are as follows (note that all these are read-only):</p>
<ul>
<li><b>Frame Count</b>: The number of frames since this ROM was loaded or reset.</li>
<li><b>Frame Cycle</b>: The number of CPU cycles that have been executed this frame since
VSYNC was cleared at scanline 0.</li>
<li><b>WSync Cycl.</b>: The number of CPU cycles that have been skipped by WSYNC this frame since
VSYNC was cleared at scanline 0.</li>
<li><b>Timer Cycl.</b>: The number of CPU cycles (approximately) that have been used by timer read loops since
VSYNC was cleared at scanline 0.</li>
<li><b>Total</b>: The total number of CPU cycles since this ROM was loaded or reset.</li>
<li><b>Delta</b>: The number of CPU cycles that have been executed since the last debugger
interrupt.</li>
<li><b>Frame Cnt.</b>: The number of frames since this ROM was loaded or reset.</li>
<li><b>Scanline</b>: The scanline that's currently being drawn, and the count from the
previous frame. Scanline 0 is the one on which VSYNC is cleared (after being set for
3 scanlines, as per the Stella Programmer's Guide).</li>
@ -1275,10 +1281,9 @@ are lost (they will NOT end up in the Carry flag).</p>
<p>This is a spreadsheet-like GUI for inspecting and changing the contents
of the 2600's zero-page RAM.</p>
<p>You can navigate with either the mouse or the keyboard arrow keys.
To change a RAM location, either double-click on it or press Enter while
it's highlighted. Enter the new value (hex only for now, sorry), then
press Enter to make the change. If you change your mind, press Escape
and the original value will be restored. The currently selected RAM cell
To change a RAM location, either double-click on it or press 'Enter' while
it's highlighted. Enter the new value (hex, other formats using the bottom textboxes), then
press 'Enter' to make the change. The currently selected RAM cell
can also be changed by using the
<a href="#DataOpButtons"><b>Data Operations Buttons</b></a> or the associated
shortcut keys.</p>
@ -1289,7 +1294,8 @@ more comprehensive. It will undo <b>all</b> operations on <b>all</b> cells
since you first made a change.</p>
<p>The UI objects at the bottom refer to the currently selected RAM cell.
The 'Label' textbox shows the label attached to this RAM location (if any),
and the other two textboxes show the decimal and binary equivalent value.</p>
and the other three textboxes show the hex, decimal and binary equivalent value.
The values can be edited here too.</p>
<p>The remaining buttons to the right are further explained in the next section.</p>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

After

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -877,7 +877,7 @@ std::array<Debugger::BuiltinFunction, 18> Debugger::ourBuiltinFunctions = { {
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Names are defined here, but processed in YaccParser
std::array<Debugger::PseudoRegister, 15> Debugger::ourPseudoRegisters = { {
std::array<Debugger::PseudoRegister, 16> Debugger::ourPseudoRegisters = { {
// Debugger::PseudoRegister Debugger::ourPseudoRegisters[NUM_PSEUDO_REGS] = {
{ "_bank", "Currently selected bank" },
{ "_cclocks", "Color clocks on current scanline" },
@ -885,6 +885,7 @@ std::array<Debugger::PseudoRegister, 15> Debugger::ourPseudoRegisters = { {
{ "_cycleslo", "Lower 32 bits of number of cycles since emulation started" },
{ "_fcount", "Number of frames since emulation started" },
{ "_fcycles", "Number of cycles since frame started" },
{ "_ftimreadcycles","Number of cycles used by timer reads since frame started" },
{ "_fwsynccycles", "Number of cycles skipped by WSYNC since frame started" },
{ "_icycles", "Number of cycles of last instruction" },
{ "_scan", "Current scanline count" },

View File

@ -363,7 +363,7 @@ class Debugger : public DialogContainer
string name, help;
};
static std::array<BuiltinFunction, 18> ourBuiltinFunctions;
static std::array<PseudoRegister, 15> ourPseudoRegisters;
static std::array<PseudoRegister, 16> ourPseudoRegisters;
private:
// rewind/unwind n states

View File

@ -68,6 +68,8 @@ const DebuggerState& RiotDebug::getState()
myState.INTIMCLKS = intimClocks();
myState.TIMDIV = timDivider();
myState.timReadCycles = timReadCycles();
return myState;
}
@ -111,6 +113,8 @@ void RiotDebug::saveOldState()
myOldState.TIMCLKS = timClocks();
myOldState.INTIMCLKS = intimClocks();
myOldState.TIMDIV = timDivider();
myOldState.timReadCycles = timReadCycles();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -247,6 +251,12 @@ int RiotDebug::timWrappedOnWrite() const
return mySystem.m6532().myTimWrappedOnWrite;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
int RiotDebug::timReadCycles() const
{
return mySystem.m6532().myTimReadCycles;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool RiotDebug::diffP0(int newVal)
{

View File

@ -41,6 +41,7 @@ class RiotState : public DebuggerState
uInt8 TIM1T{0}, TIM8T{0}, TIM64T{0}, T1024T{0}, INTIM{0}, TIMINT{0};
Int32 TIMCLKS{0}, INTIMCLKS{0}, TIMDIV{0};
uInt16 timReadCycles;
// These are actually from the TIA, but are I/O related
uInt8 INPT0{0}, INPT1{0}, INPT2{0}, INPT3{0}, INPT4{0}, INPT5{0};
@ -83,6 +84,8 @@ class RiotDebug : public DebuggerSystem
int timWrappedOnRead() const;
int timWrappedOnWrite() const;
int timReadCycles() const;
/* Console switches */
bool diffP0(int newVal = -1);
bool diffP1(int newVal = -1);

View File

@ -478,20 +478,21 @@ void DebuggerDialog::addStatusArea()
{
const int lineHeight = myLFont->getLineHeight();
const Common::Rect& r = getStatusBounds();
const int HBORDER = 10;
const int VGAP = lineHeight / 3;
int xpos, ypos;
xpos = r.x(); ypos = r.y();
myTiaInfo = new TiaInfoWidget(this, *myLFont, *myNFont, xpos, ypos, r.w());
xpos = r.x() + HBORDER; ypos = r.y();
myTiaInfo = new TiaInfoWidget(this, *myLFont, *myNFont, xpos, ypos, r.w() - HBORDER);
ypos += myTiaInfo->getHeight() + 8;
myTiaZoom = new TiaZoomWidget(this, *myNFont, xpos + 10, ypos,
r.w() - 10, r.h() - lineHeight - ypos - 3);
ypos = myTiaInfo->getBottom() + VGAP;
myTiaZoom = new TiaZoomWidget(this, *myNFont, xpos, ypos,
r.w() - HBORDER, r.h() - ypos - VGAP - lineHeight + 3);
addToFocusList(myTiaZoom->getFocusList());
xpos += 10; ypos += myTiaZoom->getHeight() + 6;
myMessageBox = new EditTextWidget(this, *myLFont,
xpos, ypos, myTiaZoom->getWidth(),
myLFont->getLineHeight(), "");
ypos = myTiaZoom->getBottom() + VGAP;
myMessageBox = new EditTextWidget(this, *myLFont, xpos, ypos,
myTiaZoom->getWidth(), lineHeight);
myMessageBox->setEditable(false, false);
myMessageBox->clearFlags(Widget::FLAG_RETAIN_FOCUS);
myMessageBox->setTextColor(kTextColorEm);

View File

@ -19,6 +19,7 @@
#include "Font.hxx"
#include "OSystem.hxx"
#include "Debugger.hxx"
#include "RiotDebug.hxx"
#include "TIADebug.hxx"
#include "TIA.hxx"
#include "Widget.hxx"
@ -34,27 +35,29 @@ TiaInfoWidget::TiaInfoWidget(GuiObject* boss, const GUI::Font& lfont,
: Widget(boss, lfont, x, y, 16, 16),
CommandSender(boss)
{
bool longstr = 11 + 32 * lfont.getMaxCharWidth() + 9
+ EditTextWidget::calcWidth(lfont) * 3 <= max_w;
const int VGAP = lfont.getLineHeight() / 4;
const int VBORDER = 5 + 1;
x += 11;
const int COLUMN_GAP = _fontWidth * 1.25;
bool longstr = lfont.getStringWidth("Frame Cycle12345") + _fontWidth * 0.5
+ COLUMN_GAP + lfont.getStringWidth("Scanline262262")
+ EditTextWidget::calcWidth(lfont) * 3 <= max_w;
const int lineHeight = lfont.getLineHeight();
int xpos = x, ypos = y + VBORDER;
int lwidth = lfont.getStringWidth(longstr ? "Frame Cycle" : "F. Cycle") + _fontWidth * 0.5;
int l2width = lwidth - lfont.getMaxCharWidth() * 3;
int lwidth = lfont.getStringWidth(longstr ? "Frame Cycle" : "F. Cycle");
int lwidth8 = lwidth - lfont.getMaxCharWidth() * 3;
int lwidthR = lfont.getStringWidth(longstr ? "Frame Cnt." : "Frame ");
int fwidth = EditTextWidget::calcWidth(lfont, 5);
int twidth = EditTextWidget::calcWidth(lfont, 8);
const int twidth = EditTextWidget::calcWidth(lfont, 8);
const int LGAP = (max_w - lwidth - EditTextWidget::calcWidth(lfont, 5)
- lwidthR - EditTextWidget::calcWidth(lfont, 5)) / 4;
lwidth += LGAP;
lwidth8 += LGAP;
lwidthR += LGAP;
// Left column
// Left: Frame Count
new StaticTextWidget(boss, lfont, xpos, ypos + 1, longstr ? "Frame Count " : "Frame ");
myFrameCount = new EditTextWidget(boss, nfont, xpos + lwidth, ypos - 1, fwidth, lineHeight);
myFrameCount->setEditable(false, true);
// Left: Frame Cycle
xpos = x; ypos += lineHeight + VGAP;
xpos = x;
new StaticTextWidget(boss, lfont, xpos, ypos + 1, longstr ? "Frame Cycle" : "F. Cycle");
myFrameCycles = new EditTextWidget(boss, nfont, xpos + lwidth, ypos - 1, fwidth, lineHeight);
myFrameCycles->setEditable(false, true);
@ -65,24 +68,38 @@ TiaInfoWidget::TiaInfoWidget(GuiObject* boss, const GUI::Font& lfont,
myWSyncCylces = new EditTextWidget(boss, nfont, xpos + lwidth, ypos - 1, fwidth, lineHeight);
myWSyncCylces->setEditable(false, true);
// Left: Timer Cycles
ypos += lineHeight + VGAP;
new StaticTextWidget(boss, lfont, xpos, ypos + 1, longstr ? "Timer Cycl." : "Timer C.");
myTimerCylces = new EditTextWidget(boss, nfont, xpos + lwidth, ypos - 1, fwidth, lineHeight);
myTimerCylces->setEditable(false, true);
// Left: Total Cycles
ypos += lineHeight + VGAP;
new StaticTextWidget(boss, lfont, xpos, ypos + 1, "Total");
myTotalCycles = new EditTextWidget(boss, nfont, xpos + l2width, ypos - 1, twidth, lineHeight);
myTotalCycles = new EditTextWidget(boss, nfont, xpos + lwidth8, ypos - 1, twidth, lineHeight);
myTotalCycles->setEditable(false, true);
// Left: Delta Cycles
ypos += lineHeight + VGAP;
new StaticTextWidget(boss, lfont, xpos, ypos + 1, "Delta");
myDeltaCycles = new EditTextWidget(boss, nfont, xpos + l2width, ypos - 1, twidth, lineHeight);
myDeltaCycles = new EditTextWidget(boss, nfont, xpos + lwidth8, ypos - 1, twidth, lineHeight);
myDeltaCycles->setEditable(false, true);
// Right column
xpos = myFrameCycles->getRight() + _fontWidth * 1.25; ypos = y + VBORDER;
lwidth = lfont.getStringWidth(longstr ? "Color Clock " : "Pixel Pos ");
xpos = x + max_w - lwidthR - EditTextWidget::calcWidth(lfont, 5); ypos = y + VBORDER;
//xpos = myDeltaCycles->getRight() + LGAP * 2; ypos = y + VBORDER;
// Right: Frame Count
new StaticTextWidget(boss, lfont, xpos, ypos + 1, longstr ? "Frame Cnt." : "Frame");
myFrameCount = new EditTextWidget(boss, nfont, xpos + lwidthR, ypos - 1, fwidth, lineHeight);
myFrameCount->setEditable(false, true);
lwidth = lfont.getStringWidth(longstr ? "Color Clock " : "Pixel Pos ") + LGAP;
fwidth = EditTextWidget::calcWidth(lfont, 3);
// Right: Scanline
ypos += lineHeight + VGAP;
new StaticTextWidget(boss, lfont, xpos, ypos + 1, longstr ? "Scanline" : "Scn Ln");
myScanlineCountLast = new EditTextWidget(boss, nfont, xpos + lwidth, ypos - 1, fwidth, lineHeight);
myScanlineCountLast->setEditable(false, true);
@ -111,7 +128,7 @@ TiaInfoWidget::TiaInfoWidget(GuiObject* boss, const GUI::Font& lfont,
// Calculate actual dimensions
_w = myColorClocks->getRight() - x;
_h = myDeltaCycles->getBottom();
_h = myColorClocks->getBottom();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -131,6 +148,8 @@ void TiaInfoWidget::loadConfig()
Debugger& dbg = instance().debugger();
TIADebug& tia = dbg.tiaDebug();
const TiaState& oldTia = static_cast<const TiaState&>(tia.getOldState());
RiotDebug& riot = dbg.riotDebug();
const RiotState& oldRiot = static_cast<const RiotState&>(riot.getOldState());
myFrameCount->setText(Common::Base::toString(tia.frameCount(), Common::Base::Fmt::_10_5),
tia.frameCount() != oldTia.info[0]);
@ -159,4 +178,7 @@ void TiaInfoWidget::loadConfig()
myWSyncCylces->setText(Common::Base::toString(tia.frameWsyncCycles(), Common::Base::Fmt::_10_5),
tia.frameWsyncCycles() != oldTia.info[7]);
myTimerCylces->setText(Common::Base::toString(riot.timReadCycles(), Common::Base::Fmt::_10_5),
riot.timReadCycles() != oldRiot.timReadCycles);
}

View File

@ -41,6 +41,7 @@ class TiaInfoWidget : public Widget, public CommandSender
EditTextWidget* myTotalCycles{nullptr};
EditTextWidget* myDeltaCycles{nullptr};
EditTextWidget* myWSyncCylces{nullptr};
EditTextWidget* myTimerCylces{nullptr};
EditTextWidget* myScanlineCount{nullptr};
EditTextWidget* myScanlineCountLast{nullptr};

View File

@ -226,6 +226,7 @@ uInt8 M6532::peek(uInt16 addr)
if (!myWrappedThisCycle) myInterruptFlag &= ~TimerBit;
#ifdef DEBUGGER_SUPPORT
myTimWrappedOnRead = myWrappedThisCycle;
myTimReadCycles += 7;
#endif
return myTimer;
}
@ -236,6 +237,9 @@ uInt8 M6532::peek(uInt16 addr)
// PA7 Flag is always cleared after accessing TIMINT
uInt8 result = myInterruptFlag;
myInterruptFlag &= ~PA7Bit;
#ifdef DEBUGGER_SUPPORT
myTimReadCycles += 7;
#endif
return result;
}
@ -376,6 +380,9 @@ bool M6532::save(Serializer& out) const
out.putBool(myWrappedThisCycle);
out.putLong(myLastCycle);
out.putLong(mySetTimerCycle);
#ifdef DEBUGGER_SUPPORT
out.putInt(myTimReadCycles);
#endif
out.putByte(myDDRA);
out.putByte(myDDRB);
@ -408,6 +415,9 @@ bool M6532::load(Serializer& in)
myWrappedThisCycle = in.getBool();
myLastCycle = in.getLong();
mySetTimerCycle = in.getLong();
#ifdef DEBUGGER_SUPPORT
myTimReadCycles = in.getInt();
#endif
myDDRA = in.getByte();
myDDRB = in.getByte();

View File

@ -137,6 +137,11 @@ class M6532 : public Device
@return The access counters as comma separated string
*/
string getAccessCounters() const override;
/**
Reset the timer read CPU cycle counter
*/
void resetTimReadCylces() { myTimReadCycles = 0; }
#endif
private:
@ -254,6 +259,8 @@ class M6532 : public Device
// Detect timer being accessed on wraparound
bool myTimWrappedOnRead{false};
bool myTimWrappedOnWrite{false};
// Timer read CPU cycles
uInt16 myTimReadCycles{0};
#endif // DEBUGGER_SUPPORT
private:

View File

@ -17,6 +17,7 @@
#include "TIA.hxx"
#include "M6502.hxx"
#include "M6532.hxx"
#include "Control.hxx"
#include "Paddles.hxx"
#include "DelayQueueIteratorImpl.hxx"
@ -162,8 +163,10 @@ void TIA::initialize()
myDelayQueue.reset();
#ifdef DEBUGGER_SUPPORT
myCyclesAtFrameStart = 0;
myFrameWsyncCycles = 0;
#endif
if (myFrameManager)
myFrameManager->reset();
@ -279,8 +282,10 @@ bool TIA::save(Serializer& out) const
out.putByteArray(myShadowRegisters.data(), myShadowRegisters.size());
#ifdef DEBUGGER_SUPPORT
out.putLong(myCyclesAtFrameStart);
out.putLong(myFrameWsyncCycles);
#endif
out.putInt(myFrameBufferScanlines);
out.putInt(myFrontBufferScanlines);
@ -352,8 +357,10 @@ bool TIA::load(Serializer& in)
in.getByteArray(myShadowRegisters.data(), myShadowRegisters.size());
#ifdef DEBUGGER_SUPPORT
myCyclesAtFrameStart = in.getLong();
myFrameWsyncCycles = in.getLong();
#endif
myFrameBufferScanlines = in.getInt();
myFrontBufferScanlines = in.getInt();
@ -1307,7 +1314,10 @@ void TIA::updateEmulation()
void TIA::onFrameStart()
{
myXAtRenderingStart = 0;
#ifdef DEBUGGER_SUPPORT
myFrameWsyncCycles = 0;
mySystem->m6532().resetTimReadCylces();
#endif
// Check for colour-loss emulation
if (myColorLossEnabled)
@ -1333,7 +1343,9 @@ void TIA::onFrameStart()
void TIA::onFrameComplete()
{
mySystem->m6502().stop();
#ifdef DEBUGGER_SUPPORT
myCyclesAtFrameStart = mySystem->cycles();
#endif
if (myXAtRenderingStart > 0)
std::fill_n(myBackBuffer.begin(), myXAtRenderingStart, 0);
@ -1355,7 +1367,9 @@ void TIA::onHalt()
{
mySubClock += (TIAConstants::H_CLOCKS - myHctr) % TIAConstants::H_CLOCKS;
mySystem->incrementCycles(mySubClock / TIAConstants::CYCLE_CLOCKS);
myFrameWsyncCycles += mySubClock / TIAConstants::CYCLE_CLOCKS;
#ifdef DEBUGGER_SUPPORT
myFrameWsyncCycles += 3 + mySubClock / TIAConstants::CYCLE_CLOCKS;
#endif
mySubClock %= TIAConstants::CYCLE_CLOCKS;
}

View File

@ -322,6 +322,7 @@ class TIA : public Device
*/
uInt64 cycles() const { return uInt64(mySystem->cycles()); }
#ifdef DEBUGGER_SUPPORT
/**
Answers the frame count from the start of the emulation.
*/
@ -340,6 +341,7 @@ class TIA : public Device
uInt32 frameWSyncCycles() const {
return uInt32(myFrameWsyncCycles);
}
#endif // DEBUGGER_SUPPORT
/**
* Get the CPU cycles since the last dump ports change.
@ -560,7 +562,7 @@ class TIA : public Device
@return The access counters as comma separated string
*/
string getAccessCounters() const override;
#endif
#endif // DEBUGGER_SUPPORT
private:
/**
@ -941,6 +943,7 @@ class TIA : public Device
std::array<uInt32, 16> myColorCounts;
#ifdef DEBUGGER_SUPPORT
/**
* System cycles at the end of the previous frame / beginning of next frame.
*/
@ -950,6 +953,7 @@ class TIA : public Device
* System cycles used by WSYNC during current frame.
*/
uInt64 myFrameWsyncCycles{0};
#endif // DEBUGGER_SUPPORT
/**
* The frame manager can change during our lifetime, so we buffer those two.

View File

@ -236,6 +236,8 @@ RiotMethod getRiotSpecial(char* ch)
return &RiotDebug::timWrappedOnRead;
else if(BSPF::equalsIgnoreCase(ch, "_timwrapwrite"))
return &RiotDebug::timWrappedOnWrite;
else if(BSPF::equalsIgnoreCase(ch, "_ftimreadcycles"))
return &RiotDebug::timReadCycles;
else
return nullptr;
}