From 97ac46c82ab2f41b75b9b5ca758a0495517dd140 Mon Sep 17 00:00:00 2001 From: Nik Henson Date: Wed, 29 Jun 2011 23:21:50 +0000 Subject: [PATCH] Changes to debugger classes: - Added CPrintBreakpoint Changes to console-based debugger: - Added new scp command and tidied up some other commands - Tidied up event logging - Sorted commands into common groups - Added ReadMe.txt help file --- Src/Debugger/Breakpoint.cpp | 24 + Src/Debugger/Breakpoint.h | 14 + Src/Debugger/CPUDebug.cpp | 36 +- Src/Debugger/CPUDebug.h | 8 + Src/Debugger/ConsoleDebugger.cpp | 1613 +++++++++++++++------------ Src/Debugger/ConsoleDebugger.h | 14 +- Src/Debugger/Debugger.cpp | 12 +- Src/Debugger/Debugger.h | 14 +- Src/Debugger/ReadMe.txt | 396 +++++++ Src/Debugger/SupermodelDebugger.cpp | 31 +- Src/Debugger/Watch.cpp | 8 +- Src/Debugger/Watch.h | 1 + 12 files changed, 1422 insertions(+), 749 deletions(-) create mode 100755 Src/Debugger/ReadMe.txt diff --git a/Src/Debugger/Breakpoint.cpp b/Src/Debugger/Breakpoint.cpp index 37b1518..7720cd4 100644 --- a/Src/Debugger/Breakpoint.cpp +++ b/Src/Debugger/Breakpoint.cpp @@ -1,5 +1,6 @@ #ifdef SUPERMODEL_DEBUGGER +#include "CPUDebug.h" #include "Breakpoint.h" #include @@ -58,6 +59,29 @@ namespace Debugger sprintf(str, "%d / %d", m_counter, m_count); return true; } + + CPrintBreakpoint::CPrintBreakpoint(CCPUDebug *bpCPU, int bpAddr) : CBreakpoint(bpCPU, bpAddr, 'P', "print") + { + // + } + + bool CPrintBreakpoint::CheckBreak(UINT32 pc, UINT32 opcode) + { + char addrStr[50]; + cpu->FormatAddress(addrStr, addr, true); + cpu->debugger->PrintEvent(cpu, "Breakpoint #%u hit at %s.\n", num, addrStr); + return false; + } + + void CPrintBreakpoint::Reset() + { + // + } + + bool CPrintBreakpoint::GetInfo(char *str) + { + return false; + } } #endif // SUPERMODEL_DEBUGGER diff --git a/Src/Debugger/Breakpoint.h b/Src/Debugger/Breakpoint.h index 7c0dc5e..3ba7669 100644 --- a/Src/Debugger/Breakpoint.h +++ b/Src/Debugger/Breakpoint.h @@ -21,6 +21,8 @@ namespace Debugger const char symbol; const char *type; + unsigned num; + bool active; bool Check(UINT32 pc, UINT32 opcode); @@ -64,6 +66,18 @@ namespace Debugger bool GetInfo(char *str); }; + class CPrintBreakpoint : public CBreakpoint + { + public: + CPrintBreakpoint(CCPUDebug *bpCPU, int bpAddr); + + bool CheckBreak(UINT32 pc, UINT32 opcode); + + void Reset(); + + bool GetInfo(char *str); + }; + //class CConditionBreakpoint : public CBrekapoint //{ // // TODO diff --git a/Src/Debugger/CPUDebug.cpp b/Src/Debugger/CPUDebug.cpp index 6899dda..7bf699b 100644 --- a/Src/Debugger/CPUDebug.cpp +++ b/Src/Debugger/CPUDebug.cpp @@ -777,12 +777,14 @@ namespace Debugger if (io->watch != NULL) RemoveWatch(io->watch); ioWatches.push_back(watch); + UpdateIOWatchNums(); io->watch = watch; } else { RemoveMemWatch(watch->addr, watch->size); memWatches.push_back(watch); + UpdateMemWatchNums(); if (m_memWatchTable == NULL) m_memWatchTable = new CAddressTable(); m_memWatchTable->Add(watch); @@ -799,6 +801,7 @@ namespace Debugger if (it == ioWatches.end()) return false; ioWatches.erase(it); + UpdateIOWatchNums(); } else { @@ -806,6 +809,7 @@ namespace Debugger if (it == memWatches.end()) return false; memWatches.erase(it); + UpdateMemWatchNums(); m_memWatchTable->Remove(watch); if (m_memWatchTable->IsEmpty()) { @@ -817,6 +821,20 @@ namespace Debugger return true; } + void CCPUDebug::UpdateIOWatchNums() + { + unsigned num = 0; + for (vector::iterator it = ioWatches.begin(); it != ioWatches.end(); it++) + (*it)->num = num++; + } + + void CCPUDebug::UpdateMemWatchNums() + { + unsigned num = 0; + for (vector::iterator it = memWatches.begin(); it != memWatches.end(); it++) + (*it)->num = num++; + } + CSimpleBreakpoint *CCPUDebug::AddSimpleBreakpoint(UINT32 addr) { CSimpleBreakpoint *bp = new CSimpleBreakpoint(this, addr); @@ -829,7 +847,14 @@ namespace Debugger CCountBreakpoint *bp = new CCountBreakpoint(this, addr, count); AddBreakpoint(bp); return bp; - } + } + + CPrintBreakpoint *CCPUDebug::AddPrintBreakpoint(UINT32 addr) + { + CPrintBreakpoint *bp = new CPrintBreakpoint(this, addr); + AddBreakpoint(bp); + return bp; + } CBreakpoint *CCPUDebug::GetBreakpoint(UINT32 addr) { @@ -842,6 +867,7 @@ namespace Debugger { RemoveBreakpoint(bp->addr); bps.push_back(bp); + UpdateBreakpointNums(); if (m_bpTable == NULL) m_bpTable = new CAddressTable(); m_bpTable->Add(bp); @@ -861,6 +887,7 @@ namespace Debugger if (it == bps.end()) return false; bps.erase(it); + UpdateBreakpointNums(); m_bpTable->Remove(bp); if (m_bpTable->IsEmpty()) { @@ -883,6 +910,13 @@ namespace Debugger return true; } + void CCPUDebug::UpdateBreakpointNums() + { + unsigned num = 0; + for (vector::iterator it = bps.begin(); it != bps.end(); it++) + (*it)->num = num++; + } + CRegMonitor *CCPUDebug::AddRegMonitor(const char *regName) { RemoveRegMonitor(regName); diff --git a/Src/Debugger/CPUDebug.h b/Src/Debugger/CPUDebug.h index a3b084a..0c2963f 100644 --- a/Src/Debugger/CPUDebug.h +++ b/Src/Debugger/CPUDebug.h @@ -411,6 +411,10 @@ namespace Debugger bool RemoveWatch(CWatch *watch); + void UpdateIOWatchNums(); + + void UpdateMemWatchNums(); + // // Breakpoint handling // @@ -419,6 +423,8 @@ namespace Debugger CCountBreakpoint *AddCountBreakpoint(UINT32 addr, unsigned count); + CPrintBreakpoint *AddPrintBreakpoint(UINT32 addr); + void AddBreakpoint(CBreakpoint *bp); CBreakpoint *GetBreakpoint(UINT32 addr); @@ -429,6 +435,8 @@ namespace Debugger bool RemoveAllBreakpoints(); + void UpdateBreakpointNums(); + // // Register monitor handling // diff --git a/Src/Debugger/ConsoleDebugger.cpp b/Src/Debugger/ConsoleDebugger.cpp index 953c71d..ae8c337 100644 --- a/Src/Debugger/ConsoleDebugger.cpp +++ b/Src/Debugger/ConsoleDebugger.cpp @@ -159,14 +159,12 @@ namespace Debugger char portNumStr[50]; char dataStr[50]; char mod[10]; + EFormat fmt; - if (CheckToken(token, "x", "exit")) // exit - { - Print("Exiting...\n"); - SetExit(); - return true; - } - else if (CheckToken(token, "n", "next")) // next [=1] + // + // Execution + // + if (CheckToken(token, "n", "next")) // next [=1] { // Parse arguments token = strtok(NULL, " "); @@ -228,7 +226,7 @@ namespace Debugger token = strtok(NULL, " "); if (token != NULL) { - if (!ParseAddress(m_cpu, token, &addr)) + if (!ParseAddress(token, &addr)) { Error("Enter a valid address.\n"); return false; @@ -242,6 +240,34 @@ namespace Debugger m_cpu->SetContinue(); return true; } + else if (CheckToken(token, "spc", "setpc")) // setpc + { + // Parse arguments + token = strtok(NULL, " "); + if (token == NULL) + { + Error("Missing address.\n"); + return false; + } + if (!ParseAddress(token, &addr)) + { + Error("Enter a valid address.\n"); + return false; + } + + if (!m_cpu->SetPC(addr)) + { + Error("Unable to set PC.\n"); + return false; + } + + m_cpu->FormatAddress(addrStr, addr); + Print("PC set to %s.\n", addrStr); + return true; + } + // + // CPUs + // else if (CheckToken(token, "lc", "listcpus")) // listcpus { ListCPUs(); @@ -297,10 +323,81 @@ namespace Debugger cpu->enabled = true; Print("Enabled debugging on CPU %s.\n", cpu->name); } + // + // Registers + // else if (CheckToken(token, "lr", "listregisters")) // listregisters { ListRegisters(); } + else if (CheckToken(token, "pr", "printregister")) // printregister + { + // Parse arguments + token = strtok(NULL, " "); + CRegister *reg; + if (!ParseRegister(token, reg)) + return false; + + reg->GetValue(dataStr); + Print("Register %s = %s\n", reg->name, dataStr); + } + else if (CheckToken(token, "sr", "setregister")) // setregister + { + // Parse arguments + token = strtok(NULL, " "); + CRegister *reg; + if (!ParseRegister(token, reg)) + return false; + token = strtok(NULL, " "); + if (token == NULL) + { + Error("Missing value to set.\n"); + return false; + } + + if (!reg->SetValue(token)) + { + Error("Unable to set value of register %s.\n", reg->name); + return false; + } + reg->GetValue(dataStr); + Print("Set register %s to %s.\n", reg->name, dataStr); + } + else if (CheckToken(token, "lm", "listmonitors")) // listmonitors + { + ListMonitors(); + } + else if (CheckToken(token, "m", "monitor") || // addmonitor + CheckToken(token, "am", "addmonitor")) + { + // Parse arguments + token = strtok(NULL, " "); + CRegister *reg; + if (!ParseRegister(token, reg)) + return false; + + m_cpu->AddRegMonitor(reg->name); + Print("Monitor added to register %s.\n", reg->name); + } + else if (CheckToken(token, "rm", "removemonitor")) // removemonitor + { + // Parse arguments + token = strtok(NULL, " "); + CRegister *reg; + if (!ParseRegister(token, reg)) + return false; + + m_cpu->RemoveRegMonitor(reg->name); + Print("Monitor for register %s removed.\n", reg->name); + } + else if (CheckToken(token, "ram", "removeallmonitors")) // removeallmonitors + { + m_cpu->RemoveAllRegMonitors(); + Print("All register monitors removed.\n"); + } + // + // Exceptions & interrupts + // else if (CheckToken(token, "le", "listexceptions")) // listexceptions { ListExceptions(); @@ -309,193 +406,6 @@ namespace Debugger { ListInterrupts(); } - else if (CheckToken(token, "lo", "listios")) // listios - { - ListIOs(); - } - else if (CheckToken(token, "ln", "listregions")) // listregions - { - ListRegions(); - } - else if (CheckToken(token, "ll", "listlabels")) // listlabels [(d)efault|(c)ustom|(a)utos|(e)ntrypoints|e(x)cephandlers|(i)interhandlers|(j)umptargets|(l)ooppoints] - { - // Parse arguments - token = strtok(NULL, " "); - bool customLabels; - ELabelFlags labelFlags; - if (token == NULL || CheckToken(token, "d", "default")) - { - customLabels = true; - labelFlags = (ELabelFlags)(LFEntryPoint | LFExcepHandler | LFInterHandler | LFSubroutine); - } - else if (CheckToken(token, "c", "custom")) - { - customLabels = true; - labelFlags = LFNone; - } - else if (CheckToken(token, "a", "autos")) - { - customLabels = true; - labelFlags = (ELabelFlags)(LFEntryPoint | LFExcepHandler | LFInterHandler | LFSubroutine | LFJumpTarget | LFLoopPoint); - } - else if (CheckToken(token, "e", "entrypoints")) - { - customLabels = false; - labelFlags = LFEntryPoint; - } - else if (CheckToken(token, "x", "excephandlers")) - { - customLabels = false; - labelFlags = LFExcepHandler; - } - else if (CheckToken(token, "i", "interhandlers")) - { - customLabels = false; - labelFlags = LFInterHandler; - } - else if (CheckToken(token, "s", "subroutines")) - { - customLabels = false; - labelFlags = LFSubroutine; - } - else if (CheckToken(token, "j", "jumptargets")) - { - customLabels = false; - labelFlags = LFJumpTarget; - } - else if (CheckToken(token, "l", "looppoints")) - { - customLabels = false; - labelFlags = LFLoopPoint; - } - else - { - Error("Enter a valid filter (a)ll, (c)ustom, (e)ntrypoints, e(x)cephandlers, (i)interhandlers, (j)umptargets or (l)ooppoints.\n"); - return false; - } - - ListLabels(customLabels, labelFlags); - } - else if (CheckToken(token, "al", "addlabel")) // addlabel - { - // Parse arguments - token = strtok(NULL, " "); - if (token == NULL) - { - Error("Missing label address.\n"); - return false; - } - else if (!ParseAddress(m_cpu, token, &addr)) - { - Error("Enter a valid label address.\n"); - return false; - } - - token = strtok(NULL, " "); - if (token == NULL) - { - Error("Missing label name.\n"); - return false; - } - const char *name = token; - - // Add label - CLabel *label = m_cpu->AddLabel(addr, name); - m_cpu->FormatAddress(addrStr, label->addr); - Print("Label '%s' added at %s.\n", label->name, addrStr); - } - else if (CheckToken(token, "rl", "removelabel")) // removelabel [|] - { - // Parse arguments - token = strtok(NULL, " "); - if (token == NULL) - token = "-"; - - if (!ParseAddress(m_cpu, token, &addr)) - { - Error("Enter a valid label name or address.\n"); - return false; - } - - CLabel *label = m_cpu->GetLabel(addr); - if (label == NULL) - { - m_cpu->FormatAddress(addrStr, addr); - Error("No label at %s.\n", addrStr); - return false; - } - - const char *name = label->name; - m_cpu->FormatAddress(addrStr, label->addr); - Print("Custom label '%s' removed at address %s.\n", name, addrStr); - } - else if (CheckToken(token, "ral", "removealllabels")) // removealllabels - { - m_cpu->RemoveAllLabels(); - Print("All custom labels removed.\m"); - } - else if (CheckToken(token, "ac", "addcomment")) // addcomment - { - // Parse arguments - token = strtok(NULL, " "); - if (token == NULL) - { - Error("Missing comment address.\n"); - return false; - } - else if (!ParseAddress(m_cpu, token, &addr)) - { - Error("Enter a valid comment address.\n"); - return false; - } - - char text[255]; - text[0] = '\0'; - token = strtok(NULL, " "); - while (token != NULL) - { - size_t len = strlen(text); - if (len + strlen(token) > 253) - break; - if (len > 0) - strcat(text, " "); - strcat(text, token); - token = strtok(NULL, " "); - } - if (text[0] == '\0') - { - Error("Missing comment text.\n"); - return false; - } - - // Add comment - CComment *comment = m_cpu->AddComment(addr, text); - m_cpu->FormatAddress(addrStr, comment->addr); - Print("Comment added at %s.\n", addrStr); - } - else if (CheckToken(token, "rc", "removecomment")) // removecomment [] - { - // Parse arguments - token = strtok(NULL, " "); - if (token == NULL) - token = "-"; - if (!ParseAddress(m_cpu, token, &addr)) - { - Error("Enter a valid comment address.\n"); - return false; - } - - m_cpu->FormatAddress(addrStr, addr); - if (m_cpu->RemoveComment(addr)) - Print("Comment at address %s removed.\n", addrStr); - else - Error("No comment at address %s.\n", addrStr); - } - else if (CheckToken(token, "rac", "removeallcomments")) // removeallcomments - { - m_cpu->RemoveAllComments(); - Print("All comments removed.\n"); - } else if (CheckToken(token, "t", "trap") || // addtrap ((e)xception|(i)nterrupt) CheckToken(token, "at", "addtrap")) { @@ -642,6 +552,465 @@ namespace Debugger } Print("All traps for %s removed.\n", coverage); } + // + // Disassembly, labels & comments + // + else if (CheckToken(token, "l", "list") || // listdisassembly [=last [#=20|]] + CheckToken(token, "ld", "listdisassembly")) + { + // Get start address + UINT32 start; + token = strtok(NULL, " "); + if (token == NULL) + token = "last"; + if (stricmp(token, "last") == 0) + { + // Use end of last listing + start = m_listDism; + } + else if (!ParseAddress(token, &start)) + { + Error("Enter a valid start address.\n"); + return false; + } + + // Get end address + UINT32 end; + unsigned numInstrs; + token = strtok(NULL, " "); + if (token != NULL) + { + if (token[0] == '#') + { + if (!ParseInt(token + 1, &number) || number <= 0) + { + Error("Enter a valid number of instructions.\n"); + return false; + } + numInstrs = (unsigned)number; + end = 0xFFFFFFFF; + } + else + { + if (!ParseAddress(token, &end)) + { + Error("Enter a valid end address.\n"); + return false; + } + numInstrs = 0xFFFFFFFF; + } + } + else + { + // Default is 20 instructions after start + end = 0xFFFFFFFF; + numInstrs = 20; + } + + // List the disassembled code + m_listDism = ListDisassembly(start, end, numInstrs); + } + else if (CheckToken(token, "ll", "listlabels")) // listlabels [(d)efault|(c)ustom|(a)utos|(e)ntrypoints|e(x)cephandlers|(i)interhandlers|(j)umptargets|(l)ooppoints] + { + // Parse arguments + token = strtok(NULL, " "); + bool customLabels; + ELabelFlags labelFlags; + if (token == NULL || CheckToken(token, "d", "default")) + { + customLabels = true; + labelFlags = (ELabelFlags)(LFEntryPoint | LFExcepHandler | LFInterHandler | LFSubroutine); + } + else if (CheckToken(token, "c", "custom")) + { + customLabels = true; + labelFlags = LFNone; + } + else if (CheckToken(token, "a", "autos")) + { + customLabels = true; + labelFlags = (ELabelFlags)(LFEntryPoint | LFExcepHandler | LFInterHandler | LFSubroutine | LFJumpTarget | LFLoopPoint); + } + else if (CheckToken(token, "e", "entrypoints")) + { + customLabels = false; + labelFlags = LFEntryPoint; + } + else if (CheckToken(token, "x", "excephandlers")) + { + customLabels = false; + labelFlags = LFExcepHandler; + } + else if (CheckToken(token, "i", "interhandlers")) + { + customLabels = false; + labelFlags = LFInterHandler; + } + else if (CheckToken(token, "s", "subroutines")) + { + customLabels = false; + labelFlags = LFSubroutine; + } + else if (CheckToken(token, "j", "jumptargets")) + { + customLabels = false; + labelFlags = LFJumpTarget; + } + else if (CheckToken(token, "l", "looppoints")) + { + customLabels = false; + labelFlags = LFLoopPoint; + } + else + { + Error("Enter a valid filter (a)ll, (c)ustom, (e)ntrypoints, e(x)cephandlers, (i)interhandlers, (j)umptargets or (l)ooppoints.\n"); + return false; + } + + ListLabels(customLabels, labelFlags); + } + else if (CheckToken(token, "al", "addlabel")) // addlabel + { + // Parse arguments + token = strtok(NULL, " "); + if (token == NULL) + { + Error("Missing label address.\n"); + return false; + } + else if (!ParseAddress(token, &addr)) + { + Error("Enter a valid label address.\n"); + return false; + } + + token = strtok(NULL, " "); + if (token == NULL) + { + Error("Missing label name.\n"); + return false; + } + const char *name = token; + + // Add label + CLabel *label = m_cpu->AddLabel(addr, name); + m_cpu->FormatAddress(addrStr, label->addr); + Print("Label %s added at %s.\n", label->name, addrStr); + } + else if (CheckToken(token, "rl", "removelabel")) // removelabel [|] + { + // Parse arguments + token = strtok(NULL, " "); + if (token == NULL) + token = "-"; + + if (!ParseAddress(token, &addr)) + { + Error("Enter a valid label name or address.\n"); + return false; + } + + CLabel *label = m_cpu->GetLabel(addr); + if (label == NULL) + { + m_cpu->FormatAddress(addrStr, addr); + Error("No label at %s.\n", addrStr); + return false; + } + + const char *name = label->name; + m_cpu->FormatAddress(addrStr, label->addr); + Print("Custom label '%s' removed at address %s.\n", name, addrStr); + } + else if (CheckToken(token, "ral", "removealllabels")) // removealllabels + { + m_cpu->RemoveAllLabels(); + Print("All custom labels removed.\m"); + } + else if (CheckToken(token, "ac", "addcomment")) // addcomment + { + // Parse arguments + token = strtok(NULL, " "); + if (token == NULL) + { + Error("Missing comment address.\n"); + return false; + } + else if (!ParseAddress(token, &addr)) + { + Error("Enter a valid comment address.\n"); + return false; + } + + char text[255]; + text[0] = '\0'; + token = strtok(NULL, " "); + while (token != NULL) + { + size_t len = strlen(text); + if (len + strlen(token) > 253) + break; + if (len > 0) + strcat(text, " "); + strcat(text, token); + token = strtok(NULL, " "); + } + if (text[0] == '\0') + { + Error("Missing comment text.\n"); + return false; + } + + // Add comment + CComment *comment = m_cpu->AddComment(addr, text); + m_cpu->FormatAddress(addrStr, comment->addr); + Print("Comment added at %s.\n", addrStr); + } + else if (CheckToken(token, "rc", "removecomment")) // removecomment [] + { + // Parse arguments + token = strtok(NULL, " "); + if (token == NULL) + token = "-"; + if (!ParseAddress(token, &addr)) + { + Error("Enter a valid comment address.\n"); + return false; + } + + m_cpu->FormatAddress(addrStr, addr); + if (m_cpu->RemoveComment(addr)) + Print("Comment at address %s removed.\n", addrStr); + else + Error("No comment at address %s.\n", addrStr); + } + else if (CheckToken(token, "rac", "removeallcomments")) // removeallcomments + { + m_cpu->RemoveAllComments(); + Print("All comments removed.\n"); + } + // + // Breakpoints + // + else if (CheckToken(token, "lb", "listbreakpoints")) // listbreakpoints + { + ListBreakpoints(); + } + else if (CheckToken(token, "b", "breakpoint") || // addbreakpoint [ [[s)imple|(c)ount )]] + CheckToken(token, "ab", "addbreakpoint")) + { + // Parse arguments + token = strtok(NULL, " "); + if (token == NULL) + token = "-"; + if (!ParseAddress(token, &addr)) + { + Error("Enter a valid address.\n"); + return false; + } + token = strtok(NULL, " "); + CBreakpoint *bp; + if (token == NULL || CheckToken(token, "s", "simple")) + bp = m_cpu->AddSimpleBreakpoint(addr); + else if (CheckToken(token, "c", "count")) + { + token = strtok(NULL, " "); + if (token == NULL) + { + Error("Missing count.\n"); + return false; + } + int count; + if (!ParseInt(token, &count) || count <= 0) + { + Error("Enter a valid count.\n"); + return false; + } + + bp = m_cpu->AddCountBreakpoint(addr, count); + } + else if (CheckToken(token, "p", "print")) + bp = m_cpu->AddPrintBreakpoint(addr); + else + { + Error("Enter a valid breakpoint type (s)imple or (c)ount.\n"); + return false; + } + + m_cpu->FormatAddress(addrStr, bp->addr); + Print("Breakpoint #%d added at address %s.\n", bp->num, addrStr); + } + else if (CheckToken(token, "rb", "removebreakpoint")) // removebreakpoint [#|] + { + // Parse arguments + token = strtok(NULL, " "); + if (token == NULL) + token = "-"; + if (token[0] == '#') + { + // Remove breakpoint by number + if (!ParseInt(token + 1, &number) || number < 0 || number >= m_cpu->bps.size()) + { + Error("Enter a valid breakpoint number.\n"); + return false; + } + + // Remove breakpoint + m_cpu->RemoveBreakpoint(m_cpu->bps[number]); + Print("Breakpoint #%d removed.\n", number); + } + else + { + // Remove breakpoint by address + if (!ParseAddress(token, &addr)) + { + Error("Enter a valid address.\n"); + return false; + } + + // Remove breakpoint + m_cpu->FormatAddress(addrStr, addr); + if (m_cpu->RemoveBreakpoint(addr)) + Print("Breakpoint at address %s removed.\n", addrStr); + else + Error("No breakpoint at address %s.\n", addrStr); + } + } + else if (CheckToken(token, "rab", "removeallbreakpoints")) // removeallbreakpoints + { + m_cpu->RemoveAllBreakpoints(); + Print("All breakpoints removed.\n"); + } + // + // Memory, I/O & watches + // + else if (CheckToken(token, "ln", "listregions")) // listregions + { + ListRegions(); + } + else if (CheckToken(token, "ly", "listmemory")) // listmemory [=last [#=8|]] + { + // Get start address + UINT32 start; + token = strtok(NULL, " "); + if (token == NULL) + token = "last"; + if (stricmp(token, "last") == 0) + { + // Use end of last listing + start = m_listMem; + } + else if (!ParseAddress(token, &start)) + { + Error("Enter a valid start address.\n"); + return false; + } + + // Get end address + UINT32 end; + token = strtok(NULL, " "); + if (token != NULL) + { + if (token[0] == '#') + { + if (!ParseInt(token + 1, &number) || number <= 0) + { + Error("Enter a valid number of rows.\n"); + return false; + } + end = start + number * m_memBytesPerRow; + } + else + { + if (!ParseAddress(token, &end)) + { + Error("Enter a valid end address.\n"); + return false; + } + } + } + else + // Default is 8 rows after start + end = start + 8 * m_memBytesPerRow; + + // List the memory + m_listMem = ListMemory(start, end, m_memBytesPerRow); + } + else if (CheckToken(token, "py", "printmemory", mod, 9, "b")) // printmemory[.=b] [(h)ex|hexdo(l)lar|hex(p)osth|(d)ecimal|(b)inary] + { + // Parse arguments + if (!ParseDataSize(mod, size)) + return false; + token = strtok(NULL, " "); + if (token == NULL) + { + Error("Missing address.\n"); + return false; + } + if (!ParseAddress(token, &addr)) + { + Error("Enter a valid address.\n"); + return false; + } + token = strtok(NULL, " "); + if (token != NULL) + { + if (!ParseFormat(token, fmt)) + return false; + } + else + fmt = m_dataFmt; + + // Read and print memory + char uSizeStr[12]; + sizeStr = GetDataSizeStr(size, false); + UpperFirst(uSizeStr, sizeStr); + data = m_cpu->ReadMem(addr, size); + FormatData(dataStr, fmt, size, data); + m_cpu->FormatAddress(addrStr, addr); + Print("%s data at %s = %s.\n", uSizeStr, addrStr, dataStr); + } + else if (CheckToken(token, "sy", "setmemory", mod, 9, "b")) // setmemory[.=b] + { + // Parse arguments + if (!ParseDataSize(mod, size)) + return false; + token = strtok(NULL, " "); + if (token == NULL) + { + Error("Missing address.\n"); + return false; + } + if (!ParseAddress(token, &addr)) + { + Error("Enter a valid address.\n"); + return false; + } + token = strtok(NULL, " "); + if (token == NULL) + { + Error("Missing value to set.\n"); + return false; + } + sizeStr = GetDataSizeStr(size, false); + if (!m_cpu->ParseData(token, size, &data)) + { + Error("Enter a valid %s value.\n", sizeStr); + return false; + } + + // Set memory + char uSizeStr[12]; + UpperFirst(uSizeStr, sizeStr); + m_cpu->WriteMem(addr, size, data); + m_cpu->FormatData(dataStr, size, data); + m_cpu->FormatAddress(addrStr, addr); + Print("Set %s data at %s to %s.\n", addrStr, dataStr); + } + else if (CheckToken(token, "lo", "listios")) // listios + { + ListIOs(); + } else if (CheckToken(token, "lw", "listmemwatches")) // listmemwatches { ListMemWatches(); @@ -658,7 +1027,7 @@ namespace Debugger Error("Missing address.\n"); return false; } - if (!ParseAddress(m_cpu, token, &addr)) + if (!ParseAddress(token, &addr)) { Error("Enter a valid address.\n"); return false; @@ -723,7 +1092,7 @@ namespace Debugger } if (dataSeq.size() == 0) { - sizeStr = GetSizeString(size); + sizeStr = GetDataSizeStr(size, false); Error("Enter a sequence of %s data to match.", sizeStr); return false; } @@ -754,7 +1123,8 @@ namespace Debugger } m_cpu->FormatAddress(addrStr, watch->addr); - Print("Memory watch [%s %s] added at address %s.\n", GetSizeString(watch->size), watch->type, addrStr); + number = GetIndexOfMemWatch(watch); + Print("Memory watch #%d added at address %s.\n", number, addrStr); } else if (CheckToken(token, "rw", "removememwatch")) // removememwatch (#|) { @@ -783,7 +1153,7 @@ namespace Debugger else { // Remove watch by address - if (!ParseAddress(m_cpu, token, &addr)) + if (!ParseAddress(token, &addr)) { Error("Enter a valid address.\n"); return false; @@ -886,7 +1256,7 @@ namespace Debugger } if (dataSeq.size() == 0) { - Error("Enter a sequence of %s to match.", GetSizeString(port->dataSize)); + Error("Enter a sequence of %s to match.", GetDataSizeStr(port->dataSize, false)); return false; } watch = port->AddMatchWatch(input, output, dataSeq); @@ -916,7 +1286,8 @@ namespace Debugger } m_cpu->FormatPortNum(portNumStr, portNum); - Print("Port watch [%s] added for port %u.\n", watch->type, portNumStr); + number = GetIndexOfPortWatch(watch); + Print("Port watch %d added for port %u.\n", number, portNumStr); } else if (CheckToken(token, "rpw", "removeportwatch")) // removeportwatch (a|n|p) [NUM|PORT] { @@ -969,365 +1340,86 @@ namespace Debugger m_cpu->RemoveWatch(*it); Print("All port watches removed.\n"); } - else if (CheckToken(token, "lb", "listbreakpoints")) // listbreakpoints - { - ListBreakpoints(); - } - else if (CheckToken(token, "b", "breakpoint") || // addbreakpoint [ [[s)imple|(c)ount )]] - CheckToken(token, "ab", "addbreakpoint")) + // + // General + // + else if (CheckToken(token, "p", "print", mod, 9, "")) // print[.=v] [(h)ex|hexdo(l)lar|hex(p)osth|(d)ecimal|(b)inary] { // Parse arguments - token = strtok(NULL, " "); - if (token == NULL) - token = "-"; - if (!ParseAddress(m_cpu, token, &addr)) - { - Error("Enter a valid address.\n"); + bool useDefSize; + if (mod[0] == '\0') + useDefSize = true; + else if (ParseDataSize(mod, size)) + useDefSize = false; + else return false; - } - token = strtok(NULL, " "); - CBreakpoint *bp; - if (token == NULL || CheckToken(token, "s", "simple")) - bp = m_cpu->AddSimpleBreakpoint(addr); - else if (CheckToken(token, "c", "count")) - { - token = strtok(NULL, " "); - if (token == NULL) - { - Error("Missing count.\n"); - return false; - } - int count; - if (!ParseInt(token, &count) || count <= 0) - { - Error("Enter a valid count.\n"); - return false; - } - - bp = m_cpu->AddCountBreakpoint(addr, count); - } - else - { - Error("Enter a valid breakpoint type (s)imple or (c)ount.\n"); - return false; - } - - m_cpu->FormatAddress(addrStr, bp->addr); - Print("Breakpoint [%s] added at address %s.\n", bp->type, addrStr); - } - else if (CheckToken(token, "rb", "removebreakpoint")) // removebreakpoint [#|] - { - // Parse arguments - token = strtok(NULL, " "); - if (token == NULL) - token = "-"; - if (token[0] == '#') - { - // Remove breakpoint by number - if (!ParseInt(token + 1, &number) || number < 0 || number >= m_cpu->bps.size()) - { - Error("Enter a valid breakpoint number.\n"); - return false; - } - - // Remove breakpoint - m_cpu->RemoveBreakpoint(m_cpu->bps[number]); - Print("Breakpoint #%d removed.\n", number); - } - else - { - // Remove breakpoint by address - if (!ParseAddress(m_cpu, token, &addr)) - { - Error("Enter a valid address.\n"); - return false; - } - - // Remove breakpoint - m_cpu->FormatAddress(addrStr, addr); - if (m_cpu->RemoveBreakpoint(addr)) - Print("Breakpoint at address %s removed.\n", addrStr); - else - Error("No breakpoint at address %s.\n", addrStr); - } - } - else if (CheckToken(token, "rab", "removeallbreakpoints")) // removeallbreakpoints - { - m_cpu->RemoveAllBreakpoints(); - Print("All breakpoints removed.\n"); - } - else if (CheckToken(token, "lm", "listmonitors")) // listmonitors - { - ListMonitors(); - } - else if (CheckToken(token, "m", "monitor") || // addmonitor - CheckToken(token, "am", "addmonitor")) - { - // Parse arguments - token = strtok(NULL, " "); - CRegister *reg; - if (!ParseRegister(token, reg)) - return false; - - m_cpu->AddRegMonitor(reg->name); - Print("Monitor added to register %s.\n", reg->name); - } - else if (CheckToken(token, "rm", "removemonitor")) // removemonitor - { - // Parse arguments - token = strtok(NULL, " "); - CRegister *reg; - if (!ParseRegister(token, reg)) - return false; - - m_cpu->RemoveRegMonitor(reg->name); - Print("Monitor for register %s removed.\n", reg->name); - } - else if (CheckToken(token, "ram", "removeallmonitors")) // removeallmonitors - { - m_cpu->RemoveAllRegMonitors(); - Print("All register monitors removed.\n"); - } - else if (CheckToken(token, "l", "list") || // listdisassembly [=last [#=20|]] - CheckToken(token, "ld", "listdisassembly")) - { - // Get start address - UINT32 start; - token = strtok(NULL, " "); - if (token != NULL) - { - if (!ParseAddress(m_cpu, token, &start)) - { - Error("Enter a valid start address.\n"); - return false; - } - } - else - { - // Default is end of last listing - start = m_listDism; - } - - // Get end address - UINT32 end; - unsigned numInstrs; - token = strtok(NULL, " "); - if (token != NULL) - { - if (token[0] == '#') - { - if (!ParseInt(token + 1, &number) || number <= 0) - { - Error("Enter a valid number of instructions.\n"); - return false; - } - numInstrs = (unsigned)number; - end = 0xFFFFFFFF; - } - else - { - if (!ParseAddress(m_cpu, token, &end)) - { - Error("Enter a valid end address.\n"); - return false; - } - numInstrs = 0xFFFFFFFF; - } - } - else - { - // Default is 20 instructions after start - end = 0xFFFFFFFF; - numInstrs = 20; - } - - // List the disassembled code - m_listDism = ListDisassembly(start, end, numInstrs); - } - else if (CheckToken(token, "ly", "listmemory")) // listmemory [=last [#=8|]] - { - // Get start address - UINT32 start; - token = strtok(NULL, " "); - if (token != NULL) - { - if (!ParseAddress(m_cpu, token, &start)) - { - Error("Enter a valid start address.\n"); - return false; - } - } - else - { - // Default is end of last listing - start = m_listMem; - } - - // Get end address - UINT32 end; - token = strtok(NULL, " "); - if (token != NULL) - { - if (token[0] == '#') - { - if (!ParseInt(token + 1, &number) || number <= 0) - { - Error("Enter a valid number of rows.\n"); - return false; - } - end = start + number * m_memBytesPerRow; - } - else - { - if (!ParseAddress(m_cpu, token, &end)) - { - Error("Enter a valid end address.\n"); - return false; - } - } - } - else - // Default is 8 rows after start - end = start + 8 * m_memBytesPerRow; - - // List the memory - m_listMem = ListMemory(start, end, m_memBytesPerRow); - } - else if (CheckToken(token, "p", "print")) // print - { - // Parse arguments token = strtok(NULL, " "); if (token == NULL) { Error("Missing expression.\n"); return false; } + const char *expr = token; + token = strtok(NULL, " "); + if (token != NULL) + { + if (!ParseFormat(token, fmt)) + return false; + } + else + fmt = m_dataFmt; - char str[255]; - const char *result; - if (ParseAddress(m_cpu, token, &addr)) + // Check labels and registers + CLabel *label = m_cpu->GetLabel(expr); + if (label != NULL) { - m_cpu->FormatAddress(str, addr, true, LFAll); - if (stricmp(token, str) == 0) - m_cpu->FormatAddress(str, addr, false, LFNone); - result = str; + data = label->addr; + if (useDefSize) + size = m_cpu->memBusWidth / 8; } - else if (m_cpu->ParseData(token, 8, &data)) + else { - m_cpu->FormatData(str, 8, data); - result = str; + CCodeAnalyser *analyser = m_cpu->GetCodeAnalyser(); + CAutoLabel *autoLabel = analyser->analysis->GetAutoLabel(expr); + if (autoLabel != NULL) + { + data = autoLabel->addr; + if (useDefSize) + size = m_cpu->memBusWidth / 8; + } + else + { + CRegister *reg = m_cpu->GetRegister(expr); + if (reg != NULL) + { + data = reg->GetValueAsInt(); + if (useDefSize) + size = reg->dataWidth / 8; + } + else + { + if (!ParseData(expr, m_dataFmt, 8, &data)) + { + Print("Unable to parse expression %s\n", expr); + return false; + } + if (useDefSize) + size = 8; + } + } } - else - result = token; - Print("%s = %s\n", token, result); - } - else if (CheckToken(token, "pr", "printregister")) // printregister - { - // Parse arguments - token = strtok(NULL, " "); - CRegister *reg; - if (!ParseRegister(token, reg)) - return false; - - char valStr[50]; - // TODO - hook up size - reg->GetValue(valStr); - Print("Register %s = %s\n", reg->name, valStr); - } - else if (CheckToken(token, "sr", "setregister", mod, 9, "vl")) // setregister - { - // Parse arguments - if (!ParseDataSize(mod, size)) - return false; - token = strtok(NULL, " "); - CRegister *reg; - if (!ParseRegister(token, reg)) - return false; - token = strtok(NULL, " "); - if (token == NULL) - { - Error("Missing value to set.\n"); - return false; - } - - if (!reg->SetValue(token)) - { - Error("Unable to set value of register %s.\n", reg->name); - return false; - } - - char valStr[50]; - reg->GetValue(valStr); - Print("Set register %s to %s.\n", reg->name, valStr); - } - else if (CheckToken(token, "py", "printmemory", mod, 9, "b")) // printmemory[.=b] - { - // Parse arguments - if (!ParseDataSize(mod, size)) - return false; - token = strtok(NULL, " "); - if (token == NULL) - { - Error("Missing address.\n"); - return false; - } - if (!ParseAddress(m_cpu, token, &addr)) - { - Error("Enter a valid address.\n"); - return false; - } - - // Read and print memory - sizeStr = GetSizeString(size); - data = m_cpu->ReadMem(addr, size); - m_cpu->FormatData(dataStr, size, data); - m_cpu->FormatAddress(addrStr, addr); - Print("%s data at %s = %s.\n", sizeStr, addrStr, dataStr); - } - else if (CheckToken(token, "sy", "setmemory", mod, 9, "b")) // setmemory[.=b] - { - // Parse arguments - if (!ParseDataSize(mod, size)) - return false; - token = strtok(NULL, " "); - if (token == NULL) - { - Error("Missing address.\n"); - return false; - } - if (!ParseAddress(m_cpu, token, &addr)) - { - Error("Enter a valid address.\n"); - return false; - } - token = strtok(NULL, " "); - if (token == NULL) - { - Error("Missing value to set.\n"); - return false; - } - sizeStr = GetSizeString(size); - if (!m_cpu->ParseData(token, size, &data)) - { - Error("Enter a valid %s value.\n", sizeStr); - return false; - } - // Set memory - m_cpu->WriteMem(addr, size, data); - m_cpu->FormatData(dataStr, size, data); - m_cpu->FormatAddress(addrStr, addr); - Print("Set %s data at %s to %s.\n", sizeStr, addrStr, dataStr); + char result[255]; + FormatData(result, fmt, size, data); + if (useDefSize) + Print("%s = %s\n", expr, result); + else + { + sizeStr = GetDataSizeStr(size, true); + Print("%s = %s.%s\n", expr, result, sizeStr); + } } - //else if (CheckToken(token, "pp", "printio")) // printio - //{ - // // TODO - read I/O - //} - //else if (CheckToken(token, "sp", "sendio")) // sendio - //{ - // // TODO - send I/O - //} - else if (CheckToken(token, "cfg", "configure")) // configure ... + else if (CheckToken(token, "cfg", "configure")) // configure { // Parse arguments token = strtok(NULL, " "); @@ -1386,7 +1478,7 @@ namespace Debugger return false; } } - else if (CheckToken(token, "ls", "loadstate")) // loadstate FILENAME + else if (CheckToken(token, "ls", "loadstate")) // loadstate { // Parse arguments token = strtok(NULL, " "); @@ -1401,7 +1493,7 @@ namespace Debugger else Error("Unable to load debugger state from <%s>\n", token); } - else if (CheckToken(token, "ss", "savestate")) // savestate FILENAME + else if (CheckToken(token, "ss", "savestate")) // savestate { // Parse arguments token = strtok(NULL, " "); @@ -1418,25 +1510,44 @@ namespace Debugger } else if (CheckToken(token, "h", "help")) // help { - // TODO - do this better - const char *fmt = " %-6s %-20s %s\n"; + // TODO - improve the following + const char *fmt = " %-6s %-25s %s\n"; + Print("Debugger Commands:\n"); - Print(fmt, "x", "exit", ""); + + Print(" Execution:\n"); Print(fmt, "n", "next", "[=1]"); Print(fmt, "nf", "nextframe", "[=1]"); Print(fmt, "s", "stepover", ""); Print(fmt, "si", "stepinto", ""); Print(fmt, "so", "stepout", ""); Print(fmt, "c", "continue", "[]"); + Print(fmt, "spc", "setpc", ""); + + Print(" CPUs:\n"); Print(fmt, "lc", "listcpus", ""); Print(fmt, "sc", "switchcpu", "(|)"); Print(fmt, "dc", "disablecpu", "(|)"); Print(fmt, "ec", "enablecpu", "(|)"); + + Print(" Registers:\n"); Print(fmt, "lr", "listregisters", ""); + Print(fmt, "pr", "printregister", ""); + Print(fmt, "sr", "setregister", " "); + Print(fmt, "lm", "listmonitors", ""); + Print(fmt, "m/am", "addmonitor", ""); + Print(fmt, "rm", "removemonitor", ""); + Print(fmt, "ram", "removeallmonitors", ""); + + Print(" Exceptions & interrupts:\n"); Print(fmt, "le", "listexceptions", ""); Print(fmt, "li", "listinterrupts", ""); - Print(fmt, "lo", "listios", ""); - Print(fmt, "ln", "listregions", ""); + Print(fmt, "t/at", "addtrap", "((e)xception|(i)nterrupt) "); + Print(fmt, "rt", "removetrap", "((e)xception|(i)nterrupt) "); + Print(fmt, "rat", "removealltraps", "[(a)ll|(e)xceptions|(i)nterrupts]"); + + Print(" Disassembly, labels & comments:\n"); + Print(fmt, "l/ld", "listdisassembly", "[=last [#=20|]]"); Print(fmt, "ll", "listlabels", "[(d)efault|(c)ustom|(a)utos|(e)ntrypoints|e(x)cephandlers|(i)interhandlers|(j)umptargets|(l)ooppoints]"); Print(fmt, "al", "addlabel", " "); Print(fmt, "rl", "removelabel", "[|]"); @@ -1444,31 +1555,41 @@ namespace Debugger Print(fmt, "ac", "addcomment", " "); Print(fmt, "rc", "removecomment", "[]"); Print(fmt, "rac", "removeallcomments", ""); - Print(fmt, "t/at", "addtrap", "((e)xception|(i)nterrupt) "); - Print(fmt, "rt", "removetrap", "((e)xception|(i)nterrupt) "); - Print(fmt, "rat", "removealltraps", "[(a)ll|(e)xceptions|(i)nterrupts]"); - Print(fmt, "lw", "listmemwatches", ""); - Print(fmt, "w/aw", "addmemwatch[.=b]", " [((n)one|(r)ead|(w)rite|(rw)eadwrite) [((s)imple|(c)ount |(m)atch |captu(r)e |(p)rint)]]"); - Print(fmt, "rw", "removememwatch", "(#|)"); - Print(fmt, "raw", "removeallmemwatches", ""); - Print(fmt, "pw/apw", "addportwatch", " [((n)one|(i)nput|(o)utput|(io)nputoutput) [(s)imple|(c)ount |(m)atch |captu(r)e |(p)rint]]"); - Print(fmt, "rpw", "removeportwatch", "(#|)"); - Print(fmt, "rapw", "removeallportwatches", ""); + + Print(" Breakpoints:\n"); Print(fmt, "lb", "listbreakpoints", ""); Print(fmt, "b/ab", "addbreakpoint", "[ [[s)imple|(c)ount )]]"); Print(fmt, "rb", "removebreakpoint", "[#|]"); Print(fmt, "rab", "removeallbreakpoints", ""); - Print(fmt, "lm", "listmonitors", ""); - Print(fmt, "m/am", "addmonitor", ""); - Print(fmt, "rm", "removemonitor", ""); - Print(fmt, "ram", "removeallmonitors", ""); - Print(fmt, "l/ld", "listdisassembly", "[=last [#=20|]]"); - Print(fmt, "l/ly", "listmemory", "[=last [#=8|]]"); - Print(fmt, "p", "print", ""); - Print(fmt, "pr", "printregister", ""); - Print(fmt, "sr", "setregister", " "); + + Print(" Memory, I/O & watches:\n"); + Print(fmt, "ln", "listregions", ""); + Print(fmt, "ly", "listmemory", "[=last [#=8|]]"); Print(fmt, "py", "printmemory[.=b]", ""); Print(fmt, "sy", "setmemory[.=b]", " "); + Print(fmt, "lo", "listios", ""); + Print(fmt, "lw", "listmemwatches", ""); + Print(fmt, "w/aw", "addmemwatch[.=b]", " [((n)one|(r)ead|(w)rite|(rw)eadwrite) [((s)imple|(c)ount |(m)atch |captu(r)e |(p)rint)]]"); + Print(fmt, "rw", "removememwatch", "(#|)"); + Print(fmt, "raw", "removeallmemwatches", ""); + Print(fmt, "lpw", "listportwatches", ""); + Print(fmt, "pw/apw", "addportwatch", " [((n)one|(i)nput|(o)utput|(io)nputoutput) [(s)imple|(c)ount |(m)atch |captu(r)e |(p)rint]]"); + Print(fmt, "rpw", "removeportwatch", "(#|)"); + Print(fmt, "rapw", "removeallportwatches", ""); + + Print("General:\n"); + Print(fmt, "p", "print[.=v]", " [(h)ex|hexdo(l)lar|hex(p)osth|(d)ecimal|(b)inary]"); + Print(fmt, "cfg", "configure", ""); + Print(fmt, "ls", "loststate", ""); + Print(fmt, "ss", "savestate", ""); + Print(fmt, "h", "help", ""); + Print(fmt, "x", "exit", ""); + } + else if (CheckToken(token, "x", "exit")) // exit + { + Print("Exiting...\n"); + SetExit(); + return true; } else Print("Unknown command '%s'.\n", token); @@ -1611,88 +1732,6 @@ namespace Debugger return false; } - bool CConsoleDebugger::ParseAddress(CCPUDebug *cpu, const char *str, UINT32 *addr) - { - if (cpu->instrCount > 0 && CheckToken(str, "-", "current")) - { - *addr = cpu->pc; - return true; - } - return cpu->ParseAddress(str, addr); - } - - bool CConsoleDebugger::ParseDataSize(const char *str, unsigned &dataSize) - { - int num = -1; - ParseInt(str, &num); - if (CheckToken(str, "b", "byte") || num == 1) - { - dataSize = 1; - return true; - } - else if (CheckToken(str, "w", "word") || num == 2) - { - dataSize = 2; - return true; - } - else if (CheckToken(str, "l", "long") || num == 4) - { - dataSize = 4; - return true; - } - else if (CheckToken(str, "v", "verylong") || num == 8) - { - dataSize = 8; - return true; - } - else - { - Error("Enter a valid size (b)yte, (w)ord, (l)ong or (v)erylong or a number 1, 2, 4 or 8.\n"); - return false; - } - } - - bool CConsoleDebugger::ParseCPU(const char *str, CCPUDebug *&cpu) - { - if (str == NULL) - { - Error("Missing CPU name or number.\n"); - return false; - } - int cpuNum; - if (ParseInt(str, &cpuNum)) - { - if (cpuNum >= 0 && cpuNum < (int)cpus.size()) - { - cpu = cpus[cpuNum]; - return true; - } - } - cpu = GetCPU(str); - if (cpu == NULL) - { - Error("No CPU with that name or number.\n"); - return false; - } - return true; - } - - bool CConsoleDebugger::ParseRegister(const char *str, CRegister *®) - { - if (str == NULL) - { - Error("Missing register name.\n"); - return false; - } - reg = m_cpu->GetRegister(str); - if (reg == NULL) - { - Error("Enter a valid register name.\n"); - return false; - } - return true; - } - bool CConsoleDebugger::SetBoolConfig(const char *str, bool &cfg) { if (str == NULL) @@ -1765,24 +1804,125 @@ namespace Debugger Print("Change setting with (h)ex, hex(z)ero, hexdo(l)ar, hex(p)osth, (d)ecimal, (b)inary.\n"); return false; } - - if (CheckToken(str, "h", "hex")) cfg = Hex; - else if (CheckToken(str, "z", "hexzero")) cfg = Hex0x; - else if (CheckToken(str, "l", "hexdollar")) cfg = HexDollar; - else if (CheckToken(str, "p", "hexposth")) cfg = HexPostH; - else if (CheckToken(str, "d", "decimal")) cfg = Decimal; - else if (CheckToken(str, "b", "binary")) cfg = Binary; - else - { - Error("Enter a valid setting (h)ex, hex(z)ero, hexdo(l)ar, hex(p)osth, (d)ecimal, (b)inary.\n"); + + if (!ParseFormat(str, cfg)) return false; - } Print("Changed setting: %-12s\n", GetFmtConfig(cfg)); ApplyConfig(); return true; } + bool CConsoleDebugger::ParseAddress(const char *str, UINT32 *addr) + { + if (m_cpu->instrCount > 0 && CheckToken(str, "-", "current")) + { + *addr = m_cpu->pc; + return true; + } + return m_cpu->ParseAddress(str, addr); + } + + const char *CConsoleDebugger::GetDataSizeStr(unsigned dataSize, bool shortName) + { + switch (dataSize) + { + case 1: return (shortName ? "b" : "byte"); + case 2: return (shortName ? "w" : "word"); + case 4: return (shortName ? "l" : "long"); + case 8: return (shortName ? "v" : "vlong"); + default: return GetSizeString(dataSize); + } + } + + bool CConsoleDebugger::ParseDataSize(const char *str, unsigned &dataSize) + { + int num = -1; + ParseInt(str, &num); + if (CheckToken(str, "b", "byte") || num == 1) + { + dataSize = 1; + return true; + } + else if (CheckToken(str, "w", "word") || num == 2) + { + dataSize = 2; + return true; + } + else if (CheckToken(str, "l", "long") || num == 4) + { + dataSize = 4; + return true; + } + else if (CheckToken(str, "v", "verylong") || num == 8) + { + dataSize = 8; + return true; + } + else + { + Error("Enter a valid size (b)yte, (w)ord, (l)ong or (v)erylong or a number 1, 2, 4 or 8.\n"); + return false; + } + } + + bool CConsoleDebugger::ParseFormat(const char *str, EFormat &fmt) + { + if (CheckToken(str, "h", "hex")) fmt = Hex; + else if (CheckToken(str, "z", "hexzero")) fmt = Hex0x; + else if (CheckToken(str, "l", "hexdollar")) fmt = HexDollar; + else if (CheckToken(str, "p", "hexposth")) fmt = HexPostH; + else if (CheckToken(str, "d", "decimal")) fmt = Decimal; + else if (CheckToken(str, "b", "binary")) fmt = Binary; + else + { + Error("Enter a valid format (h)ex, hex(z)ero, hexdo(l)ar, hex(p)osth, (d)ecimal, (b)inary.\n"); + return false; + } + return true; + } + + bool CConsoleDebugger::ParseCPU(const char *str, CCPUDebug *&cpu) + { + if (str == NULL) + { + Error("Missing CPU name or number.\n"); + return false; + } + int cpuNum; + if (ParseInt(str, &cpuNum)) + { + if (cpuNum >= 0 && cpuNum < (int)cpus.size()) + { + cpu = cpus[cpuNum]; + return true; + } + } + cpu = GetCPU(str); + if (cpu == NULL) + { + Error("No CPU with that name or number.\n"); + return false; + } + return true; + } + + bool CConsoleDebugger::ParseRegister(const char *str, CRegister *®) + { + if (str == NULL) + { + Error("Missing register name.\n"); + return false; + } + reg = m_cpu->GetRegister(str); + if (reg == NULL) + { + Error("Enter a valid register name.\n"); + return false; + } + return true; + } + void CConsoleDebugger::ListCPUs() { Print("CPUs:\n"); @@ -1794,8 +1934,15 @@ namespace Debugger Print(" %-3s %-12s %-9s %12s\n", "Num", "CPU", "Debugging", "Instr Count"); unsigned num = 0; + const char *stateStr; for (vector::iterator it = cpus.begin(); it != cpus.end(); it++) - Print(" %-3u %-12s %-9s %12llu\n", num++, (*it)->name, ((*it)->enabled ? "Enabled" : "Disabled"), (*it)->instrCount); + { + stateStr = ((*it)->enabled ? "Enabled" : "Disabled"); + if ((*it)->instrCount > 0) + Print(" %-3u %-12s %-9s %12llu\n", num++, (*it)->name, stateStr, (*it)->instrCount - 1); + else + Print(" %-3u %-12s %-9s -\n", num++, (*it)->name, stateStr); + } } void CConsoleDebugger::ListRegisters() @@ -1974,8 +2121,8 @@ namespace Debugger int outRPad = 5 - (int)outLen + (int)outLen / 2; // Print details - Print(" %c%-12s %-30s %-8s %*s%s%*s%c%*s%s%*s\n", ((*it)->watch != NULL ? '*' : ' '), locStr, (*it)->name, GetSizeString((*it)->dataSize), - inLPad, "", inStr, inRPad, "", dirChar, outLPad, "", outStr, outRPad, ""); + Print(" %c%-12s %-30s %-8s %*s%s%*s%c%*s%s%*s\n", ((*it)->watch != NULL ? '*' : ' '), locStr, (*it)->name, + GetDataSizeStr((*it)->dataSize, true), inLPad, "", inStr, inRPad, "", dirChar, outLPad, "", outStr, outRPad, ""); } } @@ -2064,6 +2211,16 @@ namespace Debugger } } + int CConsoleDebugger::GetIndexOfMemWatch(CWatch *watch) + { + vector watches; + GetAllMemWatches(watches); + vector::iterator it = find(watches.begin(), watches.end(), watch); + if (it == watches.end()) + return -1; + return it - watches.begin(); + } + void CConsoleDebugger::ListMemWatches() { Print("%s Memory Watches:\n", m_cpu->name); @@ -2077,26 +2234,33 @@ namespace Debugger return; } - Print(" %-3s %-8s %-12s %-6s %-4s %-20s %s\n", "Num", "Type", "Address", "Size", "Trig", "Value", "Info"); + Print(" %-3s %-8s %-12s %-5s %-4s %-20s %s\n", "Num", "Type", "Address", "Size", "Trig", "Value", "Info"); + char typeStr[12]; char addrStr[20]; - const char *sizeStr; + char sizeStr[20]; + const char *dSizeStr; const char *trigStr; char valStr[20]; char infoStr[255]; unsigned wNum = 0; for (vector::iterator it = watches.begin(); it != watches.end(); it++) { + UpperFirst(typeStr, (*it)->type); m_cpu->FormatAddress(addrStr, (*it)->addr, true); - sizeStr = GetSizeString((*it)->size); + dSizeStr = GetDataSizeStr((*it)->size, false); + UpperFirst(sizeStr, dSizeStr); if ((*it)->trigRead && (*it)->trigWrite) trigStr = "R/W"; else if ((*it)->trigRead) trigStr = "R"; else if ((*it)->trigWrite) trigStr = "W"; else trigStr = "-"; m_cpu->FormatData(valStr, (*it)->size, (*it)->GetValue()); if (!(*it)->GetInfo(infoStr)) - infoStr[0] = '\0'; - Print(" %-3u %-8s %-12s %-6s %-4s %-20s %s\n", wNum++, (*it)->type, addrStr, sizeStr, trigStr, valStr, infoStr); + { + infoStr[0] = '-'; + infoStr[1] = '\0'; + } + Print(" %-3u %-8s %-12s %-5s %-4s %-20s %s\n", wNum++, typeStr, addrStr, sizeStr, trigStr, valStr, infoStr); } } @@ -2109,6 +2273,16 @@ namespace Debugger } } + int CConsoleDebugger::GetIndexOfPortWatch(CWatch *watch) + { + vector watches; + GetAllPortWatches(watches); + vector::iterator it = find(watches.begin(), watches.end(), watch); + if (it == watches.end()) + return -1; + return it - watches.begin(); + } + void CConsoleDebugger::ListPortWatches() { Print("%s I/O Port Watches:\n", m_cpu->name); @@ -2124,13 +2298,15 @@ namespace Debugger Print(" %-3s %-8s %-12s %-4s %-20s %s\n", "Num", "Type", "Location", "Trig", "Last In/Out", "Info"); + char typeStr[12]; char locStr[255]; const char *trigStr; char valStr[20]; char infoStr[255]; - unsigned wNum; + unsigned wNum = 0; for (vector::iterator it = watches.begin(); it != watches.end(); it++) { + UpperFirst(typeStr, (*it)->type); (*it)->io->GetLocation(locStr); if ((*it)->trigRead && (*it)->trigWrite) trigStr = "I/O"; else if ((*it)->trigRead) trigStr = "I"; @@ -2144,8 +2320,11 @@ namespace Debugger else m_cpu->FormatData(valStr, (*it)->size, (*it)->GetValue()); if (!(*it)->GetInfo(infoStr)) - infoStr[0] = '\0'; - Print(" %-3u %-8s %-12s %-4s %-20s %s\n", wNum++, (*it)->type, locStr, trigStr, valStr, infoStr); + { + infoStr[0] = '-'; + infoStr[1] = '\0'; + } + Print(" %-3u %-8s %-12s %-4s %-20s %s\n", wNum++, typeStr, locStr, trigStr, valStr, infoStr); } } @@ -2158,16 +2337,21 @@ namespace Debugger return; } + Print(" %-3s %-8s %-12s %-20s\n", "Num", "Type", "Address", "Info"); + + char typeStr[12]; char addrStr[20]; char infoStr[255]; - unsigned bpNum = 0; for (vector::iterator it = m_cpu->bps.begin(); it != m_cpu->bps.end(); it++) { + UpperFirst(typeStr, (*it)->type); m_cpu->FormatAddress(addrStr, (*it)->addr, true, (m_analyseCode ? LFAll : LFNone)); - if ((*it)->GetInfo(infoStr)) - Print(" %u - %s breakpoint [%s] at %s\n", bpNum++, (*it)->type, infoStr, addrStr); - else - Print(" %u - %s breakpoint at %s\n", bpNum++, (*it)->type, addrStr); + if (!(*it)->GetInfo(infoStr)) + { + infoStr[0] = '-'; + infoStr[1] = '\0'; + } + Print(" %-3u %-8s %-12s %-20s\n", (*it)->num, typeStr, addrStr, infoStr); } } @@ -2389,6 +2573,7 @@ namespace Debugger data = (UINT8)m_cpu->ReadMem(lAddr, 1); dChar = (data >= 32 && data <= 126 ? (char)data : '.'); Print("%c", dChar); + lAddr++; } Print("\n"); addr += bytesPerRow; @@ -2403,93 +2588,75 @@ namespace Debugger void CConsoleDebugger::ExceptionTrapped(CException *ex) { - char addrStr[255]; - ex->cpu->FormatAddress(addrStr, ex->cpu->pc, true, (m_analyseCode ? LFExcepHandler : LFNone)); - Print("Exception %s (%s) on %s trapped at %s.\n", ex->id, ex->name, ex->cpu->name, addrStr); + PrintEvent(ex->cpu, "Exception %s (%s) trapped.\n", ex->id, ex->name); } void CConsoleDebugger::InterruptTrapped(CInterrupt *in) { - char addrStr[255]; - in->cpu->FormatAddress(addrStr, in->cpu->pc, true, (m_analyseCode ? LFInterHandler : LFNone)); - Print("Interrupt %s (%s) on %s trapped at %s.\n", in->id, in->name, in->cpu->name, addrStr); + PrintEvent(in->cpu, "Interrupt %s (%s) trapped.\n", in->id, in->name); } void CConsoleDebugger::MemWatchTriggered(CWatch *watch, UINT32 addr, unsigned dataSize, UINT64 data, bool isRead) { - const char *sizeStr = GetSizeString(dataSize); + const char *sizeStr = GetDataSizeStr(dataSize, true); + const char *rwStr = (isRead ? "Read from" : "Write to"); char dataStr[50]; - m_cpu->FormatData(dataStr, dataSize, data); - const char *rwStr = (isRead ? "read" : "write"); char addrStr[255]; - char infoStr[255]; + m_cpu->FormatData(dataStr, dataSize, data); watch->cpu->FormatAddress(addrStr, addr, true); - if (watch->GetInfo(infoStr)) - Print("%s memory %s (%s) by %s at address %s triggered %s watch.\n", sizeStr, rwStr, dataStr, watch->cpu->name, addrStr, watch->type, infoStr); - else - Print("%s memory %s (%s) by %s at address %s triggered %s watch.\n", sizeStr, rwStr, dataStr, watch->cpu->name, addrStr, watch->type); + int num = GetIndexOfMemWatch(watch); + PrintEvent(watch->cpu, "%s %s (%s.%s) triggered memory watch #%d.\n", rwStr, addrStr, dataStr, sizeStr, num); } void CConsoleDebugger::IOWatchTriggered(CWatch *watch, CIO *io, UINT64 data, bool isInput) { - const char *sizeStr = GetSizeString(io->dataSize); + const char *sizeStr = GetDataSizeStr(io->dataSize, true); + const char *ioStr = (isInput ? "Input from" : "Output to"); char dataStr[50]; - m_cpu->FormatData(dataStr, io->dataSize, data); - const char *ioStr = (isInput ? "input" : "output"); - const char *tfStr = (isInput ? "from" : "to"); char locStr[255]; - char infoStr[255]; + m_cpu->FormatData(dataStr, io->dataSize, data); watch->io->GetLocation(locStr); - if (watch->GetInfo(infoStr)) - Print("%s I/O %s (%s) by %s %s %s triggered %s watch [%s].\n", sizeStr, ioStr, dataStr, watch->cpu->name, tfStr, locStr, watch->type, infoStr); + int num = GetIndexOfPortWatch(watch); + if (num >= 0) + PrintEvent(watch->cpu, "%s %s (%s.%s) triggered port watch #%d.\n", ioStr, locStr, dataStr, sizeStr, num); else - Print("%s I/O %s (%s) by %s %s %s triggered %s watch.\n", sizeStr, ioStr, dataStr, watch->cpu->name, tfStr, locStr, watch->type); + { + num = GetIndexOfMemWatch(watch); + PrintEvent(watch->cpu, "%s %s (%s.%s) triggered memory watch #%d.\n", ioStr, locStr, dataStr, sizeStr, num); + } } void CConsoleDebugger::BreakpointReached(CBreakpoint *bp) { - char addrStr[255]; - char infoStr[255]; - bp->cpu->FormatAddress(addrStr, bp->cpu->pc, true, (m_analyseCode ? LFAll : LFNone)); - if (bp->GetInfo(infoStr)) - Print("%s reached %s breakpoint [%s] at %s.\n", bp->cpu->name, bp->type, infoStr, addrStr); - else - Print("%s reached %s breakpoint at %s.\n", bp->cpu->name, bp->type, addrStr); + PrintEvent(bp->cpu, "Breakpoint #%d triggered.\n", bp->num); } void CConsoleDebugger::MonitorTriggered(CRegMonitor *regMon) { - char addrStr[255]; char valStr[255]; CRegister *reg = regMon->reg; - reg->cpu->FormatAddress(addrStr, reg->cpu->pc, true, (m_analyseCode ? LFAll : LFNone)); reg->GetValue(valStr); - Print("Register %s of %s has changed value at %s to %s.\n", reg->name, reg->cpu->name, addrStr, valStr); + PrintEvent(reg->cpu, "Write to register %s (%s) triggered monitor.\n", reg->name, valStr); } void CConsoleDebugger::ExecutionHalted(CCPUDebug *cpu, EHaltReason reason) { if (reason&HaltUser) - { - char addrStr[255]; - cpu->FormatAddress(addrStr, cpu->pc, true, (m_analyseCode ? LFAll : LFNone)); - Print("Execution halted on %s at %s.\n", cpu->name, addrStr); - } + PrintEvent(cpu, "Execution halted.\n"); } - void CConsoleDebugger::WriteOut(CCPUDebug *cpu, const char *typeStr, const char *fmtStr, va_list vl) + void CConsoleDebugger::Log(CCPUDebug *cpu, const char *typeStr, const char *fmtStr, va_list vl) { if (cpu != NULL) - Print("%s: ", cpu->name); + { + char pcStr[255]; + cpu->FormatAddress(pcStr, cpu->pc, true, LFNone); + Print("%s @ %s: ", cpu->name, pcStr); + } if (typeStr != NULL) Print(" %s - ", typeStr); PrintVL(fmtStr, vl); } - - void CConsoleDebugger::FlushOut(CCPUDebug *cpu) - { - Flush(); - } void CConsoleDebugger::Attach() { diff --git a/Src/Debugger/ConsoleDebugger.h b/Src/Debugger/ConsoleDebugger.h index ac2d0ca..8b2c71f 100644 --- a/Src/Debugger/ConsoleDebugger.h +++ b/Src/Debugger/ConsoleDebugger.h @@ -79,10 +79,14 @@ namespace Debugger bool SetFmtConfig(const char *str, EFormat &cfg); - bool ParseAddress(CCPUDebug *cpu, const char *str, UINT32 *addr); + bool ParseAddress(const char *str, UINT32 *addr); + + const char *GetDataSizeStr(unsigned dataSize, bool shortName); bool ParseDataSize(const char *str, unsigned &dataSize); + bool ParseFormat(const char *str, EFormat &fmt); + bool ParseCPU(const char *str, CCPUDebug *&cpu); bool ParseRegister(const char *str, CRegister *®); @@ -103,10 +107,14 @@ namespace Debugger void GetAllMemWatches(vector &watches); + int GetIndexOfMemWatch(CWatch *watch); + void ListMemWatches(); void GetAllPortWatches(vector &watches); + int GetIndexOfPortWatch(CWatch *watch); + void ListPortWatches(); void ListBreakpoints(); @@ -143,9 +151,7 @@ namespace Debugger virtual bool ProcessToken(const char *token, const char *cmd); - virtual void WriteOut(CCPUDebug *cpu, const char *typeStr, const char *fmtStr, va_list vl); - - virtual void FlushOut(CCPUDebug *cpu); + virtual void Log(CCPUDebug *cpu, const char *typeStr, const char *fmtStr, va_list vl); public: CConsoleDebugger(); diff --git a/Src/Debugger/Debugger.cpp b/Src/Debugger/Debugger.cpp index 150e7ce..e3bb3f4 100644 --- a/Src/Debugger/Debugger.cpp +++ b/Src/Debugger/Debugger.cpp @@ -198,11 +198,11 @@ namespace Debugger frameCount++; } - void CDebugger::Log(CCPUDebug *cpu, const char *typeStr, const char *fmtStr, ...) + void CDebugger::PrintEvent(CCPUDebug *cpu, const char *fmt, ...) { va_list vl; - va_start(vl, fmtStr); - WriteOut(cpu, typeStr, fmtStr, vl); + va_start(vl, fmt); + Log(cpu, NULL, fmt, vl); va_end(vl); } @@ -210,19 +210,19 @@ namespace Debugger void CDebugger::DebugLog(const char *fmt, va_list vl) { if (logDebug) - WriteOut(NULL, "Debug", fmt, vl); + Log(NULL, "Debug", fmt, vl); } void CDebugger::InfoLog(const char *fmt, va_list vl) { if (logInfo) - WriteOut(NULL, "Info", fmt, vl); + Log(NULL, "Info", fmt, vl); } void CDebugger::ErrorLog(const char *fmt, va_list vl) { if (logError) - WriteOut(NULL, "Error", fmt, vl); + Log(NULL, "Error", fmt, vl); } #endif // DEBUGGER_HASLOGGER diff --git a/Src/Debugger/Debugger.h b/Src/Debugger/Debugger.h index bfbff81..adcb726 100644 --- a/Src/Debugger/Debugger.h +++ b/Src/Debugger/Debugger.h @@ -89,7 +89,7 @@ namespace Debugger virtual bool LoadState(CBlockFile *state); virtual bool SaveState(CBlockFile *state); -#endif +#endif // DEBUGGER_HASBLOCKFILE // // Protected virtual methods for sub-classes to implement @@ -113,9 +113,7 @@ namespace Debugger virtual void WaitCommand(CCPUDebug *cpu) = 0; - virtual void WriteOut(CCPUDebug *cpu, const char *typeStr, const char *fmtStr, va_list vl) = 0; - - virtual void FlushOut(CCPUDebug *cpu) = 0; + virtual void Log(CCPUDebug *cpu, const char *typeStr, const char *fmtStr, va_list vl) = 0; public: vector cpus; @@ -161,17 +159,15 @@ namespace Debugger bool CheckExit(); bool CheckPause(); - + // - // Log + // Printing/logging // - void Log(CCPUDebug *cpu, const char *typeStr, const char *fmtStr, ...); + void PrintEvent(CCPUDebug *cpu, const char *fmt, ...); #ifdef DEBUGGER_HASLOGGER - // // CLogger logging methods - // virtual void DebugLog(const char *fmt, va_list vl); virtual void InfoLog(const char *fmt, va_list vl); diff --git a/Src/Debugger/ReadMe.txt b/Src/Debugger/ReadMe.txt new file mode 100755 index 0000000..d9e82be --- /dev/null +++ b/Src/Debugger/ReadMe.txt @@ -0,0 +1,396 @@ +Help for Supermodel Console-based Debugger +========================================== + +Relevant Supermodel Command Line Options +---------------------------------------- + + -enter-debugger Enters the debugger at the start of emulation, + + -disable-debugger Completely disables the debugger in emulator. + +At any point whilst running the emulator, execution can be halted and the debugger entered by pressing Alt+B. + +Code Analysis +------------- + +At startup, and whenever new code is encountered, the debugger analyses the program's code in order to work out all +reachable code locations from a given set of entry points (reset address, exception handlers etc). During the analysis, +it keeps track of all valid code locations and this is used to align instructions for CPUs with variable length instruction +sets. It also generates a set of auto-labels that identify places of interest such as sub-routines, jump destinations and +exception handlers etc. Code analysis can be switched off via the 'configure' command if required. + +Debugger Commands +================= + +Execution +--------- + +n next [=1] + + Runs the next single instruction or the next instructions. + +nf nextframe [=1] + + Runs until the next frame or for the next frames. + +s stepover + + Runs the next instruction. If it is a call to a sub-routine then the sub-routine is 'stepped over' (ie control + breaks at the return address of the sub-routine). + +si stepinto + + Runs the next single instruction, entering any sub-routines (ie same as 'next' above). + +so stepout + + Runs until control returns from the current sub-routine or exception/interrupt handler. Note that if an + exception/interrupt occurs whilst stepping out then this will break execution too as control will have left + the sub-routine or handler. + +c continue [] + + Continues running until a break is forced or if specified, until the given address is reached. + +spc setpc + + Sets the PC of the current CPU to the given address. + +CPUs +---- + +lc listcpus + + Lists all the available CPUs with details about them. + +sc switchcpu (|) + + Switches to another CPU with the given name or number. + +dc disablecpu (|) + + Disables debugging for the CPU with the given name or number. Once disabled, it is no longer + possible to switch to or halt execution for that CPU, ie all breakpoints, watches, monitors and traps + are ignored. The current CPU cannot be disabled. + +ec enablecpu (|) + + Enables debugging for the CPU with the given name or number. + +Registers +--------- + +lr listregisters + + Lists all registers for the current CPU and their current values. + +pr printregister + + Prints the current value of the given register for the current CPU. + +sr setregister + + Sets the value of the given register for the current CPU. + +lm listmonitors + + Lists all register monitors for the current CPU with details about them. + +m monitor +am addmonitor + + Adds a register monitor for the register with the given name, causing execution to break whenever the register's value + changes. + +rm removemonitor + + Removes the register monitor with the given name. + +ram removeallmonitors + + Removes all register monitors for the current CPU. + +Exceptions & Interrupts +----------------------- + +le listexceptions + + Lists all known exceptions for the current CPU and details about them. + +li listinterrupts + + Lists all known interrupts for the current CPU and details about them. + +t trap ((e)xception|(i)nterrupt) +at addtrap ((e)xception|(i)nterrupt) + + Adds an exception or interrupt trap for the exception/interrupt with the given identifier. + +rt removetrap ((e)xception|(i)nterrupt) + + Removes an exception or interrupt trap for the exception/interrupt with the given identifier. + +rat removealltraps [(a)ll|(e)xceptions|(i)nterrupts] + + Removes all exception and/or all interrupt traps for the current CPU. If no arguments are supplied, all exception and all + interrupt traps are removed. + +Disassembly, Labels & Comments +------------------------------ + +l list [=last [#=20|]] +ld listdisassembly [=last [#=20|]] + + Lists the disassembled code of the current CPU for the given address range and/or number of instructions. + + If the start address is not supplied, the listing continues on from the last call, or from just before the current PC + address if this is the first call. + If the end address or instruction count is not supplied, then the default of 20 instructions are listed. + + If code analysis is enabled, then this will be used to align the instructions in the disassembly. If code analysis is + off and the current PC address falls within the disassembly address range, then the instructions will line up with this. + Otherwise, instruction alignment will simply begin at the start address. + +ll listlabels [(d)efault|(c)ustom|(a)utos|(e)ntrypoints|e(x)cephandlers|(i)interhandlers|(j)umptargets|(l)ooppoints] + + Lists all labels for the current CPU. The default option is to list all custom labels (user-added) and any auto-labels + (labels generated by code analysis) of interest. The other options allow the display of particular types of auto-labels. + +al addlabel + + Adds a custom label at the given address and with the given name. + +rl removelabel [|] + + Removes a custom label with the given name or at the given address. If no argument is supplied, then it removes the + custom label at the current PC address. + +ral removealllabels + + Removes all custom labels for the current CPU. + +ac addcomment + + Adds a code comment at the given address and with the given text. + +rc removecomment [] + + Removes the code comment at the given address. If no argument is supplied, then it removes the code comment at the + current PC address. + +rac removeallcomments + + Removes all code comments for the current CPU. + +Breakpoints +----------- + +lb listbreakpoints + + Lists all breakpoints for the current CPU with details about them. + +b breakpoint [ [[s)imple|(c)ount |(p)rint)]] +ab addbreakpoint [ [[s)imple|(c)ount |(p)rint)]] + + Adds a breakpoint at the given address. If no arguments are supplied, then it adds a simple breakpoint at the current PC + address. + + Types of breakpoint (default is simple): + (s)imple - if the location is hit, execution always breaks, + (c)ount - if the location is hit the number of times specified by count, execution breaks. + (p)rint - if the location is hit, a message is printed to the console, but control does not break. + +rb removebreakpoint [#|] + + Removes the breakpoint with the given number of at the given address. If no arguments is supplied, then it removes + the breakpoint at the current PC address. + +Memory, I/O & Watches +--------------------- + +ln listregions + + Lists all known memory regions in the current CPU's address space. + +ly listmemory [=last [#=8|]] + + Lists the memory contents of the current CPU for the given address range and/or number of rows. + + If the start address is not supplied, the listing continues on from the last call, or from address 0 if this is the + first call. + If the end address or row count is not supplied, then the default of 8 rows are listed. + +py printmemory[.=b] [(h)ex|hexdo(l)lar|hex(p)osth|(d)ecimal|(b)inary] + + Prints the current memory value at the given address for the current CPU. If no format is supplied, the default data + format is used. + + The size specifier may be a number or (b)yte, (w)ord, (l)ong or (v)erylong. The default if omitted is byte. + +sy setmemory[.=b] + + Sets the memory value at the given address for the current CPU. + + The size specifier may be a number or (b)yte, (w)ord, (l)ong or (v)erylong. The default if omitted is byte. + +lo listios + + Lists all known I/Os (both mapped I/O addresses and I/O ports) for the current CPU and details about them. + +lw listmemwatches + + Lists all memory watches for the current CPU with details about them. + +w memwatch[.=b] [((n)one|(r)ead|(w)rite|(rw)eadwrite) [((s)imple|(c)ount |(m)atch |captu(r)e |(p)rint)]] +aw addmemwatch[.=b] [((n)one|(r)ead|(w)rite|(rw)eadwrite) [((s)imple|(c)ount |(m)atch |captu(r)e |(p)rint)]] + + Adds a read/write memory watch at the given address. + + If (r)ead, (w)rite or (rw)eadwrite are specified then the watch also triggers an event (see below) when the + address is read, written or either read or written, respectively. + If (n)one is specified (the default), the watch does not trigger and simply remembers the last read/write at the + given address (details about which are visible when listing memory watches). + + The size specifier may be a number or (b)yte, (w)ord, (l)ong or (v)erylong. The default if omitted is byte. If the + address is mapped I/O address, then the size is ignored. + + Types of watch (default is simple): + (s)imple - if the watch is triggered, execution always breaks, + (c)ount - if the watch is triggered, execution breaks after the number of times specified by count, + (m)atch - if the watch is triggered, execution breaks if the series of values read/written matches the given + sequence of data (comma separated), + captu(r)e - if the watch is triggered, the value read/written is recorded in a history whose maximum length + is as specified, + (p)rint - if the watch is triggered, a message with the value read/written is printed to the console. + +rw removememwatch (#|) + + Removes the memory watch with the given number or at the given address. + +raw removeallmemwatches + + Removes all memory watches for the current CPU. + +lpw listportwatches + + Lists all I/O port watches for the current CPU with details about them. + +pw portwatch [((n)one|(i)nput|(o)utput|(io)nputoutput) [(s)imple|(c)ount |(m)atch |captu(r)e |(p)rint]] +apw addportwatch [((n)one|(i)nput|(o)utput|(io)nputoutput) [(s)imple|(c)ount |(m)atch |captu(r)e |(p)rint]] + + Adds an input/output watch for the given I/O port. + + If (i)nput, (o)utput or (io)nputoutput are specified then the watch also triggers an event (see below) when the + port is read (input), written (output) or either read or written, respectively. + If (n)one is specified (the default), the watch does not trigger and simply remembers the last read/write for the + given port (details about which are visible when listing port watches). + + Types of watch (default is simple): + (s)imple - if the watch is triggered, execution always breaks, + (c)ount - if the watch is triggered the number of times specified by count, execution breaks, + (m)atch - if the watch is triggered, execution breaks if the series of values input/output matches the given + sequence of data (comma separated), + captu(r)e - if the watch is triggered, the value inputted/outputted is recorded in a history whose maximum length + is as specified, + (p)rint - if the watch is triggered, a message with the value inputted/outputted is printed to the console. + +rpw removeportwatch (#|) + + Removes the port watch with the given number or for the given port. + +rapw removeallportwatches + + Removes all port watches for the current CPU. + +General +-------- + +p print[.=v] [(h)ex|hex(z)ero|hexdo(l)lar|hex(p)osth|(d)ecimal|(b)inary] + + Prints the given expression as a number in the given format. If no format is supplied, the default data format is used. + Currently the "expression" can just be a number, label or register name. + + The size specifier may be a number or (b)yte, (w)ord, (l)ong or (v)erylong. The default if omitted depends on the + type of expression. + +cfg configure analysis [(o)n|of(f)] + addrfmt [(h)ex|hex(z)ero|hexdo(l)lar|hex(p)osth|(d)ecimal|(b)inary] + portfmt [(h)ex|hex(z)ero|hexdo(l)lar|hex(p)osth|(d)ecimal|(b)inary] + datafmt [(h)ex|hex(z)ero|hexdo(l)lar|hex(p)osth|(d)ecimal|(b)inary] + showlabels [(o)n|of(f)] + showopcodes [(o)n|of(f)] + membytesrow [] + + Configures the debugger. + If no arguments are passed, it outputs all the current settings. + If a setting is supplied, it outputs the current value for the given setting. + If both a setting and a value are supplied, it sets the current value for the given setting. + +ls loadstate + + Loads the debugger state (custom labels and code comments) from the given file. + +ss savestate + + Save the debugger state (custom labels and code comments) to the given file. + +h help + + Prints the available commands. + +x exit + + Exits the debugger and emulator. + +Emulator +-------- + +les loademustate + + Loads the emulator state from the given file. + +ses saveemustate + + Saves the emulator state to the given file. + +res resetemu + + Resets the emulator. + +Inputs +------ + +lip listinputs + + Lists all available inputs for the current game. + +pip printinput (|