Update version number and build instructions in the spec file.
[kdbg.git] / kdbg / regwnd.cpp
blob82d7b2981668921850eb51ea34e6083a1b2fbbde
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 <kglobalsettings.h>
10 #include <klocale.h> /* i18n */
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);
116 bool matchName(const QString& str) const
118 return matcher.exactMatch(str);
121 virtual void setMode(RegisterDisplay mode)
123 gmode=mode;
124 for(int i = 0; i < childCount(); i++)
126 static_cast<ModeItem*>(child(i))->setMode(gmode);
130 virtual RegisterDisplay mode()
132 return gmode;
135 private:
136 QRegExp matcher;
137 RegisterDisplay gmode;
140 class RegisterViewItem : public ModeItem
142 public:
143 RegisterViewItem(GroupingViewItem* parent,
144 const RegisterInfo& regInfo);
145 ~RegisterViewItem();
147 void setValue(const RegisterInfo& regInfo);
148 virtual void setMode(RegisterDisplay mode);
149 virtual RegisterDisplay mode() { return m_mode; }
150 RegisterInfo m_reg;
151 RegisterDisplay m_mode; /* display mode */
152 bool m_changes;
153 bool m_found;
157 RegisterViewItem::RegisterViewItem(GroupingViewItem* parent,
158 const RegisterInfo& regInfo) :
159 ModeItem(parent),
160 m_reg(regInfo),
161 m_changes(false),
162 m_found(true)
164 setValue(m_reg);
165 setText(0, m_reg.regName);
166 setMode(parent->mode());
169 RegisterViewItem::~RegisterViewItem()
174 * We must be careful when converting the hex value because
175 * it may exceed this computer's long values.
177 inline int hexCharToDigit(char h)
179 if (h < '0')
180 return -1;
181 if (h <= '9')
182 return h - '0';
183 if (h < 'A')
184 return -1;
185 if (h <= 'F')
186 return h - ('A' - 10);
187 if (h < 'a')
188 return -1;
189 if (h <= 'f')
190 return h - ('a' - 10);
191 return -1;
194 static QString toBinary(QString hex)
196 static const char digits[16][8] = {
197 "0000", "0001", "0010", "0011", "0100", "0101", "0110", "0111",
198 "1000", "1001", "1010", "1011", "1100", "1101", "1110", "1111"
200 QString result;
202 for (int i = 2; i < hex.length(); i++) {
203 int idx = hexCharToDigit(hex[i].toLatin1());
204 if (idx < 0) {
205 // not a hex digit; no conversion
206 return hex;
208 const char* bindigits = digits[idx];
209 result += bindigits;
211 // remove leading zeros
212 switch (hexCharToDigit(hex[2].toLatin1())) {
213 case 0: case 1: result.remove(0, 3); break;
214 case 2: case 3: result.remove(0, 2); break;
215 case 4: case 5:
216 case 6: case 7: result.remove(0, 1); break;
218 return result;
221 static QString toOctal(QString hex)
223 QString result;
224 int shift = 0;
225 unsigned v = 0;
226 for (int i = hex.length()-1; i >= 2; i--) {
227 int idx = hexCharToDigit(hex[i].toLatin1());
228 if (idx < 0)
229 return hex;
230 v += idx << shift;
231 result.insert(0, (v & 7) + '0');
232 v >>= 3;
233 shift++;
234 if (shift == 3) {
235 // an extra digit this round
236 result.insert(0, v + '0');
237 shift = v = 0;
240 if (v != 0) {
241 result.insert(0, v + '0');
243 return "0" + result;
246 static QString toDecimal(QString hex)
249 * We convert only numbers that are small enough for this computer's
250 * size of long integers.
252 if (hex.length() > int(sizeof(unsigned long)*2+2)) /* count in leading "0x" */
253 return hex;
255 bool ok = false;
256 unsigned long val = hex.toULong(&ok, 0);
257 if (!ok)
258 return hex;
259 else
260 return QString().setNum(val);
263 static QString toBCD(const QString& hex)
265 return hex.right(2);
268 static char* toRaw(const QString& hex, uint& length)
270 static uint testNum=1;
271 static void* testVoid=(void*)&testNum;
272 static char* testChar=(char*)testVoid;
273 static bool littleendian=(*testChar==1);
275 length=((hex.length()-2)%2)+((hex.length()-2)/2);
276 char* data=new char[length];
278 if (littleendian) {
279 uint j=0;
280 if (hex.length()<=2) return 0;
281 for (int i=hex.length()-1; i>=2; ) {
282 if (j%2==0)
283 data[j/2]=hexCharToDigit(hex[i].toLatin1());
284 else
285 data[j/2]|=(hexCharToDigit(hex[i].toLatin1())<<4);
286 i--;j++;
288 } else { // big endian
289 uint j=0;
290 if (hex.length()<=2) return 0;
291 for (int i=2; i<hex.length(); ) {
292 if (j%2==0)
293 data[j/2]=hexCharToDigit(hex[i].toLatin1())<<4;
294 else
295 data[j/2]|=hexCharToDigit(hex[i].toLatin1());
296 i++;j++;
299 return data;
302 static long double extractNumber(const QString& hex)
304 uint length;
305 char* data=toRaw(hex, length);
306 long double val;
307 if (length==4) { // float
308 val=*((float*)data);
309 } else if (length==8) { // double
310 val=*((double*)data);
311 } else if (length==10) { // long double
312 val=*((long double*)data);
313 } else {
314 val=*((float*)data);
316 delete[] data;
318 return val;
321 static QString toFloat(const QString& hex, char p)
323 int prec=6;
324 if (hex.length() <= 10)
325 prec = 6;
326 else if (hex.length() <= 18)
327 prec = 17;
328 else
329 prec = 20;
331 char fmt[8] = "%.*Lf";
332 fmt[4] = p;
333 char buf[50];
334 sprintf(buf, fmt, prec, extractNumber(hex));
335 QString cooked = QString::fromLatin1(buf);
336 if (p=='e') {
337 prec+=7;
338 while (cooked.length()<prec) cooked=cooked.prepend(" ");
340 return cooked;
343 static QString convertSingle(const QString& raw, const RegisterDisplay mode)
345 switch (mode.presentationFlag()) {
346 case RegisterDisplay::binary: return toBinary(raw);
347 case RegisterDisplay::octal: return toOctal(raw);
348 case RegisterDisplay::decimal: return toDecimal(raw);
349 case RegisterDisplay::hex: return raw;
350 case RegisterDisplay::bcd: return toBCD(raw);
351 case RegisterDisplay::realE: return toFloat(raw, 'e');
352 case RegisterDisplay::realG: return toFloat(raw, 'g');
353 case RegisterDisplay::realF: return toFloat(raw, 'f');
354 default: return raw;
358 QString convertRaw(const RegisterInfo reg, RegisterDisplay mode)
360 QString cooked;
361 int totalNibles=0, nibles=mode.bits()>>2;
362 if (RegisterDisplay::nada!=mode.presentationFlag() &&
363 reg.rawValue.length() > 2 && reg.rawValue[0] == '0' && reg.rawValue[1] == 'x')
365 if ("uint128"==reg.type) totalNibles=32;
366 else if ("uint64"==reg.type) totalNibles=16;
367 else if (reg.type.isEmpty()) totalNibles=nibles;
368 else {
369 return "don't know how to handle vector type <"+reg.type+">";
371 if (0==nibles) nibles=8; // default to 4 byte, 32 bits values
372 if (nibles>totalNibles) totalNibles=nibles; // minimum one value
374 QString raw=reg.rawValue.right(reg.rawValue.length()-2); // clip off "0x"
375 while (raw.length()<totalNibles) raw.prepend("0"); // pad out to totalNibles
377 QString separator=","; // locale-specific?
378 for (int nib=totalNibles-nibles; nib>=0; nib-=nibles) {
379 QString qstr=convertSingle(raw.mid(nib, nibles).prepend("0x"), mode);
381 if (nib==int(totalNibles-nibles)) cooked=qstr+cooked;
382 else cooked=qstr+separator+cooked;
385 else
387 cooked = reg.cookedValue;
389 if (!cooked.isEmpty() && cooked.at(0) != ' ' && cooked.at(0) != '-' && cooked.at(0) != '+')
390 cooked.prepend(" ");
391 return cooked;
394 void RegisterViewItem::setValue(const RegisterInfo& reg)
396 m_reg = reg;
398 setText(1, reg.rawValue);
399 QString cookedValue = convertRaw(reg, m_mode);
400 setText(2, cookedValue);
403 void RegisterViewItem::setMode(RegisterDisplay mode)
405 m_mode = mode;
407 QString cookedValue = convertRaw(m_reg, mode);
408 setText(2, cookedValue);
412 RegisterView::RegisterView(QWidget* parent) :
413 QTreeWidget(parent)
415 setFont(KGlobalSettings::fixedFont());
417 QTreeWidgetItem* header = headerItem();
418 header->setText(0, i18n("Register"));
419 header->setText(1, i18n("Value"));
420 header->setText(2, i18n("Decoded value"));
422 setAllColumnsShowFocus(true);
424 m_modemenu = new QMenu("ERROR", this);
425 for (uint i=0; i<sizeof(menuitems)/sizeof(MenuPair); i++) {
426 if (menuitems[i].isSeparator())
427 m_modemenu->addSeparator();
428 else {
429 QAction* action = m_modemenu->addAction(i18n(menuitems[i].name));
430 action->setData(menuitems[i].mode);
431 action->setCheckable(true);
434 connect(m_modemenu, SIGNAL(triggered(QAction*)), SLOT(slotModeChange(QAction*)));
436 new GroupingViewItem(this, i18n("GP and others"), "^$",
437 RegisterDisplay::nada);
438 new GroupingViewItem(this, i18n("Flags"),
439 "(^eflags$|^fctrl$|^mxcsr$|^cr$|^fpscr$|^vscr$|^ftag$|^fstat$)",
440 RegisterDisplay::bits32|RegisterDisplay::binary);
441 new GroupingViewItem(this, i18n("x86/x87 segment"),
442 "(^cs$|^ss$|^ds$|^es$|^fs$|^gs$|^fiseg$|^foseg$)",
443 RegisterDisplay::nada);
444 new GroupingViewItem(this, "x87", "^st.*",
445 RegisterDisplay::bits80|RegisterDisplay::realE);
446 new GroupingViewItem(this, "SSE", "^xmm.*",
447 RegisterDisplay::bits32|RegisterDisplay::realE);
448 new GroupingViewItem(this, "MMX", "^mm.*",
449 RegisterDisplay::bits32|RegisterDisplay::realE);
450 new GroupingViewItem(this, "POWER real", "^fpr.*",
451 RegisterDisplay::bits32|RegisterDisplay::realE);
452 new GroupingViewItem(this, "AltiVec", "^vr.*",
453 RegisterDisplay::bits32|RegisterDisplay::realE);
454 new GroupingViewItem(this, "MIPS VU", "^vu.*",
455 RegisterDisplay::bits32|RegisterDisplay::realE);
457 updateGroupVisibility();
458 setRootIsDecorated(true);
460 resize(200,300);
463 RegisterView::~RegisterView()
467 GroupingViewItem* RegisterView::findMatchingGroup(const QString& regName)
469 for (int i = 0; i < topLevelItemCount(); i++)
471 GroupingViewItem* it = static_cast<GroupingViewItem*>(topLevelItem(i));
472 if (it->matchName(regName))
473 return it;
475 // not better match found, so return "GP and others"
476 return static_cast<GroupingViewItem*>(topLevelItem(0));
479 GroupingViewItem* RegisterView::findGroup(const QString& groupName)
481 for (int i = 0; i < topLevelItemCount(); i++)
483 QTreeWidgetItem* it = topLevelItem(i);
484 if (it->text(0) == groupName)
485 return static_cast<GroupingViewItem*>(it);
487 // return that nothing was found.
488 return 0;
491 // only show a group if it has subitems.
492 void RegisterView::updateGroupVisibility()
494 for(int i = 0; i < topLevelItemCount(); i++)
496 QTreeWidgetItem* item = topLevelItem(i);
497 item->setHidden(item->childCount() == 0);
501 void RegisterView::updateRegisters(const std::list<RegisterInfo>& regs)
503 setUpdatesEnabled(false);
505 // mark all items as 'not found'
506 for (RegMap::iterator i = m_registers.begin(); i != m_registers.end(); ++i)
508 i->second->m_found = false;
511 // parse register values
512 for (std::list<RegisterInfo>::const_iterator reg = regs.begin(); reg != regs.end(); ++reg)
514 // check if this is a new register
515 RegMap::iterator i = m_registers.find(reg->regName);
517 if (i != m_registers.end())
519 RegisterViewItem* it = i->second;
520 it->m_found = true;
521 if (it->m_reg.rawValue != reg->rawValue ||
522 it->m_reg.cookedValue != reg->cookedValue)
524 it->m_changes = true;
525 it->setValue(*reg);
527 it->setForeground(0,Qt::red);
528 it->setForeground(1,Qt::red);
529 it->setForeground(2,Qt::red);
531 } else {
533 * If there was a change last time, but not now, we
534 * must revert the color.
536 if (it->m_changes) {
537 it->m_changes = false;
538 it->setForeground(0,Qt::black);
539 it->setForeground(1,Qt::black);
540 it->setForeground(2,Qt::black);
544 else
546 GroupingViewItem* group = findMatchingGroup(reg->regName);
547 m_registers[reg->regName] =
548 new RegisterViewItem(group, *reg);
552 // remove all 'not found' items;
553 QStringList del;
554 for (RegMap::iterator i = m_registers.begin(); i != m_registers.end(); ++i)
556 if (!i->second->m_found) {
557 del.push_back(i->first);
560 for (QStringList::Iterator i = del.begin(); i != del.end(); ++i)
562 RegMap::iterator it = m_registers.find(*i);
563 delete it->second;
564 m_registers.erase(it);
567 updateGroupVisibility();
568 setUpdatesEnabled(true);
572 void RegisterView::contextMenuEvent(QContextMenuEvent* event)
574 QTreeWidgetItem *item = itemAt(event->pos());
576 if (item) {
577 RegisterDisplay mode=static_cast<ModeItem*>(item)->mode();
578 int i = 0;
579 foreach(QAction* action, m_modemenu->actions())
581 action->setChecked(mode.contains(menuitems[i].mode));
582 ++i;
584 m_modemenu->setTitle(item->text(0));
585 m_modemenu->popup(event->globalPos());
587 event->accept();
591 void RegisterView::slotModeChange(QAction* action)
593 RegMap::iterator it=m_registers.find(m_modemenu->title());
594 ModeItem* view;
595 if (it != m_registers.end())
596 view = it->second;
597 else
598 view = findGroup(m_modemenu->title());
600 if (view) {
601 RegisterDisplay mode = view->mode();
602 mode.changeFlag(action->data().toInt());
603 view->setMode(mode);
607 void RegisterView::paletteChange(const QPalette&)
609 setFont(KGlobalSettings::fixedFont());
612 #include "regwnd.moc"