2 * Copyright Max Judin, Johannes Sixt, Daniel Kristjansson
3 * This file is licensed under the GNU General Public License Version 2.
4 * See the file COPYING in the toplevel directory of the source directory.
9 #include <klocalizedstring.h> /* i18n */
10 #include <QFontDatabase>
13 #include <QStringList>
14 #include <QHeaderView>
15 #include <QContextMenuEvent>
16 #include <stdlib.h> /* strtoul */
19 * Register display modes
21 class RegisterDisplay
{
44 RegisterDisplay() : mode(bitsUnknown
|nada
) { }
45 RegisterDisplay(uint newMode
) : mode(newMode
) { }
47 bool contains(uint pmode
) const {
48 bool val
=((mode
&0xf0)==pmode
)||((mode
&0x0f)==pmode
);
51 uint
bitsFlag() { return mode
&0xf0; }
52 uint
presentationFlag() const { return mode
&0x0f; }
53 uint
bits() const { return bitMap
[(mode
>>4)&0x07]; }
54 void changeFlag(uint code
) {
55 uint mask
=((code
&0xf0)==code
)?0x0f:0xf0;
56 mode
= code
| (mode
& mask
);
68 bool isSeparator() { return name
== 0; }
71 static MenuPair menuitems
[] = {
73 { I18N_NOOP("&GDB default"), RegisterDisplay::nada
},
74 { I18N_NOOP("&Binary"), RegisterDisplay::binary
},
75 { I18N_NOOP("&Octal"), RegisterDisplay::octal
},
76 { I18N_NOOP("&Decimal"), RegisterDisplay::decimal
},
77 { I18N_NOOP("He&xadecimal"), RegisterDisplay::hex
},
78 { I18N_NOOP("Real (&e)"), RegisterDisplay::realE
},
79 { I18N_NOOP("Real (&f)"), RegisterDisplay::realF
},
80 { I18N_NOOP("&Real (g)"), RegisterDisplay::realG
},
82 { "8 bits", RegisterDisplay::bits8
},
83 { "16 bits", RegisterDisplay::bits16
},
84 { "32 bits", RegisterDisplay::bits32
},
85 { "64 bits", RegisterDisplay::bits64
},
86 { "80 bits", RegisterDisplay::bits80
},
87 { "128 bits",RegisterDisplay::bits128
},
90 uint
RegisterDisplay::bitMap
[] = {
92 64, 80, 128, /*default*/32,
95 class ModeItem
: public QTreeWidgetItem
98 ModeItem(QTreeWidget
* parent
, const QString
& name
) : QTreeWidgetItem(parent
, QStringList(name
)) {}
99 ModeItem(QTreeWidgetItem
* parent
) : QTreeWidgetItem(parent
) {}
101 virtual void setMode(RegisterDisplay mode
) = 0;
102 virtual RegisterDisplay
mode() = 0;
105 class GroupingViewItem
: public ModeItem
108 GroupingViewItem(RegisterView
* parent
,
109 const QString
& name
, const QString
& pattern
,
110 RegisterDisplay mode
) :
111 ModeItem(parent
, name
), matcher(pattern
), gmode(mode
)
114 setFirstColumnSpanned(true);
117 bool matchName(const QString
& str
) const
119 return matcher
.exactMatch(str
);
122 virtual void setMode(RegisterDisplay mode
)
125 for(int i
= 0; i
< childCount(); i
++)
127 static_cast<ModeItem
*>(child(i
))->setMode(gmode
);
131 virtual RegisterDisplay
mode()
138 RegisterDisplay gmode
;
141 class RegisterViewItem
: public ModeItem
144 RegisterViewItem(GroupingViewItem
* parent
,
145 const RegisterInfo
& regInfo
);
148 void setValue(const RegisterInfo
& regInfo
);
149 virtual void setMode(RegisterDisplay mode
);
150 virtual RegisterDisplay
mode() { return m_mode
; }
152 RegisterDisplay m_mode
; /* display mode */
158 RegisterViewItem::RegisterViewItem(GroupingViewItem
* parent
,
159 const RegisterInfo
& regInfo
) :
166 setText(0, m_reg
.regName
);
167 setMode(parent
->mode());
170 RegisterViewItem::~RegisterViewItem()
175 * We must be careful when converting the hex value because
176 * it may exceed this computer's long values.
178 inline int hexCharToDigit(char h
)
187 return h
- ('A' - 10);
191 return h
- ('a' - 10);
195 static QString
toBinary(QString hex
)
197 static const char digits
[16][8] = {
198 "0000", "0001", "0010", "0011", "0100", "0101", "0110", "0111",
199 "1000", "1001", "1010", "1011", "1100", "1101", "1110", "1111"
203 for (int i
= 2; i
< hex
.length(); i
++) {
204 int idx
= hexCharToDigit(hex
[i
].toLatin1());
206 // not a hex digit; no conversion
209 const char* bindigits
= digits
[idx
];
212 // remove leading zeros
213 switch (hexCharToDigit(hex
[2].toLatin1())) {
214 case 0: case 1: result
.remove(0, 3); break;
215 case 2: case 3: result
.remove(0, 2); break;
217 case 6: case 7: result
.remove(0, 1); break;
222 static QString
toOctal(QString hex
)
227 for (int i
= hex
.length()-1; i
>= 2; i
--) {
228 int idx
= hexCharToDigit(hex
[i
].toLatin1());
232 result
.insert(0, (v
& 7) + '0');
236 // an extra digit this round
237 result
.insert(0, v
+ '0');
242 result
.insert(0, v
+ '0');
247 static QString
toDecimal(QString hex
)
250 * We convert only numbers that are small enough for this computer's
251 * size of long integers.
253 if (hex
.length() > int(sizeof(unsigned long)*2+2)) /* count in leading "0x" */
257 unsigned long val
= hex
.toULong(&ok
, 0);
261 return QString().setNum(val
);
264 static QString
toBCD(const QString
& hex
)
269 static char* toRaw(const QString
& hex
, uint
& length
)
271 static uint testNum
=1;
272 static void* testVoid
=(void*)&testNum
;
273 static char* testChar
=(char*)testVoid
;
274 static bool littleendian
=(*testChar
==1);
276 length
=((hex
.length()-2)%2)+((hex
.length()-2)/2);
277 char* data
=new char[length
];
281 if (hex
.length()<=2) return 0;
282 for (int i
=hex
.length()-1; i
>=2; ) {
284 data
[j
/2]=hexCharToDigit(hex
[i
].toLatin1());
286 data
[j
/2]|=(hexCharToDigit(hex
[i
].toLatin1())<<4);
289 } else { // big endian
291 if (hex
.length()<=2) return 0;
292 for (int i
=2; i
<hex
.length(); ) {
294 data
[j
/2]=hexCharToDigit(hex
[i
].toLatin1())<<4;
296 data
[j
/2]|=hexCharToDigit(hex
[i
].toLatin1());
303 static long double extractNumber(const QString
& hex
)
306 char* data
=toRaw(hex
, length
);
308 if (length
==4) { // float
310 } else if (length
==8) { // double
311 val
=*((double*)data
);
312 } else if (length
==10) { // long double
313 val
=*((long double*)data
);
322 static QString
toFloat(const QString
& hex
, char p
)
325 if (hex
.length() <= 10)
327 else if (hex
.length() <= 18)
332 char fmt
[8] = "%.*Lf";
335 sprintf(buf
, fmt
, prec
, extractNumber(hex
));
336 QString cooked
= QString::fromLatin1(buf
);
339 while (cooked
.length()<prec
) cooked
=cooked
.prepend(" ");
344 static QString
convertSingle(const QString
& raw
, const RegisterDisplay mode
)
346 switch (mode
.presentationFlag()) {
347 case RegisterDisplay::binary
: return toBinary(raw
);
348 case RegisterDisplay::octal
: return toOctal(raw
);
349 case RegisterDisplay::decimal
: return toDecimal(raw
);
350 case RegisterDisplay::hex
: return raw
;
351 case RegisterDisplay::bcd
: return toBCD(raw
);
352 case RegisterDisplay::realE
: return toFloat(raw
, 'e');
353 case RegisterDisplay::realG
: return toFloat(raw
, 'g');
354 case RegisterDisplay::realF
: return toFloat(raw
, 'f');
359 QString
convertRaw(const RegisterInfo reg
, RegisterDisplay mode
)
362 int totalNibles
=0, nibles
=mode
.bits()>>2;
363 if (RegisterDisplay::nada
!=mode
.presentationFlag() &&
364 reg
.rawValue
.length() > 2 && reg
.rawValue
[0] == '0' && reg
.rawValue
[1] == 'x')
366 if ("uint128"==reg
.type
) totalNibles
=32;
367 else if ("uint64"==reg
.type
) totalNibles
=16;
368 else if (reg
.type
.isEmpty()) totalNibles
=nibles
;
370 return "don't know how to handle vector type <"+reg
.type
+">";
372 if (0==nibles
) nibles
=8; // default to 4 byte, 32 bits values
373 if (nibles
>totalNibles
) totalNibles
=nibles
; // minimum one value
375 QString raw
=reg
.rawValue
.right(reg
.rawValue
.length()-2); // clip off "0x"
376 while (raw
.length()<totalNibles
) raw
.prepend("0"); // pad out to totalNibles
378 QString separator
=","; // locale-specific?
379 for (int nib
=totalNibles
-nibles
; nib
>=0; nib
-=nibles
) {
380 QString qstr
=convertSingle(raw
.mid(nib
, nibles
).prepend("0x"), mode
);
382 if (nib
==int(totalNibles
-nibles
)) cooked
=qstr
+cooked
;
383 else cooked
=qstr
+separator
+cooked
;
388 cooked
= reg
.cookedValue
;
390 if (!cooked
.isEmpty() && cooked
.at(0) != ' ' && cooked
.at(0) != '-' && cooked
.at(0) != '+')
395 void RegisterViewItem::setValue(const RegisterInfo
& reg
)
399 setText(1, reg
.rawValue
);
400 QString cookedValue
= convertRaw(reg
, m_mode
);
401 setText(2, cookedValue
);
404 void RegisterViewItem::setMode(RegisterDisplay mode
)
408 QString cookedValue
= convertRaw(m_reg
, mode
);
409 setText(2, cookedValue
);
413 RegisterView::RegisterView(QWidget
* parent
) :
416 setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont
));
418 QTreeWidgetItem
* header
= headerItem();
419 header
->setText(0, i18n("Register"));
420 header
->setText(1, i18n("Value"));
421 header
->setText(2, i18n("Decoded value"));
423 setAllColumnsShowFocus(true);
425 m_modemenu
= new QMenu("ERROR", this);
426 for (uint i
=0; i
<sizeof(menuitems
)/sizeof(MenuPair
); i
++) {
427 if (menuitems
[i
].isSeparator())
428 m_modemenu
->addSeparator();
430 QAction
* action
= m_modemenu
->addAction(i18n(menuitems
[i
].name
));
431 action
->setData(menuitems
[i
].mode
);
432 action
->setCheckable(true);
435 connect(m_modemenu
, SIGNAL(triggered(QAction
*)), SLOT(slotModeChange(QAction
*)));
437 new GroupingViewItem(this, i18n("GP and others"), "^$",
438 RegisterDisplay::nada
);
439 new GroupingViewItem(this, i18n("Flags"),
440 "(^eflags$|^fctrl$|^mxcsr$|^cr$|^fpscr$|^vscr$|^ftag$|^fstat$)",
441 RegisterDisplay::bits32
|RegisterDisplay::binary
);
442 new GroupingViewItem(this, i18n("x86/x87 segment"),
443 "(^cs$|^ss$|^ds$|^es$|^fs$|^gs$|^fiseg$|^foseg$)",
444 RegisterDisplay::nada
);
445 new GroupingViewItem(this, "x87", "^st.*",
446 RegisterDisplay::bits80
|RegisterDisplay::realE
);
447 new GroupingViewItem(this, "SSE", "^xmm.*",
448 RegisterDisplay::bits32
|RegisterDisplay::realE
);
449 new GroupingViewItem(this, "MMX", "^mm.*",
450 RegisterDisplay::bits32
|RegisterDisplay::realE
);
451 new GroupingViewItem(this, "POWER real", "^fpr.*",
452 RegisterDisplay::bits32
|RegisterDisplay::realE
);
453 new GroupingViewItem(this, "AltiVec", "^vr.*",
454 RegisterDisplay::bits32
|RegisterDisplay::realE
);
455 new GroupingViewItem(this, "MIPS VU", "^vu.*",
456 RegisterDisplay::bits32
|RegisterDisplay::realE
);
458 updateGroupVisibility();
459 setRootIsDecorated(true);
464 RegisterView::~RegisterView()
468 GroupingViewItem
* RegisterView::findMatchingGroup(const QString
& regName
)
470 for (int i
= 0; i
< topLevelItemCount(); i
++)
472 GroupingViewItem
* it
= static_cast<GroupingViewItem
*>(topLevelItem(i
));
473 if (it
->matchName(regName
))
476 // not better match found, so return "GP and others"
477 return static_cast<GroupingViewItem
*>(topLevelItem(0));
480 GroupingViewItem
* RegisterView::findGroup(const QString
& groupName
)
482 for (int i
= 0; i
< topLevelItemCount(); i
++)
484 QTreeWidgetItem
* it
= topLevelItem(i
);
485 if (it
->text(0) == groupName
)
486 return static_cast<GroupingViewItem
*>(it
);
488 // return that nothing was found.
492 // only show a group if it has subitems.
493 void RegisterView::updateGroupVisibility()
495 for(int i
= 0; i
< topLevelItemCount(); i
++)
497 QTreeWidgetItem
* item
= topLevelItem(i
);
498 item
->setHidden(item
->childCount() == 0);
502 void RegisterView::updateRegisters(const std::list
<RegisterInfo
>& regs
)
504 setUpdatesEnabled(false);
506 // mark all items as 'not found'
507 for (RegMap::iterator i
= m_registers
.begin(); i
!= m_registers
.end(); ++i
)
509 i
->second
->m_found
= false;
512 // parse register values
513 for (std::list
<RegisterInfo
>::const_iterator reg
= regs
.begin(); reg
!= regs
.end(); ++reg
)
515 // check if this is a new register
516 RegMap::iterator i
= m_registers
.find(reg
->regName
);
518 if (i
!= m_registers
.end())
520 RegisterViewItem
* it
= i
->second
;
522 if (it
->m_reg
.rawValue
!= reg
->rawValue
||
523 it
->m_reg
.cookedValue
!= reg
->cookedValue
)
525 it
->m_changes
= true;
528 it
->setForeground(0,Qt::red
);
529 it
->setForeground(1,Qt::red
);
530 it
->setForeground(2,Qt::red
);
534 * If there was a change last time, but not now, we
535 * must revert the color.
538 it
->m_changes
= false;
539 it
->setForeground(0,Qt::black
);
540 it
->setForeground(1,Qt::black
);
541 it
->setForeground(2,Qt::black
);
547 GroupingViewItem
* group
= findMatchingGroup(reg
->regName
);
548 m_registers
[reg
->regName
] =
549 new RegisterViewItem(group
, *reg
);
553 // remove all 'not found' items;
555 for (RegMap::iterator i
= m_registers
.begin(); i
!= m_registers
.end(); ++i
)
557 if (!i
->second
->m_found
) {
558 del
.push_back(i
->first
);
561 for (QStringList::Iterator i
= del
.begin(); i
!= del
.end(); ++i
)
563 RegMap::iterator it
= m_registers
.find(*i
);
565 m_registers
.erase(it
);
568 updateGroupVisibility();
569 setUpdatesEnabled(true);
573 void RegisterView::contextMenuEvent(QContextMenuEvent
* event
)
575 QTreeWidgetItem
*item
= itemAt(event
->pos());
578 RegisterDisplay mode
=static_cast<ModeItem
*>(item
)->mode();
580 foreach(QAction
* action
, m_modemenu
->actions())
582 action
->setChecked(mode
.contains(menuitems
[i
].mode
));
585 m_modemenu
->setTitle(item
->text(0));
586 m_modemenu
->popup(event
->globalPos());
592 void RegisterView::slotModeChange(QAction
* action
)
594 RegMap::iterator it
=m_registers
.find(m_modemenu
->title());
596 if (it
!= m_registers
.end())
599 view
= findGroup(m_modemenu
->title());
602 RegisterDisplay mode
= view
->mode();
603 mode
.changeFlag(action
->data().toInt());
608 void RegisterView::changeEvent(QEvent
* ev
)
610 switch (ev
->type()) {
611 case QEvent::ApplicationFontChange
:
612 case QEvent::FontChange
:
613 setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont
));
618 QTreeWidget::changeEvent(ev
);
621 #include "regwnd.moc"