1 #include "stdafx.h"
2 #include <thread>
3 #include "../Utilities/FolderUtilities.h"
4 #include "MessageManager.h"
5 #include "Debugger.h"
6 #include "Console.h"
7 #include "BaseMapper.h"
8 #include "Disassembler.h"
9 #include "VideoDecoder.h"
10 #include "APU.h"
11 #include "SoundMixer.h"
12 #include "CodeDataLogger.h"
13 #include "ExpressionEvaluator.h"
14 #include "LabelManager.h"
15 #include "MemoryDumper.h"
16 #include "MemoryAccessCounter.h"
17 #include "Profiler.h"
18 #include "Assembler.h"
19 #include "CodeRunner.h"
20 #include "DisassemblyInfo.h"
21 #include "PPU.h"
22 #include "MemoryManager.h"
23 #include "RewindManager.h"
24 #include "DebugBreakHelper.h"
25 #include "ScriptHost.h"
26 #include "StandardController.h"
27 #include "TraceLogger.h"
28 #include "Breakpoint.h"
29 #include "CodeDataLogger.h"
30 #include "NotificationManager.h"
31 #include "DebugHud.h"
32 #include "DummyCpu.h"
33 #include "PerformanceTracker.h"
34
35 const int Debugger::BreakpointTypeCount;
36 string Debugger::_disassemblerOutput = "";
37
Debugger(shared_ptr<Console> console,shared_ptr<CPU> cpu,shared_ptr<PPU> ppu,shared_ptr<APU> apu,shared_ptr<MemoryManager> memoryManager,shared_ptr<BaseMapper> mapper)38 Debugger::Debugger(shared_ptr<Console> console, shared_ptr<CPU> cpu, shared_ptr<PPU> ppu, shared_ptr<APU> apu, shared_ptr<MemoryManager> memoryManager, shared_ptr<BaseMapper> mapper)
39 {
40 _romName = console->GetRomInfo().RomName;
41 _console = console;
42 _cpu = cpu;
43 _apu = apu;
44 _memoryManager = memoryManager;
45 _mapper = mapper;
46
47 _dummyCpu.reset(new DummyCpu(console));
48 _breakOnFirstCycle = false;
49
50 _labelManager.reset(new LabelManager(_mapper));
51 _assembler.reset(new Assembler(_labelManager));
52 _disassembler.reset(new Disassembler(memoryManager.get(), mapper.get(), this));
53 _codeDataLogger.reset(new CodeDataLogger(this, mapper->GetMemorySize(DebugMemoryType::PrgRom), mapper->GetMemorySize(DebugMemoryType::ChrRom)));
54
55 SetPpu(ppu);
56
57 _memoryAccessCounter.reset(new MemoryAccessCounter(this));
58 _profiler.reset(new Profiler(this));
59 _performanceTracker.reset(new PerformanceTracker(console));
60 _traceLogger.reset(new TraceLogger(this, memoryManager, _labelManager));
61
62 _bpExpEval.reset(new ExpressionEvaluator(this));
63 _watchExpEval.reset(new ExpressionEvaluator(this));
64
65 #if _DEBUG
66 _bpExpEval->RunTests();
67 #endif
68
69 _stepOut = false;
70 _stepCount = -1;
71 _stepOverAddr = -1;
72 _stepCycleCount = -1;
73 _ppuStepCount = -1;
74 _breakRequested = false;
75 _pausedForDebugHelper = false;
76 _breakOnScanline = -2;
77 _breakSource = BreakSource::Unspecified;
78
79 memset(_hasBreakpoint, 0, sizeof(_hasBreakpoint));
80 _bpDummyCpuRequired = false;
81
82 _preventResume = 0;
83 _stopFlag = false;
84 _suspendCount = 0;
85
86 _opCodeCycle = 0;
87 _lastInstruction = 0;
88
89 _stepOutReturnAddress = -1;
90
91 _currentReadAddr = nullptr;
92 _currentReadValue = nullptr;
93 _nextReadAddr = -1;
94 _returnToAddress = 0;
95
96 _ppuScrollX = 0;
97 _ppuScrollY = 0;
98
99 _flags = 0;
100
101 _runToCycle = 0;
102 _prevInstructionCycle = 0;
103 _curInstructionCycle = 0;
104 _needRewind = false;
105
106 //Only enable break on uninitialized reads when debugger is opened at power on/reset
107 _enableBreakOnUninitRead = _cpu->GetPC() == 0;
108
109 _executionStopped = false;
110
111 _disassemblerOutput = "";
112
113 memset(_inputOverride, 0, sizeof(_inputOverride));
114
115 _frozenAddresses.insert(_frozenAddresses.end(), 0x10000, 0);
116
117 if(!LoadCdlFile(FolderUtilities::CombinePath(FolderUtilities::GetDebuggerFolder(), FolderUtilities::GetFilename(_romName, false) + ".cdl"))) {
118 _disassembler->Reset();
119 }
120
121 _hasScript = false;
122 _nextScriptId = 0;
123
124 _released = false;
125
126 UpdatePpuCyclesToProcess();
127 }
128
~Debugger()129 Debugger::~Debugger()
130 {
131 if(!_released) {
132 ReleaseDebugger(true);
133 }
134 }
135
ReleaseDebugger(bool needPause)136 void Debugger::ReleaseDebugger(bool needPause)
137 {
138 auto lock = _releaseLock.AcquireSafe();
139 if(!_released) {
140 _codeDataLogger->SaveCdlFile(FolderUtilities::CombinePath(FolderUtilities::GetDebuggerFolder(), FolderUtilities::GetFilename(_romName, false) + ".cdl"));
141
142 _stopFlag = true;
143
144 if(needPause) {
145 //ReleaseDebugger is called in the callback for "BeforeEmulationStop"
146 //calling Pause in this scenario will cause a deadlock, but doing so is
147 //unnecessary, so we can just skip it.
148 _console->Pause();
149 }
150
151 {
152 auto lock = _scriptLock.AcquireSafe();
153 for(shared_ptr<ScriptHost> script : _scripts) {
154 //Send a ScriptEnded event to all active scripts
155 script->ProcessEvent(EventType::ScriptEnded);
156 }
157 _scripts.clear();
158 _hasScript = false;
159 }
160
161 _breakLock.Acquire();
162 _breakLock.Release();
163
164 if(needPause) {
165 _console->Resume();
166 }
167
168 _released = true;
169 }
170 }
171
SetPpu(shared_ptr<PPU> ppu)172 void Debugger::SetPpu(shared_ptr<PPU> ppu)
173 {
174 _ppu = ppu;
175 _memoryDumper.reset(new MemoryDumper(_ppu, _memoryManager, _mapper, _codeDataLogger, this, _disassembler));
176 }
177
GetConsole()178 Console* Debugger::GetConsole()
179 {
180 return _console.get();
181 }
182
Suspend()183 void Debugger::Suspend()
184 {
185 _suspendCount++;
186 while(_executionStopped) {}
187 }
188
Resume()189 void Debugger::Resume()
190 {
191 _suspendCount--;
192 if(_suspendCount < 0) {
193 _suspendCount = 0;
194 }
195 }
196
SetFlags(uint32_t flags)197 void Debugger::SetFlags(uint32_t flags)
198 {
199 bool needUpdate = ((flags ^ _flags) & (int)DebuggerFlags::DisplayOpCodesInLowerCase) != 0;
200 _flags = flags;
201 _breakOnFirstCycle = CheckFlag(DebuggerFlags::BreakOnFirstCycle);
202 if(needUpdate) {
203 _disassembler->BuildOpCodeTables(CheckFlag(DebuggerFlags::DisplayOpCodesInLowerCase));
204 }
205 }
206
CheckFlag(DebuggerFlags flag)207 bool Debugger::CheckFlag(DebuggerFlags flag)
208 {
209 return (_flags & (uint32_t)flag) == (uint32_t)flag;
210 }
211
LoadCdlFile(string cdlFilepath)212 bool Debugger::LoadCdlFile(string cdlFilepath)
213 {
214 if(_codeDataLogger->LoadCdlFile(cdlFilepath)) {
215 //Can't use DebugBreakHelper due to the fact this is called in the constructor
216 bool isEmulationThread = _console->GetEmulationThreadId() == std::this_thread::get_id();
217 if(!isEmulationThread) {
218 _console->Pause();
219 }
220 UpdateCdlCache();
221 if(!isEmulationThread) {
222 _console->Resume();
223 }
224 return true;
225 }
226 return false;
227 }
228
SetCdlData(uint8_t * cdlData,uint32_t length)229 void Debugger::SetCdlData(uint8_t* cdlData, uint32_t length)
230 {
231 DebugBreakHelper helper(this);
232 _codeDataLogger->SetCdlData(cdlData, length);
233 UpdateCdlCache();
234 }
235
ResetCdl()236 void Debugger::ResetCdl()
237 {
238 DebugBreakHelper helper(this);
239 _codeDataLogger->Reset();
240 UpdateCdlCache();
241 }
242
UpdateCdlCache()243 void Debugger::UpdateCdlCache()
244 {
245 DebugBreakHelper helper(this);
246
247 _disassembler->Reset();
248 for(int i = 0, len = _mapper->GetMemorySize(DebugMemoryType::PrgRom); i < len; i++) {
249 if(_codeDataLogger->IsCode(i)) {
250 AddressTypeInfo info = { i, AddressType::PrgRom };
251 i = _disassembler->BuildCache(info, 0, false, false) - 1;
252 }
253 }
254
255 _functionEntryPoints.clear();
256 for(int i = 0, len = _mapper->GetMemorySize(DebugMemoryType::PrgRom); i < len; i++) {
257 if(_codeDataLogger->IsSubEntryPoint(i)) {
258 _functionEntryPoints.emplace(i);
259
260 //After resetting the cache, set the entry point flags in the disassembly cache
261 AddressTypeInfo info = { i, AddressType::PrgRom };
262 _disassembler->BuildCache(info, 0, true, false);
263 }
264 }
265 }
266
IsMarkedAsCode(uint16_t relativeAddress)267 bool Debugger::IsMarkedAsCode(uint16_t relativeAddress)
268 {
269 AddressTypeInfo info;
270 GetAbsoluteAddressAndType(relativeAddress, &info);
271 if(info.Address >= 0 && info.Type == AddressType::PrgRom) {
272 return _codeDataLogger->IsCode(info.Address);
273 } else {
274 return false;
275 }
276 }
277
GetCodeDataLogger()278 shared_ptr<CodeDataLogger> Debugger::GetCodeDataLogger()
279 {
280 return _codeDataLogger;
281 }
282
GetLabelManager()283 shared_ptr<LabelManager> Debugger::GetLabelManager()
284 {
285 return _labelManager;
286 }
287
GetProfiler()288 shared_ptr<Profiler> Debugger::GetProfiler()
289 {
290 return _profiler;
291 }
292
SetBreakpoints(Breakpoint breakpoints[],uint32_t length)293 void Debugger::SetBreakpoints(Breakpoint breakpoints[], uint32_t length)
294 {
295 DebugBreakHelper helper(this);
296
297 for(int i = 0; i < Debugger::BreakpointTypeCount; i++) {
298 _breakpoints[i].clear();
299 _breakpointRpnList[i].clear();
300 _hasBreakpoint[i] = false;
301 }
302
303 _bpDummyCpuRequired = false;
304
305 _bpExpEval.reset(new ExpressionEvaluator(this));
306 for(uint32_t j = 0; j < length; j++) {
307 Breakpoint &bp = breakpoints[j];
308 for(int i = 0; i < Debugger::BreakpointTypeCount; i++) {
309 bool isEnabled = bp.IsEnabled() && _console->GetSettings()->CheckFlag(EmulationFlags::DebuggerWindowEnabled);
310 if((bp.IsMarked() || isEnabled) && bp.HasBreakpointType((BreakpointType)i)) {
311 _breakpoints[i].push_back(bp);
312
313 if(bp.HasCondition()) {
314 bool success = true;
315 ExpressionData data = _bpExpEval->GetRpnList(bp.GetCondition(), success);
316 _breakpointRpnList[i].push_back(success ? data : ExpressionData());
317 } else {
318 _breakpointRpnList[i].push_back(ExpressionData());
319 }
320
321 if(isEnabled) {
322 bool isReadWriteBp = i == BreakpointType::ReadVram || i == BreakpointType::ReadRam || i == BreakpointType::WriteVram || i == BreakpointType::WriteRam || i == BreakpointType::DummyReadRam || i == BreakpointType::DummyWriteRam;
323 _bpDummyCpuRequired |= isReadWriteBp;
324 }
325
326 _hasBreakpoint[i] = true;
327 }
328 }
329 }
330 }
331
ProcessBreakpoints(BreakpointType type,OperationInfo & operationInfo,bool allowBreak,bool allowMark)332 bool Debugger::ProcessBreakpoints(BreakpointType type, OperationInfo &operationInfo, bool allowBreak, bool allowMark)
333 {
334 //Disable breakpoints if debugger window is closed
335 allowBreak &= _console->GetSettings()->CheckFlag(EmulationFlags::DebuggerWindowEnabled);
336
337 if(_runToCycle != 0) {
338 //Disable all breakpoints while stepping backwards
339 return false;
340 } else if(!allowBreak && !allowMark) {
341 //Nothing to be done, skip processing
342 return false;
343 }
344
345 AddressTypeInfo info { -1, AddressType::InternalRam };
346 PpuAddressTypeInfo ppuInfo { -1, PpuAddressType::None };
347 bool isPpuBreakpoint = false;
348 switch(type) {
349 case BreakpointType::Global:
350 break;
351
352 case BreakpointType::Execute:
353 case BreakpointType::ReadRam:
354 case BreakpointType::WriteRam:
355 case BreakpointType::DummyReadRam:
356 case BreakpointType::DummyWriteRam:
357 GetAbsoluteAddressAndType(operationInfo.Address, &info);
358 break;
359
360 case BreakpointType::ReadVram:
361 case BreakpointType::WriteVram:
362 GetPpuAbsoluteAddressAndType(operationInfo.Address, &ppuInfo);
363 isPpuBreakpoint = true;
364 break;
365 }
366
367 vector<Breakpoint> &breakpoints = _breakpoints[(int)type];
368
369 bool needBreak = false;
370 bool needMark = false;
371 bool needState = true;
372 uint32_t markBreakpointId = 0;
373 uint32_t breakpointId = 0;
374 EvalResultType resultType;
375
376 auto processBreakpoint = [&needMark, &needBreak, &markBreakpointId, &breakpointId](Breakpoint &bp) {
377 if(bp.IsMarked()) {
378 needMark = true;
379 markBreakpointId = bp.GetId();
380 }
381 if(bp.IsEnabled()) {
382 needBreak = true;
383 breakpointId = bp.GetId();
384 }
385 };
386
387 for(size_t i = 0, len = breakpoints.size(); i < len; i++) {
388 Breakpoint &breakpoint = breakpoints[i];
389
390 if(
391 ((breakpoint.IsEnabled() && allowBreak) || (breakpoint.IsMarked() && allowMark)) &&
392 (type == BreakpointType::Global ||
393 (!isPpuBreakpoint && breakpoint.Matches(operationInfo.Address, info)) ||
394 (isPpuBreakpoint && breakpoint.Matches(operationInfo.Address, ppuInfo)))
395 ) {
396 if(!breakpoint.HasCondition()) {
397 processBreakpoint(breakpoint);
398 } else {
399 if(needState) {
400 GetState(&_debugState, false);
401 needState = false;
402 }
403 if(_bpExpEval->Evaluate(_breakpointRpnList[(int)type][i], _debugState, resultType, operationInfo) != 0) {
404 processBreakpoint(breakpoint);
405 }
406 }
407
408 if((needMark || !allowMark) && (needBreak || !allowBreak)) {
409 //No need to process remaining breakpoints
410 break;
411 }
412 }
413 }
414
415 if(needMark && allowMark) {
416 AddDebugEvent(DebugEventType::Breakpoint, operationInfo.Address, (uint8_t)operationInfo.Value, markBreakpointId);
417 }
418
419 if(needBreak && allowBreak) {
420 //Found a matching breakpoint, stop execution
421 Step(1);
422 SleepUntilResume(BreakSource::Breakpoint, breakpointId, type, operationInfo.Address, (uint8_t)operationInfo.Value, operationInfo.OperationType);
423 return true;
424 } else {
425 return false;
426 }
427 }
428
ProcessAllBreakpoints(OperationInfo & operationInfo)429 void Debugger::ProcessAllBreakpoints(OperationInfo &operationInfo)
430 {
431 if(_runToCycle != 0) {
432 //Disable all breakpoints while stepping backwards
433 return;
434 }
435
436 if(_hasBreakpoint[BreakpointType::Execute]) {
437 ProcessBreakpoints(BreakpointType::Execute, operationInfo, true, true);
438 }
439
440 bool checkUninitReads = _enableBreakOnUninitRead && CheckFlag(DebuggerFlags::BreakOnUninitMemoryRead);
441
442 if(!checkUninitReads && !_bpDummyCpuRequired) {
443 //Nothing to do, no read/write breakpoints are active and don't need to check uninit reads
444 return;
445 }
446
447 _dummyCpu->SetDummyState(_cpu.get());
448 _dummyCpu->Exec();
449
450 DebugState &state = _debugState;
451 uint32_t readCount = _dummyCpu->GetReadCount();
452 if(readCount > 0) {
453 uint16_t addr;
454 uint8_t value;
455 bool isDummyRead;
456 for(uint32_t i = 0; i < readCount; i++) {
457 _dummyCpu->GetReadAddr(i, addr, value, isDummyRead);
458
459 OperationInfo info;
460
461 if(addr >= 0x2000 && addr < 0x4000 && (addr & 0x07) == 0x07) {
462 //Reads to $2007 will trigger a PPU read
463 if(_hasBreakpoint[BreakpointType::ReadVram]) {
464 OperationInfo ppuInfo;
465 ppuInfo.OperationType = MemoryOperationType::Read;
466 ppuInfo.Address = state.PPU.BusAddress;
467 ppuInfo.Value = _ppu->PeekRAM(addr);
468 if(ProcessBreakpoints(BreakpointType::ReadVram, ppuInfo, true, false)) {
469 return;
470 }
471 }
472
473 info.Value = state.PPU.MemoryReadBuffer;
474 } else {
475 if(!isDummyRead && checkUninitReads) {
476 //Break on uninit memory read
477 AddressTypeInfo info;
478 GetAbsoluteAddressAndType(addr, &info);
479 if(info.Address >= 0 && _memoryAccessCounter->IsAddressUninitialized(info)) {
480 Step(1);
481 SleepUntilResume(BreakSource::BreakOnUninitMemoryRead, 0, BreakpointType::ReadRam, addr, value);
482 return;
483 }
484 }
485
486 info.Value = value;
487 }
488
489 info.Address = addr;
490 if(isDummyRead) {
491 if(_hasBreakpoint[BreakpointType::DummyReadRam]) {
492 info.OperationType = MemoryOperationType::DummyRead;
493 if(ProcessBreakpoints(BreakpointType::DummyReadRam, info, true, false)) {
494 return;
495 }
496 }
497 } else {
498 if(_hasBreakpoint[BreakpointType::ReadRam]) {
499 info.OperationType = MemoryOperationType::Read;
500 if(ProcessBreakpoints(BreakpointType::ReadRam, info, true, false)) {
501 return;
502 }
503 }
504 }
505 }
506 }
507
508 uint32_t writeCount = _dummyCpu->GetWriteCount();
509 if(writeCount > 0) {
510 uint16_t addr;
511 uint8_t value;
512 bool isDummyWrite;
513 for(uint32_t i = 0; i < writeCount; i++) {
514 _dummyCpu->GetWriteAddrValue(i, addr, value, isDummyWrite);
515
516 OperationInfo info;
517 info.Address = addr;
518 info.Value = value;
519 if(isDummyWrite) {
520 if(_hasBreakpoint[BreakpointType::DummyWriteRam]) {
521 info.OperationType = MemoryOperationType::DummyWrite;
522 if(ProcessBreakpoints(BreakpointType::DummyWriteRam, info, true, false)) {
523 return;
524 }
525 }
526 } else {
527 if(_hasBreakpoint[BreakpointType::WriteRam]) {
528 info.OperationType = MemoryOperationType::Write;
529 if(ProcessBreakpoints(BreakpointType::WriteRam, info, true, false)) {
530 return;
531 }
532 }
533 }
534
535 if(_hasBreakpoint[BreakpointType::WriteVram]) {
536 if(addr >= 0x2000 && addr < 0x4000 && (addr & 0x07) == 0x07) {
537 //Write to $2007 will trigger a PPU write
538 OperationInfo ppuInfo;
539 ppuInfo.Address = state.PPU.BusAddress;
540 ppuInfo.Value = value;
541 ppuInfo.OperationType = MemoryOperationType::Write;
542 if(ProcessBreakpoints(BreakpointType::WriteVram, ppuInfo, true, false)) {
543 return;
544 }
545 }
546 }
547 }
548 }
549 }
550
EvaluateExpression(string expression,EvalResultType & resultType,bool useCache)551 int32_t Debugger::EvaluateExpression(string expression, EvalResultType &resultType, bool useCache)
552 {
553 DebugState state;
554 OperationInfo operationInfo { 0, 0, MemoryOperationType::DummyRead };
555 GetState(&state);
556 if(useCache) {
557 return _watchExpEval->Evaluate(expression, state, resultType, operationInfo);
558 } else {
559 ExpressionEvaluator expEval(this);
560 return expEval.Evaluate(expression, state, resultType, operationInfo);
561 }
562 }
563
UpdateCallstack(uint8_t instruction,uint32_t addr)564 void Debugger::UpdateCallstack(uint8_t instruction, uint32_t addr)
565 {
566 if((instruction == 0x60 || instruction == 0x40) && !_callstack.empty()) {
567 //RTS & RTI
568 uint16_t expectedReturnAddress = _callstack[_callstack.size() - 1].JumpSource;
569
570 _callstack.pop_back();
571 _subReturnAddresses.pop_back();
572
573 int spOffset = instruction == 0x40 ? 2 : 1; //RTI has an extra byte on the stack (flags)
574
575 uint16_t targetAddr = _memoryManager->DebugReadWord(0x100 + ((_debugState.CPU.SP + spOffset) & 0xFF));
576 if(targetAddr < expectedReturnAddress || targetAddr - expectedReturnAddress > 3) {
577 //Mismatch, pop that stack frame and add the new one
578 if(!_callstack.empty()) {
579 bool foundMatch = false;
580 for(int i = (int)_callstack.size() - 1; i >= 0; i--) {
581 if(targetAddr > _callstack[i].JumpSource && targetAddr < _callstack[i].JumpSource + 3) {
582 //Found a matching stack frame, unstack until that point
583 foundMatch = true;
584 for(int j = (int)_callstack.size() - i - 1; j >= 0; j--) {
585 _callstack.pop_back();
586 _subReturnAddresses.pop_back();
587 }
588 break;
589 }
590 }
591 if(!foundMatch) {
592 //Couldn't find a matching frame, replace the current one
593 AddCallstackFrame(expectedReturnAddress, targetAddr, StackFrameFlags::None);
594 _subReturnAddresses.push_back(expectedReturnAddress + 3);
595 }
596 }
597 }
598
599 _profiler->UnstackFunction();
600 } else if(instruction == 0x20) {
601 //JSR
602 uint16_t targetAddr = _memoryManager->DebugRead(addr + 1) | (_memoryManager->DebugRead(addr + 2) << 8);
603 AddCallstackFrame(addr, targetAddr, StackFrameFlags::None);
604 _subReturnAddresses.push_back(addr + 3);
605
606 _profiler->StackFunction(_mapper->ToAbsoluteAddress(addr), _mapper->ToAbsoluteAddress(targetAddr));
607 }
608 }
609
AddCallstackFrame(uint16_t source,uint16_t target,StackFrameFlags flags)610 void Debugger::AddCallstackFrame(uint16_t source, uint16_t target, StackFrameFlags flags)
611 {
612 if(_callstack.size() >= 511) {
613 //Ensure callstack stays below 512 entries - games can use various tricks that could keep making the callstack grow
614 _callstack.pop_front();
615 _subReturnAddresses.pop_front();
616 }
617
618 StackFrameInfo stackFrame;
619 stackFrame.JumpSource = source;
620 stackFrame.JumpSourceAbsolute = _mapper->ToAbsoluteAddress(source);
621
622 stackFrame.JumpTarget = target;
623 stackFrame.JumpTargetAbsolute = _mapper->ToAbsoluteAddress(target);
624
625 stackFrame.Flags = flags;
626
627 _callstack.push_back(stackFrame);
628 }
629
ProcessInterrupt(uint16_t cpuAddr,uint16_t destCpuAddr,bool forNmi)630 void Debugger::ProcessInterrupt(uint16_t cpuAddr, uint16_t destCpuAddr, bool forNmi)
631 {
632 AddCallstackFrame(cpuAddr, destCpuAddr, forNmi ? StackFrameFlags::Nmi : StackFrameFlags::Irq);
633 _subReturnAddresses.push_back(cpuAddr);
634
635 _profiler->StackFunction(-1, _mapper->ToAbsoluteAddress(destCpuAddr));
636
637 ProcessEvent(forNmi ? EventType::Nmi : EventType::Irq);
638 }
639
ProcessStepConditions(uint16_t addr)640 void Debugger::ProcessStepConditions(uint16_t addr)
641 {
642 if(_stepOut && (_lastInstruction == 0x60 || _lastInstruction == 0x40) && _stepOutReturnAddress == addr) {
643 //RTS/RTI found, if we're on the expected return address, break immediately
644 Step(1);
645 } else if(_stepOverAddr != -1 && addr == (uint32_t)_stepOverAddr) {
646 Step(1);
647 }
648 }
649
IsPpuCycleToProcess()650 bool Debugger::IsPpuCycleToProcess()
651 {
652 return _proccessPpuCycle[_ppu->GetCurrentCycle()] || _hasBreakpoint[BreakpointType::Global] || _ppuStepCount > 0;
653 }
654
ProcessPpuCycle()655 void Debugger::ProcessPpuCycle()
656 {
657 if(_proccessPpuCycle[_ppu->GetCurrentCycle()]) {
658 int32_t currentCycle = (_ppu->GetCurrentCycle() << 9) + _ppu->GetCurrentScanline();
659 for(auto updateCycle : _ppuViewerUpdateCycle) {
660 if(updateCycle.second == currentCycle) {
661 _console->GetNotificationManager()->SendNotification(ConsoleNotificationType::PpuViewerDisplayFrame, (void*)(uint64_t)updateCycle.first);
662 }
663 }
664
665 if(_ppu->GetCurrentCycle() == 0) {
666 if(_breakOnScanline == _ppu->GetCurrentScanline()) {
667 Step(1);
668 SleepUntilResume(BreakSource::Pause);
669 }
670 if(_ppu->GetCurrentScanline() == 240) {
671 ProcessEvent(EventType::EndFrame);
672 } else if(_ppu->GetCurrentScanline() == -1) {
673 ProcessEvent(EventType::StartFrame);
674 }
675 }
676 }
677
678 if(_hasBreakpoint[BreakpointType::Global]) {
679 OperationInfo operationInfo { 0, 0, MemoryOperationType::DummyRead };
680 ProcessBreakpoints(BreakpointType::Global, operationInfo);
681 }
682
683 if(_ppuStepCount > 0) {
684 _ppuStepCount--;
685 if(_ppuStepCount == 0) {
686 Step(1);
687 SleepUntilResume(BreakSource::PpuStep);
688 }
689 }
690 }
691
ProcessRamOperation(MemoryOperationType type,uint16_t & addr,uint8_t & value)692 bool Debugger::ProcessRamOperation(MemoryOperationType type, uint16_t &addr, uint8_t &value)
693 {
694 OperationInfo operationInfo { addr, (int16_t)value, type };
695
696 _memoryOperationType = type;
697
698 bool isDmcRead = false;
699 if(type == MemoryOperationType::DmcRead) {
700 //Used to flag the data in the CDL file
701 isDmcRead = true;
702 type = MemoryOperationType::Read;
703 }
704
705 ProcessCpuOperation(addr, value, type);
706
707 if(type == MemoryOperationType::ExecOpCode) {
708 _cpu->SetDebugPC(addr);
709
710 if(_runToCycle == 0) {
711 _rewindCache.clear();
712 _rewindPrevInstructionCycleCache.clear();
713 }
714
715 if(_nextReadAddr != -1) {
716 //SetNextStatement (either from manual action or code runner)
717 if(addr < 0x3000 || addr >= 0x4000) {
718 _returnToAddress = addr;
719 }
720
721 addr = _nextReadAddr;
722 value = _memoryManager->DebugRead(addr, true);
723 _cpu->SetDebugPC(addr);
724 _nextReadAddr = -1;
725 } else if(_needRewind) {
726 //Step back - Need to load a state, and then alter the current opcode based on the new program counter
727 if(!_rewindCache.empty()) {
728 //Restore the state, and the cycle number of the instruction that preceeded that state
729 //Otherwise, the target cycle number when building the next cache will be incorrect
730 _console->LoadState(_rewindCache.back());
731 _curInstructionCycle = _rewindPrevInstructionCycleCache.back();
732
733 _rewindCache.pop_back();
734 _rewindPrevInstructionCycleCache.pop_back();
735
736 //This state is for the instruction we want to stop on, break here.
737 _runToCycle = 0;
738 Step(1);
739 } else {
740 _console->GetRewindManager()->StartRewinding(true);
741 }
742 UpdateProgramCounter(addr, value);
743 _needRewind = false;
744 }
745 ProcessScriptSaveState(addr, value);
746
747 _currentReadAddr = &addr;
748 _currentReadValue = &value;
749 }
750
751 //Check if a breakpoint has been hit and freeze execution if one has
752 bool breakDone = false;
753 AddressTypeInfo addressInfo;
754 GetAbsoluteAddressAndType(addr, &addressInfo);
755 int32_t absoluteAddr = addressInfo.Type == AddressType::PrgRom ? addressInfo.Address : -1;
756 int32_t absoluteRamAddr = addressInfo.Type == AddressType::WorkRam ? addressInfo.Address : -1;
757
758 if(addressInfo.Address >= 0 && type != MemoryOperationType::DummyRead && type != MemoryOperationType::DummyWrite && _runToCycle == 0) {
759 //Ignore dummy read/writes and do not change counters while using the step back feature
760 if(type == MemoryOperationType::Write && CheckFlag(DebuggerFlags::IgnoreRedundantWrites)) {
761 if(_memoryManager->DebugRead(addr) != value) {
762 _memoryAccessCounter->ProcessMemoryAccess(addressInfo, type, _cpu->GetCycleCount());
763 }
764 } else {
765 if(_memoryAccessCounter->ProcessMemoryAccess(addressInfo, type, _cpu->GetCycleCount())) {
766 if(!_breakOnFirstCycle && _enableBreakOnUninitRead && CheckFlag(DebuggerFlags::BreakOnUninitMemoryRead)) {
767 //Break on uninit memory read
768 Step(1);
769 breakDone = SleepUntilResume(BreakSource::BreakOnUninitMemoryRead, 0, BreakpointType::Global, operationInfo.Address, (uint8_t)operationInfo.Value, operationInfo.OperationType);
770 }
771 }
772 }
773 }
774
775 if(absoluteAddr >= 0) {
776 if(type == MemoryOperationType::ExecOperand) {
777 _codeDataLogger->SetFlag(absoluteAddr, CdlPrgFlags::Code);
778 } else if(type == MemoryOperationType::Read) {
779 _codeDataLogger->SetFlag(absoluteAddr, CdlPrgFlags::Data);
780 if(isDmcRead) {
781 _codeDataLogger->SetFlag(absoluteAddr, CdlPrgFlags::PcmData);
782 }
783 }
784 } else if(addr < 0x2000 || absoluteRamAddr >= 0) {
785 if(type == MemoryOperationType::Write) {
786 _disassembler->InvalidateCache(addressInfo);
787 }
788 }
789
790 if(type == MemoryOperationType::ExecOpCode) {
791 _opCodeCycle = 0;
792 _prevInstructionCycle = _curInstructionCycle;
793 _curInstructionCycle = _cpu->GetCycleCount();
794
795 _disassembler->BuildCache(addressInfo, addr, false, true);
796
797 if(absoluteAddr >= 0) {
798 _codeDataLogger->SetFlag(absoluteAddr, CdlPrgFlags::Code);
799 }
800
801 if(_disassembler->IsJump(value)) {
802 uint16_t targetPc = _disassembler->GetDisassemblyInfo(addressInfo).GetJumpDestination(_cpu->GetPC(), _memoryManager.get());
803 AddressTypeInfo addressInfo;
804 GetAbsoluteAddressAndType(targetPc, &addressInfo);
805 if(addressInfo.Address >= 0 && addressInfo.Type == AddressType::PrgRom) {
806 if(value == 0x20) {
807 //JSR, mark target as a sub entry point
808 _disassembler->BuildCache(addressInfo, targetPc, true, false);
809 _functionEntryPoints.emplace(addressInfo.Address);
810 _codeDataLogger->SetFlag(addressInfo.Address, CdlPrgFlags::SubEntryPoint);
811 } else {
812 //Only mark as jump target if not marked as sub entry point
813 _codeDataLogger->SetFlag(addressInfo.Address, CdlPrgFlags::JumpTarget);
814 }
815 }
816 }
817
818 ProcessStepConditions(addr);
819
820 _performanceTracker->ProcessCpuExec(addressInfo);
821 _profiler->ProcessInstructionStart(absoluteAddr);
822
823 BreakSource breakSource = BreakSource::Unspecified;
824 if(value == 0 && CheckFlag(DebuggerFlags::BreakOnBrk)) {
825 Step(1);
826 breakSource = BreakSource::BreakOnBrk;
827 } else if(CheckFlag(DebuggerFlags::BreakOnUnofficialOpCode) && _disassembler->IsUnofficialOpCode(value)) {
828 Step(1);
829 breakSource = BreakSource::BreakOnUnofficialOpCode;
830 }
831
832 if(_runToCycle != 0) {
833 if(_cpu->GetCycleCount() >= _runToCycle) {
834 //Step back operation is done, revert RewindManager's state & break debugger
835 _console->GetRewindManager()->StopRewinding(true);
836 _runToCycle = 0;
837 Step(1);
838 } else if(_runToCycle - _cpu->GetCycleCount() < 500) {
839 _rewindCache.push_back(stringstream());
840 _console->SaveState(_rewindCache.back());
841 _rewindPrevInstructionCycleCache.push_back(_prevInstructionCycle);
842 }
843 }
844
845 _lastInstruction = value;
846 breakDone = SleepUntilResume(breakSource);
847
848 if(_codeRunner && !_codeRunner->IsRunning()) {
849 _codeRunner.reset();
850 }
851
852 GetState(&_debugState, false);
853
854 DisassemblyInfo disassemblyInfo;
855 if(_codeRunner && _codeRunner->IsRunning() && addr >= 0x3000 && addr < 0x4000) {
856 disassemblyInfo = _codeRunner->GetDisassemblyInfo(addr);
857 } else {
858 disassemblyInfo = _disassembler->GetDisassemblyInfo(addressInfo);
859 }
860 _traceLogger->Log(_debugState, disassemblyInfo, operationInfo);
861 } else {
862 _opCodeCycle++;
863 _traceLogger->LogNonExec(operationInfo);
864 _profiler->ProcessCycle();
865 }
866
867 if(!breakDone && _stepCycleCount > 0) {
868 _stepCycleCount--;
869 if(_stepCycleCount == 0) {
870 Step(1);
871 breakDone = SleepUntilResume(BreakSource::CpuStep);
872 }
873 }
874
875 BreakpointType breakpointType;
876 switch(type) {
877 default: breakpointType = BreakpointType::Execute; break;
878
879 case MemoryOperationType::DummyRead:
880 case MemoryOperationType::Read: breakpointType = BreakpointType::ReadRam; break;
881
882 case MemoryOperationType::DummyWrite:
883 case MemoryOperationType::Write: breakpointType = BreakpointType::WriteRam; break;
884 }
885
886 //For DMC reads, always break when it happens, rather than at the start (because we can't predict it easily)
887 if(_breakOnFirstCycle && !isDmcRead) {
888 if(type == MemoryOperationType::ExecOpCode && !breakDone) {
889 ProcessAllBreakpoints(operationInfo);
890 } else if(_hasBreakpoint[breakpointType]) {
891 //Process marked breakpoints
892 ProcessBreakpoints(breakpointType, operationInfo, false, true);
893 }
894 } else {
895 if(_hasBreakpoint[breakpointType]) {
896 ProcessBreakpoints(breakpointType, operationInfo, !breakDone, true);
897 }
898 }
899
900 _currentReadAddr = nullptr;
901 _currentReadValue = nullptr;
902
903 if(type == MemoryOperationType::Write) {
904 if(addr >= 0x2000 && addr <= 0x3FFF) {
905 if((addr & 0x07) == 5 || (addr & 0x07) == 6) {
906 GetState(&_debugState, false);
907 AddDebugEvent(DebugEventType::PpuRegisterWrite, addr, value, -1, _debugState.PPU.State.WriteToggle ? 1 : 0);
908 } else {
909 AddDebugEvent(DebugEventType::PpuRegisterWrite, addr, value);
910 }
911 } else if(addr >= 0x4018 && _mapper->IsWriteRegister(addr)) {
912 AddDebugEvent(DebugEventType::MapperRegisterWrite, addr, value);
913 }
914
915 if(_frozenAddresses[addr]) {
916 return false;
917 }
918 } else if(type == MemoryOperationType::Read) {
919 if(addr >= 0x2000 && addr <= 0x3FFF) {
920 AddDebugEvent(DebugEventType::PpuRegisterRead, addr, value);
921 } else if(addr >= 0x4018 && _mapper->IsReadRegister(addr)) {
922 AddDebugEvent(DebugEventType::MapperRegisterRead, addr, value);
923 }
924 } else if(type == MemoryOperationType::ExecOpCode) {
925 UpdateCallstack(_lastInstruction, addr);
926 }
927
928 return true;
929 }
930
SleepUntilResume(BreakSource source,uint32_t breakpointId,BreakpointType bpType,uint16_t bpAddress,uint8_t bpValue,MemoryOperationType bpMemOpType)931 bool Debugger::SleepUntilResume(BreakSource source, uint32_t breakpointId, BreakpointType bpType, uint16_t bpAddress, uint8_t bpValue, MemoryOperationType bpMemOpType)
932 {
933 int32_t stepCount = _stepCount.load();
934 if(stepCount > 0) {
935 _stepCount--;
936 stepCount = _stepCount.load();
937 } else if(stepCount == 0) {
938 //If stepCount was already 0 when we enter the function, it means
939 //Debugger::Suspend() and Debugger::Resume() were called by another thread
940 source = BreakSource::BreakAfterSuspend;
941 }
942
943 //Read both values here since they might change while executing the code below
944 int32_t preventResume = _preventResume;
945 bool breakRequested = _breakRequested;
946
947 if((stepCount == 0 || breakRequested) && !_stopFlag && _suspendCount == 0) {
948 //Break
949 auto lock = _breakLock.AcquireSafe();
950
951 if(preventResume == 0) {
952 _console->GetSoundMixer()->StopAudio();
953 if(source == BreakSource::Unspecified) {
954 source = _breakSource;
955 }
956 _breakSource = BreakSource::Unspecified;
957
958 uint64_t param = (
959 ((uint64_t)breakpointId << 40) |
960 ((uint64_t)bpValue << 32) |
961 ((uint64_t)(bpAddress & 0xFFFF) << 16) |
962 ((uint64_t)((int)bpMemOpType & 0x0F) << 12) |
963 ((uint64_t)(bpType & 0x0F) << 8) |
964 ((uint64_t)source & 0xFF)
965 );
966
967 _console->GetNotificationManager()->SendNotification(ConsoleNotificationType::CodeBreak, (void*)(uint64_t)param);
968
969 ProcessEvent(EventType::CodeBreak);
970 _stepOverAddr = -1;
971 if(CheckFlag(DebuggerFlags::PpuPartialDraw)) {
972 _ppu->DebugSendFrame();
973 }
974 }
975
976 _executionStopped = true;
977 _pausedForDebugHelper = breakRequested;
978 while((((stepCount == 0 || _breakRequested) && _suspendCount == 0) || _preventResume > 0) && !_stopFlag) {
979 std::this_thread::sleep_for(std::chrono::duration<int, std::milli>(10));
980 if(stepCount == 0) {
981 _console->ResetRunTimers();
982 }
983 stepCount = _stepCount.load();
984 }
985 _pausedForDebugHelper = false;
986 _executionStopped = false;
987 return true;
988 }
989 return false;
990 }
991
ProcessVramReadOperation(MemoryOperationType type,uint16_t addr,uint8_t & value)992 void Debugger::ProcessVramReadOperation(MemoryOperationType type, uint16_t addr, uint8_t &value)
993 {
994 PpuAddressTypeInfo addressInfo;
995 _mapper->GetPpuAbsoluteAddressAndType(addr, &addressInfo);
996 _codeDataLogger->SetFlag(addressInfo.Address, type == MemoryOperationType::Read ? CdlChrFlags::Read : CdlChrFlags::Drawn);
997
998 if(_hasBreakpoint[BreakpointType::ReadVram]) {
999 OperationInfo operationInfo{ addr, value, type };
1000 ProcessBreakpoints(BreakpointType::ReadVram, operationInfo, !_breakOnFirstCycle, true);
1001 }
1002 _memoryAccessCounter->ProcessPpuMemoryAccess(addressInfo, type, _cpu->GetCycleCount());
1003 ProcessPpuOperation(addr, value, MemoryOperationType::Read);
1004 }
1005
ProcessVramWriteOperation(uint16_t addr,uint8_t & value)1006 void Debugger::ProcessVramWriteOperation(uint16_t addr, uint8_t &value)
1007 {
1008 PpuAddressTypeInfo addressInfo;
1009 _mapper->GetPpuAbsoluteAddressAndType(addr, &addressInfo);
1010
1011 if(_hasBreakpoint[BreakpointType::WriteVram]) {
1012 OperationInfo operationInfo{ addr, value, MemoryOperationType::Write };
1013 ProcessBreakpoints(BreakpointType::WriteVram, operationInfo, !_breakOnFirstCycle, true);
1014 }
1015 _memoryAccessCounter->ProcessPpuMemoryAccess(addressInfo, MemoryOperationType::Write, _cpu->GetCycleCount());
1016 ProcessPpuOperation(addr, value, MemoryOperationType::Write);
1017 }
1018
GetInstructionProgress(InstructionProgress & state)1019 void Debugger::GetInstructionProgress(InstructionProgress &state)
1020 {
1021 state.OpCode = _lastInstruction;
1022 state.OpCycle = _opCodeCycle;
1023 state.OpMemoryOperationType = _memoryOperationType;
1024 }
1025
GetApuState(ApuState * state)1026 void Debugger::GetApuState(ApuState *state)
1027 {
1028 //Pause the emulation
1029 DebugBreakHelper helper(this);
1030
1031 //Force APU to catch up before we retrieve its state
1032 _apu->Run();
1033
1034 *state = _apu->GetState();
1035 }
1036
GetState(DebugState * state,bool includeMapperInfo)1037 void Debugger::GetState(DebugState *state, bool includeMapperInfo)
1038 {
1039 state->Model = _console->GetModel();
1040 state->ClockRate = _cpu->GetClockRate(_console->GetModel());
1041 _cpu->GetState(state->CPU);
1042 _ppu->GetState(state->PPU);
1043 if(includeMapperInfo) {
1044 state->Cartridge = _mapper->GetState();
1045 state->APU = _apu->GetState();
1046 }
1047 }
1048
SetState(DebugState state)1049 void Debugger::SetState(DebugState state)
1050 {
1051 _cpu->SetState(state.CPU);
1052 _ppu->SetState(state.PPU);
1053 if(state.CPU.PC != _cpu->GetPC()) {
1054 SetNextStatement(state.CPU.PC);
1055 }
1056 }
1057
Break()1058 void Debugger::Break()
1059 {
1060 _breakRequested = true;
1061 }
1062
ResumeFromBreak()1063 void Debugger::ResumeFromBreak()
1064 {
1065 _breakRequested = false;
1066 }
1067
ResetStepState()1068 void Debugger::ResetStepState()
1069 {
1070 _ppuStepCount = -1;
1071 _stepOverAddr = -1;
1072 _stepCycleCount = -1;
1073 _stepCount = -1;
1074 _breakOnScanline = -2;
1075 _stepOut = false;
1076 }
1077
PpuStep(uint32_t count)1078 void Debugger::PpuStep(uint32_t count)
1079 {
1080 ResetStepState();
1081 _ppuStepCount = count;
1082 _breakSource = BreakSource::PpuStep;
1083 }
1084
Step(uint32_t count,BreakSource source)1085 void Debugger::Step(uint32_t count, BreakSource source)
1086 {
1087 //Run CPU for [count] INSTRUCTIONS before breaking again
1088 ResetStepState();
1089 _stepCount = count;
1090 _breakSource = source;
1091 }
1092
StepCycles(uint32_t count)1093 void Debugger::StepCycles(uint32_t count)
1094 {
1095 //Run CPU for [count] CYCLES before breaking again
1096 ResetStepState();
1097 _stepCycleCount = count;
1098 _breakSource = BreakSource::CpuStep;
1099 }
1100
StepOut()1101 void Debugger::StepOut()
1102 {
1103 if(_subReturnAddresses.empty()) {
1104 return;
1105 }
1106
1107 ResetStepState();
1108 _stepOut = true;
1109 _stepOutReturnAddress = _subReturnAddresses.back();
1110 }
1111
StepOver()1112 void Debugger::StepOver()
1113 {
1114 if(_lastInstruction == 0x20 || _lastInstruction == 0x00) {
1115 //We are on a JSR/BRK instruction, need to continue until the following instruction
1116 _stepOverAddr = _cpu->GetPC() + (_lastInstruction == 0x20 ? 3 : 1);
1117 Run();
1118 } else {
1119 //Except for JSR & BRK, StepOver behaves the same as StepTnto
1120 Step(1);
1121 }
1122 }
1123
StepBack()1124 void Debugger::StepBack()
1125 {
1126 if(_runToCycle == 0) {
1127 _runToCycle = _prevInstructionCycle;
1128 _needRewind = true;
1129 Run();
1130 }
1131 }
1132
Run()1133 void Debugger::Run()
1134 {
1135 //Resume execution after a breakpoint has been hit
1136 _ppuStepCount = -1;
1137 _stepCount = -1;
1138 _breakOnScanline = -2;
1139 _stepCycleCount = -1;
1140 _stepOut = false;
1141 }
1142
BreakImmediately(BreakSource source)1143 void Debugger::BreakImmediately(BreakSource source)
1144 {
1145 Step(1);
1146 SleepUntilResume(source);
1147 }
1148
BreakOnScanline(int16_t scanline)1149 void Debugger::BreakOnScanline(int16_t scanline)
1150 {
1151 Run();
1152 _breakOnScanline = scanline;
1153 }
1154
GenerateCodeOutput()1155 void Debugger::GenerateCodeOutput()
1156 {
1157 State cpuState;
1158 _cpu->GetState(cpuState);
1159
1160 _disassemblerOutput.clear();
1161 _disassemblerOutput.reserve(10000);
1162
1163 for(uint32_t i = 0; i < 0x10000; i += 0x100) {
1164 //Merge all sequential ranges into 1 chunk
1165 AddressTypeInfo startInfo, currentInfo, endInfo;
1166 GetAbsoluteAddressAndType(i, &startInfo);
1167 currentInfo = startInfo;
1168 GetAbsoluteAddressAndType(i+0x100, &endInfo);
1169
1170 uint32_t startMemoryAddr = i;
1171 int32_t startAddr, endAddr;
1172
1173 if(startInfo.Address >= 0) {
1174 startAddr = startInfo.Address;
1175 endAddr = startAddr + 0xFF;
1176 while(currentInfo.Type == endInfo.Type && currentInfo.Address + 0x100 == endInfo.Address && i < 0x10000) {
1177 endAddr += 0x100;
1178 currentInfo = endInfo;
1179 i+=0x100;
1180 GetAbsoluteAddressAndType(i + 0x100, &endInfo);
1181 }
1182 _disassemblerOutput += _disassembler->GetCode(startInfo, endAddr, startMemoryAddr, cpuState, _memoryManager, _labelManager);
1183 }
1184 }
1185 }
1186
GetCode(uint32_t & length)1187 const char* Debugger::GetCode(uint32_t &length)
1188 {
1189 string previousCode = _disassemblerOutput;
1190 GenerateCodeOutput();
1191 bool forceRefresh = length == (uint32_t)-1;
1192 length = (uint32_t)_disassemblerOutput.size();
1193 if(!forceRefresh && previousCode.compare(_disassemblerOutput) == 0) {
1194 //Return null pointer if the code is identical to last call
1195 //This avois the UTF8->UTF16 conversion that the UI needs to do
1196 //before comparing the strings
1197 return nullptr;
1198 } else {
1199 return _disassemblerOutput.c_str();
1200 }
1201 }
1202
GetRelativeAddress(uint32_t addr,AddressType type)1203 int32_t Debugger::GetRelativeAddress(uint32_t addr, AddressType type)
1204 {
1205 switch(type) {
1206 case AddressType::InternalRam:
1207 case AddressType::Register:
1208 return addr;
1209
1210 case AddressType::PrgRom:
1211 case AddressType::WorkRam:
1212 case AddressType::SaveRam:
1213 return _mapper->FromAbsoluteAddress(addr, type);
1214 }
1215
1216 return -1;
1217 }
1218
GetRelativePpuAddress(uint32_t addr,PpuAddressType type)1219 int32_t Debugger::GetRelativePpuAddress(uint32_t addr, PpuAddressType type)
1220 {
1221 if(type == PpuAddressType::PaletteRam) {
1222 return 0x3F00 | (addr & 0x1F);
1223 }
1224 return _mapper->FromAbsolutePpuAddress(addr, type);
1225 }
1226
GetAbsoluteAddress(uint32_t addr)1227 int32_t Debugger::GetAbsoluteAddress(uint32_t addr)
1228 {
1229 return _mapper->ToAbsoluteAddress(addr);
1230 }
1231
GetAbsoluteChrAddress(uint32_t addr)1232 int32_t Debugger::GetAbsoluteChrAddress(uint32_t addr)
1233 {
1234 return _mapper->ToAbsoluteChrAddress(addr);
1235 }
1236
SetNextStatement(uint16_t addr)1237 void Debugger::SetNextStatement(uint16_t addr)
1238 {
1239 if(_currentReadAddr) {
1240 _cpu->SetDebugPC(addr);
1241 *_currentReadAddr = addr;
1242 *_currentReadValue = _memoryManager->DebugRead(addr, false);
1243 } else {
1244 //Can't change the address right away (CPU is in the middle of an instruction)
1245 //Address will change after current instruction is done executing
1246 _nextReadAddr = addr;
1247
1248 //Force the current instruction to finish
1249 Step(1);
1250 }
1251 }
1252
GetCallstack(StackFrameInfo * callstackArray,uint32_t & callstackSize)1253 void Debugger::GetCallstack(StackFrameInfo* callstackArray, uint32_t &callstackSize)
1254 {
1255 DebugBreakHelper helper(this);
1256 int i = 0;
1257 for(StackFrameInfo &info : _callstack) {
1258 callstackArray[i] = info;
1259 i++;
1260 }
1261 callstackSize = i;
1262 }
1263
GetFunctionEntryPointCount()1264 int32_t Debugger::GetFunctionEntryPointCount()
1265 {
1266 DebugBreakHelper helper(this);
1267 return (int32_t)_functionEntryPoints.size();
1268 }
1269
GetFunctionEntryPoints(int32_t * entryPoints,int32_t maxCount)1270 void Debugger::GetFunctionEntryPoints(int32_t* entryPoints, int32_t maxCount)
1271 {
1272 DebugBreakHelper helper(this);
1273 int32_t i = 0;
1274 for(auto itt = _functionEntryPoints.begin(); itt != _functionEntryPoints.end(); itt++) {
1275 entryPoints[i] = *itt;
1276 i++;
1277 if(i == maxCount - 1) {
1278 break;
1279 }
1280 }
1281 entryPoints[i] = -1;
1282 }
1283
GetAssembler()1284 shared_ptr<Assembler> Debugger::GetAssembler()
1285 {
1286 return _assembler;
1287 }
1288
GetTraceLogger()1289 shared_ptr<TraceLogger> Debugger::GetTraceLogger()
1290 {
1291 return _traceLogger;
1292 }
1293
GetMemoryDumper()1294 shared_ptr<MemoryDumper> Debugger::GetMemoryDumper()
1295 {
1296 return _memoryDumper;
1297 }
1298
GetMemoryAccessCounter()1299 shared_ptr<MemoryAccessCounter> Debugger::GetMemoryAccessCounter()
1300 {
1301 return _memoryAccessCounter;
1302 }
1303
GetPerformanceTracker()1304 shared_ptr<PerformanceTracker> Debugger::GetPerformanceTracker()
1305 {
1306 return _performanceTracker;
1307 }
1308
IsExecutionStopped()1309 bool Debugger::IsExecutionStopped()
1310 {
1311 return _executionStopped || _console->IsExecutionStopped();
1312 }
1313
IsPauseIconShown()1314 bool Debugger::IsPauseIconShown()
1315 {
1316 return (_executionStopped || _console->IsPaused()) && !CheckFlag(DebuggerFlags::HidePauseIcon) && _preventResume == 0 && !_pausedForDebugHelper;
1317 }
1318
PreventResume()1319 void Debugger::PreventResume()
1320 {
1321 _preventResume++;
1322 }
1323
AllowResume()1324 void Debugger::AllowResume()
1325 {
1326 _preventResume--;
1327 }
1328
GetAbsoluteAddressAndType(uint32_t relativeAddr,AddressTypeInfo * info)1329 void Debugger::GetAbsoluteAddressAndType(uint32_t relativeAddr, AddressTypeInfo* info)
1330 {
1331 return _mapper->GetAbsoluteAddressAndType(relativeAddr, info);
1332 }
1333
GetPpuAbsoluteAddressAndType(uint32_t relativeAddr,PpuAddressTypeInfo * info)1334 void Debugger::GetPpuAbsoluteAddressAndType(uint32_t relativeAddr, PpuAddressTypeInfo* info)
1335 {
1336 return _mapper->GetPpuAbsoluteAddressAndType(relativeAddr, info);
1337 }
1338
UpdatePpuCyclesToProcess()1339 void Debugger::UpdatePpuCyclesToProcess()
1340 {
1341 memset(_proccessPpuCycle, 0, sizeof(_proccessPpuCycle));
1342 for(auto updateCycle : _ppuViewerUpdateCycle) {
1343 int16_t cycle = updateCycle.second >> 9;
1344 if(cycle < 341) {
1345 _proccessPpuCycle[cycle] = true;
1346 }
1347 }
1348 _proccessPpuCycle[0] = true;
1349 }
1350
SetPpuViewerScanlineCycle(int32_t ppuViewerId,int32_t scanline,int32_t cycle)1351 void Debugger::SetPpuViewerScanlineCycle(int32_t ppuViewerId, int32_t scanline, int32_t cycle)
1352 {
1353 DebugBreakHelper helper(this);
1354 _ppuViewerUpdateCycle[ppuViewerId] = (cycle << 9) + scanline;
1355 UpdatePpuCyclesToProcess();
1356 }
1357
ClearPpuViewerSettings(int32_t ppuViewer)1358 void Debugger::ClearPpuViewerSettings(int32_t ppuViewer)
1359 {
1360 DebugBreakHelper helper(this);
1361 _ppuViewerUpdateCycle.erase(ppuViewer);
1362 UpdatePpuCyclesToProcess();
1363 }
1364
SetLastFramePpuScroll(uint16_t addr,uint8_t xScroll,bool updateHorizontalScrollOnly)1365 void Debugger::SetLastFramePpuScroll(uint16_t addr, uint8_t xScroll, bool updateHorizontalScrollOnly)
1366 {
1367 _ppuScrollX = ((addr & 0x1F) << 3) | xScroll | ((addr & 0x400) ? 0x100 : 0);
1368 if(!updateHorizontalScrollOnly) {
1369 _ppuScrollY = (((addr & 0x3E0) >> 2) | ((addr & 0x7000) >> 12)) + ((addr & 0x800) ? 240 : 0);
1370 }
1371 }
1372
GetPpuScroll()1373 uint32_t Debugger::GetPpuScroll()
1374 {
1375 return (_ppuScrollY << 16) | _ppuScrollX;
1376 }
1377
SetFreezeState(uint16_t address,bool frozen)1378 void Debugger::SetFreezeState(uint16_t address, bool frozen)
1379 {
1380 _frozenAddresses[address] = frozen ? 1 : 0;
1381 }
1382
GetFreezeState(uint16_t startAddress,uint16_t length,bool * freezeState)1383 void Debugger::GetFreezeState(uint16_t startAddress, uint16_t length, bool* freezeState)
1384 {
1385 for(uint16_t i = 0; i < length; i++) {
1386 freezeState[i] = _frozenAddresses[startAddress + i] ? true : false;
1387 }
1388 }
1389
StartCodeRunner(uint8_t * byteCode,uint32_t codeLength)1390 void Debugger::StartCodeRunner(uint8_t *byteCode, uint32_t codeLength)
1391 {
1392 _codeRunner.reset(new CodeRunner(vector<uint8_t>(byteCode, byteCode + codeLength), this));
1393 _memoryManager->RegisterIODevice(_codeRunner.get());
1394 _returnToAddress = _cpu->GetDebugPC();
1395 SetNextStatement(CodeRunner::BaseAddress);
1396 }
1397
StopCodeRunner()1398 void Debugger::StopCodeRunner()
1399 {
1400 _memoryManager->UnregisterIODevice(_codeRunner.get());
1401 _memoryManager->RegisterIODevice(_ppu.get());
1402
1403 //Break debugger when code has finished executing
1404 SetNextStatement(_returnToAddress);
1405
1406 if(_console->GetSettings()->CheckFlag(EmulationFlags::DebuggerWindowEnabled)) {
1407 Step(1);
1408 } else {
1409 Run();
1410 }
1411 }
1412
GetNesHeader(uint8_t * header)1413 void Debugger::GetNesHeader(uint8_t* header)
1414 {
1415 NESHeader nesHeader = _mapper->GetRomInfo().NesHeader;
1416 memcpy(header, &nesHeader, sizeof(NESHeader));
1417 }
1418
SaveRomToDisk(string filename,bool saveAsIps,uint8_t * header,CdlStripFlag cdlStripflag)1419 void Debugger::SaveRomToDisk(string filename, bool saveAsIps, uint8_t* header, CdlStripFlag cdlStripflag)
1420 {
1421 vector<uint8_t> fileData;
1422 _mapper->GetRomFileData(fileData, saveAsIps, header);
1423
1424 _codeDataLogger->StripData(fileData.data() + sizeof(NESHeader), cdlStripflag);
1425
1426 ofstream file(filename, ios::out | ios::binary);
1427 if(file.good()) {
1428 file.write((char*)fileData.data(), fileData.size());
1429 file.close();
1430 }
1431 }
1432
RevertPrgChrChanges()1433 void Debugger::RevertPrgChrChanges()
1434 {
1435 DebugBreakHelper helper(this);
1436 _mapper->RevertPrgChrChanges();
1437 _disassembler->Reset();
1438 UpdateCdlCache();
1439 }
1440
HasPrgChrChanges()1441 bool Debugger::HasPrgChrChanges()
1442 {
1443 return _mapper->HasPrgChrChanges();
1444 }
1445
FindSubEntryPoint(uint16_t relativeAddress)1446 int32_t Debugger::FindSubEntryPoint(uint16_t relativeAddress)
1447 {
1448 AddressTypeInfo info;
1449 int32_t address = relativeAddress;
1450 do {
1451 GetAbsoluteAddressAndType(address, &info);
1452 if(info.Address < 0 || info.Type != AddressType::PrgRom || _codeDataLogger->IsData(info.Address)) {
1453 break;
1454 }
1455 address--;
1456 if(_codeDataLogger->IsSubEntryPoint(info.Address)) {
1457 break;
1458 }
1459 } while(address >= 0);
1460
1461 return address > relativeAddress ? relativeAddress : (address + 1);
1462 }
1463
SetInputOverride(uint8_t port,uint32_t state)1464 void Debugger::SetInputOverride(uint8_t port, uint32_t state)
1465 {
1466 _inputOverride[port] = state;
1467 }
1468
LoadScript(string name,string content,int32_t scriptId)1469 int Debugger::LoadScript(string name, string content, int32_t scriptId)
1470 {
1471 DebugBreakHelper helper(this);
1472 auto lock = _scriptLock.AcquireSafe();
1473
1474 if(scriptId < 0) {
1475 shared_ptr<ScriptHost> script(new ScriptHost(_nextScriptId++));
1476 script->LoadScript(name, content, this);
1477 _scripts.push_back(script);
1478 _hasScript = true;
1479 return script->GetScriptId();
1480 } else {
1481 auto result = std::find_if(_scripts.begin(), _scripts.end(), [=](shared_ptr<ScriptHost> &script) {
1482 return script->GetScriptId() == scriptId;
1483 });
1484 if(result != _scripts.end()) {
1485 //Send a ScriptEnded event before reloading the code
1486 (*result)->ProcessEvent(EventType::ScriptEnded);
1487
1488 (*result)->LoadScript(name, content, this);
1489 return scriptId;
1490 }
1491 }
1492
1493 return -1;
1494 }
1495
RemoveScript(int32_t scriptId)1496 void Debugger::RemoveScript(int32_t scriptId)
1497 {
1498 DebugBreakHelper helper(this);
1499 auto lock = _scriptLock.AcquireSafe();
1500 _scripts.erase(std::remove_if(_scripts.begin(), _scripts.end(), [=](const shared_ptr<ScriptHost>& script) {
1501 if(script->GetScriptId() == scriptId) {
1502 //Send a ScriptEnded event before unloading the script
1503 script->ProcessEvent(EventType::ScriptEnded);
1504 _console->GetDebugHud()->ClearScreen();
1505 return true;
1506 }
1507 return false;
1508 }), _scripts.end());
1509 _hasScript = _scripts.size() > 0;
1510 }
1511
GetScriptLog(int32_t scriptId)1512 const char* Debugger::GetScriptLog(int32_t scriptId)
1513 {
1514 auto lock = _scriptLock.AcquireSafe();
1515 for(shared_ptr<ScriptHost> &script : _scripts) {
1516 if(script->GetScriptId() == scriptId) {
1517 return script->GetLog();
1518 }
1519 }
1520 return "";
1521 }
1522
ResetCounters()1523 void Debugger::ResetCounters()
1524 {
1525 //This is called when loading a state (among other things)
1526 //Prevent counter reset when using step back (_runToCycle != 0), because step back will load a state
1527 if(_runToCycle == 0) {
1528 _memoryAccessCounter->ResetCounts();
1529 }
1530 _profiler->Reset();
1531 }
1532
UpdateProgramCounter(uint16_t & addr,uint8_t & value)1533 void Debugger::UpdateProgramCounter(uint16_t &addr, uint8_t &value)
1534 {
1535 addr = _cpu->GetPC();
1536 value = _memoryManager->DebugRead(addr, true);
1537 _cpu->SetDebugPC(addr);
1538 }
1539
ProcessScriptSaveState(uint16_t & addr,uint8_t & value)1540 void Debugger::ProcessScriptSaveState(uint16_t &addr, uint8_t &value)
1541 {
1542 if(_hasScript) {
1543 for(shared_ptr<ScriptHost> &script : _scripts) {
1544 if(script->ProcessSavestate()) {
1545 //Adjust PC and current addr/value if a state was loaded due to a call to loadSavestateAsync
1546 UpdateProgramCounter(addr, value);
1547 }
1548 }
1549 }
1550 }
1551
ProcessCpuOperation(uint16_t & addr,uint8_t & value,MemoryOperationType type)1552 void Debugger::ProcessCpuOperation(uint16_t &addr, uint8_t &value, MemoryOperationType type)
1553 {
1554 if(_hasScript) {
1555 for(shared_ptr<ScriptHost> &script : _scripts) {
1556 script->ProcessCpuOperation(addr, value, type);
1557 if(type == MemoryOperationType::ExecOpCode && script->CheckStateLoadedFlag()) {
1558 //Adjust PC and current addr/value when a state was loaded during a CpuExec callback
1559 UpdateProgramCounter(addr, value);
1560 }
1561 }
1562 }
1563 }
1564
ProcessPpuOperation(uint16_t addr,uint8_t & value,MemoryOperationType type)1565 void Debugger::ProcessPpuOperation(uint16_t addr, uint8_t &value, MemoryOperationType type)
1566 {
1567 if(_hasScript) {
1568 for(shared_ptr<ScriptHost> &script : _scripts) {
1569 script->ProcessPpuOperation(addr, value, type);
1570 }
1571 }
1572 }
1573
ProcessEvent(EventType type)1574 void Debugger::ProcessEvent(EventType type)
1575 {
1576 if(_hasScript) {
1577 for(shared_ptr<ScriptHost> &script : _scripts) {
1578 script->ProcessEvent(type);
1579 }
1580 }
1581
1582 if(type == EventType::InputPolled) {
1583 for(int i = 0; i < 4; i++) {
1584 if(_inputOverride[i] != 0) {
1585 shared_ptr<StandardController> controller = std::dynamic_pointer_cast<StandardController>(_console->GetControlManager()->GetControlDevice(i));
1586 if(controller) {
1587 controller->SetBitValue(StandardController::Buttons::A, (_inputOverride[i] & 0x01) != 0);
1588 controller->SetBitValue(StandardController::Buttons::B, (_inputOverride[i] & 0x02) != 0);
1589 controller->SetBitValue(StandardController::Buttons::Select, (_inputOverride[i] & 0x04) != 0);
1590 controller->SetBitValue(StandardController::Buttons::Start, (_inputOverride[i] & 0x08) != 0);
1591 controller->SetBitValue(StandardController::Buttons::Up, (_inputOverride[i] & 0x10) != 0);
1592 controller->SetBitValue(StandardController::Buttons::Down, (_inputOverride[i] & 0x20) != 0);
1593 controller->SetBitValue(StandardController::Buttons::Left, (_inputOverride[i] & 0x40) != 0);
1594 controller->SetBitValue(StandardController::Buttons::Right, (_inputOverride[i] & 0x80) != 0);
1595 }
1596 }
1597 }
1598 } else if(type == EventType::EndFrame) {
1599 _performanceTracker->ProcessEndOfFrame();
1600 _memoryDumper->GatherChrPaletteInfo();
1601 } else if(type == EventType::StartFrame) {
1602 //Update the event viewer
1603 _console->GetNotificationManager()->SendNotification(ConsoleNotificationType::EventViewerDisplayFrame);
1604
1605 //Clear the current frame/event log
1606 if(CheckFlag(DebuggerFlags::PpuPartialDraw)) {
1607 _ppu->DebugUpdateFrameBuffer(CheckFlag(DebuggerFlags::PpuShowPreviousFrame));
1608 }
1609 _prevDebugEvents = _debugEvents;
1610 _debugEvents.clear();
1611 } else if(type == EventType::Nmi) {
1612 AddDebugEvent(DebugEventType::Nmi);
1613 } else if(type == EventType::Irq) {
1614 AddDebugEvent(DebugEventType::Irq);
1615 } else if(type == EventType::SpriteZeroHit) {
1616 AddDebugEvent(DebugEventType::SpriteZeroHit);
1617 } else if(type == EventType::Reset) {
1618 _enableBreakOnUninitRead = true;
1619 }
1620 }
1621
AddDebugEvent(DebugEventType type,uint16_t address,uint8_t value,int16_t breakpointId,int8_t ppuLatch)1622 void Debugger::AddDebugEvent(DebugEventType type, uint16_t address, uint8_t value, int16_t breakpointId, int8_t ppuLatch)
1623 {
1624 _debugEvents.push_back({
1625 (uint16_t)_ppu->GetCurrentCycle(),
1626 (int16_t)_ppu->GetCurrentScanline(),
1627 _cpu->GetDebugPC(),
1628 address,
1629 breakpointId,
1630 type,
1631 value,
1632 ppuLatch,
1633 });
1634 }
1635
GetDebugEvents(uint32_t * pictureBuffer,DebugEventInfo * infoArray,uint32_t & maxEventCount,bool returnPreviousFrameData)1636 void Debugger::GetDebugEvents(uint32_t* pictureBuffer, DebugEventInfo *infoArray, uint32_t &maxEventCount, bool returnPreviousFrameData)
1637 {
1638 DebugBreakHelper helper(this);
1639
1640 uint16_t *ppuBuffer = new uint16_t[PPU::PixelCount];
1641 uint32_t *palette = _console->GetSettings()->GetRgbPalette();
1642 _ppu->DebugCopyOutputBuffer(ppuBuffer);
1643
1644 for(int i = 0; i < PPU::PixelCount; i++) {
1645 pictureBuffer[i] = palette[ppuBuffer[i] & 0x3F];
1646 }
1647
1648 delete[] ppuBuffer;
1649
1650 vector<DebugEventInfo> &events = returnPreviousFrameData ? _prevDebugEvents : _debugEvents;
1651 uint32_t eventCount = std::min(maxEventCount, (uint32_t)events.size());
1652 memcpy(infoArray, events.data(), eventCount * sizeof(DebugEventInfo));
1653 maxEventCount = eventCount;
1654 }
1655
GetDebugEventCount(bool returnPreviousFrameData)1656 uint32_t Debugger::GetDebugEventCount(bool returnPreviousFrameData)
1657 {
1658 DebugBreakHelper helper(this);
1659 return (uint32_t)(returnPreviousFrameData ? _prevDebugEvents.size() : _debugEvents.size());
1660 }
1661
GetScreenPixel(uint8_t x,uint8_t y)1662 uint32_t Debugger::GetScreenPixel(uint8_t x, uint8_t y)
1663 {
1664 return _ppu->GetPixel(x, y);
1665 }
1666
AddTrace(const char * log)1667 void Debugger::AddTrace(const char* log)
1668 {
1669 _traceLogger->LogExtraInfo(log, _cpu->GetCycleCount());
1670 }