1 #include "stdafx.h"
2 #include <QtGui>
3 #include <QMenu>
4 #include <QPainter>
5 #include <QScrollBar>
6 #include <QStyle>
7 #include <QStyleFactory>
8 #include <QStyleOptionFocusRect>
9 #include <QToolBar>
10 #include "main.h"
11 #include "qmemoryview.h"
12 #include "Emulator.h"
13 #include "emubase/Emubase.h"
14 #include "qdialogs.h"
15 
16 
17 enum MemoryViewMode
18 {
19     MEMMODE_RAM0 = 0,  // RAM plane 0
20     MEMMODE_RAM1 = 1,  // RAM plane 1
21     MEMMODE_RAM2 = 2,  // RAM plane 2
22     MEMMODE_ROM  = 3,  // ROM
23     MEMMODE_CPU  = 4,  // CPU memory
24     MEMMODE_PPU  = 5,  // PPU memory
25     MEMMODE_LAST = 5   // Last mode number
26 };
27 
28 static const char * MemoryView_ModeNames[] =
29 {
30     "RAM0", "RAM1", "RAM2", "ROM", "CPU", "PPU"
31 };
32 
33 
QMemoryView()34 QMemoryView::QMemoryView()
35 {
36     m_Mode = Settings_GetDebugMemoryMode();
37     if (m_Mode > MEMMODE_LAST) m_Mode = MEMMODE_LAST;
38     m_ByteMode = Settings_GetDebugMemoryByte();
39     m_wBaseAddress = Settings_GetDebugMemoryAddress();
40     m_cyLineMemory = 0;
41     m_nPageSize = 0;
42 
43     QFont font = Common_GetMonospacedFont();
44     QFontMetrics fontmetrics(font);
45     int cxChar = fontmetrics.averageCharWidth();
46     int cyLine = fontmetrics.height();
47 
48     m_cyLine = cyLine;
49 
50     this->setFont(font);
51     this->setMinimumSize(cxChar * 68, cyLine * 9 + cyLine / 2);
52 
53     m_scrollbar = new QScrollBar(Qt::Vertical, this);
54     m_scrollbar->setRange(0, 65536 - 16);
55     m_scrollbar->setSingleStep(16);
56     QObject::connect(m_scrollbar, SIGNAL(valueChanged(int)), this, SLOT(scrollValueChanged()));
57 
58     m_toolbar = new QToolBar(this);
59     m_toolbar->setGeometry(4, 4, 28, 2000);
60     m_toolbar->setOrientation(Qt::Vertical);
61     m_toolbar->setIconSize(QSize(16, 16));
62     m_toolbar->setToolButtonStyle(Qt::ToolButtonIconOnly);
63     m_toolbar->setFocusPolicy(Qt::NoFocus);
64     m_toolbar->setStyle(QStyleFactory::create("windows"));  // fix for macOS to remove gradient background
65 
66     QAction* actionGotoAddr = m_toolbar->addAction(QIcon(":/images/iconEditAddress.png"), "");
67     m_toolbar->addSeparator();
68     QAction* actionWordByte = m_toolbar->addAction(QIcon(":/images/iconWordByte.png"), "");
69 
70     QObject::connect(actionGotoAddr, SIGNAL(triggered()), this, SLOT(gotoAddress()));
71     QObject::connect(actionWordByte, SIGNAL(triggered()), this, SLOT(changeWordByteMode()));
72 
73     setFocusPolicy(Qt::ClickFocus);
74 }
75 
~QMemoryView()76 QMemoryView::~QMemoryView()
77 {
78     delete m_scrollbar;
79 }
80 
updateScrollPos()81 void QMemoryView::updateScrollPos()
82 {
83     m_scrollbar->setValue(m_wBaseAddress);
84 }
85 
GetMemoryModeName(int mode)86 static const char * GetMemoryModeName(int mode)
87 {
88     if (mode < 0 || mode > MEMMODE_LAST)
89         return "UKWN";  // Unknown mode
90     return MemoryView_ModeNames[mode];
91 }
92 
updateWindowText()93 void QMemoryView::updateWindowText()
94 {
95     QString buffer = QString(tr("Memory - %1")).arg(GetMemoryModeName(m_Mode));
96     parentWidget()->setWindowTitle(buffer);
97 }
98 
updateData()99 void QMemoryView::updateData()
100 {
101 }
102 
focusInEvent(QFocusEvent *)103 void QMemoryView::focusInEvent(QFocusEvent *)
104 {
105     repaint();  // Need to draw focus rect
106 }
focusOutEvent(QFocusEvent *)107 void QMemoryView::focusOutEvent(QFocusEvent *)
108 {
109     repaint();  // Need to draw focus rect
110 }
111 
contextMenuEvent(QContextMenuEvent * event)112 void QMemoryView::contextMenuEvent(QContextMenuEvent *event)
113 {
114     QMenu menu(this);
115     menu.addAction("Go to Address...", this, SLOT(gotoAddress()));
116     menu.addSeparator();
117 
118     for (int mode = 0; mode <= MEMMODE_LAST; mode++)
119     {
120         const char * modeName = MemoryView_ModeNames[mode];
121         QAction * action = menu.addAction(modeName, this, SLOT(changeMemoryMode()));
122         action->setCheckable(true);
123         action->setData(mode);
124         if (m_Mode == mode)
125             action->setChecked(true);
126     }
127 
128     menu.addSeparator();
129     menu.addAction("Words / Bytes", this, SLOT(changeWordByteMode()));
130 
131     menu.exec(event->globalPos());
132 }
133 
changeMemoryMode()134 void QMemoryView::changeMemoryMode()
135 {
136     QAction * action = qobject_cast<QAction*>(sender());
137     if (action == nullptr) return;
138     int mode = action->data().toInt();
139     if (mode < 0 || mode > MEMMODE_LAST) return;
140 
141     m_Mode = mode;
142     Settings_SetDebugMemoryMode(m_Mode);
143 
144     repaint();
145     updateWindowText();
146 }
147 
changeWordByteMode()148 void QMemoryView::changeWordByteMode()
149 {
150     m_ByteMode = !m_ByteMode;
151     Settings_SetDebugMemoryByte(m_ByteMode);
152 
153     repaint();
154 }
155 
scrollBy(qint16 delta)156 void QMemoryView::scrollBy(qint16 delta)
157 {
158     if (delta == 0) return;
159 
160     m_wBaseAddress = (quint16)(m_wBaseAddress + delta);
161     m_wBaseAddress = m_wBaseAddress & ((quint16)~15);
162     Settings_SetDebugMemoryAddress(m_wBaseAddress);
163 
164     repaint();
165     updateScrollPos();
166 }
167 
gotoAddress()168 void QMemoryView::gotoAddress()
169 {
170     quint16 value = m_wBaseAddress;
171     QInputOctalDialog dialog(this, "Go To Address", "Address (octal):", &value);
172     if (dialog.exec() == QDialog::Rejected) return;
173 
174     // Scroll to the address
175     m_wBaseAddress = value & ((quint16)~15);
176     Settings_SetDebugMemoryAddress(m_wBaseAddress);
177 
178     repaint();
179     updateScrollPos();
180 }
181 
resizeEvent(QResizeEvent *)182 void QMemoryView::resizeEvent(QResizeEvent *)
183 {
184     int cxScroll = this->style()->pixelMetric(QStyle::PM_ScrollBarExtent);
185     m_scrollbar->setGeometry(this->width() - cxScroll, 0, cxScroll, this->height());
186     m_scrollbar->setPageStep((this->height() / m_cyLine - 2) * 16);
187 }
188 
scrollValueChanged()189 void QMemoryView::scrollValueChanged()
190 {
191     int value = m_scrollbar->value();
192     m_wBaseAddress = (unsigned short)value & ((quint16)~15);
193     Settings_SetDebugMemoryAddress(m_wBaseAddress);
194 
195     this->repaint();
196 }
197 
paintEvent(QPaintEvent *)198 void QMemoryView::paintEvent(QPaintEvent * /*event*/)
199 {
200     if (g_pBoard == nullptr) return;
201 
202     QColor colorBackground = palette().color(QPalette::Base);
203     QPainter painter(this);
204     painter.fillRect(0, 0, this->width(), this->height(), colorBackground);
205 
206     QFont font = Common_GetMonospacedFont();
207     painter.setFont(font);
208     QFontMetrics fontmetrics(font);
209     int cxChar = fontmetrics.averageCharWidth();
210     int cyLine = fontmetrics.height();
211     QColor colorText = palette().color(QPalette::Text);
212     QColor colorChanged = Common_GetColorShifted(palette(), COLOR_VALUECHANGED);
213     QColor colorMemoryRom = Common_GetColorShifted(palette(), COLOR_MEMORYROM);
214     QColor colorMemoryIO = Common_GetColorShifted(palette(), COLOR_MEMORYIO);
215     QColor colorMemoryNA = Common_GetColorShifted(palette(), COLOR_MEMORYNA);
216 
217     m_cyLineMemory = cyLine;
218 
219     char buffer[7];
220     const char * ADDRESS_LINE = "  addr   0      2      4      6      10     12     14     16";
221     painter.drawText(30, cyLine, ADDRESS_LINE);
222 
223     // Calculate m_nPageSize
224     m_nPageSize = this->height() / cyLine - 1;
225 
226     quint16 address = m_wBaseAddress;
227     int y = 2 * cyLine;
228     for (;;)    // Draw lines
229     {
230         DrawOctalValue(painter, 30 + 1 * cxChar, y, address);
231 
232         int x = 30 + 9 * cxChar;
233         ushort wchars[16];
234 
235         for (int j = 0; j < 8; j++)    // Draw words as octal value
236         {
237             // Get word from memory
238             quint16 word = 0;
239             int okValid = true;
240             int addrtype = ADDRTYPE_NONE;
241             bool okHalt = false;
242             quint16 wChanged = 0;
243             switch (m_Mode)
244             {
245             case MEMMODE_RAM0:
246             case MEMMODE_RAM1:
247             case MEMMODE_RAM2:
248                 word = g_pBoard->GetRAMWord(m_Mode, address);
249                 wChanged = Emulator_GetChangeRamStatus(m_Mode, address);
250                 break;
251             case MEMMODE_ROM:  // ROM - only 32 Kbytes
252                 if (address < 0100000)
253                     okValid = false;
254                 else
255                 {
256                     addrtype = ADDRTYPE_ROM;
257                     word = g_pBoard->GetROMWord(address - 0100000);
258                 }
259                 break;
260             case MEMMODE_CPU:
261                 okHalt = g_pBoard->GetCPU()->IsHaltMode();
262                 word = g_pBoard->GetCPUMemoryController()->GetWordView(address, okHalt, false, &addrtype);
263                 okValid = (addrtype != ADDRTYPE_IO) && (addrtype != ADDRTYPE_DENY);
264                 wChanged = Emulator_GetChangeRamStatus(ADDRTYPE_RAM12, address);
265                 break;
266             case MEMMODE_PPU:
267                 okHalt = g_pBoard->GetPPU()->IsHaltMode();
268                 word = g_pBoard->GetPPUMemoryController()->GetWordView(address, okHalt, false, &addrtype);
269                 okValid = (addrtype != ADDRTYPE_IO) && (addrtype != ADDRTYPE_DENY);
270                 if (address < 0100000)
271                     wChanged = Emulator_GetChangeRamStatus(ADDRTYPE_RAM0, address);
272                 else
273                     wChanged = 0;
274                 break;
275             }
276 
277             if (okValid)
278             {
279                 if (addrtype == ADDRTYPE_ROM)
280                     painter.setPen(colorMemoryRom);
281                 else
282                     painter.setPen(wChanged != 0 ? colorChanged : colorText);
283                 if (m_ByteMode)
284                 {
285                     PrintOctalValue(buffer, (word & 0xff));
286                     painter.drawText(x, y, buffer + 3);
287                     PrintOctalValue(buffer, (word >> 8));
288                     painter.drawText(x + 3 * cxChar + cxChar / 2, y, buffer + 3);
289                 }
290                 else
291                     DrawOctalValue(painter, x, y, word);
292             }
293             else
294             {
295                 if (addrtype == ADDRTYPE_IO)
296                 {
297                     painter.setPen(colorMemoryIO);
298                     painter.drawText(x, y, "  IO  ");
299                 }
300                 else
301                 {
302                     painter.setPen(colorMemoryNA);
303                     painter.drawText(x, y, "  NA  ");
304                 }
305             }
306 
307             // Prepare characters to draw at right
308             quint8 ch1 = (quint8)(word & 0xff);
309             ushort wch1 = Translate_KOI8R(ch1);
310             if (ch1 < 32) wch1 = 0x00b7;
311             wchars[j * 2] = wch1;
312             quint8 ch2 = (quint8)((word >> 8) & 0xff);
313             ushort wch2 = Translate_KOI8R(ch2);
314             if (ch2 < 32) wch2 = 0x00b7;
315             wchars[j * 2 + 1] = wch2;
316 
317             address += 2;
318             x += 7 * cxChar;
319         }
320         painter.setPen(colorText);
321 
322         // Draw characters at right
323         int xch = x + cxChar;
324         QString wstr = QString::fromUtf16(wchars, 16);
325         painter.drawText(xch, y, wstr);
326 
327         y += cyLine;
328         if (y > this->height()) break;
329     }  // Draw lines
330 
331     // Draw focus rect
332     if (hasFocus())
333     {
334         QStyleOptionFocusRect option;
335         option.initFrom(this);
336         option.state |= QStyle::State_KeyboardFocusChange;
337         option.backgroundColor = QColor(Qt::gray);
338         option.rect = QRect(30, cyLine + fontmetrics.descent(), 83 * cxChar, cyLine * m_nPageSize);
339         style()->drawPrimitive(QStyle::PE_FrameFocusRect, &option, &painter, this);
340     }
341 }
342 
keyPressEvent(QKeyEvent * event)343 void QMemoryView::keyPressEvent(QKeyEvent *event)
344 {
345     switch (event->key())
346     {
347     case Qt::Key_Space:
348         if (event->isAutoRepeat()) return;
349         event->accept();
350         if (m_Mode == MEMMODE_LAST)
351             m_Mode = 0;
352         else
353             m_Mode++;
354         Settings_SetDebugMemoryMode(m_Mode);
355         this->repaint();
356         updateWindowText();
357         break;
358 
359     case Qt::Key_G:
360         event->accept();
361         gotoAddress();
362         break;
363     case Qt::Key_B:
364         event->accept();
365         changeWordByteMode();
366         break;
367 
368     case Qt::Key_Up:
369         event->accept();
370         scrollBy(-16);
371         break;
372     case Qt::Key_Down:
373         event->accept();
374         scrollBy(16);
375         break;
376 
377     case Qt::Key_PageUp:
378         event->accept();
379         scrollBy(-m_nPageSize * 16);
380         break;
381     case Qt::Key_PageDown:
382         event->accept();
383         scrollBy(m_nPageSize * 16);
384         break;
385     }
386 }
387 
wheelEvent(QWheelEvent * event)388 void QMemoryView::wheelEvent(QWheelEvent * event)
389 {
390     if (event->orientation() == Qt::Horizontal)
391         return;
392     event->accept();
393 
394     int steps = -event->delta() / 60;
395     scrollBy(steps * 16);
396 }
397