mirror of https://github.com/stella-emu/stella.git
341 lines
11 KiB
C++
341 lines
11 KiB
C++
//============================================================================
|
|
//
|
|
// 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-2020 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.
|
|
//============================================================================
|
|
|
|
#include <sstream>
|
|
|
|
#include "OSystem.hxx"
|
|
#include "GuiObject.hxx"
|
|
#include "Debugger.hxx"
|
|
#include "CartDebug.hxx"
|
|
#include "CpuDebug.hxx"
|
|
#include "Widget.hxx"
|
|
#include "Font.hxx"
|
|
#include "DataGridWidget.hxx"
|
|
#include "EditTextWidget.hxx"
|
|
#include "ToggleBitWidget.hxx"
|
|
|
|
#include "CpuWidget.hxx"
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
CpuWidget::CpuWidget(GuiObject* boss, const GUI::Font& lfont, const GUI::Font& nfont,
|
|
int x, int y, int max_w)
|
|
: Widget(boss, lfont, x, y, 16, 16),
|
|
CommandSender(boss)
|
|
{
|
|
const int fontWidth = lfont.getMaxCharWidth(),
|
|
fontHeight = lfont.getFontHeight(),
|
|
lineHeight = lfont.getLineHeight();
|
|
int xpos, ypos, lwidth;
|
|
|
|
// Create a 1x1 grid with label for the PC register
|
|
xpos = x; ypos = y; lwidth = 4 * fontWidth;
|
|
new StaticTextWidget(boss, lfont, xpos, ypos+1, lwidth-2, fontHeight,
|
|
"PC ", TextAlign::Left);
|
|
myPCGrid =
|
|
new DataGridWidget(boss, nfont, xpos + lwidth, ypos, 1, 1, 4, 16, Common::Base::Fmt::_16);
|
|
myPCGrid->setTarget(this);
|
|
myPCGrid->setID(kPCRegID);
|
|
addFocusWidget(myPCGrid);
|
|
|
|
// Create a read-only textbox containing the current PC label
|
|
xpos += lwidth + myPCGrid->getWidth() + 10;
|
|
myPCLabel = new EditTextWidget(boss, nfont, xpos, ypos, (max_w - xpos + x) - 10,
|
|
fontHeight+1, "");
|
|
myPCLabel->setEditable(false, true);
|
|
|
|
// Create a 1x4 grid with labels for the other CPU registers
|
|
xpos = x + lwidth; ypos += myPCGrid->getHeight() + 1;
|
|
myCpuGrid =
|
|
new DataGridWidget(boss, nfont, xpos, ypos, 1, 4, 2, 8, Common::Base::Fmt::_16);
|
|
myCpuGrid->setTarget(this);
|
|
myCpuGrid->setID(kCpuRegID);
|
|
addFocusWidget(myCpuGrid);
|
|
|
|
// Create a 1x4 grid with decimal and binary values for the other CPU registers
|
|
xpos = x + lwidth + myPCGrid->getWidth() + 10;
|
|
myCpuGridDecValue =
|
|
new DataGridWidget(boss, nfont, xpos, ypos, 1, 4, 3, 8, Common::Base::Fmt::_10);
|
|
myCpuGridDecValue->setTarget(this);
|
|
myCpuGridDecValue->setID(kCpuRegDecID);
|
|
addFocusWidget(myCpuGridDecValue);
|
|
|
|
xpos += myCpuGridDecValue->getWidth() + 5;
|
|
myCpuGridBinValue =
|
|
new DataGridWidget(boss, nfont, xpos, ypos, 1, 4, 8, 8, Common::Base::Fmt::_2);
|
|
myCpuGridBinValue->setTarget(this);
|
|
myCpuGridBinValue->setID(kCpuRegBinID);
|
|
addFocusWidget(myCpuGridBinValue);
|
|
|
|
// Calculate real dimensions (_y will be calculated at the end)
|
|
_w = lwidth + myPCGrid->getWidth() + myPCLabel->getWidth() + 20;
|
|
|
|
// Create labels showing the source of data for SP/A/X/Y registers
|
|
xpos += myCpuGridBinValue->getWidth() + 20;
|
|
int src_y = ypos, src_w = (max_w - xpos + x) - 10;
|
|
for(int i = 0; i < 4; ++i)
|
|
{
|
|
myCpuDataSrc[i] = new EditTextWidget(boss, nfont, xpos, src_y, src_w,
|
|
fontHeight+1, "");
|
|
myCpuDataSrc[i]->setEditable(false, true);
|
|
src_y += fontHeight+2;
|
|
}
|
|
|
|
// Last write destination address
|
|
new StaticTextWidget(boss, lfont, xpos - fontWidth * 4.5, src_y + 4, "Dest");
|
|
myCpuDataDest = new EditTextWidget(boss, nfont, xpos, src_y + 2, src_w, fontHeight+1);
|
|
myCpuDataDest->setEditable(false, true);
|
|
|
|
// Add labels for other CPU registers
|
|
xpos = x;
|
|
const std::array<string, 4> labels = { "SP ", "A ", "X ", "Y " };
|
|
for(int row = 0; row < 4; ++row)
|
|
{
|
|
new StaticTextWidget(boss, lfont, xpos, ypos + row*lineHeight + 1,
|
|
lwidth-2, fontHeight,
|
|
labels[row], TextAlign::Left);
|
|
}
|
|
|
|
// Create a bitfield widget for changing the processor status
|
|
xpos = x; ypos += 4*lineHeight + 2;
|
|
new StaticTextWidget(boss, lfont, xpos, ypos+1, lwidth-2, fontHeight,
|
|
"PS ", TextAlign::Left);
|
|
myPSRegister = new ToggleBitWidget(boss, nfont, xpos+lwidth, ypos, 8, 1);
|
|
myPSRegister->setTarget(this);
|
|
addFocusWidget(myPSRegister);
|
|
|
|
// Set the strings to be used in the PSRegister
|
|
// We only do this once because it's the state that changes, not the strings
|
|
const std::array<string, 8> offstr = { "n", "v", "-", "b", "d", "i", "z", "c" };
|
|
const std::array<string, 8> onstr = { "N", "V", "-", "B", "D", "I", "Z", "C" };
|
|
StringList off, on;
|
|
for(int i = 0; i < 8; ++i)
|
|
{
|
|
off.push_back(offstr[i]);
|
|
on.push_back(onstr[i]);
|
|
}
|
|
myPSRegister->setList(off, on);
|
|
|
|
_h = ypos + myPSRegister->getHeight() - y;
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
void CpuWidget::setOpsWidget(DataGridOpsWidget* w)
|
|
{
|
|
myPCGrid->setOpsWidget(w);
|
|
myCpuGrid->setOpsWidget(w);
|
|
myCpuGridDecValue->setOpsWidget(w);
|
|
myCpuGridBinValue->setOpsWidget(w);
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
void CpuWidget::handleCommand(CommandSender* sender, int cmd, int data, int id)
|
|
{
|
|
int addr = -1, value = -1;
|
|
CpuDebug& dbg = instance().debugger().cpuDebug();
|
|
|
|
switch(cmd)
|
|
{
|
|
case DataGridWidget::kItemDataChangedCmd:
|
|
switch(id)
|
|
{
|
|
case kPCRegID:
|
|
addr = myPCGrid->getSelectedAddr();
|
|
value = myPCGrid->getSelectedValue();
|
|
break;
|
|
|
|
case kCpuRegID:
|
|
addr = myCpuGrid->getSelectedAddr();
|
|
value = myCpuGrid->getSelectedValue();
|
|
break;
|
|
|
|
case kCpuRegDecID:
|
|
addr = myCpuGridDecValue->getSelectedAddr();
|
|
value = myCpuGridDecValue->getSelectedValue();
|
|
break;
|
|
|
|
case kCpuRegBinID:
|
|
addr = myCpuGridBinValue->getSelectedAddr();
|
|
value = myCpuGridBinValue->getSelectedValue();
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
switch(addr)
|
|
{
|
|
case kPCRegAddr:
|
|
{
|
|
// Use the parser to set PC, since we want to propagate the
|
|
// event the rest of the debugger widgets
|
|
ostringstream command;
|
|
command << "pc #" << value;
|
|
instance().debugger().run(command.str());
|
|
break;
|
|
}
|
|
|
|
case kSPRegAddr:
|
|
dbg.setSP(value);
|
|
myCpuGrid->setValueInternal(0, value);
|
|
myCpuGridDecValue->setValueInternal(0, value);
|
|
myCpuGridBinValue->setValueInternal(0, value);
|
|
break;
|
|
|
|
case kARegAddr:
|
|
dbg.setA(value);
|
|
myCpuGrid->setValueInternal(1, value);
|
|
myCpuGridDecValue->setValueInternal(1, value);
|
|
myCpuGridBinValue->setValueInternal(1, value);
|
|
break;
|
|
|
|
case kXRegAddr:
|
|
dbg.setX(value);
|
|
myCpuGrid->setValueInternal(2, value);
|
|
myCpuGridDecValue->setValueInternal(2, value);
|
|
myCpuGridBinValue->setValueInternal(2, value);
|
|
break;
|
|
|
|
case kYRegAddr:
|
|
dbg.setY(value);
|
|
myCpuGrid->setValueInternal(3, value);
|
|
myCpuGridDecValue->setValueInternal(3, value);
|
|
myCpuGridBinValue->setValueInternal(3, value);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case ToggleWidget::kItemDataChangedCmd:
|
|
{
|
|
bool state = myPSRegister->getSelectedState();
|
|
|
|
switch(data)
|
|
{
|
|
case kPSRegN:
|
|
dbg.setN(state);
|
|
break;
|
|
|
|
case kPSRegV:
|
|
dbg.setV(state);
|
|
break;
|
|
|
|
case kPSRegB:
|
|
dbg.setB(state);
|
|
break;
|
|
|
|
case kPSRegD:
|
|
dbg.setD(state);
|
|
break;
|
|
|
|
case kPSRegI:
|
|
dbg.setI(state);
|
|
break;
|
|
|
|
case kPSRegZ:
|
|
dbg.setZ(state);
|
|
break;
|
|
|
|
case kPSRegC:
|
|
dbg.setC(state);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
void CpuWidget::loadConfig()
|
|
{
|
|
IntArray alist;
|
|
IntArray vlist;
|
|
BoolArray changed;
|
|
|
|
// We push the enumerated items as addresses, and deal with the real
|
|
// address in the callback (handleCommand)
|
|
Debugger& dbg = instance().debugger();
|
|
CartDebug& cart = dbg.cartDebug();
|
|
CpuDebug& cpu = dbg.cpuDebug();
|
|
const CpuState& state = static_cast<const CpuState&>(cpu.getState());
|
|
const CpuState& oldstate = static_cast<const CpuState&>(cpu.getOldState());
|
|
|
|
// Add PC to its own DataGridWidget
|
|
alist.push_back(kPCRegAddr);
|
|
vlist.push_back(state.PC);
|
|
changed.push_back(state.PC != oldstate.PC);
|
|
|
|
myPCGrid->setList(alist, vlist, changed);
|
|
|
|
// Add the other registers
|
|
alist.clear(); vlist.clear(); changed.clear();
|
|
alist.push_back(kSPRegAddr);
|
|
alist.push_back(kARegAddr);
|
|
alist.push_back(kXRegAddr);
|
|
alist.push_back(kYRegAddr);
|
|
|
|
// And now fill the values
|
|
vlist.push_back(state.SP);
|
|
vlist.push_back(state.A);
|
|
vlist.push_back(state.X);
|
|
vlist.push_back(state.Y);
|
|
|
|
// Figure out which items have changed
|
|
changed.push_back(state.SP != oldstate.SP);
|
|
changed.push_back(state.A != oldstate.A);
|
|
changed.push_back(state.X != oldstate.X);
|
|
changed.push_back(state.Y != oldstate.Y);
|
|
|
|
// Finally, update the register list
|
|
myCpuGrid->setList(alist, vlist, changed);
|
|
myCpuGridDecValue->setList(alist, vlist, changed);
|
|
myCpuGridBinValue->setList(alist, vlist, changed);
|
|
|
|
// Update the data sources for the SP/A/X/Y registers
|
|
const string& srcS = state.srcS < 0 ? "IMM" : cart.getLabel(state.srcS, true);
|
|
myCpuDataSrc[0]->setText((srcS != EmptyString ? srcS : Common::Base::toString(state.srcS)),
|
|
state.srcS != oldstate.srcS);
|
|
const string& srcA = state.srcA < 0 ? "IMM" : cart.getLabel(state.srcA, true);
|
|
myCpuDataSrc[1]->setText((srcA != EmptyString ? srcA : Common::Base::toString(state.srcA)),
|
|
state.srcA != oldstate.srcA);
|
|
const string& srcX = state.srcX < 0 ? "IMM" : cart.getLabel(state.srcX, true);
|
|
myCpuDataSrc[2]->setText((srcX != EmptyString ? srcX : Common::Base::toString(state.srcX)),
|
|
state.srcX != oldstate.srcX);
|
|
const string& srcY = state.srcY < 0 ? "IMM" : cart.getLabel(state.srcY, true);
|
|
myCpuDataSrc[3]->setText((srcY != EmptyString ? srcY : Common::Base::toString(state.srcY)),
|
|
state.srcY != oldstate.srcY);
|
|
|
|
const string& dest = state.dest < 0 ? "" : cart.getLabel(state.dest, false);
|
|
myCpuDataDest->setText((dest != EmptyString ? dest : Common::Base::toString(state.dest)),
|
|
state.dest != oldstate.dest);
|
|
|
|
// Update the PS register booleans
|
|
changed.clear();
|
|
for(uInt32 i = 0; i < state.PSbits.size(); ++i)
|
|
changed.push_back(state.PSbits[i] != oldstate.PSbits[i]);
|
|
|
|
myPSRegister->setState(state.PSbits, changed);
|
|
myPCLabel->setText(dbg.cartDebug().getLabel(state.PC, true));
|
|
}
|