Extend copyright to 2018.
[kdbg.git] / kdbg / regwnd.cpp
blob090af6d87d3e1565113baddb0b386df319d05631
1 /*
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.
5 */
7 #include "regwnd.h"
8 #include "dbgdriver.h"
9 #include <klocalizedstring.h> /* i18n */
10 #include <QFontDatabase>
11 #include <QMenu>
12 #include <QRegExp>
13 #include <QStringList>
14 #include <QHeaderView>
15 #include <QContextMenuEvent>
16 #include <stdlib.h> /* strtoul */
18 /**
19 * Register display modes
21 class RegisterDisplay {
22 public:
23 enum BitSize {
24 bits8 = 0x10,
25 bits16 = 0x20,
26 bits32 = 0x30,
27 bits64 = 0x40,
28 bits80 = 0x50,
29 bits128 = 0x60,
30 bitsUnknown = 0x70
33 enum Format {
34 nada = 0x01,
35 binary = 0x02,
36 octal = 0x03,
37 decimal = 0x04,
38 hex = 0x05,
39 bcd = 0x06,
40 realE = 0x07,
41 realG = 0x08,
42 realF = 0x09
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);
49 return val;
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);
58 private:
59 uint mode;
60 static uint bitMap[];
63 // helper struct
64 struct MenuPair
66 const char* name;
67 uint mode;
68 bool isSeparator() { return name == 0; }
71 static MenuPair menuitems[] = {
72 // treat as
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 },
81 { 0, 0 },
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[] = {
91 0, 8, 16, 32,
92 64, 80, 128, /*default*/32,
95 class ModeItem : public QTreeWidgetItem
97 public:
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
107 public:
108 GroupingViewItem(RegisterView* parent,
109 const QString& name, const QString& pattern,
110 RegisterDisplay mode) :
111 ModeItem(parent, name), matcher(pattern), gmode(mode)
113 setExpanded(true);
114 setFirstColumnSpanned(true);
117 bool matchName(const QString& str) const
119 return matcher.exactMatch(str);
122 virtual void setMode(RegisterDisplay mode)
124 gmode=mode;
125 for(int i = 0; i < childCount(); i++)
127 static_cast<ModeItem*>(child(i))->setMode(gmode);
131 virtual RegisterDisplay mode()
133 return gmode;
136 private:
137 QRegExp matcher;
138 RegisterDisplay gmode;
141 class RegisterViewItem : public ModeItem
143 public:
144 RegisterViewItem(GroupingViewItem* parent,
145 const RegisterInfo& regInfo);
146 ~RegisterViewItem();
148 void setValue(const RegisterInfo& regInfo);
149 virtual void setMode(RegisterDisplay mode);
150 virtual RegisterDisplay mode() { return m_mode; }
151 RegisterInfo m_reg;
152 RegisterDisplay m_mode; /* display mode */
153 bool m_changes;
154 bool m_found;
158 RegisterViewItem::RegisterViewItem(GroupingViewItem* parent,
159 const RegisterInfo& regInfo) :
160 ModeItem(parent),
161 m_reg(regInfo),
162 m_changes(false),
163 m_found(true)
165 setValue(m_reg);
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)
180 if (h < '0')
181 return -1;
182 if (h <= '9')
183 return h - '0';
184 if (h < 'A')
185 return -1;
186 if (h <= 'F')
187 return h - ('A' - 10);
188 if (h < 'a')
189 return -1;
190 if (h <= 'f')
191 return h - ('a' - 10);
192 return -1;
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"
201 QString result;
203 for (int i = 2; i < hex.length(); i++) {
204 int idx = hexCharToDigit(hex[i].toLatin1());
205 if (idx < 0) {
206 // not a hex digit; no conversion
207 return hex;
209 const char* bindigits = digits[idx];
210 result += bindigits;
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;
216 case 4: case 5:
217 case 6: case 7: result.remove(0, 1); break;
219 return result;
222 static QString toOctal(QString hex)
224 QString result;
225 int shift = 0;
226 unsigned v = 0;
227 for (int i = hex.length()-1; i >= 2; i--) {
228 int idx = hexCharToDigit(hex[i].toLatin1());
229 if (idx < 0)
230 return hex;
231 v += idx << shift;
232 result.insert(0, (v & 7) + '0');
233 v >>= 3;
234 shift++;
235 if (shift == 3) {
236 // an extra digit this round
237 result.insert(0, v + '0');
238 shift = v = 0;
241 if (v != 0) {
242 result.insert(0, v + '0');
244 return "0" + result;
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" */
254 return hex;
256 bool ok = false;
257 unsigned long val = hex.toULong(&ok, 0);
258 if (!ok)
259 return hex;
260 else
261 return QString().setNum(val);
264 static QString toBCD(const QString& hex)
266 return hex.right(2);
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];
279 if (littleendian) {
280 uint j=0;
281 if (hex.length()<=2) return 0;
282 for (int i=hex.length()-1; i>=2; ) {
283 if (j%2==0)
284 data[j/2]=hexCharToDigit(hex[i].toLatin1());
285 else
286 data[j/2]|=(hexCharToDigit(hex[i].toLatin1())<<4);
287 i--;j++;
289 } else { // big endian
290 uint j=0;
291 if (hex.length()<=2) return 0;
292 for (int i=2; i<hex.length(); ) {
293 if (j%2==0)
294 data[j/2]=hexCharToDigit(hex[i].toLatin1())<<4;
295 else
296 data[j/2]|=hexCharToDigit(hex[i].toLatin1());
297 i++;j++;
300 return data;
303 static long double extractNumber(const QString& hex)
305 uint length;
306 char* data=toRaw(hex, length);
307 long double val;
308 if (length==4) { // float
309 val=*((float*)data);
310 } else if (length==8) { // double
311 val=*((double*)data);
312 } else if (length==10) { // long double
313 val=*((long double*)data);
314 } else {
315 val=*((float*)data);
317 delete[] data;
319 return val;
322 static QString toFloat(const QString& hex, char p)
324 int prec=6;
325 if (hex.length() <= 10)
326 prec = 6;
327 else if (hex.length() <= 18)
328 prec = 17;
329 else
330 prec = 20;
332 char fmt[8] = "%.*Lf";
333 fmt[4] = p;
334 char buf[50];
335 sprintf(buf, fmt, prec, extractNumber(hex));
336 QString cooked = QString::fromLatin1(buf);
337 if (p=='e') {
338 prec+=7;
339 while (cooked.length()<prec) cooked=cooked.prepend(" ");
341 return cooked;
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');
355 default: return raw;
359 QString convertRaw(const RegisterInfo reg, RegisterDisplay mode)
361 QString cooked;
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;
369 else {
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;
386 else
388 cooked = reg.cookedValue;
390 if (!cooked.isEmpty() && cooked.at(0) != ' ' && cooked.at(0) != '-' && cooked.at(0) != '+')
391 cooked.prepend(" ");
392 return cooked;
395 void RegisterViewItem::setValue(const RegisterInfo& reg)
397 m_reg = reg;
399 setText(1, reg.rawValue);
400 QString cookedValue = convertRaw(reg, m_mode);
401 setText(2, cookedValue);
404 void RegisterViewItem::setMode(RegisterDisplay mode)
406 m_mode = mode;
408 QString cookedValue = convertRaw(m_reg, mode);
409 setText(2, cookedValue);
413 RegisterView::RegisterView(QWidget* parent) :
414 QTreeWidget(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();
429 else {
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);
461 resize(200,300);
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))
474 return it;
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.
489 return 0;
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;
521 it->m_found = true;
522 if (it->m_reg.rawValue != reg->rawValue ||
523 it->m_reg.cookedValue != reg->cookedValue)
525 it->m_changes = true;
526 it->setValue(*reg);
528 it->setForeground(0,Qt::red);
529 it->setForeground(1,Qt::red);
530 it->setForeground(2,Qt::red);
532 } else {
534 * If there was a change last time, but not now, we
535 * must revert the color.
537 if (it->m_changes) {
538 it->m_changes = false;
539 it->setForeground(0,Qt::black);
540 it->setForeground(1,Qt::black);
541 it->setForeground(2,Qt::black);
545 else
547 GroupingViewItem* group = findMatchingGroup(reg->regName);
548 m_registers[reg->regName] =
549 new RegisterViewItem(group, *reg);
553 // remove all 'not found' items;
554 QStringList del;
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);
564 delete it->second;
565 m_registers.erase(it);
568 updateGroupVisibility();
569 setUpdatesEnabled(true);
573 void RegisterView::contextMenuEvent(QContextMenuEvent* event)
575 QTreeWidgetItem *item = itemAt(event->pos());
577 if (item) {
578 RegisterDisplay mode=static_cast<ModeItem*>(item)->mode();
579 int i = 0;
580 foreach(QAction* action, m_modemenu->actions())
582 action->setChecked(mode.contains(menuitems[i].mode));
583 ++i;
585 m_modemenu->setTitle(item->text(0));
586 m_modemenu->popup(event->globalPos());
588 event->accept();
592 void RegisterView::slotModeChange(QAction* action)
594 RegMap::iterator it=m_registers.find(m_modemenu->title());
595 ModeItem* view;
596 if (it != m_registers.end())
597 view = it->second;
598 else
599 view = findGroup(m_modemenu->title());
601 if (view) {
602 RegisterDisplay mode = view->mode();
603 mode.changeFlag(action->data().toInt());
604 view->setMode(mode);
608 void RegisterView::changeEvent(QEvent* ev)
610 switch (ev->type()) {
611 case QEvent::ApplicationFontChange:
612 case QEvent::FontChange:
613 setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont));
614 break;
615 default:
616 break;
618 QTreeWidget::changeEvent(ev);
621 #include "regwnd.moc"