1//============================================================================
2//
3// SSSS tt lll lll
4// SS SS tt ll ll
5// SS tttttt eeee ll ll aaaa
6// SSSS tt ee ee ll ll aa
7// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
8// SS SS tt ee ll ll aa aa
9// SSSS ttt eeeee llll llll aaaaa
10//
11// Copyright (c) 1995-2019 by Bradford W. Mott, Stephen Anthony
12// and the Stella Team
13//
14// See the file "License.txt" for information on usage and redistribution of
15// this file, and for a DISCLAIMER OF ALL WARRANTIES.
16//============================================================================
17
18#include <map>
19
20#include "bspf.hxx"
21
22#include "Version.hxx"
23#include "OSystem.hxx"
24#include "FrameBuffer.hxx"
25#include "EventHandler.hxx"
26#include "FSNode.hxx"
27#include "Settings.hxx"
28#include "DebuggerDialog.hxx"
29#include "DebuggerParser.hxx"
30#include "StateManager.hxx"
31#include "RewindManager.hxx"
32
33#include "Console.hxx"
34#include "System.hxx"
35#include "M6502.hxx"
36#include "Cart.hxx"
37
38#include "CartDebug.hxx"
39#include "CartDebugWidget.hxx"
40#include "CartRamWidget.hxx"
41#include "CpuDebug.hxx"
42#include "RiotDebug.hxx"
43#include "TIADebug.hxx"
44
45#include "TiaInfoWidget.hxx"
46#include "TiaOutputWidget.hxx"
47#include "TiaZoomWidget.hxx"
48#include "EditTextWidget.hxx"
49
50#include "RomWidget.hxx"
51#include "Expression.hxx"
52#include "YaccParser.hxx"
53
54#include "TIA.hxx"
55#include "Debugger.hxx"
56#include "DispatchResult.hxx"
57
58using Common::Base;
59
60Debugger* Debugger::myStaticDebugger = nullptr;
61
62// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
63Debugger::Debugger(OSystem& osystem, Console& console)
64 : DialogContainer(osystem),
65 myConsole(console),
66 mySystem(console.system()),
67 myDialog(nullptr),
68 myWidth(DebuggerDialog::kSmallFontMinW),
69 myHeight(DebuggerDialog::kSmallFontMinH)
70{
71 // Init parser
72 myParser = make_unique<DebuggerParser>(*this, osystem.settings());
73
74 // Create debugger subsystems
75 myCpuDebug = make_unique<CpuDebug>(*this, myConsole);
76 myCartDebug = make_unique<CartDebug>(*this, myConsole, osystem);
77 myRiotDebug = make_unique<RiotDebug>(*this, myConsole);
78 myTiaDebug = make_unique<TIADebug>(*this, myConsole);
79
80 // Allow access to this object from any class
81 // Technically this violates pure OO programming, but since I know
82 // there will only be ever one instance of debugger in Stella,
83 // I don't care :)
84 myStaticDebugger = this;
85}
86
87// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
88Debugger::~Debugger()
89{
90 delete myDialog; myDialog = nullptr;
91}
92
93// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
94void Debugger::initialize()
95{
96 const Common::Size& s = myOSystem.settings().getSize("dbg.res");
97 const Common::Size& d = myOSystem.frameBuffer().desktopSize();
98 myWidth = s.w; myHeight = s.h;
99
100 // The debugger dialog is resizable, within certain bounds
101 // We check those bounds now
102 myWidth = std::max(myWidth, uInt32(DebuggerDialog::kSmallFontMinW));
103 myHeight = std::max(myHeight, uInt32(DebuggerDialog::kSmallFontMinH));
104 myWidth = std::min(myWidth, uInt32(d.w));
105 myHeight = std::min(myHeight, uInt32(d.h));
106
107 myOSystem.settings().setValue("dbg.res", Common::Size(myWidth, myHeight));
108
109 delete myDialog; myDialog = nullptr;
110 myDialog = new DebuggerDialog(myOSystem, *this, 0, 0, myWidth, myHeight);
111
112 myCartDebug->setDebugWidget(&(myDialog->cartDebug()));
113
114 saveOldState();
115}
116
117// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
118FBInitStatus Debugger::initializeVideo()
119{
120 string title = string("Stella ") + STELLA_VERSION + ": Debugger mode";
121 return myOSystem.frameBuffer().createDisplay(title, myWidth, myHeight);
122}
123
124// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
125bool Debugger::start(const string& message, int address, bool read)
126{
127 if(myOSystem.eventHandler().enterDebugMode())
128 {
129 // This must be done *after* we enter debug mode,
130 // so the message isn't erased
131 ostringstream buf;
132 buf << message;
133 if(address > -1)
134 buf << cartDebug().getLabel(address, read, 4);
135 myDialog->message().setText(buf.str());
136 return true;
137 }
138 return false;
139}
140
141// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
142bool Debugger::startWithFatalError(const string& message)
143{
144 if(myOSystem.eventHandler().enterDebugMode())
145 {
146 // This must be done *after* we enter debug mode,
147 // so the dialog is properly shown
148 myDialog->showFatalMessage(message);
149 return true;
150 }
151 return false;
152}
153
154// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
155void Debugger::quit(bool exitrom)
156{
157 if(exitrom)
158 myOSystem.eventHandler().handleEvent(Event::ExitMode);
159 else
160 {
161 myOSystem.eventHandler().leaveDebugMode();
162 myOSystem.console().tia().clearPendingFrame();
163 }
164}
165
166// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
167string Debugger::autoExec(StringList* history)
168{
169 ostringstream buf;
170
171 // autoexec.script is always run
172 FilesystemNode autoexec(myOSystem.baseDir() + "autoexec.script");
173 buf << "autoExec():" << endl
174 << myParser->exec(autoexec, history) << endl;
175
176 // Also, "romname.script" if present
177 FilesystemNode romname(myOSystem.romFile().getPathWithExt(".script"));
178 buf << myParser->exec(romname, history) << endl;
179
180 // Init builtins
181 for(const auto& func: ourBuiltinFunctions)
182 {
183 // TODO - check this for memory leaks
184 int res = YaccParser::parse(func.defn);
185 if(res == 0)
186 addFunction(func.name, func.defn, YaccParser::getResult(), true);
187 else
188 cerr << "ERROR in builtin function!" << endl;
189 }
190 return buf.str();
191}
192
193// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
194BreakpointMap& Debugger::breakPoints() const
195{
196 return mySystem.m6502().breakPoints();
197}
198
199// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
200TrapArray& Debugger::readTraps() const
201{
202 return mySystem.m6502().readTraps();
203}
204
205// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
206TrapArray& Debugger::writeTraps() const
207{
208 return mySystem.m6502().writeTraps();
209}
210
211// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
212const string Debugger::run(const string& command)
213{
214 return myParser->run(command);
215}
216
217// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
218const string Debugger::invIfChanged(int reg, int oldReg)
219{
220 string ret;
221
222 bool changed = reg != oldReg;
223 if(changed) ret += "\177";
224 ret += Common::Base::toString(reg, Common::Base::F_16_2);
225 if(changed) ret += "\177";
226
227 return ret;
228}
229
230// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
231void Debugger::reset()
232{
233 unlockSystem();
234 mySystem.reset();
235 lockSystem();
236}
237
238// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
239/* Element 0 of args is the address. The remaining elements are the data
240 to poke, starting at the given address.
241*/
242string Debugger::setRAM(IntArray& args)
243{
244 ostringstream buf;
245
246 int count = int(args.size());
247 int address = args[0];
248 for(int i = 1; i < count; ++i)
249 mySystem.poke(address++, args[i]);
250
251 buf << "changed " << (count-1) << " location";
252 if(count != 2)
253 buf << "s";
254 return buf.str();
255}
256
257// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
258void Debugger::saveState(int state)
259{
260 // Saving a state is implicitly a read-only operation, so we keep the
261 // system locked, so no changes can occur
262 myOSystem.state().saveState(state);
263}
264
265// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
266void Debugger::saveAllStates()
267{
268 // Saving states is implicitly a read-only operation, so we keep the
269 // system locked, so no changes can occur
270 myOSystem.state().rewindManager().saveAllStates();
271}
272
273// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
274void Debugger::loadState(int state)
275{
276 // We're loading a new state, so we start with a clean slate
277 mySystem.clearDirtyPages();
278
279 // State loading could initiate a bankswitch, so we allow it temporarily
280 unlockSystem();
281 myOSystem.state().loadState(state);
282 lockSystem();
283}
284
285// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
286void Debugger::loadAllStates()
287{
288 // We're loading new states, so we start with a clean slate
289 mySystem.clearDirtyPages();
290
291 // State loading could initiate a bankswitch, so we allow it temporarily
292 unlockSystem();
293 myOSystem.state().rewindManager().loadAllStates();
294 lockSystem();
295}
296
297// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
298int Debugger::step()
299{
300 saveOldState();
301
302 uInt64 startCycle = mySystem.cycles();
303
304 unlockSystem();
305 myOSystem.console().tia().updateScanlineByStep().flushLineCache();
306 lockSystem();
307
308 addState("step");
309 return int(mySystem.cycles() - startCycle);
310}
311
312// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
313// trace is just like step, except it treats a subroutine call as one
314// instruction.
315
316// This implementation is not perfect: it just watches the program counter,
317// instead of tracking (possibly) nested JSR/RTS pairs. In particular, it
318// will fail for recursive subroutine calls. However, with 128 bytes of RAM
319// to share between stack and variables, I doubt any 2600 games will ever
320// use recursion...
321
322int Debugger::trace()
323{
324 // 32 is the 6502 JSR instruction:
325 if(mySystem.peek(myCpuDebug->pc()) == 32)
326 {
327 saveOldState();
328
329 uInt64 startCycle = mySystem.cycles();
330 int targetPC = myCpuDebug->pc() + 3; // return address
331
332 // set temporary breakpoint at target PC (if not existing already)
333 Int8 bank = myCartDebug->getBank(targetPC);
334 if(!checkBreakPoint(targetPC, bank))
335 {
336 // add temporary breakpoint and remove later
337 setBreakPoint(targetPC, bank, BreakpointMap::ONE_SHOT);
338 }
339
340 unlockSystem();
341 mySystem.m6502().execute(11900000); // max. ~10 seconds
342 myOSystem.console().tia().flushLineCache();
343 lockSystem();
344
345 addState("trace");
346 return int(mySystem.cycles() - startCycle);
347 }
348 else
349 return step();
350}
351
352// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
353bool Debugger::setBreakPoint(uInt16 addr, uInt8 bank, uInt32 flags)
354{
355 bool exists = checkBreakPoint(addr, bank);
356
357 if(exists)
358 return false;
359
360 breakPoints().add(addr, bank, flags);
361 return true;
362}
363
364// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
365bool Debugger::clearBreakPoint(uInt16 addr, uInt8 bank)
366{
367 bool exists = checkBreakPoint(addr, bank);
368
369 if(!exists)
370 return false;
371
372 breakPoints().erase(addr, bank);
373 return true;
374}
375
376// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
377bool Debugger::checkBreakPoint(uInt16 addr, uInt8 bank)
378{
379 return breakPoints().check(addr, bank);
380}
381
382// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
383bool Debugger::toggleBreakPoint(uInt16 addr, uInt8 bank)
384{
385 if(checkBreakPoint(addr, bank))
386 clearBreakPoint(addr, bank);
387 else
388 setBreakPoint(addr, bank);
389
390 return breakPoints().check(addr, bank);
391}
392
393// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
394void Debugger::addReadTrap(uInt16 t)
395{
396 readTraps().initialize();
397 readTraps().add(t);
398}
399
400void Debugger::addWriteTrap(uInt16 t)
401{
402 writeTraps().initialize();
403 writeTraps().add(t);
404}
405
406// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
407void Debugger::addTrap(uInt16 t)
408{
409 addReadTrap(t);
410 addWriteTrap(t);
411}
412
413// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
414void Debugger::removeReadTrap(uInt16 t)
415{
416 readTraps().initialize();
417 readTraps().remove(t);
418}
419
420void Debugger::removeWriteTrap(uInt16 t)
421{
422 writeTraps().initialize();
423 writeTraps().remove(t);
424}
425
426// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
427void Debugger::removeTrap(uInt16 t)
428{
429 removeReadTrap(t);
430 removeWriteTrap(t);
431}
432
433// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
434bool Debugger::readTrap(uInt16 t)
435{
436 return readTraps().isInitialized() && readTraps().isSet(t);
437}
438
439// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
440bool Debugger::writeTrap(uInt16 t)
441{
442 return writeTraps().isInitialized() && writeTraps().isSet(t);
443}
444
445// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
446uInt8 Debugger::peek(uInt16 addr, uInt8 flags)
447{
448 return mySystem.peek(addr, flags);
449}
450
451// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
452uInt16 Debugger::dpeek(uInt16 addr, uInt8 flags)
453{
454 return uInt16(mySystem.peek(addr, flags) | (mySystem.peek(addr+1, flags) << 8));
455}
456
457// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
458void Debugger::poke(uInt16 addr, uInt8 value, uInt8 flags)
459{
460 mySystem.poke(addr, value, flags);
461}
462
463// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
464M6502& Debugger::m6502() const
465{
466 return mySystem.m6502();
467}
468
469// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
470int Debugger::peekAsInt(int addr, uInt8 flags)
471{
472 return mySystem.peek(uInt16(addr), flags);
473}
474
475// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
476int Debugger::dpeekAsInt(int addr, uInt8 flags)
477{
478 return mySystem.peek(uInt16(addr), flags) |
479 (mySystem.peek(uInt16(addr+1), flags) << 8);
480}
481
482// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
483int Debugger::getAccessFlags(uInt16 addr) const
484{
485 return mySystem.getAccessFlags(addr);
486}
487
488// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
489void Debugger::setAccessFlags(uInt16 addr, uInt8 flags)
490{
491 mySystem.setAccessFlags(addr, flags);
492}
493
494// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
495uInt32 Debugger::getBaseAddress(uInt32 addr, bool read)
496{
497 if((addr & 0x1080) == 0x0000) // (addr & 0b 0001 0000 1000 0000) == 0b 0000 0000 0000 0000
498 {
499 if(read)
500 // ADDR_TIA read (%xxx0 xxxx 0xxx ????)
501 return addr & 0x000f; // 0b 0000 0000 0000 1111
502 else
503 // ADDR_TIA write (%xxx0 xxxx 0x?? ????)
504 return addr & 0x003f; // 0b 0000 0000 0011 1111
505 }
506
507 // ADDR_ZPRAM (%xxx0 xx0x 1??? ????)
508 if((addr & 0x1280) == 0x0080) // (addr & 0b 0001 0010 1000 0000) == 0b 0000 0000 1000 0000
509 return addr & 0x00ff; // 0b 0000 0000 1111 1111
510
511 // ADDR_ROM
512 if(addr & 0x1000)
513 return addr & 0x1fff; // 0b 0001 1111 1111 1111
514
515 // ADDR_IO read/write I/O registers (%xxx0 xx1x 1xxx x0??)
516 if((addr & 0x1284) == 0x0280) // (addr & 0b 0001 0010 1000 0100) == 0b 0000 0010 1000 0000
517 return addr & 0x0283; // 0b 0000 0010 1000 0011
518
519 // ADDR_IO write timers (%xxx0 xx1x 1xx1 ?1??)
520 if(!read && (addr & 0x1294) == 0x0294) // (addr & 0b 0001 0010 1001 0100) == 0b 0000 0010 1001 0100
521 return addr & 0x029f; // 0b 0000 0010 1001 1111
522
523 // ADDR_IO read timers (%xxx0 xx1x 1xxx ?1x0)
524 if(read && (addr & 0x1285) == 0x0284) // (addr & 0b 0001 0010 1000 0101) == 0b 0000 0010 1000 0100
525 return addr & 0x028c; // 0b 0000 0010 1000 1100
526
527 // ADDR_IO read timer/PA7 interrupt (%xxx0 xx1x 1xxx x1x1)
528 if(read && (addr & 0x1285) == 0x0285) // (addr & 0b 0001 0010 1000 0101) == 0b 0000 0010 1000 0101
529 return addr & 0x0285; // 0b 0000 0010 1000 0101
530
531 // ADDR_IO write PA7 edge control (%xxx0 xx1x 1xx0 x1??)
532 if(!read && (addr & 0x1294) == 0x0284) // (addr & 0b 0001 0010 1001 0100) == 0b 0000 0010 1000 0100
533 return addr & 0x0287; // 0b 0000 0010 1000 0111
534
535 return 0;
536}
537
538// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
539void Debugger::nextScanline(int lines)
540{
541 ostringstream buf;
542 buf << "scanline + " << lines;
543
544 saveOldState();
545
546 unlockSystem();
547 while(lines)
548 {
549 myOSystem.console().tia().updateScanline();
550 --lines;
551 }
552 lockSystem();
553
554 addState(buf.str());
555 myOSystem.console().tia().flushLineCache();
556}
557
558// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
559void Debugger::nextFrame(int frames)
560{
561 ostringstream buf;
562 buf << "frame + " << frames;
563
564 saveOldState();
565
566 unlockSystem();
567 DispatchResult dispatchResult;
568 while(frames)
569 {
570 do
571 myOSystem.console().tia().update(dispatchResult, myOSystem.console().emulationTiming().maxCyclesPerTimeslice());
572 while (dispatchResult.getStatus() == DispatchResult::Status::debugger);
573 --frames;
574 }
575 lockSystem();
576
577 addState(buf.str());
578}
579
580// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
581void Debugger::updateRewindbuttons(const RewindManager& r)
582{
583 myDialog->rewindButton().setEnabled(!r.atFirst());
584 myDialog->unwindButton().setEnabled(!r.atLast());
585}
586
587// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
588uInt16 Debugger::windStates(uInt16 numStates, bool unwind, string& message)
589{
590 RewindManager& r = myOSystem.state().rewindManager();
591
592 saveOldState();
593
594 unlockSystem();
595
596 uInt64 startCycles = myOSystem.console().tia().cycles();
597 uInt16 winds = r.windStates(numStates, unwind);
598 message = r.getUnitString(myOSystem.console().tia().cycles() - startCycles);
599
600 lockSystem();
601
602 updateRewindbuttons(r);
603 return winds;
604}
605
606// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
607uInt16 Debugger::rewindStates(const uInt16 numStates, string& message)
608{
609 return windStates(numStates, false, message);
610}
611
612// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
613uInt16 Debugger::unwindStates(const uInt16 numStates, string& message)
614{
615 return windStates(numStates, true, message);
616}
617
618// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
619void Debugger::clearAllBreakPoints()
620{
621 breakPoints().clear();
622}
623
624// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
625void Debugger::clearAllTraps()
626{
627 readTraps().clearAll();
628 writeTraps().clearAll();
629}
630
631// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
632string Debugger::showWatches()
633{
634 return myParser->showWatches();
635}
636
637// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
638int Debugger::stringToValue(const string& stringval)
639{
640 return myParser->decipher_arg(stringval);
641}
642
643// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
644bool Debugger::patchROM(uInt16 addr, uInt8 value)
645{
646 return myConsole.cartridge().patch(addr, value);
647}
648
649// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
650void Debugger::saveOldState(bool clearDirtyPages)
651{
652 if(clearDirtyPages)
653 mySystem.clearDirtyPages();
654
655 lockSystem();
656 myCartDebug->saveOldState();
657 myCpuDebug->saveOldState();
658 myRiotDebug->saveOldState();
659 myTiaDebug->saveOldState();
660 unlockSystem();
661}
662
663// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
664void Debugger::addState(string rewindMsg)
665{
666 // Add another rewind level to the Time Machine buffer
667 RewindManager& r = myOSystem.state().rewindManager();
668 r.addState(rewindMsg);
669 updateRewindbuttons(r);
670}
671
672// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
673void Debugger::setStartState()
674{
675 // Lock the bus each time the debugger is entered, so we don't disturb anything
676 lockSystem();
677
678 // Save initial state and add it to the rewind list (except when in currently rewinding)
679 RewindManager& r = myOSystem.state().rewindManager();
680 // avoid invalidating future states when entering the debugger e.g. during rewind
681 if(r.atLast() && (myOSystem.eventHandler().state() != EventHandlerState::TIMEMACHINE
682 || myOSystem.state().mode() == StateManager::Mode::Off))
683 addState("enter debugger");
684 else
685 updateRewindbuttons(r);
686
687 // Set the 're-disassemble' flag, but don't do it until the next scheduled time
688 myDialog->rom().invalidate(false);
689}
690
691// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
692void Debugger::setQuitState()
693{
694 saveOldState();
695
696 // Bus must be unlocked for normal operation when leaving debugger mode
697 unlockSystem();
698
699 // execute one instruction on quit. If we're
700 // sitting at a breakpoint/trap, this will get us past it.
701 // Somehow this feels like a hack to me, but I don't know why
702 mySystem.m6502().execute(1);
703}
704
705// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
706bool Debugger::addFunction(const string& name, const string& definition,
707 Expression* exp, bool builtin)
708{
709 myFunctions.emplace(name, unique_ptr<Expression>(exp));
710 myFunctionDefs.emplace(name, definition);
711
712 return true;
713}
714
715// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
716bool Debugger::isBuiltinFunction(const string& name)
717{
718 for(const auto& func: ourBuiltinFunctions)
719 if(name == func.name)
720 return true;
721 return false;
722}
723
724// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
725bool Debugger::delFunction(const string& name)
726{
727 const auto& iter = myFunctions.find(name);
728 if(iter == myFunctions.end())
729 return false;
730
731 // We never want to delete built-in functions
732 if(isBuiltinFunction(name))
733 return false;
734
735 myFunctions.erase(name);
736
737 const auto& def_iter = myFunctionDefs.find(name);
738 if(def_iter == myFunctionDefs.end())
739 return false;
740
741 myFunctionDefs.erase(name);
742 return true;
743}
744
745// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
746const Expression& Debugger::getFunction(const string& name) const
747{
748 const auto& iter = myFunctions.find(name);
749 return iter != myFunctions.end() ? *(iter->second.get()) : EmptyExpression;
750}
751
752// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
753const string& Debugger::getFunctionDef(const string& name) const
754{
755 const auto& iter = myFunctionDefs.find(name);
756 return iter != myFunctionDefs.end() ? iter->second : EmptyString;
757}
758
759// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
760const Debugger::FunctionDefMap Debugger::getFunctionDefMap() const
761{
762 return myFunctionDefs;
763}
764
765// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
766string Debugger::builtinHelp() const
767{
768 ostringstream buf;
769 uInt32 len, c_maxlen = 0, i_maxlen = 0;
770
771 // Get column widths for aligned output (functions)
772 for(const auto& func: ourBuiltinFunctions)
773 {
774 len = uInt32(func.name.size());
775 if(len > c_maxlen) c_maxlen = len;
776 len = uInt32(func.defn.size());
777 if(len > i_maxlen) i_maxlen = len;
778 }
779
780 buf << std::setfill(' ') << endl << "Built-in functions:" << endl;
781 for(const auto& func: ourBuiltinFunctions)
782 {
783 buf << std::setw(c_maxlen) << std::left << func.name
784 << std::setw(2) << std::right << "{"
785 << std::setw(i_maxlen) << std::left << func.defn
786 << std::setw(4) << "}"
787 << func.help
788 << endl;
789 }
790
791 // Get column widths for aligned output (pseudo-registers)
792 c_maxlen = 0;
793 for(const auto& reg: ourPseudoRegisters)
794 {
795 len = uInt32(reg.name.size());
796 if(len > c_maxlen) c_maxlen = len;
797 }
798
799 buf << endl << "Pseudo-registers:" << endl;
800 for(const auto& reg: ourPseudoRegisters)
801 {
802 buf << std::setw(c_maxlen) << std::left << reg.name
803 << std::setw(2) << " "
804 << std::setw(i_maxlen) << std::left << reg.help
805 << endl;
806 }
807
808 return buf.str();
809}
810
811// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
812void Debugger::getCompletions(const char* in, StringList& list) const
813{
814 // skip if filter equals "_" only
815 if(!BSPF::equalsIgnoreCase(in, "_"))
816 {
817 for(const auto& iter : myFunctions)
818 {
819 const char* l = iter.first.c_str();
820 if(BSPF::matches(l, in))
821 list.push_back(l);
822 }
823
824 for(const auto& reg: ourPseudoRegisters)
825 if(BSPF::matches(reg.name, in))
826 list.push_back(reg.name);
827 }
828}
829
830// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
831void Debugger::lockSystem()
832{
833 mySystem.lockDataBus();
834 myConsole.cartridge().lockBank();
835}
836
837// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
838void Debugger::unlockSystem()
839{
840 mySystem.unlockDataBus();
841 myConsole.cartridge().unlockBank();
842}
843
844// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
845bool Debugger::canExit() const
846{
847 return baseDialogIsActive();
848}
849
850// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
851std::array<Debugger::BuiltinFunction, 18> Debugger::ourBuiltinFunctions = { {
852 // left joystick:
853 { "_joy0left", "!(*SWCHA & $40)", "Left joystick moved left" },
854 { "_joy0right", "!(*SWCHA & $80)", "Left joystick moved right" },
855 { "_joy0up", "!(*SWCHA & $10)", "Left joystick moved up" },
856 { "_joy0down", "!(*SWCHA & $20)", "Left joystick moved down" },
857 { "_joy0button", "!(*INPT4 & $80)", "Left joystick button pressed" },
858
859 // right joystick:
860 { "_joy1left", "!(*SWCHA & $04)", "Right joystick moved left" },
861 { "_joy1right", "!(*SWCHA & $08)", "Right joystick moved right" },
862 { "_joy1up", "!(*SWCHA & $01)", "Right joystick moved up" },
863 { "_joy1down", "!(*SWCHA & $02)", "Right joystick moved down" },
864 { "_joy1button", "!(*INPT5 & $80)", "Right joystick button pressed" },
865
866 // console switches:
867 { "_select", "!(*SWCHB & $02)", "Game Select pressed" },
868 { "_reset", "!(*SWCHB & $01)", "Game Reset pressed" },
869 { "_color", "*SWCHB & $08", "Color/BW set to Color" },
870 { "_bw", "!(*SWCHB & $08)", "Color/BW set to BW" },
871 { "_diff0b", "!(*SWCHB & $40)", "Left diff. set to B (easy)" },
872 { "_diff0a", "*SWCHB & $40", "Left diff. set to A (hard)" },
873 { "_diff1b", "!(*SWCHB & $80)", "Right diff. set to B (easy)" },
874 { "_diff1a", "*SWCHB & $80", "Right diff. set to A (hard)" }
875} };
876
877// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
878// Names are defined here, but processed in YaccParser
879std::array<Debugger::PseudoRegister, 11> Debugger::ourPseudoRegisters = { {
880// Debugger::PseudoRegister Debugger::ourPseudoRegisters[NUM_PSEUDO_REGS] = {
881 { "_bank", "Currently selected bank" },
882 { "_cclocks", "Color clocks on current scanline" },
883 { "_cycleshi", "Higher 32 bits of number of cycles since emulation started" },
884 { "_cycleslo", "Lower 32 bits of number of cycles since emulation started" },
885 { "_fcount", "Number of frames since emulation started" },
886 { "_fcycles", "Number of cycles since frame started" },
887 { "_icycles", "Number of cycles of last instruction" },
888 { "_scan", "Current scanline count" },
889 { "_scycles", "Number of cycles in current scanline" },
890 { "_vblank", "Whether vertical blank is enabled (1 or 0)" },
891 { "_vsync", "Whether vertical sync is enabled (1 or 0)" }
892 // CPU address access functions:
893 /*{ "__lastread", "last CPU read address" },
894 { "__lastwrite", "last CPU write address" },*/
895} };
896