1 #include "mainwindow.h"
2 #include "ui_mainwindow.h"
3 #include "dockwidget.h"
4 #include "utils.h"
5 #include "visualizerwidget.h"
6 #include "tivarslib/TIVarType.h"
7 #include "tivarslib/TypeHandlers/TypeHandlers.h"
8 #include "../../core/asic.h"
9 #include "../../core/cpu.h"
10 #include "../../core/misc.h"
11 #include "../../core/mem.h"
12 #include "../../core/cert.h"
13 #include "../../core/interrupt.h"
14 #include "../../core/keypad.h"
15 #include "../../core/control.h"
16 #include "../../core/flash.h"
17 #include "../../core/lcd.h"
18 #include "../../core/spi.h"
19 #include "../../core/backlight.h"
20 #include "../../core/timers.h"
21 #include "../../core/usb.h"
22 #include "../../core/realclock.h"
23 #include "../../core/sha256.h"
24 #include "../../core/schedule.h"
25 
26 #include <QtWidgets/QToolTip>
27 #include <QtCore/QFileInfo>
28 #include <QtCore/QRegularExpression>
29 #include <QtNetwork/QNetworkAccessManager>
30 #include <QtWidgets/QMessageBox>
31 #include <QtWidgets/QInputDialog>
32 #include <QtWidgets/QScrollBar>
33 #include <QtWidgets/QDesktopWidget>
34 #include <QtNetwork/QNetworkReply>
35 #include <QtGui/QClipboard>
36 
37 #ifdef _MSC_VER
38     #include <direct.h>
39     #define chdir _chdir
40 #else
41     #include <unistd.h>
42 #endif
43 
44 const QString MainWindow::DEBUG_UNSET_ADDR = QStringLiteral("XXXXXX");
45 const QString MainWindow::DEBUG_UNSET_PORT = QStringLiteral("XXXX");
46 
47 // -----------------------------------------------
48 // Debugger Initialization
49 // -----------------------------------------------
50 
debugInit()51 void MainWindow::debugInit() {
52     disasmInit();
53 
54     ui->afregView->installEventFilter(this);
55     ui->hlregView->installEventFilter(this);
56     ui->bcregView->installEventFilter(this);
57     ui->deregView->installEventFilter(this);
58     ui->ixregView->installEventFilter(this);
59     ui->iyregView->installEventFilter(this);
60     ui->af_regView->installEventFilter(this);
61     ui->hl_regView->installEventFilter(this);
62     ui->bc_regView->installEventFilter(this);
63     ui->de_regView->installEventFilter(this);
64     ui->rregView->installEventFilter(this);
65     ui->hl->installEventFilter(this);
66     ui->bc->installEventFilter(this);
67     ui->de->installEventFilter(this);
68     ui->ix->installEventFilter(this);
69     ui->iy->installEventFilter(this);
70     ui->hl_->installEventFilter(this);
71     ui->bc_->installEventFilter(this);
72     ui->de_->installEventFilter(this);
73     ui->pc->installEventFilter(this);
74     ui->spl->installEventFilter(this);
75 
76     ui->checkADLDisasm->blockSignals(true);
77     ui->checkADLDisasm->setCheckState(Qt::PartiallyChecked);
78     ui->checkADLDisasm->blockSignals(false);
79     ui->checkADLStack->blockSignals(true);
80     ui->checkADLStack->setCheckState(Qt::PartiallyChecked);
81     ui->checkADLStack->blockSignals(false);
82 
83     debug_init();
84 }
85 
86 // ------------------------------------------------
87 // Main Debugger things
88 // ------------------------------------------------
89 
debugDisable()90 void MainWindow::debugDisable() {
91     guiDebug = false;
92     debugGuiState(false);
93 }
94 
debugEnable()95 void MainWindow::debugEnable() {
96     guiDebug = true;
97     debugGuiState(true);
98 }
99 
debugStep(int mode)100 void MainWindow::debugStep(int mode) {
101     if (mode == DBG_RUN_UNTIL) {
102         debug_step(mode, m_runUntilAddr);
103     } else {
104         disasm.base = static_cast<int32_t>(cpu.registers.PC);
105         disasm.ctx.zdis_adl = cpu.ADL;
106         disasmGet();
107         debug_step(mode, static_cast<uint32_t>(disasm.next));
108     }
109     emu.resume();
110 }
111 
debugImportFile(const QString & file)112 void MainWindow::debugImportFile(const QString &file) {
113     if (file.isEmpty()) {
114         return;
115     }
116     int i;
117 
118     // check the debug file version
119     QSettings info(file, QSettings::IniFormat);
120     if (info.value(QStringLiteral("version")) != VERSION_DBG) {
121 error:
122         QMessageBox *warn = new QMessageBox;
123         warn->setWindowTitle(tr("Invalid Configuration"));
124         warn->setText(tr("The debugging configuration is incompatible with CEmu"));
125         warn->setWindowModality(Qt::ApplicationModal);
126         warn->setAttribute(Qt::WA_DeleteOnClose);
127         warn->show();
128         return;
129     }
130 
131     // load the breakpoint information
132     QStringList breakLabel = info.value(QStringLiteral("breakpoints/label")).toStringList();
133     QStringList breakAddr = info.value(QStringLiteral("breakpoints/address")).toStringList();
134     QStringList breakSet = info.value(QStringLiteral("breakpoints/enable")).toStringList();
135     if ((breakLabel.size() + breakAddr.size() + breakSet.size()) / 3 != breakLabel.size()) {
136         goto error;
137     }
138     for (i = 0; i < breakLabel.size(); i++) {
139         breakAdd(breakLabel.at(i), static_cast<uint32_t>(hex2int(breakAddr.at(i))), breakSet.at(i) == TXT_YES, false, false);
140     }
141 
142     // load the watchpoint information
143     QStringList watchLabel = info.value(QStringLiteral("watchpoints/label")).toStringList();
144     QStringList watchLow = info.value(QStringLiteral("watchpoints/low")).toStringList();
145     QStringList watchHigh = info.value(QStringLiteral("watchpoints/high")).toStringList();
146     QStringList watchR = info.value(QStringLiteral("watchpoints/read")).toStringList();
147     QStringList watchW = info.value(QStringLiteral("watchpoints/write")).toStringList();
148     if ((watchLabel.size() + watchLow.size() + watchHigh.size() + watchR.size() + watchW.size()) / 5 != watchLabel.size()) {
149         goto error;
150     }
151     for (i = 0; i < watchLabel.size(); i++) {
152         int mask = (watchR.at(i) == TXT_YES ? DBG_MASK_READ : DBG_MASK_NONE) |
153                    (watchW.at(i) == TXT_YES ? DBG_MASK_WRITE : DBG_MASK_NONE);
154         watchAdd(watchLabel.at(i), static_cast<uint32_t>(hex2int(watchLow.at(i))),
155                  static_cast<uint32_t>(hex2int(watchHigh.at(i))), mask, false, false);
156     }
157 
158     // load the port monitor information
159     QStringList portAddr = info.value(QStringLiteral("portmonitor/address")).toStringList();
160     QStringList portR = info.value(QStringLiteral("portmonitor/read")).toStringList();
161     QStringList portW = info.value(QStringLiteral("portmonitor/write")).toStringList();
162     QStringList portF = info.value(QStringLiteral("portmonitor/freeze")).toStringList();
163     if ((portAddr.size() + portR.size() + portW.size() + portF.size()) / 4 != portAddr.size()) {
164         goto error;
165     }
166     for (i = 0; i < portAddr.size(); i++) {
167         int mask = (portR.at(i) == TXT_YES ? DBG_MASK_PORT_READ : DBG_MASK_NONE)  |
168                    (portW.at(i) == TXT_YES ? DBG_MASK_PORT_WRITE : DBG_MASK_NONE) |
169                    (portF.at(i) == TXT_YES ? DBG_MASK_PORT_FREEZE : DBG_MASK_NONE);
170         portAdd(static_cast<uint16_t>(hex2int(portAddr.at(i))), mask, false);
171     }
172 
173     // add all the equate files and load them in
174     m_equateFiles = info.value(QStringLiteral("equates/files")).toStringList();
175 
176     disasm.map.clear();
177     disasm.reverse.clear();
178     for (QString &file : m_equateFiles) {
179         equatesAddFile(file);
180     }
181 }
182 
debugExportFile(const QString & filename)183 void MainWindow::debugExportFile(const QString &filename) {
184     if (filename.isEmpty()) {
185         return;
186     }
187     int i;
188 
189     QSettings info(filename, QSettings::IniFormat);
190 
191     // Set the file format version
192     info.setValue(QStringLiteral("version"), VERSION_DBG);
193 
194     // Save the breakpoint information
195     QStringList breakLabel;
196     QStringList breakAddr;
197     QStringList breakSet;
198     for(i = 0; i < m_breakpoints->rowCount(); i++) {
199         if (m_breakpoints->item(i, BREAK_ADDR_COL)->text() != DEBUG_UNSET_ADDR) {
200             breakLabel.append(m_breakpoints->item(i, BREAK_NAME_COL)->text());
201             breakAddr.append(m_breakpoints->item(i, BREAK_ADDR_COL)->text());
202             breakSet.append(static_cast<QAbstractButton *>(m_breakpoints->cellWidget(i, BREAK_ENABLE_COL))->isChecked() ? TXT_YES : TXT_NO);
203         }
204     }
205 
206     info.setValue(QStringLiteral("breakpoints/label"), breakLabel);
207     info.setValue(QStringLiteral("breakpoints/address"), breakAddr);
208     info.setValue(QStringLiteral("breakpoints/enable"), breakSet);
209 
210     // Save watchpoint information
211     QStringList watchLabel;
212     QStringList watchLow;
213     QStringList watchHigh;
214     QStringList watchR;
215     QStringList watchW;
216     for(i = 0; i < m_watchpoints->rowCount(); i++) {
217         if (m_watchpoints->item(i, WATCH_LOW_COL)->text() != DEBUG_UNSET_ADDR) {
218             watchLabel.append(m_watchpoints->item(i, WATCH_NAME_COL)->text());
219             watchLow.append(m_watchpoints->item(i, WATCH_LOW_COL)->text());
220             watchHigh.append(m_watchpoints->item(i, WATCH_HIGH_COL)->text());
221             watchR.append(static_cast<QAbstractButton *>(m_watchpoints->cellWidget(i, WATCH_READ_COL))->isChecked() ? TXT_YES : TXT_NO);
222             watchW.append(static_cast<QAbstractButton *>(m_watchpoints->cellWidget(i, WATCH_WRITE_COL))->isChecked() ? TXT_YES : TXT_NO);
223         }
224     }
225 
226     info.setValue(QStringLiteral("watchpoints/label"), watchLabel);
227     info.setValue(QStringLiteral("watchpoints/low"), watchLow);
228     info.setValue(QStringLiteral("watchpoints/high"), watchHigh);
229     info.setValue(QStringLiteral("watchpoints/read"), watchR);
230     info.setValue(QStringLiteral("watchpoints/write"), watchW);
231 
232     // Save port monitor information
233     QStringList portAddr;
234     QStringList portR;
235     QStringList portW;
236     QStringList portF;
237     for(i = 0; i < m_ports->rowCount(); i++) {
238         if (m_ports->item(i, PORT_ADDR_COL)->text() != DEBUG_UNSET_PORT) {
239             portAddr.append(m_ports->item(i, PORT_ADDR_COL)->text());
240             portR.append(static_cast<QAbstractButton *>(m_ports->cellWidget(i, PORT_READ_COL))->isChecked() ? TXT_YES : TXT_NO);
241             portW.append(static_cast<QAbstractButton *>(m_ports->cellWidget(i, PORT_WRITE_COL))->isChecked() ? TXT_YES : TXT_NO);
242             portF.append(static_cast<QAbstractButton *>(m_ports->cellWidget(i, PORT_FREEZE_COL))->isChecked() ? TXT_YES : TXT_NO);
243         }
244     }
245 
246     info.setValue(QStringLiteral("portmonitor/address"), portAddr);
247     info.setValue(QStringLiteral("portmonitor/read"), portR);
248     info.setValue(QStringLiteral("portmonitor/write"), portW);
249     info.setValue(QStringLiteral("portmonitor/freeze"), portF);
250 
251     info.setValue(QStringLiteral("equates/files"), m_equateFiles);
252     info.sync();
253 }
254 
debugGetFile(bool save)255 QString MainWindow::debugGetFile(bool save) {
256     QString file;
257     QFileDialog dialog(this);
258 
259     dialog.setAcceptMode(save ? QFileDialog::AcceptSave : QFileDialog::AcceptOpen);
260     dialog.setFileMode(QFileDialog::AnyFile);
261     dialog.setDirectory(m_dir);
262     dialog.setNameFilter(tr("Debugging Info (*.ini)"));
263     dialog.setWindowTitle(save ? tr("Debugger Import") : tr("Debugger Export"));
264     dialog.setDefaultSuffix(QStringLiteral("ini"));
265     dialog.exec();
266 
267     if (!dialog.selectedFiles().isEmpty()) {
268         QStringList selected = dialog.selectedFiles();
269         file = selected.first();
270     }
271 
272     m_dir = dialog.directory();
273     return file;
274 }
275 
debugRaise()276 void MainWindow::debugRaise() {
277     debugEnable();
278     debugPopulate();
279     connect(m_shortcutStepIn, &QShortcut::activated, this, &MainWindow::stepIn);
280     connect(m_shortcutStepOver, &QShortcut::activated, this, &MainWindow::stepOver);
281     connect(m_shortcutStepNext, &QShortcut::activated, this, &MainWindow::stepNext);
282     connect(m_shortcutStepOut, &QShortcut::activated, this, &MainWindow::stepOut);
283 }
284 
debugExecute(uint32_t offset,uint8_t cmd)285 void MainWindow::debugExecute(uint32_t offset, uint8_t cmd) {
286     m_useSoftCom = true;
287 
288     if (offset == 0xFF) {
289         int tmp;
290         switch (cmd) {
291             case CMD_ABORT:
292                 ui->debuggerLabel->setText(QStringLiteral("Program Aborted"));
293                 debugRaise();
294                 m_useSoftCom = false;
295                 return; // don't exit the debugger
296             case CMD_DEBUG:
297                 ui->debuggerLabel->setText(QStringLiteral("Program Entered Debugger"));
298                 debugRaise();
299                 m_useSoftCom = false;
300                 return; // don't exit the debugger
301             case CMD_SET_BREAKPOINT:
302                 breakAdd(breakNextLabel(), cpu.registers.DE, true, false, false);
303                 break;
304             case CMD_REM_BREAKPOINT:
305                 breakRemove(cpu.registers.DE);
306                 break;
307             case CMD_SET_R_WATCHPOINT:
308                 watchRemove(cpu.registers.DE);
309                 if (cpu.registers.bc.l > 0) {
310                     watchAdd(watchNextLabel(), cpu.registers.DE, cpu.registers.DE + cpu.registers.bc.l - 1, DBG_MASK_READ, false, false);
311                 }
312                 break;
313             case CMD_SET_W_WATCHPOINT:
314                 watchRemove(cpu.registers.DE);
315                 if (cpu.registers.bc.l > 0) {
316                     watchAdd(watchNextLabel(), cpu.registers.DE, cpu.registers.DE + cpu.registers.bc.l - 1, DBG_MASK_WRITE, false, false);
317                 }
318                 break;
319             case CMD_SET_RW_WATCHPOINT:
320                 watchRemove(cpu.registers.DE);
321                 if (cpu.registers.bc.l > 0) {
322                     watchAdd(watchNextLabel(), cpu.registers.DE, cpu.registers.DE + cpu.registers.bc.l - 1, DBG_MASK_RW, false, false);
323                 }
324                 break;
325             case CMD_REM_WATCHPOINT:
326                 watchRemove(cpu.registers.DE);
327                 break;
328             case CMD_REM_ALL_BREAK:
329                 tmp = m_breakpoints->rowCount();
330                 for (int i = 0; i < tmp; i++) {
331                     breakRemoveRow(0);
332                 }
333                 break;
334             case CMD_REM_ALL_WATCH:
335                 tmp = m_watchpoints->rowCount();
336                 for (int i = 0; i < tmp; i++) {
337                     watchRemoveRow(0);
338                 }
339                 break;
340             case CMD_SET_E_WATCHPOINT:
341                 watchRemove(cpu.registers.DE);
342                 if (cpu.registers.bc.l > 0) {
343                     watchAdd(watchNextLabel(), cpu.registers.DE, cpu.registers.bc.l, DBG_MASK_NONE, false, false);
344                 }
345                 break;
346             default:
347                 console(QStringLiteral("[CEmu] Unknown debug Command: 0x") +
348                         QString::number(cmd, 16).rightJustified(2, '0') +
349                         QStringLiteral(",0x") +
350                         QString::number(offset + 0xFFFF00, 16) +
351                         QStringLiteral("\n"), Qt::darkRed);
352                 break;
353         }
354     }
355 
356     m_useSoftCom = false;
357 
358     // continue emulation
359     if (guiDebug) {
360         debugRaise();
361     } else {
362         emu.debug(false);
363     }
364 }
365 
debugCommand(int reason,uint32_t data)366 void MainWindow::debugCommand(int reason, uint32_t data) {
367     if (!guiEmuValid) {
368         return;
369     }
370 
371     if (reason == DBG_READY) {
372         guiReset = false;
373         emu.resume();
374         return;
375     }
376 
377     if (guiReset) {
378         emu.resume();
379         return;
380     }
381 
382     int row = 0;
383     uint32_t addr;
384 
385     // This means the program is trying to send us a debug command. Let's see what we can do with that information
386     if (reason >= DBG_NUMBER) {
387         debugExecute(static_cast<uint32_t>(reason - DBG_PORT_RANGE), static_cast<uint8_t>(data));
388         return;
389     }
390 
391     QString input, type, text;
392     QString label = QStringLiteral("Unknown");
393     bool valid = false;
394 
395     switch (reason) {
396         case DBG_BREAKPOINT:
397             input = int2hex(data, 6);
398 
399             for (int i = 0; i < m_breakpoints->rowCount(); i++) {
400                 if (m_breakpoints->item(i, BREAK_ADDR_COL)->text() == input) {
401                     label = m_breakpoints->item(row, BREAK_NAME_COL)->text();
402                     valid = true;
403                     break;
404                 }
405             }
406             if (valid == false) {
407                 emu.resume();
408                 return;
409             }
410 
411             text = tr("Hit breakpoint ") + input + QStringLiteral(" (") + label + QStringLiteral(")");
412             break;
413         case DBG_WATCHPOINT_READ:
414         case DBG_WATCHPOINT_WRITE:
415             input = int2hex(data, 6);
416             addr = static_cast<uint32_t>(hex2int(input));
417             type = (reason == DBG_WATCHPOINT_READ) ? tr("read") : tr("write");
418             text = tr("Hit ") + type + tr(" watchpoint ") + input;
419 
420             for (int i = 0; i < m_watchpoints->rowCount(); i++) {
421                 uint32_t low = static_cast<uint32_t>(hex2int(m_watchpoints->item(i, WATCH_LOW_COL)->text()));
422                 uint32_t high = static_cast<uint32_t>(hex2int(m_watchpoints->item(i, WATCH_HIGH_COL)->text()));
423                 if (addr >= low && addr <= high) {
424                     label = m_watchpoints->item(row, WATCH_NAME_COL)->text();
425                     valid = true;
426                     break;
427                 }
428             }
429             if (valid == false) {
430                 emu.resume();
431                 return;
432             }
433 
434             text = tr("Hit ") + type + tr(" watchpoint ") + input + QStringLiteral(" (") + label + QStringLiteral(")");
435 
436             gotoMemAddr(static_cast<uint32_t>(hex2int(input)));
437             break;
438         case DBG_PORT_READ:
439         case DBG_PORT_WRITE:
440             input = int2hex(data, 4);
441             type = (reason == DBG_PORT_READ) ? tr("Read") : tr("Wrote");
442             text = type + tr(" port ") + input;
443             break;
444         case DBG_NMI_TRIGGERED:
445             text = tr("NMI triggered");
446             break;
447         case DBG_WATCHDOG_TIMEOUT:
448             text = tr("Watchdog timeout");
449             break;
450         case DBG_MISC_RESET:
451             text = tr("Misc. reset");
452             break;
453         default:
454         case DBG_USER:
455             debugRaise();
456             return;
457     }
458 
459     // Checked everything, now we should raise the debugger
460     if (!text.isEmpty()) {
461         ui->debuggerLabel->setText(text);
462     }
463     debugRaise();
464 }
465 
debugSync()466 void MainWindow::debugSync() {
467     if (!guiDebug) {
468         return;
469     }
470 
471     // Update all the changes in the core
472     cpu.registers.AF = static_cast<uint16_t>(hex2int(ui->afregView->text()));
473     cpu.registers.HL = static_cast<uint32_t>(hex2int(ui->hlregView->text()));
474     cpu.registers.DE = static_cast<uint32_t>(hex2int(ui->deregView->text()));
475     cpu.registers.BC = static_cast<uint32_t>(hex2int(ui->bcregView->text()));
476     cpu.registers.IX = static_cast<uint32_t>(hex2int(ui->ixregView->text()));
477     cpu.registers.IY = static_cast<uint32_t>(hex2int(ui->iyregView->text()));
478 
479     cpu.registers._AF = static_cast<uint16_t>(hex2int(ui->af_regView->text()));
480     cpu.registers._HL = static_cast<uint32_t>(hex2int(ui->hl_regView->text()));
481     cpu.registers._DE = static_cast<uint32_t>(hex2int(ui->de_regView->text()));
482     cpu.registers._BC = static_cast<uint32_t>(hex2int(ui->bc_regView->text()));
483 
484     cpu.registers.SPL = static_cast<uint32_t>(hex2int(ui->splregView->text()));
485     cpu.registers.SPS = static_cast<uint16_t>(hex2int(ui->spsregView->text()));
486 
487     cpu.registers.MBASE = static_cast<uint8_t>(hex2int(ui->mbregView->text()));
488     cpu.registers.I = static_cast<uint16_t>(hex2int(ui->iregView->text()));
489     cpu.registers.R = static_cast<uint8_t>(hex2int(ui->rregView->text()));
490     cpu.registers.R = static_cast<uint8_t>(cpu.registers.R << 1 | cpu.registers.R >> 7);
491     cpu.IM = static_cast<uint8_t>(hex2int(ui->imregView->text()));
492     cpu.IM += !!cpu.IM;
493 
494     cpu.registers.flags.Z = ui->checkZ->isChecked();
495     cpu.registers.flags.C = ui->checkC->isChecked();
496     cpu.registers.flags.H = ui->checkHC->isChecked();
497     cpu.registers.flags.PV = ui->checkPV->isChecked();
498     cpu.registers.flags.N = ui->checkN->isChecked();
499     cpu.registers.flags.S = ui->checkS->isChecked();
500     cpu.registers.flags._5 = ui->check5->isChecked();
501     cpu.registers.flags._3 = ui->check3->isChecked();
502 
503     cpu.halted = ui->checkHalted->isChecked();
504     cpu.ADL = ui->checkADL->isChecked();
505     cpu.MADL = ui->checkMADL->isChecked();
506     cpu.halted = ui->checkHalted->isChecked();
507     cpu.IEF1 = ui->checkIEF1->isChecked();
508     cpu.IEF2 = ui->checkIEF2->isChecked();
509 
510     debug.totalCycles = ui->cycleView->text().toLongLong() + (m_ignoreDmaCycles ? debug.dmaCycles : 0);
511 
512     uint32_t uiPC = static_cast<uint32_t>(hex2int(ui->pcregView->text()));
513     if (cpu.registers.PC != uiPC) {
514         cpu_flush(uiPC, ui->checkADL->isChecked());
515     }
516 
517     backlight.brightness = static_cast<uint8_t>(ui->brightnessSlider->value());
518     backlight.factor = (310.0f - static_cast<float>(backlight.brightness)) / 160.0f;
519 
520     lcd.upbase = static_cast<uint32_t>(hex2int(ui->lcdbaseView->text()));
521     lcd.upcurr = static_cast<uint32_t>(hex2int(ui->lcdcurrView->text()));
522 
523     lcd.control &= ~14u;
524     lcd.control |= static_cast<unsigned int>(ui->bppView->currentIndex() << 1);
525 
526     set_reset(ui->checkPowered->isChecked(), 0x800u, lcd.control);
527     set_reset(ui->checkBEPO->isChecked(), 0x400u, lcd.control);
528     set_reset(ui->checkBEBO->isChecked(), 0x200u, lcd.control);
529     set_reset(ui->checkBGR->isChecked(), 0x100u, lcd.control);
530 
531     set_cpu_clock(static_cast<uint32_t>(ui->freqView->text().toUInt() * 1e6));
532 
533     lcd_update();
534 
535     ui->debuggerLabel->clear();
536 }
537 
debugGuiState(bool state)538 void MainWindow::debugGuiState(bool state) {
539     if (state) {
540         ui->buttonRun->setText(tr("Run"));
541         ui->buttonRun->setIcon(m_iconRun);
542     } else {
543         ui->buttonRun->setText(tr("Stop"));
544         ui->buttonRun->setIcon(m_iconStop);
545         ui->debuggerLabel->clear();
546     }
547 
548     m_disasm->setEnabled(state);
549     ui->buttonGoto->setEnabled(state);
550     ui->buttonStepIn->setEnabled(state);
551     ui->buttonStepOver->setEnabled(state);
552     ui->buttonStepNext->setEnabled(state);
553     ui->buttonStepOut->setEnabled(state);
554     ui->buttonCertID->setEnabled(state);
555     ui->groupCPU->setEnabled(state);
556     ui->groupFlags->setEnabled(state);
557     ui->groupRegisters->setEnabled(state);
558     ui->groupInterrupts->setEnabled(state);
559     ui->groupStack->setEnabled(state);
560     ui->groupFlash->setEnabled(state);
561     ui->groupRAM->setEnabled(state);
562     ui->cycleView->setEnabled(state);
563     ui->freqView->setEnabled(state);
564     ui->groupRTC->setEnabled(state);
565     ui->groupGPT->setEnabled(state);
566     ui->groupLcdState->setEnabled(state);
567     ui->groupLcdRegs->setEnabled(state);
568     ui->groupBattery->setEnabled(state);
569     ui->groupTrigger->setEnabled(state);
570 
571     ui->opView->setEnabled(state && m_normalOs);
572     ui->vatView->setEnabled(state && m_normalOs);
573     ui->opStack->setEnabled(state && m_normalOs);
574     ui->fpStack->setEnabled(state && m_normalOs);
575 
576     ui->buttonSend->setEnabled(!state);
577     ui->buttonRefreshList->setEnabled(!state);
578     ui->emuVarView->setEnabled(!state);
579     ui->buttonResendFiles->setEnabled(!state);
580     ui->buttonReceiveFiles->setEnabled(!state && guiReceive);
581     ui->buttonReceiveFile->setEnabled(!state && guiReceive);
582 
583     QList<QDockWidget*> docks = findChildren<QDockWidget*>();
584     foreach (QDockWidget* dock, docks) {
585         if (dock->windowTitle().contains(TXT_MEM_DOCK)) {
586             if (dock->isVisible()) {
587                 QList<QPushButton*> buttons = dock->findChildren<QPushButton*>();
588                 QList<QToolButton*> tools = dock->findChildren<QToolButton*>();
589                 QList<HexWidget*> editChildren = dock->findChildren<HexWidget*>();
590                 QList<QSpinBox*> spinChildren = dock->findChildren<QSpinBox*>();
591                 editChildren.first()->setEnabled(state);
592                 spinChildren.first()->setEnabled(state);
593                 foreach (QPushButton *button, buttons) {
594                     button->setEnabled(state);
595                 }
596                 foreach (QToolButton *tool, tools) {
597                     tool->setEnabled(state);
598                 }
599             }
600         }
601     }
602 }
603 
debugToggle()604 void MainWindow::debugToggle() {
605     bool state = guiDebug;
606 
607     if (m_pathRom.isEmpty()) {
608         return;
609     }
610 
611     if (guiReceive) {
612         varToggle();
613     }
614 
615     if (state) {
616         debugSync();
617         debugDisable();
618     }
619 
620     emu.debug(!state);
621 }
622 
debugPopulate()623 void MainWindow::debugPopulate() {
624     QString tmp;
625 
626     tmp = int2hex(cpu.registers.AF, 4);
627     ui->afregView->setPalette(tmp == ui->afregView->text() ? m_cNone : m_cBack);
628     ui->afregView->setText(tmp);
629 
630     tmp = int2hex(cpu.registers.HL, 6);
631     ui->hlregView->setPalette(tmp == ui->hlregView->text() ? m_cNone : m_cBack);
632     ui->hlregView->setText(tmp);
633 
634     tmp = int2hex(cpu.registers.DE, 6);
635     ui->deregView->setPalette(tmp == ui->deregView->text() ? m_cNone : m_cBack);
636     ui->deregView->setText(tmp);
637 
638     tmp = int2hex(cpu.registers.BC, 6);
639     ui->bcregView->setPalette(tmp == ui->bcregView->text() ? m_cNone : m_cBack);
640     ui->bcregView->setText(tmp);
641 
642     tmp = int2hex(cpu.registers.IX, 6);
643     ui->ixregView->setPalette(tmp == ui->ixregView->text() ? m_cNone : m_cBack);
644     ui->ixregView->setText(tmp);
645 
646     tmp = int2hex(cpu.registers.IY, 6);
647     ui->iyregView->setPalette(tmp == ui->iyregView->text() ? m_cNone : m_cBack);
648     ui->iyregView->setText(tmp);
649 
650     tmp = int2hex(cpu.registers._AF, 4);
651     ui->af_regView->setPalette(tmp == ui->af_regView->text() ? m_cNone : m_cBack);
652     ui->af_regView->setText(tmp);
653 
654     tmp = int2hex(cpu.registers._HL, 6);
655     ui->hl_regView->setPalette(tmp == ui->hl_regView->text() ? m_cNone : m_cBack);
656     ui->hl_regView->setText(tmp);
657 
658     tmp = int2hex(cpu.registers._DE, 6);
659     ui->de_regView->setPalette(tmp == ui->de_regView->text() ? m_cNone : m_cBack);
660     ui->de_regView->setText(tmp);
661 
662     tmp = int2hex(cpu.registers._BC, 6);
663     ui->bc_regView->setPalette(tmp == ui->bc_regView->text() ? m_cNone : m_cBack);
664     ui->bc_regView->setText(tmp);
665 
666     tmp = int2hex(cpu.registers.SPS, 4);
667     ui->spsregView->setPalette(tmp == ui->spsregView->text() ? m_cNone : m_cBack);
668     ui->spsregView->setText(tmp);
669 
670     tmp = int2hex(cpu.registers.SPL, 6);
671     ui->splregView->setPalette(tmp == ui->splregView->text() ? m_cNone : m_cBack);
672     ui->splregView->setText(tmp);
673 
674     tmp = int2hex(cpu.registers.MBASE, 2);
675     ui->mbregView->setPalette(tmp == ui->mbregView->text() ? m_cNone : m_cBack);
676     ui->mbregView->setText(tmp);
677 
678     tmp = int2hex(cpu.registers.I, 4);
679     ui->iregView->setPalette(tmp == ui->iregView->text() ? m_cNone : m_cBack);
680     ui->iregView->setText(tmp);
681 
682     tmp = int2hex(cpu.IM - !!cpu.IM, 1);
683     ui->imregView->setPalette(tmp == ui->imregView->text() ? m_cNone : m_cBack);
684     ui->imregView->setText(tmp);
685 
686     tmp = int2hex(cpu.registers.PC, 6);
687     ui->pcregView->setPalette(tmp == ui->pcregView->text() ? m_cNone : m_cBack);
688     ui->pcregView->setText(tmp);
689 
690     tmp = int2hex(static_cast<uint32_t>(cpu.registers.R >> 1 | cpu.registers.R << 7), 2);
691     ui->rregView->setPalette(tmp == ui->rregView->text() ? m_cNone : m_cBack);
692     ui->rregView->setText(tmp);
693 
694     tmp = int2hex(lcd.upbase, 6);
695     ui->lcdbaseView->setPalette(tmp == ui->lcdbaseView->text() ? m_cNone : m_cBack);
696     ui->lcdbaseView->setText(tmp);
697 
698     tmp = int2hex(lcd.upcurr, 6);
699     ui->lcdcurrView->setPalette(tmp == ui->lcdcurrView->text() ? m_cNone : m_cBack);
700     ui->lcdcurrView->setText(tmp);
701 
702     tmp = QString::number(sched.clockRates[CLOCK_CPU] / 1e6);
703     ui->freqView->setPalette(tmp == ui->freqView->text() ? m_cNone : m_cBack);
704     ui->freqView->setText(tmp);
705 
706     tmp = QString::number(debug.totalCycles - (m_ignoreDmaCycles ? debug.dmaCycles : 0));
707     ui->cycleView->setPalette(tmp == ui->cycleView->text() ? m_cNone : m_cBack);
708     ui->cycleView->setText(tmp);
709 
710     tmp = QString::number(rtc.readSec);
711     ui->seconds->setPalette(tmp == ui->seconds->text() ? m_cNone : m_cBack);
712     ui->seconds->setText(tmp);
713 
714     tmp = QString::number(rtc.readMin);
715     ui->minutes->setPalette(tmp == ui->minutes->text() ? m_cNone : m_cBack);
716     ui->minutes->setText(tmp);
717 
718     tmp = QString::number(rtc.readHour);
719     ui->hours->setPalette(tmp == ui->hours->text() ? m_cNone : m_cBack);
720     ui->hours->setText(tmp);
721 
722     tmp = QString::number(rtc.readDay);
723     ui->days->setPalette(tmp == ui->days->text() ? m_cNone : m_cBack);
724     ui->days->setText(tmp);
725 
726     tmp = QString::number(gpt.timer[0].counter);
727     ui->timer1->setPalette(tmp == ui->timer1->text() ? m_cNone : m_cBack);
728     ui->timer1->setText(tmp);
729 
730     tmp = QString::number(gpt.timer[0].reset);
731     ui->timer1r->setPalette(tmp == ui->timer1r->text() ? m_cNone : m_cBack);
732     ui->timer1r->setText(tmp);
733 
734     tmp = QString::number(gpt.timer[1].counter);
735     ui->timer2->setPalette(tmp == ui->timer2->text() ? m_cNone : m_cBack);
736     ui->timer2->setText(tmp);
737 
738     tmp = QString::number(gpt.timer[1].reset);
739     ui->timer2r->setPalette(tmp == ui->timer2r->text() ? m_cNone : m_cBack);
740     ui->timer2r->setText(tmp);
741 
742     tmp = QString::number(gpt.timer[2].counter);
743     ui->timer3->setPalette(tmp == ui->timer3->text() ? m_cNone : m_cBack);
744     ui->timer3->setText(tmp);
745 
746     tmp = QString::number(gpt.timer[2].reset);
747     ui->timer3r->setPalette(tmp == ui->timer3r->text() ? m_cNone : m_cBack);
748     ui->timer3r->setText(tmp);
749 
750     batterySetCharging(control.batteryCharging);
751     batterySet(control.setBatteryStatus);
752 
753     ui->bppView->setCurrentIndex((lcd.control >> 1) & 7);
754 
755     ui->check3->setChecked(cpu.registers.flags._3);
756     ui->check5->setChecked(cpu.registers.flags._5);
757     ui->checkZ->setChecked(cpu.registers.flags.Z);
758     ui->checkC->setChecked(cpu.registers.flags.C);
759     ui->checkHC->setChecked(cpu.registers.flags.H);
760     ui->checkPV->setChecked(cpu.registers.flags.PV);
761     ui->checkN->setChecked(cpu.registers.flags.N);
762     ui->checkS->setChecked(cpu.registers.flags.S);
763 
764     ui->checkADL->blockSignals(true);
765     ui->checkADL->setChecked(cpu.ADL);
766     ui->checkADL->blockSignals(false);
767     ui->checkMADL->setChecked(cpu.MADL);
768     ui->checkHalted->setChecked(cpu.halted);
769     ui->checkIEF1->setChecked(cpu.IEF1);
770     ui->checkIEF2->setChecked(cpu.IEF2);
771 
772     ui->checkPowered->setChecked(lcd.control & 0x800);
773     ui->checkBEPO->setChecked(lcd.control & 0x400);
774     ui->checkBEBO->setChecked(lcd.control & 0x200);
775     ui->checkBGR->setChecked(lcd.control & 0x100);
776     ui->brightnessSlider->setValue(backlight.brightness);
777 
778     m_ports->blockSignals(true);
779     m_watchpoints->blockSignals(true);
780 
781     for (int i = 0; i < m_ports->rowCount(); i++) {
782         portPopulate(i);
783     }
784 
785     m_ports->blockSignals(false);
786     m_watchpoints->blockSignals(false);
787 
788     osUpdate();
789     stackUpdate();
790     disasmUpdateAddr(m_prevDisasmAddr = cpu.registers.PC, true);
791 
792     memUpdate();
793 }
794 
795 // ------------------------------------------------
796 // Clock items
797 // ------------------------------------------------
798 
debugZeroCycles()799 void MainWindow::debugZeroCycles() {
800     debug.totalCycles = 0;
801     debug.dmaCycles = 0;
802     ui->cycleView->setText(QStringLiteral("0"));
803     ui->cycleView->repaint();
804 }
805 
806 // ------------------------------------------------
807 // Breakpoints
808 // ------------------------------------------------
809 
breakSetPrev(QTableWidgetItem * current,QTableWidgetItem * previous)810 void MainWindow::breakSetPrev(QTableWidgetItem *current, QTableWidgetItem *previous) {
811     (void)previous;
812     if (current == Q_NULLPTR || current->text().isEmpty()) {
813         return;
814     }
815 
816     if (current->column() == BREAK_ADDR_COL) {
817         m_prevBreakAddr = current->text();
818     }
819 }
820 
breakRemoveRow(int row)821 void MainWindow::breakRemoveRow(int row) {
822     uint32_t address = static_cast<uint32_t>(hex2int(m_breakpoints->item(row, BREAK_ADDR_COL)->text()));
823 
824     debug_watch(address, DBG_MASK_EXEC, false);
825     if (!m_guiAdd && !m_useSoftCom) {
826         disasmUpdate();
827         memUpdate();
828     }
829     m_breakpoints->removeRow(row);
830 }
831 
breakRemoveSelected()832 void MainWindow::breakRemoveSelected() {
833     for (int row = 0; row < m_breakpoints->rowCount(); row++){
834         if (sender() == m_breakpoints->cellWidget(row, BREAK_REMOVE_COL)) {
835             breakRemoveRow(row);
836             break;
837         }
838     }
839 }
840 
breakRemove(uint32_t address)841 void MainWindow::breakRemove(uint32_t address) {
842     for (int row = 0; row < m_breakpoints->rowCount(); row++) {
843         uint32_t test = static_cast<uint32_t>(hex2int(m_breakpoints->item(row, BREAK_ADDR_COL)->text()));
844         if (address == test) {
845             breakRemoveRow(row);
846             break;
847         }
848     }
849 }
850 
breakGetMask(int row)851 int MainWindow::breakGetMask(int row) {
852     int mask;
853     if (static_cast<QAbstractButton *>(m_breakpoints->cellWidget(row, BREAK_ENABLE_COL))->isChecked()) {
854         mask = DBG_MASK_EXEC;
855     } else {
856         mask = DBG_MASK_NONE;
857     }
858     return mask;
859 }
860 
breakModified(QTableWidgetItem * item)861 void MainWindow::breakModified(QTableWidgetItem *item) {
862     if (item == Q_NULLPTR) {
863         return;
864     }
865 
866     int row = item->row();
867     int col = item->column();
868     QString addrStr;
869     uint32_t addr;
870 
871     if (col == BREAK_NAME_COL) {
872         updateLabels();
873     } else if (col == BREAK_ADDR_COL){
874         std::string s = item->text().toUpper().toStdString();
875         QString equate;
876         int mask;
877 
878         equate = getAddressOfEquate(s);
879         if (!equate.isEmpty()) {
880             s = equate.toStdString();
881             m_breakpoints->blockSignals(true);
882             if (m_breakpoints->item(row, BREAK_NAME_COL)->text() == (QStringLiteral("Label") + QString::number(row))) {
883                 m_breakpoints->item(row, BREAK_NAME_COL)->setText(item->text());
884             }
885             m_breakpoints->blockSignals(false);
886         }
887 
888         if (isNotValidHex(s) || s.length() > 6) {
889             item->setText(m_prevBreakAddr);
890             return;
891         }
892 
893         addr = static_cast<uint32_t>(hex2int(QString::fromStdString(s)));
894         addrStr = int2hex(addr, 6);
895 
896         m_breakpoints->blockSignals(true);
897 
898         // Return if address is already set
899         for (int i = 0; i < m_breakpoints->rowCount(); i++) {
900             if (m_breakpoints->item(i, BREAK_ADDR_COL)->text() == addrStr && i != row) {
901                 item->setText(m_prevBreakAddr);
902                 m_breakpoints->blockSignals(false);
903                 return;
904             }
905         }
906 
907         mask = breakGetMask(row);
908 
909         if (m_prevBreakAddr != DEBUG_UNSET_ADDR) {
910             debug_watch(hex2int(m_prevBreakAddr), DBG_MASK_EXEC, false);
911         }
912         item->setText(addrStr);
913         debug_watch(addr, mask, true);
914         m_breakpoints->blockSignals(false);
915     }
916     disasmUpdate();
917     memUpdate();
918 }
919 
breakNextLabel()920 QString MainWindow::breakNextLabel() {
921     return QStringLiteral("Label") + QString::number(m_breakpoints->rowCount());
922 }
923 
watchNextLabel()924 QString MainWindow::watchNextLabel() {
925     return QStringLiteral("Label") + QString::number(m_watchpoints->rowCount());
926 }
927 
breakAddSlot()928 void MainWindow::breakAddSlot() {
929     breakAdd(breakNextLabel(), 0, true, false, true);
930 }
931 
breakAddGui()932 void MainWindow::breakAddGui() {
933     uint32_t address = static_cast<uint32_t>(hex2int(m_disasm->getSelectedAddr()));
934 
935     QTextCursor c = m_disasm->textCursor();
936     c.setCharFormat(m_disasm->currentCharFormat());
937 
938     m_guiAdd = true;
939 
940     breakAdd(breakNextLabel(), address, true, true, false);
941 
942     m_guiAdd = false;
943 
944     int32_t base = disasm.base;
945     int32_t next = disasm.next;
946 
947     disasm.base = address;
948     disasm.highlight.breakP = false;
949     disasmGet();
950     disasm.base = base;
951     disasm.next = next;
952 
953     c.movePosition(QTextCursor::StartOfLine, QTextCursor::MoveAnchor);
954     c.setPosition(c.position()+9, QTextCursor::MoveAnchor);
955     c.deleteChar();
956 
957     // mark breakpoint
958     if (disasm.highlight.breakP) {
959         c.insertHtml(QStringLiteral("<b><font color='#800000'>X</font></b>"));
960     } else {
961         c.insertText(QStringLiteral(" "));
962     }
963 
964     if (m_disasm->labelCheck()) {
965         disasmUpdate();
966         memUpdate();
967     }
968 }
969 
breakAdd(const QString & label,uint32_t addr,bool enabled,bool toggle,bool unset)970 bool MainWindow::breakAdd(const QString &label, uint32_t addr, bool enabled, bool toggle, bool unset) {
971     const int row = m_breakpoints->rowCount();
972     QString addrStr;
973 
974     if (unset) {
975         addrStr = DEBUG_UNSET_ADDR;
976     } else {
977         addrStr = int2hex((addr &= 0xFFFFFF), 6).toUpper();
978     }
979 
980     // return if address is already set
981     for (int i = 0; i < row; i++) {
982         if (m_breakpoints->item(i, BREAK_ADDR_COL)->text() == addrStr) {
983             if (addrStr != DEBUG_UNSET_ADDR) {
984                 if (!m_useSoftCom) {
985                     m_breakpoints->selectRow(i);
986                     if (toggle) {
987                         breakRemoveRow(i);
988                     }
989                 } else {
990                     ui->lcd->setFocus();
991                 }
992                 return false;
993             }
994         }
995     }
996 
997     m_breakpoints->blockSignals(true);
998     m_breakpoints->setRowCount(row + 1);
999 
1000     QToolButton *btnRemove = new QToolButton;
1001     btnRemove->setIcon(m_iconRemove);
1002 
1003     QToolButton *btnEnable = new QToolButton;
1004     btnEnable->setIcon(enabled ? m_iconCheck : m_iconCheckGray);
1005     btnEnable->setCheckable(true);
1006     btnEnable->setChecked(enabled);
1007 
1008     connect(btnRemove, &QToolButton::clicked, this, &MainWindow::breakRemoveSelected);
1009     connect(btnEnable, &QToolButton::clicked, [this, btnEnable, row](bool checked) {
1010         uint32_t addr = static_cast<uint32_t>(hex2int(m_breakpoints->item(row, BREAK_ADDR_COL)->text()));
1011         btnEnable->setIcon(checked ? m_iconCheck : m_iconCheckGray);
1012         debug_watch(addr, DBG_MASK_EXEC, checked);
1013         disasmUpdate();
1014         memUpdate();
1015     });
1016 
1017     QTableWidgetItem *itemLabel = new QTableWidgetItem(label);
1018     QTableWidgetItem *itemAddr = new QTableWidgetItem(addrStr);
1019     QTableWidgetItem *itemBreak = new QTableWidgetItem;
1020     QTableWidgetItem *itemRemove = new QTableWidgetItem;
1021 
1022     m_breakpoints->setItem(row, BREAK_NAME_COL, itemLabel);
1023     m_breakpoints->setItem(row, BREAK_ADDR_COL, itemAddr);
1024     m_breakpoints->setItem(row, BREAK_ENABLE_COL, itemBreak);
1025     m_breakpoints->setItem(row, BREAK_REMOVE_COL, itemRemove);
1026     m_breakpoints->setCellWidget(row, BREAK_REMOVE_COL, btnRemove);
1027     m_breakpoints->setCellWidget(row, BREAK_ENABLE_COL, btnEnable);
1028 
1029     m_breakpoints->setCurrentCell(row, BREAK_REMOVE_COL);
1030 
1031     if (addrStr != DEBUG_UNSET_ADDR) {
1032         debug_watch(addr, DBG_MASK_EXEC, enabled);
1033     }
1034 
1035     if (!m_guiAdd && !m_useSoftCom) {
1036         disasmUpdate();
1037         memUpdate();
1038     }
1039 
1040     m_prevBreakAddr = addrStr;
1041     m_breakpoints->blockSignals(false);
1042 
1043     if (m_useSoftCom) {
1044         ui->lcd->setFocus();
1045     }
1046 
1047     m_breakpoints->setVisible(false);
1048     m_breakpoints->resizeColumnsToContents();
1049     m_breakpoints->setVisible(true);
1050     return true;
1051 }
1052 
1053 // ------------------------------------------------
1054 // Ports
1055 // ------------------------------------------------
1056 
portSetPrev(QTableWidgetItem * current,QTableWidgetItem * previous)1057 void MainWindow::portSetPrev(QTableWidgetItem *current, QTableWidgetItem *previous) {
1058     (void)previous;
1059     if (current == Q_NULLPTR || current->text().isEmpty()) {
1060         return;
1061     }
1062 
1063     if (current->column() == PORT_ADDR_COL) {
1064         m_prevPortAddr = current->text();
1065     }
1066 }
1067 
portRemoveRow(int row)1068 void MainWindow::portRemoveRow(int row) {
1069     uint16_t port = static_cast<uint16_t>(hex2int(m_ports->item(row, PORT_ADDR_COL)->text()));
1070     debug_ports(port, ~DBG_MASK_NONE, false);
1071     m_ports->removeRow(row);
1072 }
1073 
portRemoveSelected()1074 void MainWindow::portRemoveSelected() {
1075     for (int row = 0; row < m_ports->rowCount(); row++){
1076         if (sender() == m_ports->cellWidget(row, PORT_REMOVE_COL)) {
1077             portRemoveRow(row);
1078             break;
1079         }
1080     }
1081 }
1082 
portPopulate(int currRow)1083 void MainWindow::portPopulate(int currRow) {
1084     uint16_t port = static_cast<uint16_t>(hex2int(m_ports->item(currRow, PORT_ADDR_COL)->text()));
1085     uint8_t read = static_cast<uint8_t>(port_peek_byte(port));
1086 
1087     m_ports->item(currRow, PORT_VALUE_COL)->setText(int2hex(read, 2));
1088 }
1089 
portAddSlot()1090 void MainWindow::portAddSlot() {
1091     portAdd(0, DBG_MASK_NONE, true);
1092 }
1093 
portAdd(uint16_t port,int mask,bool unset)1094 bool MainWindow::portAdd(uint16_t port, int mask, bool unset) {
1095     const int row = m_ports->rowCount();
1096     QString portStr;
1097     uint8_t data = 0;
1098 
1099     if (unset) {
1100         portStr = DEBUG_UNSET_PORT;
1101     } else {
1102         portStr = int2hex(port, 4).toUpper();
1103         if (guiDebug) {
1104             data = port_peek_byte(port);
1105         }
1106     }
1107 
1108     // return if port is already set
1109     for (int i = 0; i < row; i++) {
1110         if (m_ports->item(i, PORT_ADDR_COL)->text() == portStr) {
1111             if (portStr != DEBUG_UNSET_PORT) {
1112                 return false;
1113             }
1114         }
1115     }
1116 
1117     m_ports->setRowCount(row + 1);
1118     m_ports->blockSignals(true);
1119 
1120     QToolButton *btnRemove = new QToolButton;
1121     btnRemove->setIcon(m_iconRemove);
1122 
1123     QToolButton *btnRead = new QToolButton;
1124     btnRead->setIcon((mask & DBG_MASK_PORT_READ) ? m_iconCheck : m_iconCheckGray);
1125     btnRead->setCheckable(true);
1126     btnRead->setChecked(mask & DBG_MASK_PORT_READ);
1127 
1128     QToolButton *btnWrite = new QToolButton;
1129     btnWrite->setIcon((mask & DBG_MASK_PORT_WRITE) ? m_iconCheck : m_iconCheckGray);
1130     btnWrite->setCheckable(true);
1131     btnWrite->setChecked(mask & DBG_MASK_PORT_WRITE);
1132 
1133     QToolButton *btnFreeze = new QToolButton;
1134     btnFreeze->setIcon((mask & DBG_MASK_PORT_FREEZE) ? m_iconCheck : m_iconCheckGray);
1135     btnFreeze->setCheckable(true);
1136     btnFreeze->setChecked(mask & DBG_MASK_PORT_FREEZE);
1137 
1138     QTableWidgetItem *itemAddr = new QTableWidgetItem(portStr);
1139     QTableWidgetItem *itemData = new QTableWidgetItem(int2hex(data, 2));
1140     QTableWidgetItem *itemRead = new QTableWidgetItem;
1141     QTableWidgetItem *itemWrite = new QTableWidgetItem;
1142     QTableWidgetItem *itemFreeze = new QTableWidgetItem;
1143     QTableWidgetItem *itemRemove = new QTableWidgetItem;
1144 
1145     connect(btnRemove, &QToolButton::clicked, this, &MainWindow::portRemoveSelected);
1146     connect(btnRead, &QToolButton::clicked, [this, btnRead, itemRead](bool checked) { btnRead->setIcon(checked ? m_iconCheck : m_iconCheckGray); portModified(itemRead); });
1147     connect(btnWrite, &QToolButton::clicked, [this, btnWrite, itemWrite](bool checked) { btnWrite->setIcon(checked ? m_iconCheck : m_iconCheckGray); portModified(itemWrite); });
1148     connect(btnFreeze, &QToolButton::clicked, [this, btnFreeze, itemFreeze](bool checked) { btnFreeze->setIcon(checked ? m_iconCheck : m_iconCheckGray); portModified(itemFreeze); });
1149 
1150     m_ports->setItem(row, PORT_ADDR_COL, itemAddr);
1151     m_ports->setItem(row, PORT_VALUE_COL, itemData);
1152     m_ports->setItem(row, PORT_READ_COL, itemRead);
1153     m_ports->setItem(row, PORT_WRITE_COL, itemWrite);
1154     m_ports->setItem(row, PORT_FREEZE_COL, itemFreeze);
1155     m_ports->setItem(row, PORT_REMOVE_COL, itemRemove);
1156     m_ports->setCellWidget(row, PORT_REMOVE_COL, btnRemove);
1157     m_ports->setCellWidget(row, PORT_READ_COL, btnRead);
1158     m_ports->setCellWidget(row, PORT_WRITE_COL, btnWrite);
1159     m_ports->setCellWidget(row, PORT_FREEZE_COL, btnFreeze);
1160 
1161     m_ports->selectRow(row);
1162     m_prevPortAddr = portStr;
1163     m_ports->blockSignals(false);
1164     m_ports->setVisible(false);
1165     m_ports->resizeColumnsToContents();
1166     m_ports->setVisible(true);
1167     return true;
1168 }
1169 
portGetMask(int row)1170 int MainWindow::portGetMask(int row) {
1171     unsigned int mask = 0;
1172     if (static_cast<QAbstractButton *>(m_ports->cellWidget(row, PORT_READ_COL))->isChecked()) {
1173         mask |= DBG_MASK_PORT_READ;
1174     }
1175     if (static_cast<QAbstractButton *>(m_ports->cellWidget(row, PORT_WRITE_COL))->isChecked()) {
1176         mask |= DBG_MASK_PORT_WRITE;
1177     }
1178     if (static_cast<QAbstractButton *>(m_ports->cellWidget(row, PORT_FREEZE_COL))->isChecked()) {
1179         mask |= DBG_MASK_PORT_FREEZE;
1180     }
1181     return mask;
1182 }
1183 
portModified(QTableWidgetItem * item)1184 void MainWindow::portModified(QTableWidgetItem *item) {
1185     if (item == Q_NULLPTR) {
1186         return;
1187     }
1188 
1189     int row = item->row();
1190     int col = item->column();
1191 
1192     if (col == PORT_READ_COL || col == PORT_WRITE_COL || col == PORT_FREEZE_COL) {
1193         uint16_t port = static_cast<uint16_t>(hex2int(m_ports->item(row, PORT_ADDR_COL)->text()));
1194         unsigned int mask = DBG_MASK_NONE;
1195 
1196         if (col == PORT_READ_COL) {   // Break on read
1197             mask = DBG_MASK_PORT_READ;
1198         }
1199         if (col == PORT_WRITE_COL) {  // Break on write
1200             mask = DBG_MASK_PORT_WRITE;
1201         }
1202         if (col == PORT_FREEZE_COL) { // Freeze
1203             mask = DBG_MASK_PORT_FREEZE;
1204         }
1205         debug_ports(port, mask, static_cast<QAbstractButton *>(m_ports->cellWidget(row, col))->isChecked());
1206     } else if (col == PORT_ADDR_COL) {
1207         std::string s = item->text().toUpper().toStdString();
1208         int mask;
1209 
1210         if (isNotValidHex(s) || s.length() > 4) {
1211             item->setText(m_prevPortAddr);
1212             return;
1213         }
1214 
1215         uint16_t port = static_cast<uint16_t>(hex2int(QString::fromStdString(s)));
1216         uint8_t data = port_peek_byte(port);
1217         QString portStr = int2hex(port, 4);
1218 
1219         m_ports->blockSignals(true);
1220 
1221         // return if port is already set
1222         for (int i=0; i<m_ports->rowCount(); i++) {
1223             if (m_ports->item(i, PORT_ADDR_COL)->text() == portStr && i != row) {
1224                 item->setText(m_prevPortAddr);
1225                 m_ports->blockSignals(false);
1226                 return;
1227             }
1228         }
1229 
1230         if (m_prevPortAddr != DEBUG_UNSET_PORT) {
1231             debug_ports(hex2int(m_prevPortAddr), ~DBG_MASK_NONE, false);
1232         }
1233 
1234         mask = portGetMask(row);
1235         debug_ports(port, mask, true);
1236         item->setText(portStr);
1237         m_ports->item(row, PORT_VALUE_COL)->setText(int2hex(data, 2));
1238     } else if (col == PORT_VALUE_COL) {
1239         if (m_ports->item(row, PORT_ADDR_COL)->text() != DEBUG_UNSET_PORT) {
1240             uint8_t pdata = static_cast<uint8_t>(hex2int(item->text()));
1241             uint16_t port = static_cast<uint16_t>(hex2int(m_ports->item(row, PORT_ADDR_COL)->text()));
1242 
1243             port_poke_byte(port, pdata);
1244             item->setText(int2hex(port_peek_byte(port), 2));
1245         }
1246     }
1247     m_ports->blockSignals(false);
1248 }
1249 
1250 // ------------------------------------------------
1251 // Watchpoints
1252 // ------------------------------------------------
1253 
watchSetPrev(QTableWidgetItem * current,QTableWidgetItem * previous)1254 void MainWindow::watchSetPrev(QTableWidgetItem *current, QTableWidgetItem *previous) {
1255     (void)previous;
1256     if (current == Q_NULLPTR || current->text().isEmpty()) {
1257         return;
1258     }
1259 
1260     if (current->column() == WATCH_LOW_COL) {
1261         m_prevWatchLow = current->text();
1262     }
1263     if (current->column() == WATCH_HIGH_COL) {
1264         m_prevWatchHigh = current->text();
1265     }
1266 }
1267 
watchRemoveRow(int row)1268 void MainWindow::watchRemoveRow(int row) {
1269     if (m_watchpoints->item(row, WATCH_LOW_COL)->text() != DEBUG_UNSET_ADDR &&
1270         m_watchpoints->item(row, WATCH_HIGH_COL)->text() != DEBUG_UNSET_ADDR) {
1271         uint32_t low = static_cast<uint32_t>(hex2int(m_watchpoints->item(row, WATCH_LOW_COL)->text()));
1272         uint32_t high = static_cast<uint32_t>(hex2int(m_watchpoints->item(row, WATCH_HIGH_COL)->text()));
1273 
1274         for (uint32_t addr = low; addr <= high; addr++) {
1275             debug_watch(addr, DBG_MASK_READ | DBG_MASK_WRITE, false);
1276         }
1277 
1278         if (!m_guiAdd && !m_useSoftCom) {
1279             disasmUpdate();
1280             memUpdate();
1281         }
1282     }
1283     m_watchpoints->removeRow(row);
1284     watchUpdate();
1285 }
1286 
watchRemoveSelected()1287 void MainWindow::watchRemoveSelected() {
1288     for (int row = 0; row < m_watchpoints->rowCount(); row++){
1289         if (sender() == m_watchpoints->cellWidget(row, WATCH_REMOVE_COL)) {
1290             watchRemoveRow(row);
1291             break;
1292         }
1293     }
1294 }
1295 
watchRemove(uint32_t address)1296 void MainWindow::watchRemove(uint32_t address) {
1297     for (int row = 0; row < m_watchpoints->rowCount(); row++) {
1298         uint32_t test = static_cast<uint32_t>(hex2int(m_watchpoints->item(row, WATCH_LOW_COL)->text()));
1299         if (address == test) {
1300             watchRemoveRow(row);
1301             break;
1302         }
1303     }
1304 }
1305 
watchAddGuiR()1306 void MainWindow::watchAddGuiR() {
1307     m_watchGUIMask = DBG_MASK_READ;
1308     watchAddGui();
1309 }
1310 
watchAddGuiW()1311 void MainWindow::watchAddGuiW() {
1312     m_watchGUIMask = DBG_MASK_WRITE;
1313     watchAddGui();
1314 }
1315 
watchAddGuiRW()1316 void MainWindow::watchAddGuiRW() {
1317     m_watchGUIMask = DBG_MASK_READ | DBG_MASK_WRITE;
1318     watchAddGui();
1319 }
1320 
watchAddGui()1321 void MainWindow::watchAddGui() {
1322     int mask = m_watchGUIMask;
1323     uint32_t addr = static_cast<uint32_t>(hex2int(m_disasm->getSelectedAddr()));
1324 
1325     QTextCursor c = m_disasm->textCursor();
1326     c.setCharFormat(m_disasm->currentCharFormat());
1327 
1328     m_guiAdd = true;
1329 
1330     watchAdd(watchNextLabel(), addr, addr, mask, true, false);
1331 
1332     m_guiAdd = false;
1333 
1334     int32_t base = disasm.base;
1335     int32_t next = disasm.next;
1336 
1337     disasm.base = static_cast<int32_t>(addr);
1338     disasm.highlight.watchR = false;
1339     disasm.highlight.watchW = false;
1340     disasmGet();
1341 
1342     disasm.base = base;
1343     disasm.next = next;
1344 
1345     c.movePosition(QTextCursor::StartOfLine, QTextCursor::MoveAnchor);
1346     c.setPosition(c.position() + 7, QTextCursor::MoveAnchor);
1347     c.deleteChar();
1348 
1349     // mark read
1350     if (disasm.highlight.watchR) {
1351         c.insertHtml(QStringLiteral("<b><font color='#008000'>R</font></b>"));
1352     } else {
1353         c.insertText(QStringLiteral(" "));
1354     }
1355 
1356     c.movePosition(QTextCursor::StartOfLine, QTextCursor::MoveAnchor);
1357     c.setPosition(c.position()+8, QTextCursor::MoveAnchor);
1358     c.deleteChar();
1359 
1360     // mark write
1361     if (disasm.highlight.watchW) {
1362         c.insertHtml(QStringLiteral("<b><font color='#808000'>W</font></b>"));
1363     } else {
1364         c.insertText(QStringLiteral(" "));
1365     }
1366 
1367     if (m_disasm->labelCheck()) {
1368         disasmUpdate();
1369         memUpdate();
1370     }
1371 }
1372 
watchAddSlot()1373 void MainWindow::watchAddSlot() {
1374     watchAdd(watchNextLabel(), 0, 0, DBG_MASK_READ | DBG_MASK_WRITE, false, true);
1375 }
1376 
watchUpdate()1377 void MainWindow::watchUpdate() {
1378 
1379     // this is needed in the case of overlapping address spaces
1380     for (int row = 0; row < m_watchpoints->rowCount(); row++) {
1381         if (m_watchpoints->item(row, WATCH_LOW_COL)->text() != DEBUG_UNSET_ADDR &&
1382             m_watchpoints->item(row, WATCH_HIGH_COL)->text() != DEBUG_UNSET_ADDR) {
1383             uint32_t low = static_cast<uint32_t>(hex2int(m_watchpoints->item(row, WATCH_LOW_COL)->text()));
1384             uint32_t high = static_cast<uint32_t>(hex2int(m_watchpoints->item(row, WATCH_HIGH_COL)->text()));
1385             int mask = watchGetMask(row);
1386 
1387             for (uint32_t addr = low; addr <= high; addr++) {
1388                 debug_watch(addr, mask, true);
1389             }
1390         }
1391     }
1392 
1393     if (!m_guiAdd && !m_useSoftCom) {
1394         disasmUpdate();
1395         memUpdate();
1396     }
1397 }
1398 
watchUpdateRow(int row)1399 void MainWindow::watchUpdateRow(int row) {
1400 
1401     // this is needed in the case of overlapping address spaces
1402     if (m_watchpoints->item(row, WATCH_LOW_COL)->text() != DEBUG_UNSET_ADDR &&
1403         m_watchpoints->item(row, WATCH_HIGH_COL)->text() != DEBUG_UNSET_ADDR) {
1404         uint32_t low = static_cast<uint32_t>(hex2int(m_watchpoints->item(row, WATCH_LOW_COL)->text()));
1405         uint32_t high = static_cast<uint32_t>(hex2int(m_watchpoints->item(row, WATCH_HIGH_COL)->text()));
1406 
1407         for (uint32_t addr = low; addr <= high; addr++) {
1408             debug_watch(addr, DBG_MASK_READ | DBG_MASK_WRITE, false);
1409         }
1410     }
1411 
1412     watchUpdate();
1413 }
1414 
watchAdd(const QString & label,uint32_t low,uint32_t high,int mask,bool toggle,bool unset)1415 bool MainWindow::watchAdd(const QString& label, uint32_t low, uint32_t high, int mask, bool toggle, bool unset) {
1416     const int row = m_watchpoints->rowCount();
1417     QString lowStr;
1418     QString highStr;
1419     QString watchLen;
1420 
1421     if (unset) {
1422         lowStr = DEBUG_UNSET_ADDR;
1423         highStr = DEBUG_UNSET_ADDR;
1424     } else {
1425         lowStr = int2hex((low &= 0xFFFFFF), 6).toUpper();
1426         highStr = int2hex((high &= 0xFFFFFF), 6).toUpper();
1427     }
1428 
1429     // return if address is already set
1430     for (int i = 0; i < row; i++) {
1431         if (m_watchpoints->item(i, WATCH_LOW_COL)->text() == lowStr &&
1432             m_watchpoints->item(i, WATCH_HIGH_COL)->text() == highStr) {
1433             if (lowStr != DEBUG_UNSET_ADDR && highStr != DEBUG_UNSET_ADDR) {
1434                 if (!m_useSoftCom) {
1435                     m_watchpoints->selectRow(i);
1436                     if (toggle) {
1437                         watchRemoveRow(i);
1438                     }
1439                 } else {
1440                     ui->lcd->setFocus();
1441                 }
1442                 return false;
1443             }
1444         }
1445     }
1446 
1447     m_watchpoints->blockSignals(true);
1448 
1449     QToolButton *button = new QToolButton();
1450     button->setIcon(m_iconRemove);
1451     connect(button, &QToolButton::clicked, this, &MainWindow::watchRemoveSelected);
1452 
1453     QTableWidgetItem *itemLabel = new QTableWidgetItem(label);
1454     QTableWidgetItem *itemLow = new QTableWidgetItem(lowStr);
1455     QTableWidgetItem *itemHigh = new QTableWidgetItem(highStr);
1456     QTableWidgetItem *itemRead = new QTableWidgetItem;
1457     QTableWidgetItem *itemWrite = new QTableWidgetItem;
1458     QTableWidgetItem *itemRemove = new QTableWidgetItem;
1459 
1460     QToolButton *btnRead = new QToolButton;
1461     btnRead->setIcon((mask & DBG_MASK_READ) ? m_iconCheck : m_iconCheckGray);
1462     btnRead->setCheckable(true);
1463     btnRead->setChecked((mask & DBG_MASK_READ) ? true : false);
1464 
1465     QToolButton *btnWrite = new QToolButton;
1466     btnWrite->setIcon((mask & DBG_MASK_WRITE) ? m_iconCheck : m_iconCheckGray);
1467     btnWrite->setCheckable(true);
1468     btnWrite->setChecked((mask & DBG_MASK_WRITE) ? true : false);
1469 
1470     connect(btnRead, &QToolButton::clicked, [this, btnRead, row](bool checked) {
1471         btnRead->setIcon(checked ? m_iconCheck : m_iconCheckGray);
1472         watchUpdateRow(row);
1473     });
1474     connect(btnWrite, &QToolButton::clicked, [this, btnWrite, row](bool checked) {
1475         btnWrite->setIcon(checked ? m_iconCheck : m_iconCheckGray);
1476         watchUpdateRow(row);
1477     });
1478 
1479     m_watchpoints->setRowCount(row + 1);
1480     m_watchpoints->setItem(row, WATCH_NAME_COL, itemLabel);
1481     m_watchpoints->setItem(row, WATCH_LOW_COL, itemLow);
1482     m_watchpoints->setItem(row, WATCH_HIGH_COL, itemHigh);
1483     m_watchpoints->setItem(row, WATCH_READ_COL, itemRead);
1484     m_watchpoints->setItem(row, WATCH_WRITE_COL, itemWrite);
1485     m_watchpoints->setItem(row, WATCH_REMOVE_COL, itemRemove);
1486     m_watchpoints->setCellWidget(row, WATCH_REMOVE_COL, button);
1487     m_watchpoints->setCellWidget(row, WATCH_READ_COL, btnRead);
1488     m_watchpoints->setCellWidget(row, WATCH_WRITE_COL, btnWrite);
1489 
1490     m_watchpoints->setCurrentCell(row, WATCH_REMOVE_COL);
1491 
1492     if (!m_guiAdd && !m_useSoftCom) {
1493         disasmUpdate();
1494         memUpdate();
1495     }
1496 
1497     m_prevWatchLow = lowStr;
1498     m_prevWatchHigh = highStr;
1499     m_watchpoints->blockSignals(false);
1500 
1501     watchUpdate();
1502 
1503     if (m_useSoftCom) {
1504         ui->lcd->setFocus();
1505     }
1506 
1507     m_watchpoints->setVisible(false);
1508     m_watchpoints->resizeColumnsToContents();
1509     m_watchpoints->setVisible(true);
1510     return true;
1511 }
1512 
memUpdate()1513 void MainWindow::memUpdate() {
1514     ramUpdate();
1515     flashUpdate();
1516     memDocksUpdate();
1517 }
1518 
watchGetMask(int row)1519 int MainWindow::watchGetMask(int row) {
1520     int mask = 0;
1521     if (static_cast<QAbstractButton *>(m_watchpoints->cellWidget(row, WATCH_READ_COL))->isChecked()) {
1522         mask |= DBG_MASK_READ;
1523     } else {
1524         mask |= DBG_MASK_NONE;
1525     }
1526     if (static_cast<QAbstractButton *>(m_watchpoints->cellWidget(row, WATCH_WRITE_COL))->isChecked()) {
1527         mask |= DBG_MASK_WRITE;
1528     } else {
1529         mask |= DBG_MASK_NONE;
1530     }
1531     return mask;
1532 }
1533 
watchModified(QTableWidgetItem * item)1534 void MainWindow::watchModified(QTableWidgetItem *item) {
1535     if (item == Q_NULLPTR) {
1536         return;
1537     }
1538 
1539     int row = item->row();
1540     int col = item->column();
1541     QString lowStr;
1542     QString highStr;
1543     uint32_t addr;
1544 
1545     m_watchpoints->blockSignals(true);
1546 
1547     if (col == WATCH_NAME_COL) {
1548         updateLabels();
1549     } if (col == WATCH_LOW_COL) {
1550         std::string s = item->text().toUpper().toStdString();
1551         QString equate;
1552 
1553         equate = getAddressOfEquate(s);
1554         if (!equate.isEmpty()) {
1555             s = equate.toStdString();
1556             m_watchpoints->blockSignals(true);
1557             if (m_watchpoints->item(row, WATCH_NAME_COL)->text() == (QStringLiteral("Label") + QString::number(row))) {
1558                 m_watchpoints->item(row, WATCH_NAME_COL)->setText(item->text());
1559             }
1560             m_watchpoints->blockSignals(false);
1561         }
1562 
1563         addr = static_cast<uint32_t>(hex2int(QString::fromStdString(s)));
1564         highStr = m_watchpoints->item(row, WATCH_HIGH_COL)->text();
1565 
1566         if (isNotValidHex(s) || s.length() > 6 ||
1567            (highStr != DEBUG_UNSET_ADDR && addr > static_cast<uint32_t>(hex2int(highStr)))) {
1568             item->setText(m_prevWatchLow);
1569             m_watchpoints->blockSignals(false);
1570             return;
1571         }
1572 
1573         lowStr = int2hex(addr, 6);
1574 
1575         // return if address is already set in this range
1576         for (int i = 0; i < m_watchpoints->rowCount(); i++) {
1577             if (m_watchpoints->item(i, WATCH_LOW_COL)->text() == lowStr &&
1578                 m_watchpoints->item(i, WATCH_HIGH_COL)->text() == highStr &&
1579                 i != row) {
1580                 item->setText(m_prevWatchLow);
1581                 m_watchpoints->blockSignals(false);
1582                 return;
1583             }
1584         }
1585 
1586         if (m_prevWatchLow != DEBUG_UNSET_ADDR) {
1587             uint32_t low = static_cast<uint32_t>(hex2int(m_prevWatchLow));
1588             uint32_t high = static_cast<uint32_t>(hex2int(m_watchpoints->item(row, WATCH_HIGH_COL)->text()));
1589 
1590             for (uint32_t addr = low; addr <= high; addr++) {
1591                 debug_watch(addr, DBG_MASK_READ | DBG_MASK_WRITE, false);
1592             }
1593         }
1594 
1595 
1596         if (highStr == DEBUG_UNSET_ADDR) {
1597             m_watchpoints->item(row, WATCH_HIGH_COL)->setText(lowStr);
1598         }
1599         item->setText(lowStr);
1600     } else if (col == WATCH_HIGH_COL) {
1601         std::string s = item->text().toUpper().toStdString();
1602         QString equate;
1603 
1604         equate = getAddressOfEquate(s);
1605         if (!equate.isEmpty()) {
1606             s = equate.toStdString();
1607             m_watchpoints->blockSignals(true);
1608             if (m_watchpoints->item(row, WATCH_NAME_COL)->text() == (QStringLiteral("Label") + QString::number(row))) {
1609                 m_watchpoints->item(row, WATCH_NAME_COL)->setText(item->text());
1610             }
1611             m_watchpoints->blockSignals(false);
1612         }
1613 
1614         addr = static_cast<uint32_t>(hex2int(QString::fromStdString(s)));
1615         lowStr = m_watchpoints->item(row, WATCH_LOW_COL)->text();
1616 
1617         if (isNotValidHex(s) || s.length() > 6 ||
1618            (lowStr != DEBUG_UNSET_ADDR && addr < static_cast<uint32_t>(hex2int(lowStr)))) {
1619             item->setText(m_prevWatchLow);
1620             m_watchpoints->blockSignals(false);
1621             return;
1622         }
1623 
1624         highStr = int2hex(addr, 6);
1625 
1626         // return if address is already set in this range
1627         for (int i = 0; i < m_watchpoints->rowCount(); i++) {
1628             if (m_watchpoints->item(i, WATCH_HIGH_COL)->text() == highStr &&
1629                 m_watchpoints->item(i, WATCH_LOW_COL)->text() == lowStr &&
1630                 i != row) {
1631                 item->setText(m_prevWatchLow);
1632                 m_watchpoints->blockSignals(false);
1633                 return;
1634             }
1635         }
1636 
1637         if (m_prevWatchHigh != DEBUG_UNSET_ADDR) {
1638             uint32_t low = static_cast<uint32_t>(hex2int(m_watchpoints->item(row, WATCH_LOW_COL)->text()));
1639             uint32_t high = static_cast<uint32_t>(hex2int(m_prevWatchHigh));
1640 
1641             for (uint32_t addr = low; addr <= high; addr++) {
1642                 debug_watch(addr, DBG_MASK_READ | DBG_MASK_WRITE, false);
1643             }
1644         }
1645 
1646         if (lowStr == DEBUG_UNSET_ADDR) {
1647             m_watchpoints->item(row, WATCH_LOW_COL)->setText(highStr);
1648         }
1649         item->setText(highStr);
1650     }
1651 
1652     m_watchpoints->blockSignals(false);
1653     disasmUpdate();
1654     watchUpdate();
1655 }
1656 
1657 // ------------------------------------------------
1658 // Battery Status
1659 // ------------------------------------------------
1660 
batterySetCharging(bool checked)1661 void MainWindow::batterySetCharging(bool checked) {
1662     control.batteryCharging = checked;
1663 }
1664 
batterySet(int value)1665 void MainWindow::batterySet(int value) {
1666     control.setBatteryStatus = static_cast<uint8_t>(value);
1667     ui->sliderBattery->setValue(value);
1668     ui->labelBattery->setText(QString::number(value * 20) + "%");
1669 }
1670 
1671 // ------------------------------------------------
1672 // Disassembly View
1673 // ------------------------------------------------
1674 
disasmScroll(int value)1675 void MainWindow::disasmScroll(int value) {
1676     QScrollBar *v = m_disasm->verticalScrollBar();
1677     if (value >= v->maximum()) {
1678         v->blockSignals(true);
1679         disasmLine();
1680         v->setValue(m_disasm->verticalScrollBar()->maximum() - 1);
1681         v->blockSignals(false);
1682     }
1683 }
1684 
stackScroll(int value)1685 void MainWindow::stackScroll(int value) {
1686     QScrollBar *v = ui->stackView->verticalScrollBar();
1687     if (value >= v->maximum()) {
1688         v->blockSignals(true);
1689         stackLine();
1690         v->setValue(ui->stackView->verticalScrollBar()->maximum() - 1);
1691         v->blockSignals(false);
1692     }
1693 }
1694 
equatesClear()1695 void MainWindow::equatesClear() {
1696     m_equateFiles.clear();
1697     disasm.map.clear();
1698     disasm.reverse.clear();
1699     disasmUpdate();
1700 }
1701 
updateLabels()1702 void MainWindow::updateLabels() {
1703     for (int row = 0; row < m_watchpoints->rowCount(); row++) {
1704         QString next = getAddressOfEquate(m_watchpoints->item(row, WATCH_NAME_COL)->text().toUpper().toStdString());
1705         QString old = m_watchpoints->item(row, WATCH_LOW_COL)->text();
1706         if (!next.isEmpty() && next != old) {
1707             unsigned int mask = (m_watchpoints->item(row, WATCH_READ_COL)->checkState() == Qt::Checked ? DBG_MASK_READ : DBG_MASK_NONE) |
1708                                 (m_watchpoints->item(row, WATCH_WRITE_COL)->checkState() == Qt::Checked ? DBG_MASK_WRITE : DBG_MASK_NONE);
1709             // remove old watchpoint and add new one
1710             m_watchpoints->blockSignals(true);
1711             debug_watch(static_cast<uint32_t>(hex2int(old)), mask, false);
1712             m_watchpoints->item(row, WATCH_LOW_COL)->setText(next);
1713             debug_watch(static_cast<uint32_t>(hex2int(next)), mask, true);
1714             m_watchpoints->blockSignals(true);
1715         }
1716     }
1717     for (int row = 0; row < m_breakpoints->rowCount(); row++) {
1718         QString next = getAddressOfEquate(m_breakpoints->item(row, BREAK_NAME_COL)->text().toUpper().toStdString());
1719         QString old = m_breakpoints->item(row, BREAK_ADDR_COL)->text();
1720         if (!next.isEmpty() && next != old) {
1721             int mask = breakGetMask(row);
1722             m_breakpoints->blockSignals(true);
1723             debug_watch(static_cast<uint32_t>(hex2int(old)), mask, false);
1724             m_breakpoints->item(row, BREAK_ADDR_COL)->setText(next);
1725             debug_watch(static_cast<uint32_t>(hex2int(next)), mask, true);
1726             m_breakpoints->blockSignals(false);
1727         }
1728     }
1729 }
1730 
equatesRefresh()1731 void MainWindow::equatesRefresh() {
1732     disasm.map.clear();
1733     disasm.reverse.clear();
1734     for (QString &file : m_equateFiles) {
1735         equatesAddFile(file);
1736     }
1737     updateLabels();
1738     disasmUpdate();
1739 }
1740 
equatesAddDialog()1741 void MainWindow::equatesAddDialog() {
1742     QFileDialog dialog(this);
1743 
1744     dialog.setAcceptMode(QFileDialog::AcceptOpen);
1745     dialog.setFileMode(QFileDialog::ExistingFile);
1746     dialog.setDirectory(m_dir);
1747 
1748     QStringList extFilters;
1749     extFilters << tr("Equate files (*.inc *.lab *.map)")
1750                << tr("All Files (*.*)");
1751     dialog.setNameFilters(extFilters);
1752 
1753     if (dialog.exec()) {
1754         m_equateFiles.append(dialog.selectedFiles());
1755         equatesRefresh();
1756     }
1757     m_dir = dialog.directory();
1758 }
1759 
equatesAddFile(const QString & fileName)1760 void MainWindow::equatesAddFile(const QString &fileName) {
1761     QFile file(fileName);
1762     if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
1763         m_equateFiles.removeAll(fileName);
1764         console(QStringLiteral("[CEmu] Debugger couldn't open this equate file (removed): ") + fileName + "\n");
1765         return;
1766     }
1767 
1768     QTextStream in(&file);
1769     QString line;
1770     if (in.readLineInto(&line) && line.startsWith(QStringLiteral("Segment"))) {
1771         while ((in.readLineInto(&line) && !line.startsWith(QStringLiteral("Label"))));
1772         if (!in.readLineInto(&line)) {
1773             return;
1774         }
1775         while (in.readLineInto(&line) && !line.isEmpty()) {
1776             QStringList split = line.split('=', QString::SkipEmptyParts);
1777             equatesAddEquate(split.at(0).simplified(), hex2int(split.at(1).simplified()));
1778         }
1779     } else {
1780         QRegularExpression equatesRegexp(QStringLiteral("^\\h*\\??\\h*([.A-Z_a-z][.\\w]*)\\h*(?::?=|\\h\\.?equ(?!\\d))\\h*([%@$]\\S+|\\d\\S*[boh]?)\\h*(?:;.*)?$"),
1781                                          QRegularExpression::CaseInsensitiveOption);
1782         do {
1783             QRegularExpressionMatch matches = equatesRegexp.match(line);
1784             if (matches.hasMatch()) {
1785                 QString addrStr = matches.captured(2);
1786                 int base = 10;
1787                 if (addrStr.startsWith('%')) {
1788                     addrStr.remove(0, 1);
1789                     base = 2;
1790                 } else if (addrStr.startsWith('@')) {
1791                     addrStr.remove(0, 1);
1792                     base = 8;
1793                 } else if (addrStr.startsWith('$')) {
1794                     addrStr.remove(0, 1);
1795                     base = 16;
1796                 } else if (addrStr.endsWith('b', Qt::CaseInsensitive)) {
1797                     addrStr.chop(1);
1798                     base = 2;
1799                 } else if (addrStr.endsWith('o', Qt::CaseInsensitive)) {
1800                     addrStr.chop(1);
1801                     base = 8;
1802                 } else if (addrStr.endsWith('h', Qt::CaseInsensitive)) {
1803                     addrStr.chop(1);
1804                     base = 16;
1805                 }
1806                 equatesAddEquate(matches.captured(1), addrStr.toUInt(Q_NULLPTR, base));
1807             }
1808         } while (in.readLineInto(&line));
1809     }
1810 
1811     console(QStringLiteral("[CEmu] Loaded equate file: ") + fileName + QStringLiteral("\n"));
1812 
1813     disasmUpdate();
1814     updateLabels();
1815 }
1816 
equatesAddEquate(const QString & name,uint32_t address)1817 void MainWindow::equatesAddEquate(const QString &name, uint32_t address) {
1818     if (address < 0x80) {
1819         return;
1820     }
1821     map_value_t::const_iterator item = disasm.reverse.find(name.toUpper().toStdString());
1822     if (item != disasm.reverse.end()) {
1823         if (address == item->second) {
1824             return;
1825         } else {
1826             disasm.reverse.erase(item);
1827         }
1828     }
1829     uint32_t &itemReverse = disasm.reverse[name.toUpper().toStdString()];
1830     itemReverse = address;
1831     disasm.map.emplace(address, name.toStdString());
1832     uint8_t *ptr = static_cast<uint8_t *>(phys_mem_ptr(address - 4, 9));
1833     if (ptr && ptr[4] == 0xC3 && (ptr[0] == 0xC3 || ptr[8] == 0xC3)) { // jump table?
1834         uint32_t address2  = ptr[5] | ptr[6] << 8 | ptr[7] << 16;
1835         if (phys_mem_ptr(address2, 1)) {
1836             disasm.map.emplace(address2, "_" + name.toStdString());
1837             uint32_t &itemReverse2 = disasm.reverse["_" + name.toUpper().toStdString()];
1838             itemReverse2 = address2;
1839         }
1840     }
1841 }
1842 
disasmUpdate()1843 void MainWindow::disasmUpdate() {
1844     disasmUpdateAddr(m_disasm->getSelectedAddr().toInt(Q_NULLPTR, 16), true);
1845 }
1846 
disasmUpdateAddr(int base,bool pane)1847 void MainWindow::disasmUpdateAddr(int base, bool pane) {
1848     if (!guiDebug) {
1849         return;
1850     }
1851     m_disasmAddr = base;
1852     m_disasmPane = pane;
1853     m_disasmOffsetSet = false;
1854     disasm.ctx.zdis_adl = adlState(ui->checkADLDisasm->checkState());
1855     disasm.base = -1;
1856     disasm.next = m_disasmAddr - ((pane) ? 0x40 : 0);
1857     if (disasm.next < 0) { disasm.next = 0; }
1858     int32_t lastAddr = disasm.next + 0x120;
1859     if (lastAddr > 0xFFFFFF) { lastAddr = 0xFFFFFF; }
1860 
1861     disconnect(m_disasm->verticalScrollBar(), &QScrollBar::valueChanged, this, &MainWindow::disasmScroll);
1862     m_disasm->clear();
1863     m_disasm->cursorState(false);
1864     m_disasm->clearAllHighlights();
1865 
1866     while (disasm.next < lastAddr) {
1867         disasmLine();
1868     }
1869 
1870     m_disasm->setTextCursor(m_disasmOffset);
1871     m_disasm->cursorState(true);
1872     m_disasm->updateAllHighlights();
1873     m_disasm->centerCursor();
1874     connect(m_disasm->verticalScrollBar(), &QScrollBar::valueChanged, this, &MainWindow::disasmScroll);
1875 }
1876 
1877 // ------------------------------------------------
1878 // Misc
1879 // ------------------------------------------------
1880 
gotoPressed()1881 void MainWindow::gotoPressed() {
1882     bool accept;
1883 
1884     if (m_gotoAddr.isEmpty()) {
1885         m_gotoAddr = m_disasm->getSelectedAddr();
1886     }
1887 
1888     QString address = getAddressString(m_gotoAddr, &accept);
1889 
1890     if (accept) {
1891         disasmUpdateAddr(hex2int(m_gotoAddr = address), false);
1892     }
1893 }
1894 
gotoDisasmAddr(uint32_t address)1895 void MainWindow::gotoDisasmAddr(uint32_t address) {
1896     disasmUpdateAddr(address, false);
1897 }
1898 
gotoMemAddr(uint32_t address)1899 void MainWindow::gotoMemAddr(uint32_t address) {
1900     if (m_memWidget != Q_NULLPTR) {
1901         memGoto(m_memWidget, address);
1902     }
1903 }
1904 
handleCtrlClickText(QPlainTextEdit * edit)1905 void MainWindow::handleCtrlClickText(QPlainTextEdit *edit) {
1906     if (QApplication::keyboardModifiers().testFlag(Qt::ControlModifier)) {
1907         bool ok = true;
1908 
1909         edit->blockSignals(true);
1910 
1911         QTextCursor cursor = edit->textCursor();
1912         cursor.select(QTextCursor::WordUnderCursor);
1913         edit->setTextCursor(cursor);
1914 
1915         QString equ = getAddressOfEquate(edit->textCursor().selectedText().toUpper().toStdString());
1916         uint32_t address;
1917 
1918         if (!equ.isEmpty()) {
1919             address = hex2int(equ);
1920         } else {
1921             address = edit->textCursor().selectedText().toUInt(&ok, 16);
1922         }
1923 
1924         if (ok) {
1925             debugForce();
1926             if (QApplication::keyboardModifiers().testFlag(Qt::ShiftModifier)) {
1927                 gotoMemAddr(address);
1928             } else {
1929                 gotoDisasmAddr(address);
1930             }
1931         }
1932 
1933         edit->blockSignals(false);
1934     }
1935 }
1936 
handleCtrlClickLine(QLineEdit * edit)1937 void MainWindow::handleCtrlClickLine(QLineEdit *edit) {
1938     if (QApplication::keyboardModifiers().testFlag(Qt::ControlModifier)) {
1939         bool ok = true;
1940 
1941         edit->blockSignals(true);
1942 
1943         uint32_t address = edit->text().toUInt(&ok, 16);
1944 
1945         if (ok) {
1946             if (QApplication::keyboardModifiers().testFlag(Qt::ShiftModifier)) {
1947                 gotoMemAddr(address);
1948             } else {
1949                 gotoDisasmAddr(address);
1950             }
1951         }
1952 
1953         edit->blockSignals(false);
1954     }
1955 }
1956 
1957 // ------------------------------------------------
1958 // Tooltips
1959 // ------------------------------------------------
1960 
eventFilter(QObject * obj,QEvent * e)1961 bool MainWindow::eventFilter(QObject *obj, QEvent *e) {
1962     if (!guiDebug) {
1963         return QMainWindow::eventFilter(obj, e);
1964     }
1965 
1966     if (e->type() == QEvent::MouseButtonPress) {
1967         QString name = obj->objectName();
1968 
1969         if (name.length() > 3) {
1970             return QMainWindow::eventFilter(obj, e);
1971         }
1972 
1973         if (name == QStringLiteral("hl"))  gotoMemAddr(hex2int(ui->hlregView->text()));
1974         if (name == QStringLiteral("de"))  gotoMemAddr(hex2int(ui->deregView->text()));
1975         if (name == QStringLiteral("bc"))  gotoMemAddr(hex2int(ui->bcregView->text()));
1976         if (name == QStringLiteral("ix"))  gotoMemAddr(hex2int(ui->ixregView->text()));
1977         if (name == QStringLiteral("iy"))  gotoMemAddr(hex2int(ui->iyregView->text()));
1978         if (name == QStringLiteral("hl_")) gotoMemAddr(hex2int(ui->hl_regView->text()));
1979         if (name == QStringLiteral("de_")) gotoMemAddr(hex2int(ui->de_regView->text()));
1980         if (name == QStringLiteral("bc_")) gotoMemAddr(hex2int(ui->bc_regView->text()));
1981         if (name == QStringLiteral("spl")) gotoMemAddr(hex2int(ui->splregView->text()));
1982         if (name == QStringLiteral("pc"))  gotoMemAddr(hex2int(ui->pcregView->text()));
1983     } else if (e->type() == QEvent::MouseMove) {
1984         QString name = obj->objectName();
1985 
1986         if (name.length() < 4) {
1987             return QMainWindow::eventFilter(obj, e);
1988         }
1989 
1990         QLineEdit *widget = static_cast<QLineEdit*>(obj);
1991 
1992         unsigned int num  = widget->text().toUInt(Q_NULLPTR, 16);
1993         unsigned int num0 = num & 255;
1994         unsigned int num1 = (num >> 8) & 255;
1995         unsigned int num2 = (num >> 16) & 255;
1996 
1997         QString t;
1998         QString val  = QString::number(num);
1999         QString val0 = QString::number(num0);
2000         QString val1 = QString::number(num1);
2001         QString val2 = QString::number(num2);
2002 
2003         if (num  > 0x7FFFFF) {
2004             val += QStringLiteral("\n\t") + QString::number(static_cast<int32_t>(num | 0xFF000000u));
2005         }
2006         if (num0 > 0x7F) {
2007             val0 += QStringLiteral("\t") + QString::number(static_cast<int8_t>(num0));
2008         }
2009         if (num1 > 0x7F) {
2010             val1 += QStringLiteral("\t") + QString::number(static_cast<int8_t>(num1));
2011         }
2012         if (num2 > 0x7F) {
2013             val2 += QStringLiteral("\t") + QString::number(static_cast<int8_t>(num2));
2014         }
2015 
2016         if (name == QStringLiteral("afregView"))
2017             t = QStringLiteral("a:\t") + val1 +
2018                 QStringLiteral("\nf:\t") + val0;
2019         if (name == QStringLiteral("hlregView"))
2020             t = QStringLiteral("hl:\t") + val +
2021                 QStringLiteral("\nu:\t") + val2 +
2022                 QStringLiteral("\nh:\t") + val1 +
2023                 QStringLiteral("\nl:\t") + val0;
2024         if (name == QStringLiteral("deregView"))
2025             t = QStringLiteral("de:\t") + val +
2026                 QStringLiteral("\nu:\t") + val2 +
2027                 QStringLiteral("\nd:\t") + val1 +
2028                 QStringLiteral("\ne:\t") + val0;
2029         if (name == QStringLiteral("bcregView"))
2030             t = QStringLiteral("bc:\t") + val +
2031                 QStringLiteral("\nu:\t") + val2 +
2032                 QStringLiteral("\nb:\t") + val1 +
2033                 QStringLiteral("\nc:\t") + val0;
2034         if (name == QStringLiteral("ixregView"))
2035             t = QStringLiteral("ix:\t") + val +
2036                 QStringLiteral("\nixh:\t") + val1 +
2037                 QStringLiteral("\nixl:\t") + val0;
2038         if (name == QStringLiteral("iyregView"))
2039             t = QStringLiteral("iy:\t") + val +
2040                 QStringLiteral("\niyh:\t") + val1 +
2041                 QStringLiteral("\niyl:\t") + val0;
2042         if (name == QStringLiteral("af_regView"))
2043             t = QStringLiteral("a':\t") + val1 +
2044                 QStringLiteral("\nf':\t") + val0;
2045         if (name == QStringLiteral("hl_regView"))
2046             t = QStringLiteral("hl':\t") + val +
2047             QStringLiteral("\nu':\t") + val2 +
2048             QStringLiteral("\nh':\t") + val1 +
2049             QStringLiteral("\nl':\t") + val0;
2050         if (name == QStringLiteral("de_regView"))
2051             t = QStringLiteral("de':\t") + val +
2052                 QStringLiteral("\nu':\t") + val2 +
2053                 QStringLiteral("\nd':\t") + val1 +
2054                 QStringLiteral("\ne':\t") + val0;
2055         if (name == QStringLiteral("bc_regView"))
2056             t = QStringLiteral("bc':\t") + val +
2057                 QStringLiteral("\nu':\t") + val2 +
2058                 QStringLiteral("\nb':\t") + val1 +
2059                 QStringLiteral("\nc':\t") + val0;
2060         if (name == QStringLiteral("rregView"))
2061             t = QStringLiteral("r:\t") + val;
2062 
2063         QToolTip::showText(static_cast<QMouseEvent*>(e)->globalPos(), t, widget, widget->rect());
2064     }
2065     return QMainWindow::eventFilter(obj, e);
2066 }
2067 
2068 // ------------------------------------------------
2069 // Stack
2070 // ------------------------------------------------
2071 
adlState(int state)2072 bool MainWindow::adlState(int state) {
2073     bool adl = ui->checkADL->isChecked();
2074     if (state == Qt::Checked) {
2075         adl = true;
2076     } else if (state == Qt::Unchecked) {
2077         adl = false;
2078     }
2079     return adl;
2080 }
2081 
stackUpdate()2082 void MainWindow::stackUpdate() {
2083     disconnect(ui->stackView->verticalScrollBar(), &QScrollBar::valueChanged, this, &MainWindow::stackScroll);
2084     ui->stackView->clear();
2085 
2086     m_stackAddr = adlState(ui->checkADLStack->checkState()) ? cpu.registers.SPL : cpu.registers.SPS;
2087 
2088     for (int i = 0; i < 20; i++) {
2089         stackLine();
2090     }
2091 
2092     ui->stackView->moveCursor(QTextCursor::Start);
2093     connect(ui->stackView->verticalScrollBar(), &QScrollBar::valueChanged, this, &MainWindow::stackScroll);
2094 }
2095 
stackLine()2096 void MainWindow::stackLine() {
2097     int width = adlState(ui->checkADLStack->checkState()) ? 6 : 4;
2098 
2099     QString line = QString(QStringLiteral("<pre><b><font color='#444'>%1</font></b> %2</pre>"))
2100                    .arg(int2hex(m_stackAddr, width),
2101                         int2hex(mem_peek_word(m_stackAddr, width == 6), width));
2102     m_stackAddr = (m_stackAddr + (width >> 1)) & 0xFFFFFF;
2103 
2104     ui->stackView->appendHtml(line);
2105 }
2106 
2107 //------------------------------------------------
2108 // TI-OS View
2109 //------------------------------------------------
2110 
osUpdate()2111 void MainWindow::osUpdate() {
2112     if (!m_normalOs) {
2113         return;
2114     }
2115 
2116     calc_var_t var;
2117     QByteArray array;
2118     QString data;
2119     QString dataString;
2120 
2121     int index = 0;
2122     ui->opView->setRowCount(0);
2123     ui->vatView->setRowCount(0);
2124     ui->opStack->setRowCount(0);
2125     ui->fpStack->setRowCount(0);
2126 
2127     QFont monospace = QFontDatabase::systemFont(QFontDatabase::FixedFont);
2128 
2129     disconnect(ui->fpStack, &QTableWidget::itemChanged, this, &MainWindow::fpModified);
2130     disconnect(ui->opView, &QTableWidget::itemChanged, this, &MainWindow::opModified);
2131 
2132     for (uint32_t i = 0xD005F8; i < 0xD005F8+77; i += 11) {
2133         array.clear();
2134         dataString.clear();
2135 
2136         for (uint32_t j = i; j < i+11; j++) {
2137             uint8_t ch = mem_peek_byte(j);
2138             array.append(static_cast<char>(ch));
2139             if ((ch < 0x20) || (ch > 0x7e)) {
2140                 ch = '.';
2141             }
2142             dataString += QChar(ch);
2143         }
2144 
2145         data_t vect(array.constData(), array.constEnd() - 2);
2146 
2147         QTableWidgetItem *opAddr = new QTableWidgetItem(int2hex(i, 6));
2148         QTableWidgetItem *opNumber = new QTableWidgetItem(QStringLiteral("OP") + QString::number(index+1));
2149         QTableWidgetItem *opData = new QTableWidgetItem(QString(array.toHex()));
2150         QTableWidgetItem *opString = new QTableWidgetItem(dataString);
2151         QTableWidgetItem *opValue;
2152         try {
2153             opValue = new QTableWidgetItem(QString::fromStdString(tivars::TH_GenericReal::makeStringFromData(vect)));
2154         } catch (...) {
2155             opValue = new QTableWidgetItem(TXT_NAN);
2156         }
2157         opNumber->setFlags(opNumber->flags() & ~Qt::ItemIsEditable);
2158         opAddr->setFlags(opNumber->flags() & ~Qt::ItemIsEditable);
2159 
2160         opAddr->setFont(monospace);
2161         opNumber->setFont(monospace);
2162         opData->setFont(monospace);
2163         opString->setFont(monospace);
2164         opValue->setFont(monospace);
2165 
2166         ui->opView->setRowCount(index+1);
2167 
2168         ui->opView->setItem(index, OP_ADDR_COL, opAddr);
2169         ui->opView->setItem(index, OP_NUMBER_COL, opNumber);
2170         ui->opView->setItem(index, OP_DATA_COL, opData);
2171         ui->opView->setItem(index, OP_STRING_COL, opString);
2172         ui->opView->setItem(index, OP_VALUE_COL, opValue);
2173 
2174         index++;
2175     }
2176 
2177     uint32_t fpBase = mem_peek_word(0xD0258A, true);
2178     uint32_t fpTop = mem_peek_word(0xD0258D, true);
2179 
2180     index = 0;
2181 
2182     if (fpTop >= fpBase && (fpTop >= 0xD00000 && fpTop < 0xD40000) && (fpBase >= 0xD00000 && fpBase < 0xD40000)) {
2183         for (uint32_t i = fpTop; i > fpBase; i -= 9) {
2184             array.clear();
2185             dataString.clear();
2186 
2187             for (uint32_t j = i; j < i+9; j++) {
2188                 uint8_t ch = mem_peek_byte(j);
2189                 array.append(ch);
2190                 if ((ch < 0x20) || (ch > 0x7e)) {
2191                     ch = '.';
2192                 }
2193                 dataString += QChar(ch);
2194             }
2195 
2196             data_t vect(array.constData(), array.constEnd());
2197 
2198             QTableWidgetItem *fpAddr = new QTableWidgetItem(int2hex(i, 6));
2199             QTableWidgetItem *fpData = new QTableWidgetItem(QString(array.toHex()));
2200             QTableWidgetItem *fpString = new QTableWidgetItem(dataString);
2201             QTableWidgetItem *fpValue;
2202             try {
2203                 fpValue = new QTableWidgetItem(QString::fromStdString(tivars::TH_GenericReal::makeStringFromData(vect)));
2204             } catch(...) {
2205                 fpValue = new QTableWidgetItem(TXT_NAN);
2206             }
2207             fpAddr->setFlags(fpAddr->flags() & ~Qt::ItemIsEditable);
2208 
2209             fpAddr->setFont(monospace);
2210             fpData->setFont(monospace);
2211             fpString->setFont(monospace);
2212             fpValue->setFont(monospace);
2213 
2214             ui->fpStack->setRowCount(index+1);
2215 
2216             ui->fpStack->setItem(index, FP_ADDR_COL, fpAddr);
2217             ui->fpStack->setItem(index, FP_DATA_COL, fpData);
2218             ui->fpStack->setItem(index, FP_STRING_COL, fpString);
2219             ui->fpStack->setItem(index, FP_VALUE_COL, fpValue);
2220 
2221             index++;
2222         }
2223     } else {
2224         ui->fpStack->setEnabled(false);
2225     }
2226 
2227     uint32_t opBase = mem_peek_word(0xD02590, true);
2228     uint32_t opTop = mem_peek_word(0xD02593, true);
2229 
2230     index = 0;
2231 
2232     if (opTop <= opBase && (opTop >= 0xD00000 && opTop < 0xD40000) && (opBase >= 0xD00000 && opBase < 0xD40000)) {
2233         for (uint32_t i = opTop; i < opBase; i++) {
2234             data.clear();
2235             data.append(int2hex(mem_peek_byte(i), 2));
2236 
2237             QTableWidgetItem *opAddr = new QTableWidgetItem(int2hex(i, 6));
2238             QTableWidgetItem *opData = new QTableWidgetItem(data);
2239             opAddr->setFlags(opAddr->flags() & ~Qt::ItemIsEditable);
2240             opData->setFlags(opData->flags() & ~Qt::ItemIsEditable);
2241 
2242             opAddr->setFont(monospace);
2243             opData->setFont(monospace);
2244 
2245             ui->opStack->setRowCount(index+1);
2246 
2247             ui->opStack->setItem(index, OPS_ADDR_COL, opAddr);
2248             ui->opStack->setItem(index, OPS_DATA_COL, opData);
2249 
2250             index++;
2251         }
2252     } else {
2253         ui->opStack->setEnabled(false);
2254     }
2255 
2256     index = 0;
2257 
2258     vat_search_init(&var);
2259     while (vat_search_next(&var)) {
2260         QTableWidgetItem *varAddr = new QTableWidgetItem(int2hex(var.address, 6));
2261         QTableWidgetItem *varVatAddr = new QTableWidgetItem(int2hex(var.vat, 6));
2262         QTableWidgetItem *varSize = new QTableWidgetItem(int2hex(var.size, 4));
2263         QTableWidgetItem *varName = new QTableWidgetItem(QString(calc_var_name_to_utf8(var.name)));
2264         QTableWidgetItem *varType = new QTableWidgetItem(QString(calc_var_type_names[var.type]));
2265 
2266         varAddr->setFont(monospace);
2267         varVatAddr->setFont(monospace);
2268         varSize->setFont(monospace);
2269         varName->setFont(monospace);
2270         varType->setFont(monospace);
2271 
2272         ui->vatView->setRowCount(index+1);
2273 
2274         ui->vatView->setItem(index, VAT_ADDR_COL, varAddr);
2275         ui->vatView->setItem(index, VAT_VAT_ADDR_COL, varVatAddr);
2276         ui->vatView->setItem(index, VAT_SIZE_COL, varSize);
2277         ui->vatView->setItem(index, VAT_NAME_COL, varName);
2278         ui->vatView->setItem(index, VAT_TYPE_COL, varType);
2279 
2280         index++;
2281     }
2282 
2283     ui->vatView->resizeColumnToContents(VAT_ADDR_COL);
2284     ui->vatView->resizeColumnToContents(VAT_VAT_ADDR_COL);
2285     ui->vatView->resizeColumnToContents(VAT_NAME_COL);
2286     ui->vatView->resizeColumnToContents(VAT_TYPE_COL);
2287     ui->vatView->resizeColumnToContents(VAT_SIZE_COL);
2288 
2289     connect(ui->opView, &QTableWidget::itemChanged, this, &MainWindow::opModified);
2290     connect(ui->fpStack, &QTableWidget::itemChanged, this, &MainWindow::fpModified);
2291 }
2292 
opModified(QTableWidgetItem * item)2293 void MainWindow::opModified(QTableWidgetItem *item) {
2294     if (item == Q_NULLPTR) {
2295         return;
2296     }
2297 
2298     int col = item->column();
2299     int row = item->row();
2300 
2301     QString txt = item->text();
2302     QString data;
2303     QByteArray array;
2304     uint32_t addr = static_cast<uint32_t>(hex2int(ui->opView->item(row, OP_ADDR_COL)->text()));
2305     array.resize(11);
2306 
2307     sender()->blockSignals(true);
2308 
2309     if (col == OP_DATA_COL) {
2310         array.fill(0);
2311         for (int i = 0; i < 11 && i < txt.length() / 2; i++) {
2312             array[i] = hex2int(txt.mid(i * 2, 2));
2313         }
2314     } else if (col == OP_STRING_COL) {
2315         array.fill('.');
2316         for (int i = 0; i < 11 && i < txt.length(); i++) {
2317             array[i] = txt[i].toLatin1();
2318         }
2319     } else if (col == OP_VALUE_COL) {
2320         array.fill(0);
2321         try {
2322             data_t value = tivars::TH_GenericReal::makeDataFromString(txt.toStdString());
2323             for (int i = 0; i < 11 && i < static_cast<int>(value.size()); i++) {
2324                 array[i] = value[i];
2325             }
2326         } catch(...) {}
2327     }
2328 
2329     for (int i = 0; i < 11; i++) {
2330         mem_poke_byte(addr + i, array[i]);
2331     }
2332 
2333     array.clear();
2334     data.clear();
2335 
2336     for (uint32_t j = addr; j < addr + 11; j++) {
2337         uint8_t ch = mem_peek_byte(j);
2338         array.append(static_cast<char>(ch));
2339         if ((ch < 0x20) || (ch > 0x7e)) {
2340             ch = '.';
2341         }
2342         data += QChar(ch);
2343     }
2344 
2345     data_t vect(array.constData(), array.constEnd() - 2);
2346 
2347     ui->opView->item(row, OP_STRING_COL)->setText(data);
2348     ui->opView->item(row, OP_DATA_COL)->setText(QString(array.toHex()));
2349     try {
2350         ui->opView->item(row, OP_VALUE_COL)->setText(QString::fromStdString(tivars::TH_GenericReal::makeStringFromData(vect)));
2351     } catch(...) {
2352         ui->opView->item(row, OP_VALUE_COL)->setText(TXT_NAN);
2353     }
2354 
2355     sender()->blockSignals(false);
2356 }
2357 
fpModified(QTableWidgetItem * item)2358 void MainWindow::fpModified(QTableWidgetItem *item) {
2359     if (item == Q_NULLPTR) {
2360         return;
2361     }
2362 
2363     int col = item->column();
2364     int row = item->row();
2365 
2366     QString txt = item->text();
2367     QString data;
2368     QByteArray array;
2369     uint32_t addr = static_cast<uint32_t>(hex2int(ui->fpStack->item(row, FP_ADDR_COL)->text()));
2370     array.resize(11);
2371 
2372     sender()->blockSignals(true);
2373 
2374     if (col == FP_DATA_COL) {
2375         array.fill(0);
2376         for (int i = 0; i < 9 && i < txt.length() / 2; i++) {
2377             array[i] = hex2int(txt.mid(i * 2, 2));
2378         }
2379     } else if (col == FP_STRING_COL) {
2380         array.fill('.');
2381         for (int i = 0; i < 9 && i < txt.length(); i++) {
2382             array[i] = txt[i].toLatin1();
2383         }
2384     } else if (col == FP_VALUE_COL) {
2385         array.fill(0);
2386         try {
2387             data_t value = tivars::TH_GenericReal::makeDataFromString(txt.toStdString());
2388             for (int i = 0; i < 9 && i < static_cast<int>(value.size()); i++) {
2389                 array[i] = value[i];
2390             }
2391         } catch(...) {}
2392     }
2393 
2394     for (int i = 0; i < 9; i++) {
2395         mem_poke_byte(addr + i, array[i]);
2396     }
2397 
2398     array.clear();
2399     data.clear();
2400 
2401     for (uint32_t j = addr; j < addr + 9; j++) {
2402         uint8_t ch = mem_peek_byte(j);
2403         array.append(static_cast<char>(ch));
2404         if ((ch < 0x20) || (ch > 0x7e)) {
2405             ch = '.';
2406         }
2407         data += QChar(ch);
2408     }
2409 
2410     data_t vect(array.constData(), array.constEnd());
2411 
2412     ui->fpStack->item(row, FP_STRING_COL)->setText(data);
2413     ui->fpStack->item(row, FP_DATA_COL)->setText(QString(array.toHex()));
2414     try {
2415         ui->fpStack->item(row, FP_VALUE_COL)->setText(QString::fromStdString(tivars::TH_GenericReal::makeStringFromData(vect)));
2416     } catch(...) {
2417         ui->fpStack->item(row, FP_VALUE_COL)->setText(TXT_NAN);
2418     }
2419 
2420     sender()->blockSignals(false);
2421 }
2422 
contextOp(const QPoint & posa)2423 void MainWindow::contextOp(const QPoint &posa) {
2424     QTableWidget *obj = static_cast<QTableWidget*>(sender());
2425     if (!obj->rowCount() || !obj->selectionModel()->isSelected(obj->currentIndex())) {
2426         return;
2427     }
2428 
2429     QString gotoMem = tr("Goto Memory View");
2430     QString copyAddr = tr("Copy Address");
2431     QString copyData = tr("Copy Data");
2432     QPoint globalPos = obj->mapToGlobal(posa);
2433 
2434     QString addr = obj->item(obj->selectionModel()->selectedRows().first().row(), OP_ADDR_COL)->text();
2435     QString data = obj->item(obj->selectionModel()->selectedRows().first().row(), obj->objectName() == QStringLiteral("opView") ? 2 : 1)->text();
2436 
2437     QMenu menu;
2438     menu.addAction(gotoMem);
2439     menu.addSeparator();
2440     menu.addAction(copyAddr);
2441     menu.addAction(copyData);
2442 
2443     QAction *item = menu.exec(globalPos);
2444     if (item != Q_NULLPTR) {
2445         if (item->text() == gotoMem) {
2446             gotoMemAddr(static_cast<uint32_t>(hex2int(addr)));
2447         }
2448         if (item->text() == copyAddr) {
2449             qApp->clipboard()->setText(addr);
2450         }
2451         if (item->text() == copyData) {
2452             qApp->clipboard()->setText(data);
2453         }
2454     }
2455 }
2456 
contextVat(const QPoint & posa)2457 void MainWindow::contextVat(const QPoint &posa) {
2458     QTableWidget *obj = static_cast<QTableWidget*>(sender());
2459     if (!obj->rowCount() || !obj->selectionModel()->isSelected(obj->currentIndex())) {
2460         return;
2461     }
2462 
2463     QString gotoMem = tr("Goto Memory View");
2464     QString gotoVat = tr("Goto VAT Memory View");
2465     QString gotoDisasm = tr("Goto Disasm View");
2466     QPoint globalPos = obj->mapToGlobal(posa);
2467 
2468     QString addr = obj->item(obj->selectionModel()->selectedRows().first().row(), VAT_ADDR_COL)->text();
2469     QString vatAddr = obj->item(obj->selectionModel()->selectedRows().first().row(), VAT_VAT_ADDR_COL)->text();
2470 
2471     QMenu menu;
2472     menu.addAction(gotoMem);
2473     menu.addAction(gotoVat);
2474     menu.addAction(gotoDisasm);
2475 
2476     QAction* item = menu.exec(globalPos);
2477     if (item) {
2478         if (item->text() == gotoMem) {
2479             gotoMemAddr(hex2int(addr));
2480         }
2481         if (item->text() == gotoVat) {
2482             gotoMemAddr(hex2int(vatAddr));
2483         }
2484         if (item->text() == gotoDisasm) {
2485             disasmUpdateAddr(hex2int(addr) + 4, false);
2486         }
2487     }
2488 }
2489 
memDocksUpdate()2490 void MainWindow::memDocksUpdate() {
2491     QList<QDockWidget*> docks = findChildren<QDockWidget*>();
2492     foreach (QDockWidget* dock, docks) {
2493         if (dock->windowTitle().contains(TXT_MEM_DOCK)) {
2494             QList<HexWidget*> editChildren = dock->findChildren<HexWidget*>();
2495             HexWidget *edit = editChildren.first();
2496             memUpdateEdit(edit);
2497         }
2498     }
2499 }
2500 
2501 //------------------------------------------------
2502 // Stepping
2503 //------------------------------------------------
2504 
stepIn()2505 void MainWindow::stepIn() {
2506     if (!guiDebug) {
2507         return;
2508     }
2509 
2510     disconnect(m_shortcutStepIn, &QShortcut::activated, this, &MainWindow::stepIn);
2511 
2512     debugSync();
2513     debugStep(DBG_STEP_IN);
2514 }
2515 
stepOver()2516 void MainWindow::stepOver() {
2517     if (!guiDebug) {
2518         return;
2519     }
2520 
2521     disconnect(m_shortcutStepOver, &QShortcut::activated, this, &MainWindow::stepOver);
2522 
2523     debugSync();
2524     debugStep(DBG_STEP_OVER);
2525 }
2526 
stepNext()2527 void MainWindow::stepNext() {
2528     if (!guiDebug) {
2529         return;
2530     }
2531 
2532     disconnect(m_shortcutStepNext, &QShortcut::activated, this, &MainWindow::stepNext);
2533 
2534     debugSync();
2535     debugStep(DBG_STEP_NEXT);
2536 }
2537 
stepOut()2538 void MainWindow::stepOut() {
2539     if (!guiDebug) {
2540         return;
2541     }
2542 
2543     disconnect(m_shortcutStepOut, &QShortcut::activated, this, &MainWindow::stepOut);
2544 
2545     debugSync();
2546     debugStep(DBG_STEP_OUT);
2547 }
2548 
2549 //------------------------------------------------
2550 // Other Functions
2551 //------------------------------------------------
2552 
debugForce()2553 void MainWindow::debugForce() {
2554     int count = 0;
2555     if (!guiDebug) {
2556         debugToggle();
2557     }
2558     while (!guiDebug && count < 20) {
2559         guiDelay(50);
2560         count++;
2561     }
2562 }
2563 
addVisualizerDock(const QString & magic,const QString & config)2564 void MainWindow::addVisualizerDock(const QString &magic, const QString &config) {
2565     if (m_docksVisualizer.contains(magic)) {
2566         return;
2567     }
2568 
2569     m_docksVisualizer.append(magic);
2570     m_docksVisualizerConfig.append(config);
2571 
2572     DockWidget *dw = new DockWidget(TXT_VISUALIZER_DOCK, this);
2573 
2574     if (m_setup) {
2575         dw->setFloating(true);
2576         dw->setGeometry(QStyle::alignedRect(Qt::LeftToRight, Qt::AlignCenter, dw->minimumSize(), qApp->desktop()->availableGeometry()));
2577     }
2578 
2579     VisualizerWidget *widget = new VisualizerWidget(this, config);
2580 
2581     connect(widget, &VisualizerWidget::configChanged, [this, widget, magic, dw]{
2582         int index;
2583         if ((index = m_docksVisualizer.indexOf(magic)) != -1) {
2584             m_docksVisualizerConfig[index] = widget->getConfig();
2585         }
2586         if (dw->isFloating() && dw->isVisible()) {
2587             dw->adjustSize();
2588         }
2589     });
2590     connect(dw, &DockWidget::closed, [this, magic]{
2591         int index;
2592         if ((index = m_docksVisualizer.indexOf(magic)) != -1) {
2593             m_docksVisualizer.removeAt(index);
2594             m_docksVisualizerConfig.removeAt(index);
2595         }
2596     });
2597 
2598     dw->setState(m_uiEditMode);
2599     addDockWidget(Qt::RightDockWidgetArea, dw);
2600     dw->setObjectName(magic);
2601     dw->setWidget(widget);
2602 
2603     if (m_setup) {
2604         dw->show();
2605         dw->activateWindow();
2606         dw->raise();
2607     }
2608 }
2609 
addMemDock(const QString & magic,int bytes,bool ascii)2610 void MainWindow::addMemDock(const QString &magic, int bytes, bool ascii) {
2611     if (m_docksMemory.contains(magic)) {
2612         return;
2613     }
2614 
2615     DockWidget *dw;
2616     dw = new DockWidget(TXT_MEM_DOCK, this);
2617     dw->setObjectName(magic);
2618     dw->setState(m_uiEditMode);
2619 
2620     if (m_setup) {
2621         dw->setFloating(true);
2622         dw->setGeometry(QStyle::alignedRect(Qt::LeftToRight, Qt::AlignCenter, dw->minimumSize(), qApp->desktop()->availableGeometry()));
2623     }
2624 
2625     m_docksMemory.append(magic);
2626     m_docksMemoryBytes.append(bytes);
2627     m_docksMemoryAscii.append(ascii);
2628 
2629     QWidget *widget = new QWidget();
2630     QVBoxLayout *vlayout = new QVBoxLayout();
2631     QHBoxLayout *hlayout = new QHBoxLayout();
2632     QPushButton *buttonGoto = new QPushButton(m_iconGoto, tr("Goto"));
2633     QPushButton *buttonSearch = new QPushButton(m_iconSearch, tr("Search"));
2634     QToolButton *buttonAscii = new QToolButton();
2635     QToolButton *buttonSync = new QToolButton();
2636     buttonAscii->setCheckable(true);
2637     buttonAscii->setChecked(ascii);
2638     buttonAscii->setIcon(m_iconAscii);
2639     buttonSync->setIcon(m_iconSync);
2640     buttonAscii->setToolTip(tr("Show ASCII"));
2641     buttonSync->setToolTip(tr("Sync Changes"));
2642     QSpacerItem *spacer = new QSpacerItem(0, 20, QSizePolicy::Expanding, QSizePolicy::Maximum);
2643     QSpinBox *spin = new QSpinBox();
2644     HexWidget *edit = new HexWidget();
2645 
2646     buttonGoto->setEnabled(guiDebug);
2647     buttonSearch->setEnabled(guiDebug);
2648     buttonAscii->setEnabled(guiDebug);
2649     buttonSync->setEnabled(guiDebug);
2650     spin->setEnabled(guiDebug);
2651     edit->setEnabled(guiDebug);
2652     edit->setContextMenuPolicy(Qt::CustomContextMenu);
2653     edit->setAsciiArea(ascii);
2654     edit->setScrollable(true);
2655 
2656     m_memWidget = edit;
2657 
2658     connect(edit, &HexWidget::customContextMenuRequested, this, &MainWindow::contextMem);
2659     connect(buttonSearch, &QPushButton::clicked, [this, edit]{ memSearchEdit(edit); });
2660     connect(buttonGoto, &QPushButton::clicked, [this, edit]{ memGotoEdit(edit); });
2661     connect(buttonSync, &QToolButton::clicked, [this, edit]{ memSyncEdit(edit); });
2662     connect(buttonAscii, &QToolButton::toggled, [this, edit, magic]{
2663         memAsciiToggle(edit);
2664         int index;
2665         if ((index = m_docksMemory.indexOf(magic)) != -1) {
2666             m_docksMemoryAscii[index] = edit->getAsciiArea();
2667         }
2668     });
2669     connect(spin, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), [this, edit, magic](int val){
2670         edit->setBytesPerLine(val);
2671         int index;
2672         if ((index = m_docksMemory.indexOf(magic)) != -1) {
2673             m_docksMemoryBytes[index] = val;
2674         }
2675     });
2676     connect(edit, &HexWidget::focused, [this, edit]{ m_memWidget = edit; });
2677     connect(dw, &DockWidget::closed, [this, magic]{
2678         int index;
2679         if ((index = m_docksMemory.indexOf(magic)) != -1) {
2680             m_docksMemory.removeAt(index);
2681             m_docksMemoryBytes.removeAt(index);
2682             m_docksMemoryAscii.removeAt(index);
2683         }
2684     });
2685 
2686     spin->setMaximum(999);
2687     spin->setMinimum(1);
2688     spin->setValue(bytes);
2689 
2690     hlayout->addWidget(buttonGoto);
2691     hlayout->addWidget(buttonSearch);
2692     hlayout->addSpacerItem(spacer);
2693     hlayout->addWidget(buttonAscii);
2694     hlayout->addWidget(buttonSync);
2695     hlayout->addWidget(spin);
2696     vlayout->addLayout(hlayout);
2697     vlayout->addWidget(edit);
2698     widget->setLayout(vlayout);
2699     dw->setWidget(widget);
2700 
2701     if (guiDebug) {
2702         memUpdateEdit(edit);
2703     }
2704 
2705     addDockWidget(Qt::RightDockWidgetArea, dw);
2706 
2707     if (m_setup) {
2708         dw->show();
2709         dw->activateWindow();
2710         dw->raise();
2711     }
2712 }
2713 
setCalcId()2714 void MainWindow::setCalcId() {
2715     bool ok = true;
2716     const uint8_t *data = mem.flash.block;
2717     const uint16_t subSize = 5;
2718     const uint8_t *contents = nullptr;
2719     uint32_t offset = 0x3B0001;
2720     uint32_t size;
2721 
2722     /* Outer field. */
2723     static const uint16_t path[] = { 0x0330, 0x0400 };
2724 
2725     ok = !cert_field_find_path(data + offset, SIZE_FLASH_SECTOR_64K, path, 2, &contents, &size);
2726 
2727     if (!ok) {
2728         QMessageBox::warning(this, MSG_WARNING, tr("Cannot locate calculator ID in the certificate. This is usually due to an improper ROM dump. Please try another ROM dump using a physical calculator."));
2729     } else {
2730         uint32_t field_offset = contents - mem.flash.block;
2731         uint8_t *ptr = mem.flash.block + field_offset;
2732         QByteArray array(reinterpret_cast<const char*>(ptr), subSize);
2733         QString str = QString(array.toHex());
2734 
2735         QString id = QInputDialog::getText(this, tr("CEmu Change Certificate ID"), tr("Old ID: ") + str, QLineEdit::Normal, Q_NULLPTR, &ok);
2736 
2737         if (ok && id.length() == 10) {
2738             QByteArray ba = QByteArray::fromHex(id.toLatin1());
2739             memcpy(ptr, ba.data(), subSize);
2740         }
2741     }
2742 }
2743 
2744