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