You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
614 lines
16 KiB
614 lines
16 KiB
/*
|
|
* Copyright Max Judin, Johannes Sixt, Daniel Kristjansson
|
|
* This file is licensed under the GNU General Public License Version 2.
|
|
* See the file COPYING in the toplevel directory of the source directory.
|
|
*/
|
|
|
|
#include <tqheader.h>
|
|
#include <tdeglobalsettings.h>
|
|
#include <tdelocale.h> /* i18n */
|
|
#include <kiconloader.h>
|
|
#include <tqfontdialog.h>
|
|
#include <tqmessagebox.h>
|
|
#include <tqpopupmenu.h>
|
|
#include <tqregexp.h>
|
|
#include <tqstringlist.h>
|
|
#include <stdlib.h> /* strtoul */
|
|
#include "regwnd.h"
|
|
#include "dbgdriver.h"
|
|
|
|
/**
|
|
* Register display modes
|
|
*/
|
|
class RegisterDisplay {
|
|
public:
|
|
enum BitSize {
|
|
bits8 = 0x10,
|
|
bits16 = 0x20,
|
|
bits32 = 0x30,
|
|
bits64 = 0x40,
|
|
bits80 = 0x50,
|
|
bits128 = 0x60,
|
|
bitsUnknown = 0x70
|
|
};
|
|
|
|
enum Format {
|
|
nada = 0x01,
|
|
binary = 0x02,
|
|
octal = 0x03,
|
|
decimal = 0x04,
|
|
hex = 0x05,
|
|
bcd = 0x06,
|
|
realE = 0x07,
|
|
realG = 0x08,
|
|
realF = 0x09
|
|
};
|
|
RegisterDisplay() : mode(bitsUnknown|nada) { }
|
|
RegisterDisplay(uint newMode) : mode(newMode) { }
|
|
|
|
bool contains(uint pmode) const {
|
|
bool val=((mode&0xf0)==pmode)||((mode&0x0f)==pmode);
|
|
return val;
|
|
}
|
|
uint bitsFlag() { return mode&0xf0; }
|
|
uint presentationFlag() const { return mode&0x0f; }
|
|
uint bits() const { return bitMap[(mode>>4)&0x07]; }
|
|
void changeFlag(uint code) {
|
|
uint mask=((code&0xf0)==code)?0x0f:0xf0;
|
|
mode = code | (mode & mask);
|
|
}
|
|
private:
|
|
uint mode;
|
|
static uint bitMap[];
|
|
};
|
|
|
|
// helper struct
|
|
struct MenuPair
|
|
{
|
|
const char* name;
|
|
uint mode;
|
|
bool isSeparator() { return name == 0; }
|
|
};
|
|
|
|
static MenuPair menuitems[] = {
|
|
// treat as
|
|
{ I18N_NOOP("&GDB default"), RegisterDisplay::nada },
|
|
{ I18N_NOOP("&Binary"), RegisterDisplay::binary },
|
|
{ I18N_NOOP("&Octal"), RegisterDisplay::octal },
|
|
{ I18N_NOOP("&Decimal"), RegisterDisplay::decimal },
|
|
{ I18N_NOOP("He&xadecimal"), RegisterDisplay::hex },
|
|
{ I18N_NOOP("Real (&e)"), RegisterDisplay::realE },
|
|
{ I18N_NOOP("Real (&f)"), RegisterDisplay::realF },
|
|
{ I18N_NOOP("&Real (g)"), RegisterDisplay::realG },
|
|
{ 0, 0 },
|
|
{ "8 bits", RegisterDisplay::bits8 },
|
|
{ "16 bits", RegisterDisplay::bits16 },
|
|
{ "32 bits", RegisterDisplay::bits32 },
|
|
{ "64 bits", RegisterDisplay::bits64 },
|
|
{ "80 bits", RegisterDisplay::bits80 },
|
|
{ "128 bits",RegisterDisplay::bits128 },
|
|
};
|
|
|
|
uint RegisterDisplay::bitMap[] = {
|
|
0, 8, 16, 32,
|
|
64, 80, 128, /*default*/32,
|
|
};
|
|
|
|
class ModeItem : public TQListViewItem
|
|
{
|
|
public:
|
|
ModeItem(TQListView* parent, const TQString& name) : TQListViewItem(parent, name) {}
|
|
ModeItem(TQListViewItem* parent) : TQListViewItem(parent) {}
|
|
|
|
virtual void setMode(RegisterDisplay mode) = 0;
|
|
virtual RegisterDisplay mode() = 0;
|
|
};
|
|
|
|
class GroupingViewItem : public ModeItem
|
|
{
|
|
public:
|
|
GroupingViewItem(RegisterView* parent,
|
|
const TQString& name, const TQString& pattern,
|
|
RegisterDisplay mode) :
|
|
ModeItem(parent, name), matcher(pattern), gmode(mode)
|
|
{
|
|
setExpandable(true);
|
|
setOpen(true);
|
|
}
|
|
bool matchName(const TQString& str) const
|
|
{
|
|
return matcher.exactMatch(str);
|
|
}
|
|
virtual void setMode(RegisterDisplay mode)
|
|
{
|
|
gmode=mode;
|
|
TQListViewItem *it=firstChild();
|
|
for (; 0!=it; it=it->nextSibling()) {
|
|
(static_cast<ModeItem*>(it))->setMode(gmode);
|
|
}
|
|
}
|
|
virtual RegisterDisplay mode() { return gmode; }
|
|
|
|
private:
|
|
TQRegExp matcher;
|
|
RegisterDisplay gmode;
|
|
};
|
|
|
|
class RegisterViewItem : public ModeItem
|
|
{
|
|
public:
|
|
RegisterViewItem(GroupingViewItem* parent,
|
|
const RegisterInfo& regInfo);
|
|
~RegisterViewItem();
|
|
|
|
void setValue(const RegisterInfo& regInfo);
|
|
virtual void setMode(RegisterDisplay mode);
|
|
virtual RegisterDisplay mode() { return m_mode; }
|
|
RegisterInfo m_reg;
|
|
RegisterDisplay m_mode; /* display mode */
|
|
bool m_changes;
|
|
bool m_found;
|
|
|
|
protected:
|
|
virtual void paintCell(TQPainter*, const TQColorGroup& cg,
|
|
int column, int width, int alignment);
|
|
|
|
};
|
|
|
|
|
|
RegisterViewItem::RegisterViewItem(GroupingViewItem* parent,
|
|
const RegisterInfo& regInfo) :
|
|
ModeItem(parent),
|
|
m_reg(regInfo),
|
|
m_changes(false),
|
|
m_found(true)
|
|
{
|
|
setValue(m_reg);
|
|
setText(0, m_reg.regName);
|
|
setMode(parent->mode());
|
|
}
|
|
|
|
RegisterViewItem::~RegisterViewItem()
|
|
{
|
|
}
|
|
|
|
/*
|
|
* We must be careful when converting the hex value because
|
|
* it may exceed this computer's long values.
|
|
*/
|
|
inline int hexCharToDigit(char h)
|
|
{
|
|
if (h < '0')
|
|
return -1;
|
|
if (h <= '9')
|
|
return h - '0';
|
|
if (h < 'A')
|
|
return -1;
|
|
if (h <= 'F')
|
|
return h - ('A' - 10);
|
|
if (h < 'a')
|
|
return -1;
|
|
if (h <= 'f')
|
|
return h - ('a' - 10);
|
|
return -1;
|
|
}
|
|
|
|
static TQString toBinary(TQString hex)
|
|
{
|
|
static const char digits[16][8] = {
|
|
"0000", "0001", "0010", "0011", "0100", "0101", "0110", "0111",
|
|
"1000", "1001", "1010", "1011", "1100", "1101", "1110", "1111"
|
|
};
|
|
TQString result;
|
|
|
|
for (unsigned i = 2; i < hex.length(); i++) {
|
|
int idx = hexCharToDigit(hex[i].latin1());
|
|
if (idx < 0) {
|
|
// not a hex digit; no conversion
|
|
return hex;
|
|
}
|
|
const char* bindigits = digits[idx];
|
|
result += bindigits;
|
|
}
|
|
// remove leading zeros
|
|
switch (hexCharToDigit(hex[2].latin1())) {
|
|
case 0: case 1: result.remove(0, 3); break;
|
|
case 2: case 3: result.remove(0, 2); break;
|
|
case 4: case 5:
|
|
case 6: case 7: result.remove(0, 1); break;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
static TQString toOctal(TQString hex)
|
|
{
|
|
TQString result;
|
|
int shift = 0;
|
|
unsigned v = 0;
|
|
for (int i = hex.length()-1; i >= 2; i--) {
|
|
int idx = hexCharToDigit(hex[i].latin1());
|
|
if (idx < 0)
|
|
return hex;
|
|
v += idx << shift;
|
|
result.insert(0, (v & 7) + '0');
|
|
v >>= 3;
|
|
shift++;
|
|
if (shift == 3) {
|
|
// an extra digit this round
|
|
result.insert(0, v + '0');
|
|
shift = v = 0;
|
|
}
|
|
}
|
|
if (v != 0) {
|
|
result.insert(0, v + '0');
|
|
}
|
|
return "0" + result;
|
|
}
|
|
|
|
static TQString toDecimal(TQString hex)
|
|
{
|
|
/*
|
|
* We convert only numbers that are small enough for this computer's
|
|
* size of long integers.
|
|
*/
|
|
if (hex.length() > sizeof(unsigned long)*2+2) /* count in leading "0x" */
|
|
return hex;
|
|
|
|
const char* start = hex.latin1();
|
|
char* end;
|
|
unsigned long val = strtoul(start, &end, 0);
|
|
if (start == end)
|
|
return hex;
|
|
else
|
|
return TQString().setNum(val);
|
|
}
|
|
|
|
static TQString toBCD(const TQString& hex)
|
|
{
|
|
return hex.right(2);
|
|
}
|
|
|
|
static char* toRaw(const TQString& hex, uint& length)
|
|
{
|
|
static uint testNum=1;
|
|
static void* testVoid=(void*)&testNum;
|
|
static char* testChar=(char*)testVoid;
|
|
static bool littleendian=(*testChar==1);
|
|
|
|
length=((hex.length()-2)%2)+((hex.length()-2)/2);
|
|
char* data=new char[length];
|
|
|
|
if (littleendian) {
|
|
uint j=0;
|
|
if (hex.length()<=2) return 0;
|
|
for (int i=hex.length()-1; i>=2; ) {
|
|
if (j%2==0) data[j/2]=hexCharToDigit(hex[i].latin1());
|
|
else data[j/2]|=(hexCharToDigit(hex[i].latin1())<<4);
|
|
i--;j++;
|
|
}
|
|
} else { // big endian
|
|
uint j=0;
|
|
if (hex.length()<=2) return 0;
|
|
for (uint i=2; i<hex.length(); ) {
|
|
if (j%2==0) data[j/2]=hexCharToDigit(hex[i].latin1())<<4;
|
|
else data[j/2]|=hexCharToDigit(hex[i].latin1());
|
|
i++;j++;
|
|
}
|
|
}
|
|
return data;
|
|
}
|
|
|
|
static long double extractNumber(const TQString& hex)
|
|
{
|
|
uint length;
|
|
char* data=toRaw(hex, length);
|
|
long double val;
|
|
if (length==4) { // float
|
|
val=*((float*)data);
|
|
} else if (length==8) { // double
|
|
val=*((double*)data);
|
|
} else if (length==10) { // long double
|
|
val=*((long double*)data);
|
|
} else {
|
|
val=*((float*)data);
|
|
}
|
|
delete[] data;
|
|
|
|
return val;
|
|
}
|
|
|
|
static TQString toFloat(const TQString& hex, char p)
|
|
{
|
|
uint bits;
|
|
uint prec=6;
|
|
if (hex.length()<=10) { bits=32; prec=6; }
|
|
else if (hex.length()<=18) { bits=64; prec=17; }
|
|
else { bits=80; prec=20; }
|
|
|
|
TQString cooked=TQString::number(extractNumber(hex), p, prec);
|
|
if (p=='e') {
|
|
prec+=7;
|
|
while (cooked.length()<prec) cooked=cooked.prepend(" ");
|
|
}
|
|
return cooked;
|
|
}
|
|
|
|
static TQString convertSingle(const TQString& raw, const RegisterDisplay mode)
|
|
{
|
|
switch (mode.presentationFlag()) {
|
|
case RegisterDisplay::binary: return toBinary(raw);
|
|
case RegisterDisplay::octal: return toOctal(raw);
|
|
case RegisterDisplay::decimal: return toDecimal(raw);
|
|
case RegisterDisplay::hex: return raw;
|
|
case RegisterDisplay::bcd: return toBCD(raw);
|
|
case RegisterDisplay::realE: return toFloat(raw, 'e');
|
|
case RegisterDisplay::realG: return toFloat(raw, 'g');
|
|
case RegisterDisplay::realF: return toFloat(raw, 'f');
|
|
default: return raw;
|
|
}
|
|
}
|
|
|
|
TQString convertRaw(const RegisterInfo reg, RegisterDisplay mode)
|
|
{
|
|
TQString cooked;
|
|
uint totalNibles=0, nibles=mode.bits()>>2;
|
|
if (RegisterDisplay::nada!=mode.presentationFlag() &&
|
|
reg.rawValue.length() > 2 && reg.rawValue[0] == '0' && reg.rawValue[1] == 'x')
|
|
{
|
|
if ("uint128"==reg.type) totalNibles=32;
|
|
else if ("uint64"==reg.type) totalNibles=16;
|
|
else if (reg.type.isEmpty()) totalNibles=nibles;
|
|
else {
|
|
return "don't know how to handle vector type <"+reg.type+">";
|
|
}
|
|
if (0==nibles) nibles=8; // default to 4 byte, 32 bits values
|
|
if (nibles>totalNibles) totalNibles=nibles; // minimum one value
|
|
|
|
TQString raw=reg.rawValue.right(reg.rawValue.length()-2); // clip off "0x"
|
|
while (raw.length()<totalNibles) raw.prepend("0"); // pad out to totalNibles
|
|
|
|
TQString separator=","; // locale-specific?
|
|
for (int nib=totalNibles-nibles; nib>=0; nib-=nibles) {
|
|
TQString qstr=convertSingle(raw.mid(nib, nibles).prepend("0x"), mode);
|
|
|
|
if (nib==int(totalNibles-nibles)) cooked=qstr+cooked;
|
|
else cooked=qstr+separator+cooked;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
cooked = reg.cookedValue;
|
|
}
|
|
if (cooked.at(0)!=' ' && cooked.at(0)!='-' && cooked.at(0)!='+')
|
|
cooked.prepend(" ");
|
|
return cooked;
|
|
}
|
|
|
|
void RegisterViewItem::setValue(const RegisterInfo& reg)
|
|
{
|
|
m_reg = reg;
|
|
|
|
setText(1, reg.rawValue);
|
|
TQString cookedValue = convertRaw(reg, m_mode);
|
|
setText(2, cookedValue);
|
|
}
|
|
|
|
void RegisterViewItem::setMode(RegisterDisplay mode)
|
|
{
|
|
m_mode = mode;
|
|
|
|
TQString cookedValue = convertRaw(m_reg, mode);
|
|
setText(2, cookedValue);
|
|
}
|
|
|
|
void RegisterViewItem::paintCell(TQPainter* p, const TQColorGroup& cg,
|
|
int column, int width, int alignment)
|
|
{
|
|
if (m_changes) {
|
|
TQColorGroup newcg = cg;
|
|
newcg.setColor(TQColorGroup::Text, red);
|
|
TQListViewItem::paintCell(p, newcg, column, width, alignment);
|
|
} else {
|
|
TQListViewItem::paintCell(p, cg, column, width, alignment);
|
|
}
|
|
}
|
|
|
|
|
|
RegisterView::RegisterView(TQWidget* parent, const char* name) :
|
|
TQListView(parent, name)
|
|
{
|
|
setSorting(-1);
|
|
setFont(TDEGlobalSettings::fixedFont());
|
|
|
|
TQPixmap iconRegs = UserIcon("regs.xpm");
|
|
TQPixmap iconWatchcoded = UserIcon("watchcoded.xpm");
|
|
TQPixmap iconWatch = UserIcon("watch.xpm");
|
|
|
|
addColumn(TQIconSet(iconRegs), i18n("Register"));
|
|
addColumn(TQIconSet(iconWatchcoded), i18n("Value"));
|
|
addColumn(TQIconSet(iconWatch), i18n("Decoded value"));
|
|
|
|
setColumnAlignment(0,AlignLeft);
|
|
setColumnAlignment(1,AlignLeft);
|
|
setColumnAlignment(2,AlignLeft);
|
|
|
|
setAllColumnsShowFocus( true );
|
|
header()->setClickEnabled(false);
|
|
|
|
connect(this, SIGNAL(contextMenuRequested(TQListViewItem*, const TQPoint&, int)),
|
|
SLOT(rightButtonClicked(TQListViewItem*,const TQPoint&,int)));
|
|
|
|
m_modemenu = new TQPopupMenu(this, "ERROR");
|
|
for (uint i=0; i<sizeof(menuitems)/sizeof(MenuPair); i++) {
|
|
if (menuitems[i].isSeparator())
|
|
m_modemenu->insertSeparator();
|
|
else
|
|
m_modemenu->insertItem(i18n(menuitems[i].name), menuitems[i].mode);
|
|
}
|
|
connect(m_modemenu,SIGNAL(activated(int)),SLOT(slotModeChange(int)));
|
|
|
|
new GroupingViewItem(this, "MIPS VU", "^vu.*",
|
|
RegisterDisplay::bits32|RegisterDisplay::realE);
|
|
new GroupingViewItem(this, "AltiVec", "^vr.*",
|
|
RegisterDisplay::bits32|RegisterDisplay::realE);
|
|
new GroupingViewItem(this, "POWER real", "^fpr.*",
|
|
RegisterDisplay::bits32|RegisterDisplay::realE);
|
|
new GroupingViewItem(this, "MMX", "^mm.*",
|
|
RegisterDisplay::bits32|RegisterDisplay::realE);
|
|
new GroupingViewItem(this, "SSE", "^xmm.*",
|
|
RegisterDisplay::bits32|RegisterDisplay::realE);
|
|
new GroupingViewItem(this, "x87", "^st.*",
|
|
RegisterDisplay::bits80|RegisterDisplay::realE);
|
|
new GroupingViewItem(this, i18n("x86/x87 segment"),
|
|
"(^cs$|^ss$|^ds$|^es$|^fs$|^gs$|^fiseg$|^foseg$)",
|
|
RegisterDisplay::nada);
|
|
new GroupingViewItem(this, i18n("Flags"),
|
|
"(^eflags$|^fctrl$|^mxcsr$|^cr$|^fpscr$|^vscr$|^ftag$|^fstat$)",
|
|
RegisterDisplay::bits32|RegisterDisplay::binary);
|
|
new GroupingViewItem(this, i18n("GP and others"), "^$",
|
|
RegisterDisplay::nada);
|
|
|
|
updateGroupVisibility();
|
|
setRootIsDecorated(true);
|
|
|
|
resize(200,300);
|
|
}
|
|
|
|
RegisterView::~RegisterView()
|
|
{
|
|
}
|
|
|
|
GroupingViewItem* RegisterView::findMatchingGroup(const TQString& regName)
|
|
{
|
|
for (TQListViewItem* it = firstChild(); it != 0; it = it->nextSibling())
|
|
{
|
|
GroupingViewItem* i = static_cast<GroupingViewItem*>(it);
|
|
if (i->matchName(regName))
|
|
return i;
|
|
}
|
|
// not better match found, so return "GP and others"
|
|
return static_cast<GroupingViewItem*>(firstChild());
|
|
}
|
|
|
|
GroupingViewItem* RegisterView::findGroup(const TQString& groupName)
|
|
{
|
|
for (TQListViewItem* it = firstChild(); it != 0; it = it->nextSibling())
|
|
{
|
|
if (it->text(0) == groupName)
|
|
return static_cast<GroupingViewItem*>(it);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void RegisterView::updateGroupVisibility()
|
|
{
|
|
for (TQListViewItem* it = firstChild(); it != 0; it = it->nextSibling())
|
|
{
|
|
it->setVisible(it->childCount() > 0);
|
|
}
|
|
}
|
|
|
|
void RegisterView::updateRegisters(const std::list<RegisterInfo>& regs)
|
|
{
|
|
setUpdatesEnabled(false);
|
|
|
|
// mark all items as 'not found'
|
|
for (RegMap::iterator i = m_registers.begin(); i != m_registers.end(); ++i)
|
|
{
|
|
i->second->m_found = false;
|
|
}
|
|
|
|
// parse register values
|
|
// must iterate last to first, since TQListView inserts at the top
|
|
for (std::list<RegisterInfo>::const_reverse_iterator reg = regs.rbegin(); reg != regs.rend(); ++reg)
|
|
{
|
|
// check if this is a new register
|
|
RegMap::iterator i = m_registers.find(reg->regName);
|
|
|
|
if (i != m_registers.end())
|
|
{
|
|
RegisterViewItem* it = i->second;
|
|
it->m_found = true;
|
|
if (it->m_reg.rawValue != reg->rawValue ||
|
|
it->m_reg.cookedValue != reg->cookedValue)
|
|
{
|
|
it->m_changes = true;
|
|
it->setValue(*reg);
|
|
repaintItem(it);
|
|
} else {
|
|
/*
|
|
* If there was a change last time, but not now, we
|
|
* must revert the color.
|
|
*/
|
|
if (it->m_changes) {
|
|
it->m_changes = false;
|
|
repaintItem(it);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
GroupingViewItem* group = findMatchingGroup(reg->regName);
|
|
m_registers[reg->regName] =
|
|
new RegisterViewItem(group, *reg);
|
|
}
|
|
}
|
|
|
|
// remove all 'not found' items;
|
|
TQStringList del;
|
|
for (RegMap::iterator i = m_registers.begin(); i != m_registers.end(); ++i)
|
|
{
|
|
if (!i->second->m_found) {
|
|
del.push_back(i->first);
|
|
}
|
|
}
|
|
for (TQStringList::Iterator i = del.begin(); i != del.end(); ++i)
|
|
{
|
|
RegMap::iterator it = m_registers.find(*i);
|
|
delete it->second;
|
|
m_registers.erase(it);
|
|
}
|
|
|
|
updateGroupVisibility();
|
|
setUpdatesEnabled(true);
|
|
triggerUpdate();
|
|
}
|
|
|
|
|
|
void RegisterView::rightButtonClicked(TQListViewItem* item, const TQPoint& p, int)
|
|
{
|
|
if (item) {
|
|
RegisterDisplay mode=static_cast<ModeItem*>(item)->mode();
|
|
for (unsigned int i = 0; i<sizeof(menuitems)/sizeof(MenuPair); i++) {
|
|
m_modemenu->setItemChecked(menuitems[i].mode,
|
|
mode.contains(menuitems[i].mode));
|
|
}
|
|
m_modemenu->setCaption(item->text(0));
|
|
m_modemenu->popup(p);
|
|
}
|
|
}
|
|
|
|
void RegisterView::slotModeChange(int pcode)
|
|
{
|
|
RegMap::iterator it=m_registers.find(m_modemenu->caption());
|
|
ModeItem* view;
|
|
if (it != m_registers.end())
|
|
view = it->second;
|
|
else
|
|
view = findGroup(m_modemenu->caption());
|
|
|
|
if (view) {
|
|
RegisterDisplay mode = view->mode();
|
|
mode.changeFlag(pcode);
|
|
view->setMode(mode);
|
|
}
|
|
}
|
|
|
|
void RegisterView::paletteChange(const TQPalette& oldPal)
|
|
{
|
|
setFont(TDEGlobalSettings::fixedFont());
|
|
TQListView::paletteChange(oldPal);
|
|
}
|
|
|
|
#include "regwnd.moc"
|