1 #include "stdafx.h"
2 #include "qconsoleview.h"
3 #include <QLabel>
4 #include <QLineEdit>
5 #include <QTextEdit>
6 #include <QHBoxLayout>
7 #include <QVBoxLayout>
8 #include "main.h"
9 #include "Emulator.h"
10 #include "emubase/Emubase.h"
11 
12 QString QConsoleView::MESSAGE_UNKNOWN_COMMAND;
13 QString QConsoleView::MESSAGE_WRONG_VALUE;
14 
QConsoleView()15 QConsoleView::QConsoleView()
16 {
17     MESSAGE_UNKNOWN_COMMAND = QConsoleView::tr("  Unknown command.\r\n");
18     MESSAGE_WRONG_VALUE = QConsoleView::tr("  Wrong value.\r\n");
19 
20     m_okCurrentProc = Settings_GetDebugCpuPpu();
21 
22     setMinimumSize(320, 120);
23 
24     m_log = new QTextEdit();
25     m_prompt = new QLabel();
26     m_edit = new QLineEdit();
27 
28     QVBoxLayout *vboxlayout = new QVBoxLayout;
29     vboxlayout->setMargin(0);
30     vboxlayout->setSpacing(4);
31     vboxlayout->addWidget(m_log);
32     QHBoxLayout *hboxlayout = new QHBoxLayout;
33     hboxlayout->addWidget(m_prompt);
34     hboxlayout->addWidget(m_edit);
35     vboxlayout->addLayout(hboxlayout);
36     this->setLayout(vboxlayout);
37 
38     QFont font = Common_GetMonospacedFont();
39     m_log->setReadOnly(true);
40     m_log->setFont(font);
41     m_edit->setFont(font);
42 
43     QObject::connect(m_edit, SIGNAL(returnPressed()), this, SLOT(execConsoleCommand()));
44 
45     this->print(tr("Use 'h' command to show help.\r\n\r\n"));
46 }
47 
~QConsoleView()48 QConsoleView::~QConsoleView()
49 {
50     delete m_log;
51     delete m_prompt;
52     delete m_edit;
53 }
54 
getCurrentProcessor()55 CProcessor* QConsoleView::getCurrentProcessor()
56 {
57     if (m_okCurrentProc)
58         return g_pBoard->GetCPU();
59     else
60         return g_pBoard->GetPPU();
61 }
62 
clear()63 void QConsoleView::clear()
64 {
65     m_log->clear();
66 }
67 
setCurrentProc(bool okProc)68 void QConsoleView::setCurrentProc(bool okProc)
69 {
70     m_okCurrentProc = okProc;
71 
72     Settings_SetDebugCpuPpu(m_okCurrentProc);
73 }
74 
updatePrompt()75 void QConsoleView::updatePrompt()
76 {
77     CProcessor* pProc = this->getCurrentProcessor();
78     if (pProc == nullptr) return;
79     char buffer[15];
80     _snprintf(buffer, 15, " %s:%06o> ", pProc->GetName(), pProc->GetPC());
81     m_prompt->setText(buffer);
82 }
83 
print(const QString & message)84 void QConsoleView::print(const QString &message)
85 {
86     m_log->moveCursor(QTextCursor::End);
87     m_log->insertPlainText(message);
88     m_log->moveCursor(QTextCursor::End);
89 }
printLine(const QString & message)90 void QConsoleView::printLine(const QString &message)
91 {
92     m_log->moveCursor(QTextCursor::End);
93     m_log->insertPlainText(message);
94     m_log->insertPlainText("\r\n");
95     m_log->moveCursor(QTextCursor::End);
96 }
97 
printConsolePrompt()98 void QConsoleView::printConsolePrompt()
99 {
100     CProcessor* pProc = this->getCurrentProcessor();
101     char buffer[14];
102     _snprintf(buffer, 14, "%s:%06o> ", pProc->GetName(), pProc->GetPC());
103     this->print(buffer);
104 }
105 
printRegister(LPCTSTR strName,quint16 value)106 void QConsoleView::printRegister(LPCTSTR strName, quint16 value)
107 {
108     char buffer[31];
109     char* p = buffer;
110     *p++ = ' ';
111     *p++ = ' ';
112     strcpy(p, strName);  p += 2;
113     *p++ = ' ';
114     PrintOctalValue(p, value);  p += 6;
115     *p++ = ' ';
116     PrintBinaryValue(p, value);  p += 16;
117     *p++ = '\r';
118     *p++ = '\n';
119     *p++ = 0;
120     this->print(buffer);
121 }
122 
123 // Print memory dump
printMemoryDump(CProcessor * pProc,quint16 address,int lines)124 void QConsoleView::printMemoryDump(CProcessor *pProc, quint16 address, int lines)
125 {
126     address &= ~1;  // Line up to even address
127 
128     CMemoryController* pMemCtl = pProc->GetMemoryController();
129     bool okHaltMode = pProc->IsHaltMode();
130 
131     for (int line = 0; line < lines; line++)
132     {
133         quint16 dump[8];
134         for (int i = 0; i < 8; i++)
135             dump[i] = pMemCtl->GetWord(address + i * 2, okHaltMode, false);
136 
137         char buffer[2 + 6 + 2 + 7 * 8 + 1 + 16 + 1 + 2];
138         char* pBuf = buffer;
139         *pBuf = ' ';  pBuf++;
140         *pBuf = ' ';  pBuf++;
141         PrintOctalValue(pBuf, address);  pBuf += 6;
142         *pBuf = ' ';  pBuf++;
143         *pBuf = ' ';  pBuf++;
144         for (int i = 0; i < 8; i++)
145         {
146             PrintOctalValue(pBuf, dump[i]);  pBuf += 6;
147             *pBuf = ' ';  pBuf++;
148         }
149         *pBuf = ' ';  pBuf++;
150 //        for (int i = 0; i < 8; i++) {
151 //            quint16 word = dump[i];
152 //            quint8 ch1 = LOBYTE(word);
153 //            char wch1 = Translate_BK_Unicode(ch1);
154 //            if (ch1 < 32) wch1 = '·';
155 //            *pBuf = wch1;  pBuf++;
156 //            quint8 ch2 = HIBYTE(word);
157 //            char wch2 = Translate_BK_Unicode(ch2);
158 //            if (ch2 < 32) wch2 = '·';
159 //            *pBuf = wch2;  pBuf++;
160 //        }
161         *pBuf++ = '\r';
162         *pBuf++ = '\n';
163         *pBuf = 0;
164 
165         this->print(buffer);
166 
167         address += 16;
168     }
169 }
170 
171 // Print disassembled instructions
172 // Return value: number of words disassembled
printDisassemble(CProcessor * pProc,quint16 address,bool okOneInstr,bool okShort)173 int QConsoleView::printDisassemble(CProcessor* pProc, quint16 address, bool okOneInstr, bool okShort)
174 {
175     CMemoryController* pMemCtl = pProc->GetMemoryController();
176     bool okHaltMode = pProc->IsHaltMode();
177 
178     const int nWindowSize = 30;
179     quint16 memory[nWindowSize + 2];
180     int addrtype;
181     for (int i = 0; i < nWindowSize + 2; i++)
182         memory[i] = pMemCtl->GetWordView(address + i * 2, okHaltMode, true, &addrtype);
183 
184     char bufaddr[7];
185     char bufvalue[7];
186     char buffer[64];
187 
188     int totalLength = 0;
189     int lastLength = 0;
190     int length = 0;
191     for (int index = 0; index < nWindowSize; index++)    // Draw strings
192     {
193         PrintOctalValue(bufaddr, address);
194         quint16 value = memory[index];
195         PrintOctalValue(bufvalue, value);
196 
197         if (length > 0)
198         {
199             if (!okShort)
200             {
201                 _snprintf(buffer, 64, "  %s  %s\r\n", bufaddr, bufvalue);
202                 this->print(buffer);
203             }
204         }
205         else
206         {
207             if (okOneInstr && index > 0)
208                 break;
209             char instr[8];
210             char args[32];
211             length = DisassembleInstruction(memory + index, address, instr, args);
212             lastLength = length;
213             if (index + length > nWindowSize)
214                 break;
215             if (okShort)
216                 _snprintf(buffer, 64, "  %s  %-7s %s\r\n", bufaddr, instr, args);
217             else
218                 _snprintf(buffer, 64, "  %s  %s  %-7s %s\r\n", bufaddr, bufvalue, instr, args);
219             this->print(buffer);
220         }
221         length--;
222         address += 2;
223         totalLength++;
224     }
225 
226     return totalLength;
227 }
228 
printHelp()229 void QConsoleView::printHelp()
230 {
231     this->print(tr("Console command list:\r\n"
232             "  c          Clear console log\r\n"
233             "  dXXXXXX    Disassemble from address XXXXXX\r\n"
234             "  g          Go; free run\r\n"
235             "  gXXXXXX    Go; run processor until breakpoint at address XXXXXX\r\n"
236             "  m          Memory dump at current address\r\n"
237             "  mXXXXXX    Memory dump at address XXXXXX\r\n"
238             "  mrN        Memory dump at address from register N; N=0..7\r\n"
239             "  p          Switch to other processor\r\n"
240             "  r          Show register values\r\n"
241             "  rN         Show value of register N; N=0..7,ps\r\n"
242             "  rN XXXXXX  Set register N to value XXXXXX; N=0..7,ps\r\n"
243             "  s          Step Into; executes one instruction\r\n"
244             "  so         Step Over; executes and stops after the current instruction\r\n"
245             "  b          List breakpoints set for the current processor\r\n"
246             "  bXXXXXX    Set breakpoint at address XXXXXX\r\n"
247             "  bcXXXXXX   Remove breakpoint at address XXXXXX\r\n"
248             "  bc         Remove all breakpoints for the current processor\r\n"
249 //            "  u          Save memory dump to file memdumpXPU.bin\r\n"
250                   ));
251 }
252 
execConsoleCommand()253 void QConsoleView::execConsoleCommand()
254 {
255     QString command = m_edit->text();
256     m_edit->clear();
257     this->execConsoleCommand(command);
258 }
259 
execConsoleCommand(const QString & command)260 void QConsoleView::execConsoleCommand(const QString &command)
261 {
262     if (command.isNull() || command.isEmpty()) return;  // Nothing to do
263     if (g_okEmulatorRunning) return;
264 
265     // Echo command to the log
266     this->printConsolePrompt();
267     this->printLine(command);
268 
269     bool okUpdateAllViews = false;  // Flag - need to update all debug views
270     bool okUpdateMenu = false;  // Flag - need to update main menu
271     CProcessor* pProc = this->getCurrentProcessor();
272 
273     // Execute the command
274     if (command == "h")
275     {
276         this->printHelp();
277     }
278     else if (command == "c")  // Clear log
279     {
280         this->clear();
281     }
282     else if (command == "p")  // Switch CPU/PPU
283     {
284         Global_SetCurrentProc(! m_okCurrentProc);
285         okUpdateAllViews = true;
286     }
287     else if (command.startsWith("r"))  // Register operations
288     {
289         if (command.length() == 1)  // Print all registers
290         {
291             for (int r = 0; r < 8; r++)
292             {
293                 LPCTSTR name = REGISTER_NAME[r];
294                 quint16 value = pProc->GetReg(r);
295                 this->printRegister(name, value);
296             }
297         }
298         else if (command[1].toLatin1() >= '0' && command[1].toLatin1() <= '7')  // "r0".."r7"
299         {
300             int r = command[1].toLatin1() - '0';
301             LPCTSTR name = REGISTER_NAME[r];
302             if (command.length() == 2)  // "rN" - show register N
303             {
304                 quint16 value = pProc->GetReg(r);
305                 this->printRegister(name, value);
306             }
307             else if (command[2].toLatin1() == '=' || command[2].toLatin1() == ' ')  // "rN=XXXXXX" - set register N to value XXXXXX
308             {
309                 quint16 value;
310                 if (! ParseOctalValue(command.mid(3), &value))
311                     this->print(MESSAGE_WRONG_VALUE);
312                 else
313                 {
314                     pProc->SetReg(r, value);
315                     this->printRegister(name, value);
316                     okUpdateAllViews = true;
317                 }
318             }
319             else
320                 this->print(MESSAGE_UNKNOWN_COMMAND);
321         }
322         else if (command.startsWith("rps"))  // "rps"
323         {
324             if (command.length() == 3)  // "rps" - show PSW
325             {
326                 quint16 value = pProc->GetPSW();
327                 this->printRegister("PS", value);
328             }
329             else if (command[3].toLatin1() == '=' || command[3].toLatin1() == ' ')  // "rps=XXXXXX" - set PSW to value XXXXXX
330             {
331                 quint16 value;
332                 if (! ParseOctalValue(command.mid(4), &value))
333                     this->print(MESSAGE_WRONG_VALUE);
334                 else
335                 {
336                     pProc->SetPSW(value);
337                     this->printRegister("PS", value);
338                     okUpdateAllViews = true;
339                 }
340             }
341             else
342                 this->print(MESSAGE_UNKNOWN_COMMAND);
343         }
344         else
345             this->print(MESSAGE_UNKNOWN_COMMAND);
346     }
347     else if (command == "s")  // "s" - Step Into, execute one instruction
348     {
349         this->printDisassemble(pProc, pProc->GetPC(), true, false);
350 
351         //pProc->Execute();
352         g_pBoard->DebugTicks();
353 
354         okUpdateAllViews = true;
355     }
356     else if (command == "so")  // "so" - Step Over
357     {
358         int instrLength = this->printDisassemble(pProc, pProc->GetPC(), true, false);
359         quint16 bpaddress = pProc->GetPC() + instrLength * 2;
360 
361         if (m_okCurrentProc)
362             Emulator_SetTempCPUBreakpoint(bpaddress);
363         else
364             Emulator_SetTempPPUBreakpoint(bpaddress);
365         Emulator_Start();
366 
367         okUpdateMenu = true;
368     }
369     else if (command.startsWith("d") ||  // Disassemble
370             command.startsWith("D"))    // Disassemble, short format
371     {
372         bool okShort = (command[0] == 'D');
373         quint16 address = 0;
374         bool okValidAddress = false;
375         if (command.length() == 1)  // "d" - disassemble at current address
376         {
377             address = pProc->GetPC();
378             okValidAddress = true;
379         }
380         else if (command[1].toLatin1() >= '0' && command[1].toLatin1() <= '7')  // "dXXXXXX" - disassemble at address XXXXXX
381         {
382             if (! ParseOctalValue(command.mid(1), &address))
383                 this->print(MESSAGE_WRONG_VALUE);
384             else
385                 okValidAddress = true;
386         }
387         else
388         {
389             this->print(MESSAGE_UNKNOWN_COMMAND);
390         }
391 
392         if (okValidAddress)
393         {
394             int length = this->printDisassemble(pProc, address, false, okShort);
395             address += length * 2;
396             QString prompt;  prompt.sprintf("%c%06o", command[0], address);
397             m_edit->setText(prompt);
398         }
399     }
400     else if (command.startsWith("m"))
401     {
402         if (command.length() == 1)  // "m" - dump memory at current address
403         {
404             this->printMemoryDump(pProc, pProc->GetPC(), 8);
405         }
406         else if (command[1].toLatin1() >= '0' && command[1].toLatin1() <= '7')  // "mXXXXXX" - dump memory at address XXXXXX
407         {
408             quint16 value;
409             if (! ParseOctalValue(command.mid(1), &value))
410                 this->print(MESSAGE_WRONG_VALUE);
411             else
412                 this->printMemoryDump(pProc, value, 8);
413         }
414         else if (command[1].toLatin1() == 'r' && command.length() >= 3 &&
415                 command[2].toLatin1() >= '0' && command[2].toLatin1() <= '7')  // "mrN" - dump memory at address from register N
416         {
417             int r = command[2].toLatin1() - '0';
418             quint16 address = pProc->GetReg(r);
419             this->printMemoryDump(pProc, address, 8);
420         }
421         else
422             this->print(MESSAGE_UNKNOWN_COMMAND);
423         //TODO: "mXXXXXX YYYYYY" - set memory cell at XXXXXX to value YYYYYY
424         //TODO: "mrN YYYYYY" - set memory cell at address from rN to value YYYYYY
425     }
426     else if (command == "g")  // Go
427     {
428         Emulator_Start();
429         okUpdateAllViews = true;
430     }
431     else if (command.startsWith("g"))  // Go
432     {
433         quint16 value;
434         if (! ParseOctalValue(command.mid(1), &value))
435             this->print(MESSAGE_WRONG_VALUE);
436         else
437         {
438             if (m_okCurrentProc)
439                 Emulator_SetTempCPUBreakpoint(value);
440             else
441                 Emulator_SetTempPPUBreakpoint(value);
442             Emulator_Start();
443 
444             okUpdateMenu = true;
445         }
446     }
447     else if (command == "b")  // b - list breakpoints
448     {
449         const quint16* pbps = m_okCurrentProc ? Emulator_GetCPUBreakpointList() : Emulator_GetPPUBreakpointList();
450         if (pbps == nullptr || *pbps == 0177777)
451         {
452             this->print("  No breakpoints.\r\n");
453         }
454         else
455         {
456             while (*pbps != 0177777)
457             {
458                 QString line;  line.sprintf("  %06ho\r\n", *pbps);
459                 this->print(line);
460                 pbps++;
461             }
462         }
463     }
464     else if (command == "bc")  // bc - remove all breakpoints
465     {
466         Emulator_RemoveAllBreakpoints(m_okCurrentProc);
467         Global_RedrawDebugView();
468         Global_RedrawDisasmView();
469     }
470     else if (command.startsWith("bc"))  // bcXXXXXX - remove breakpoint
471     {
472         quint16 value;
473         if (! ParseOctalValue(command.mid(2), &value))
474             this->print(MESSAGE_WRONG_VALUE);
475         else
476         {
477             bool result = m_okCurrentProc ? Emulator_RemoveCPUBreakpoint(value) : Emulator_RemovePPUBreakpoint(value);
478             if (!result)
479                 this->print("  Failed to remove breakpoint.\r\n");
480             Global_RedrawDebugView();
481             Global_RedrawDisasmView();
482         }
483     }
484     else if (command.startsWith("b"))  // bXXXXXX - add breakpoint
485     {
486         quint16 value;
487         if (! ParseOctalValue(command.mid(1), &value))
488             this->print(MESSAGE_WRONG_VALUE);
489         else
490         {
491             bool result = m_okCurrentProc ? Emulator_AddCPUBreakpoint(value) : Emulator_AddPPUBreakpoint(value);
492             if (!result)
493                 this->print("  Failed to add breakpoint.\r\n");
494             Global_RedrawDebugView();
495             Global_RedrawDisasmView();
496         }
497     }
498     else
499     {
500         this->print(MESSAGE_UNKNOWN_COMMAND);
501     }
502 
503     if (okUpdateAllViews)
504         Global_UpdateAllViews();
505     else if (okUpdateMenu)
506         Global_UpdateMenu();
507 }
508