Fix presentation of floating point registers.
[kdbg.git] / kdbg / regwnd.cpp
blob186ee6c0cba9ce2d0f5a8a55d750dc08a32f0a3e
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 <QPixmap>
10 #include <kglobalsettings.h>
11 #include <klocale.h> /* i18n */
12 #include <kiconloader.h>
13 #include <QMenu>
14 #include <QRegExp>
15 #include <QStringList>
16 #include <QHeaderView>
17 #include <QContextMenuEvent>
18 #include <stdlib.h> /* strtoul */
20 /**
21 * Register display modes
23 class RegisterDisplay {
24 public:
25 enum BitSize {
26 bits8 = 0x10,
27 bits16 = 0x20,
28 bits32 = 0x30,
29 bits64 = 0x40,
30 bits80 = 0x50,
31 bits128 = 0x60,
32 bitsUnknown = 0x70
35 enum Format {
36 nada = 0x01,
37 binary = 0x02,
38 octal = 0x03,
39 decimal = 0x04,
40 hex = 0x05,
41 bcd = 0x06,
42 realE = 0x07,
43 realG = 0x08,
44 realF = 0x09
46 RegisterDisplay() : mode(bitsUnknown|nada) { }
47 RegisterDisplay(uint newMode) : mode(newMode) { }
49 bool contains(uint pmode) const {
50 bool val=((mode&0xf0)==pmode)||((mode&0x0f)==pmode);
51 return val;
53 uint bitsFlag() { return mode&0xf0; }
54 uint presentationFlag() const { return mode&0x0f; }
55 uint bits() const { return bitMap[(mode>>4)&0x07]; }
56 void changeFlag(uint code) {
57 uint mask=((code&0xf0)==code)?0x0f:0xf0;
58 mode = code | (mode & mask);
60 private:
61 uint mode;
62 static uint bitMap[];
65 // helper struct
66 struct MenuPair
68 const char* name;
69 uint mode;
70 bool isSeparator() { return name == 0; }
73 static MenuPair menuitems[] = {
74 // treat as
75 { I18N_NOOP("&GDB default"), RegisterDisplay::nada },
76 { I18N_NOOP("&Binary"), RegisterDisplay::binary },
77 { I18N_NOOP("&Octal"), RegisterDisplay::octal },
78 { I18N_NOOP("&Decimal"), RegisterDisplay::decimal },
79 { I18N_NOOP("He&xadecimal"), RegisterDisplay::hex },
80 { I18N_NOOP("Real (&e)"), RegisterDisplay::realE },
81 { I18N_NOOP("Real (&f)"), RegisterDisplay::realF },
82 { I18N_NOOP("&Real (g)"), RegisterDisplay::realG },
83 { 0, 0 },
84 { "8 bits", RegisterDisplay::bits8 },
85 { "16 bits", RegisterDisplay::bits16 },
86 { "32 bits", RegisterDisplay::bits32 },
87 { "64 bits", RegisterDisplay::bits64 },
88 { "80 bits", RegisterDisplay::bits80 },
89 { "128 bits",RegisterDisplay::bits128 },
92 uint RegisterDisplay::bitMap[] = {
93 0, 8, 16, 32,
94 64, 80, 128, /*default*/32,
97 class ModeItem : public QTreeWidgetItem
99 public:
100 ModeItem(QTreeWidget* parent, const QString& name) : QTreeWidgetItem(parent, QStringList(name)) {}
101 ModeItem(QTreeWidgetItem* parent) : QTreeWidgetItem(parent) {}
103 virtual void setMode(RegisterDisplay mode) = 0;
104 virtual RegisterDisplay mode() = 0;
107 class GroupingViewItem : public ModeItem
109 public:
110 GroupingViewItem(RegisterView* parent,
111 const QString& name, const QString& pattern,
112 RegisterDisplay mode) :
113 ModeItem(parent, name), matcher(pattern), gmode(mode)
115 setExpanded(true);
118 bool matchName(const QString& str) const
120 return matcher.exactMatch(str);
123 virtual void setMode(RegisterDisplay mode)
125 gmode=mode;
126 for(int i = 0; i < childCount(); i++)
128 static_cast<ModeItem*>(child(i))->setMode(gmode);
132 virtual RegisterDisplay mode()
134 return gmode;
137 private:
138 QRegExp matcher;
139 RegisterDisplay gmode;
142 class RegisterViewItem : public ModeItem
144 public:
145 RegisterViewItem(GroupingViewItem* parent,
146 const RegisterInfo& regInfo);
147 ~RegisterViewItem();
149 void setValue(const RegisterInfo& regInfo);
150 virtual void setMode(RegisterDisplay mode);
151 virtual RegisterDisplay mode() { return m_mode; }
152 RegisterInfo m_reg;
153 RegisterDisplay m_mode; /* display mode */
154 bool m_changes;
155 bool m_found;
159 RegisterViewItem::RegisterViewItem(GroupingViewItem* parent,
160 const RegisterInfo& regInfo) :
161 ModeItem(parent),
162 m_reg(regInfo),
163 m_changes(false),
164 m_found(true)
166 setValue(m_reg);
167 setText(0, m_reg.regName);
168 setMode(parent->mode());
171 RegisterViewItem::~RegisterViewItem()
176 * We must be careful when converting the hex value because
177 * it may exceed this computer's long values.
179 inline int hexCharToDigit(char h)
181 if (h < '0')
182 return -1;
183 if (h <= '9')
184 return h - '0';
185 if (h < 'A')
186 return -1;
187 if (h <= 'F')
188 return h - ('A' - 10);
189 if (h < 'a')
190 return -1;
191 if (h <= 'f')
192 return h - ('a' - 10);
193 return -1;
196 static QString toBinary(QString hex)
198 static const char digits[16][8] = {
199 "0000", "0001", "0010", "0011", "0100", "0101", "0110", "0111",
200 "1000", "1001", "1010", "1011", "1100", "1101", "1110", "1111"
202 QString result;
204 for (int i = 2; i < hex.length(); i++) {
205 int idx = hexCharToDigit(hex[i].toLatin1());
206 if (idx < 0) {
207 // not a hex digit; no conversion
208 return hex;
210 const char* bindigits = digits[idx];
211 result += bindigits;
213 // remove leading zeros
214 switch (hexCharToDigit(hex[2].toLatin1())) {
215 case 0: case 1: result.remove(0, 3); break;
216 case 2: case 3: result.remove(0, 2); break;
217 case 4: case 5:
218 case 6: case 7: result.remove(0, 1); break;
220 return result;
223 static QString toOctal(QString hex)
225 QString result;
226 int shift = 0;
227 unsigned v = 0;
228 for (int i = hex.length()-1; i >= 2; i--) {
229 int idx = hexCharToDigit(hex[i].toLatin1());
230 if (idx < 0)
231 return hex;
232 v += idx << shift;
233 result.insert(0, (v & 7) + '0');
234 v >>= 3;
235 shift++;
236 if (shift == 3) {
237 // an extra digit this round
238 result.insert(0, v + '0');
239 shift = v = 0;
242 if (v != 0) {
243 result.insert(0, v + '0');
245 return "0" + result;
248 static QString toDecimal(QString hex)
251 * We convert only numbers that are small enough for this computer's
252 * size of long integers.
254 if (hex.length() > int(sizeof(unsigned long)*2+2)) /* count in leading "0x" */
255 return hex;
257 bool ok = false;
258 unsigned long val = hex.toULong(&ok, 0);
259 if (!ok)
260 return hex;
261 else
262 return QString().setNum(val);
265 static QString toBCD(const QString& hex)
267 return hex.right(2);
270 static char* toRaw(const QString& hex, uint& length)
272 static uint testNum=1;
273 static void* testVoid=(void*)&testNum;
274 static char* testChar=(char*)testVoid;
275 static bool littleendian=(*testChar==1);
277 length=((hex.length()-2)%2)+((hex.length()-2)/2);
278 char* data=new char[length];
280 if (littleendian) {
281 uint j=0;
282 if (hex.length()<=2) return 0;
283 for (int i=hex.length()-1; i>=2; ) {
284 if (j%2==0)
285 data[j/2]=hexCharToDigit(hex[i].toLatin1());
286 else
287 data[j/2]|=(hexCharToDigit(hex[i].toLatin1())<<4);
288 i--;j++;
290 } else { // big endian
291 uint j=0;
292 if (hex.length()<=2) return 0;
293 for (int i=2; i<hex.length(); ) {
294 if (j%2==0)
295 data[j/2]=hexCharToDigit(hex[i].toLatin1())<<4;
296 else
297 data[j/2]|=hexCharToDigit(hex[i].toLatin1());
298 i++;j++;
301 return data;
304 static long double extractNumber(const QString& hex)
306 uint length;
307 char* data=toRaw(hex, length);
308 long double val;
309 if (length==4) { // float
310 val=*((float*)data);
311 } else if (length==8) { // double
312 val=*((double*)data);
313 } else if (length==10) { // long double
314 val=*((long double*)data);
315 } else {
316 val=*((float*)data);
318 delete[] data;
320 return val;
323 static QString toFloat(const QString& hex, char p)
325 uint bits;
326 int prec=6;
327 if (hex.length()<=10) { bits=32; prec=6; }
328 else if (hex.length()<=18) { bits=64; prec=17; }
329 else { bits=80; 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.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 QPixmap iconRegs = UserIcon("regs.xpm");
418 QPixmap iconWatchcoded = UserIcon("watchcoded.xpm");
419 QPixmap iconWatch = UserIcon("watch.xpm");
421 QTreeWidgetItem* header = headerItem();
422 header->setText(0, i18n("Register"));
423 header->setIcon(0, QIcon(iconRegs));
425 header->setText(1, i18n("Value"));
426 header->setIcon(1, QIcon(iconWatchcoded));
428 header->setText(2, i18n("Decoded value"));
429 header->setIcon(2, QIcon(iconWatch));
431 setAllColumnsShowFocus(true);
433 m_modemenu = new QMenu("ERROR", this);
434 for (uint i=0; i<sizeof(menuitems)/sizeof(MenuPair); i++) {
435 if (menuitems[i].isSeparator())
436 m_modemenu->addSeparator();
437 else {
438 QAction* action = m_modemenu->addAction(i18n(menuitems[i].name));
439 action->setData(menuitems[i].mode);
440 action->setCheckable(true);
443 connect(m_modemenu, SIGNAL(triggered(QAction*)), SLOT(slotModeChange(QAction*)));
445 new GroupingViewItem(this, i18n("GP and others"), "^$",
446 RegisterDisplay::nada);
447 new GroupingViewItem(this, i18n("Flags"),
448 "(^eflags$|^fctrl$|^mxcsr$|^cr$|^fpscr$|^vscr$|^ftag$|^fstat$)",
449 RegisterDisplay::bits32|RegisterDisplay::binary);
450 new GroupingViewItem(this, i18n("x86/x87 segment"),
451 "(^cs$|^ss$|^ds$|^es$|^fs$|^gs$|^fiseg$|^foseg$)",
452 RegisterDisplay::nada);
453 new GroupingViewItem(this, "x87", "^st.*",
454 RegisterDisplay::bits80|RegisterDisplay::realE);
455 new GroupingViewItem(this, "SSE", "^xmm.*",
456 RegisterDisplay::bits32|RegisterDisplay::realE);
457 new GroupingViewItem(this, "MMX", "^mm.*",
458 RegisterDisplay::bits32|RegisterDisplay::realE);
459 new GroupingViewItem(this, "POWER real", "^fpr.*",
460 RegisterDisplay::bits32|RegisterDisplay::realE);
461 new GroupingViewItem(this, "AltiVec", "^vr.*",
462 RegisterDisplay::bits32|RegisterDisplay::realE);
463 new GroupingViewItem(this, "MIPS VU", "^vu.*",
464 RegisterDisplay::bits32|RegisterDisplay::realE);
466 updateGroupVisibility();
467 setRootIsDecorated(true);
469 resize(200,300);
472 RegisterView::~RegisterView()
476 GroupingViewItem* RegisterView::findMatchingGroup(const QString& regName)
478 for (int i = 0; i < topLevelItemCount(); i++)
480 GroupingViewItem* it = static_cast<GroupingViewItem*>(topLevelItem(i));
481 if (it->matchName(regName))
482 return it;
484 // not better match found, so return "GP and others"
485 return static_cast<GroupingViewItem*>(topLevelItem(0));
488 GroupingViewItem* RegisterView::findGroup(const QString& groupName)
490 for (int i = 0; i < topLevelItemCount(); i++)
492 QTreeWidgetItem* it = topLevelItem(i);
493 if (it->text(0) == groupName)
494 return static_cast<GroupingViewItem*>(it);
496 // return that nothing was found.
497 return 0;
500 // only show a group if it has subitems.
501 void RegisterView::updateGroupVisibility()
503 for(int i = 0; i < topLevelItemCount(); i++)
505 QTreeWidgetItem* item = topLevelItem(i);
506 item->setHidden(item->childCount() == 0);
510 void RegisterView::updateRegisters(const std::list<RegisterInfo>& regs)
512 setUpdatesEnabled(false);
514 // mark all items as 'not found'
515 for (RegMap::iterator i = m_registers.begin(); i != m_registers.end(); ++i)
517 i->second->m_found = false;
520 // parse register values
521 for (std::list<RegisterInfo>::const_iterator reg = regs.begin(); reg != regs.end(); ++reg)
523 // check if this is a new register
524 RegMap::iterator i = m_registers.find(reg->regName);
526 if (i != m_registers.end())
528 RegisterViewItem* it = i->second;
529 it->m_found = true;
530 if (it->m_reg.rawValue != reg->rawValue ||
531 it->m_reg.cookedValue != reg->cookedValue)
533 it->m_changes = true;
534 it->setValue(*reg);
536 it->setForeground(0,Qt::red);
537 it->setForeground(1,Qt::red);
538 it->setForeground(2,Qt::red);
540 } else {
542 * If there was a change last time, but not now, we
543 * must revert the color.
545 if (it->m_changes) {
546 it->m_changes = false;
547 it->setForeground(0,Qt::black);
548 it->setForeground(1,Qt::black);
549 it->setForeground(2,Qt::black);
553 else
555 GroupingViewItem* group = findMatchingGroup(reg->regName);
556 m_registers[reg->regName] =
557 new RegisterViewItem(group, *reg);
561 // remove all 'not found' items;
562 QStringList del;
563 for (RegMap::iterator i = m_registers.begin(); i != m_registers.end(); ++i)
565 if (!i->second->m_found) {
566 del.push_back(i->first);
569 for (QStringList::Iterator i = del.begin(); i != del.end(); ++i)
571 RegMap::iterator it = m_registers.find(*i);
572 delete it->second;
573 m_registers.erase(it);
576 updateGroupVisibility();
577 setUpdatesEnabled(true);
581 void RegisterView::contextMenuEvent(QContextMenuEvent* event)
583 QTreeWidgetItem *item = itemAt(event->pos());
585 if (item) {
586 RegisterDisplay mode=static_cast<ModeItem*>(item)->mode();
587 int i = 0;
588 foreach(QAction* action, m_modemenu->actions())
590 action->setChecked(mode.contains(menuitems[i].mode));
591 ++i;
593 m_modemenu->setTitle(item->text(0));
594 m_modemenu->popup(event->globalPos());
596 event->accept();
600 void RegisterView::slotModeChange(QAction* action)
602 RegMap::iterator it=m_registers.find(m_modemenu->title());
603 ModeItem* view;
604 if (it != m_registers.end())
605 view = it->second;
606 else
607 view = findGroup(m_modemenu->title());
609 if (view) {
610 RegisterDisplay mode = view->mode();
611 mode.changeFlag(action->data().toInt());
612 view->setMode(mode);
616 void RegisterView::paletteChange(const QPalette&)
618 setFont(KGlobalSettings::fixedFont());
621 #include "regwnd.moc"