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