#ifdef SUPERMODEL_DEBUGGER #include "ConsoleDebugger.h" #include "CPUDebug.h" #include "CodeAnalyser.h" #include "Label.h" #include <string.h> namespace Debugger { CConsoleDebugger::CConsoleDebugger() : CDebugger(), m_listDism(0), m_listMem(0), m_analyseCode(true), m_addrFmt(HexDollar), m_portFmt(Decimal), m_dataFmt(HexDollar), m_showLabels(true), m_labelsOverAddr(true), m_showOpCodes(false), m_memBytesPerRow(12) { // } void CConsoleDebugger::WaitCommand(CCPUDebug *cpu) { m_cpu = cpu; UINT32 pc = m_cpu->pc; m_listDism = (m_cpu->instrCount > 0 && pc > 10 * m_cpu->minInstrLen ? pc - 10 * m_cpu->minInstrLen : 0); char bpChr; char addrStr[20]; char labelStr[13]; char opCodes[50]; int codesLen; char mnemonic[255]; char operands[255]; char cmd[255]; for (;;) { // Get code analyser and if available analyse code now if required if (m_analyseCode) { CCodeAnalyser *analyser = m_cpu->GetCodeAnalyser(); if (analyser->NeedsAnalysis()) { printf("Analysing %s...\n", m_cpu->name); analyser->AnalyseCode(); } } // Get details for current PC address bool hasLabel; if (m_cpu->instrCount > 0) { m_cpu->FormatAddress(addrStr, pc); hasLabel = GetLabelText(labelStr, 12, pc); codesLen = m_cpu->Disassemble(pc, mnemonic, operands); FormatOpCodes(opCodes, pc, abs(codesLen)); } else { addrStr[0] = '-'; addrStr[1] = '\0'; hasLabel = false; labelStr[0] = '\0'; opCodes[0] = '-'; opCodes[1] = '\0'; codesLen = 0; } CBreakpoint *bp = m_cpu->GetBreakpoint(pc); bpChr = (bp != NULL ? bp->symbol : ' '); // Output command prompt printf("%s%c", m_cpu->name, bpChr); if (m_showLabels) { if (m_labelsOverAddr) printf("%-12s ", (hasLabel ? labelStr : addrStr)); else printf("%s %-12s ", addrStr, labelStr); } else printf("%s ", addrStr); if (m_showOpCodes) printf("[%s] ", opCodes); if (codesLen > 0) printf("%-*s %s > ", (int)m_cpu->maxMnemLen, mnemonic, operands); else printf("??? > "); fflush(stdout); // Wait for command gets(cmd); if (cmd[0] == '\0') { m_cpu->SetStepMode(StepInto); break; } char *token = strtok(cmd, " "); if (ProcessToken(token, cmd)) break; pc = m_cpu->pc; } } // TODO - tidy up this function - it is a mess! bool CConsoleDebugger::ProcessToken(const char *token, const char *cmd) { UINT32 pc = m_cpu->pc; UINT32 addr; UINT16 portNum; char addrStr[50]; char portNumStr[50]; if (CheckToken(token, "x", "exit")) // exit { puts("Exiting..."); SetExit(); return true; } else if (CheckToken(token, "n", "next")) // next [COUNT] { // Parse arguments token = strtok(NULL, " "); if (token != NULL) { int count; if (!ParseInt(token, &count) || count <= 0) { puts("Enter a valid instruction count."); return false; } if (count > 1) printf("Running %d instructions.\n", count); m_cpu->SetCount(count); } else m_cpu->SetStepMode(StepInto); return true; } else if (CheckToken(token, "s", "stepover")) // stepover { m_cpu->SetStepMode(StepOver); return true; } else if (CheckToken(token, "si", "stepinto")) // stepinto { m_cpu->SetStepMode(StepInto); return true; } else if (CheckToken(token, "so", "stepout")) // stepout { m_cpu->SetStepMode(StepOut); return true; } else if (CheckToken(token, "c", "continue")) // continue [ADDR] { // Parse arguments token = strtok(NULL, " "); if (token != NULL) { if (!ParseAddress(m_cpu, token, &addr)) { puts("Enter a valid address."); return false; } m_cpu->FormatAddress(addrStr, addr); printf("Continuing until %s.\n", addrStr); m_cpu->SetUntil(addr); } else m_cpu->SetContinue(); return true; } else if (CheckToken(token, "lc", "listcpus")) // listcpus { ListCPUs(); } else if (CheckToken(token, "sc", "switchcpu")) // switchcpu (NAME|NUM) { // Parse arguments token = strtok(NULL, " "); if (token == NULL) { puts("Missing CPU name or number."); return false; } CCPUDebug *cpu = NULL; int cpuNum; if (ParseInt(token, &cpuNum)) { if (cpuNum >= 0 && cpuNum < (int)cpus.size()) cpu = cpus[cpuNum]; } if (cpu == NULL) { cpu = GetCPU(token); if (cpu == NULL) { puts("No CPU with that name or number."); return false; } } m_cpu = cpu; pc = cpu->pc; m_listDism = (cpu->instrCount > 0 && pc > 10 * cpu->minInstrLen ? pc - 10 * cpu->minInstrLen : 0); return false; } else if (CheckToken(token, "lr", "listregisters")) // listregisters { ListRegisters(); } else if (CheckToken(token, "le", "listexceptions")) // listexceptions { ListExceptions(); } else if (CheckToken(token, "li", "listinterrupts")) // listinterrupts { ListInterrupts(); } else if (CheckToken(token, "lo", "listios")) // listios { ListIOs(); } else if (CheckToken(token, "ln", "listregions")) // listregions { ListRegions(); } else if (CheckToken(token, "al", "addlabel")) // addlabel ADDR NAME { // Parse arguments token = strtok(NULL, " "); if (token == NULL) { puts("Missing label address."); return false; } else if (!ParseAddress(m_cpu, token, &addr)) { puts("Enter a valid label address."); return false; } token = strtok(NULL, " "); if (token == NULL) { puts("Missing label name."); return false; } const char *name = token; // Add label CLabel *label = m_cpu->AddLabel(addr, name); m_cpu->FormatAddress(addrStr, label->addr); printf("Label %s added at %s.\n", label->name, addrStr); } else if (CheckToken(token, "ll", "listlabels")) // listlabels { // Parse arguments token = strtok(NULL, " "); bool customLabels; bool autoLabels; if (token != NULL) { if (CheckToken(token, "a", "all")) { customLabels = true; autoLabels = true; } else if (CheckToken(token, "c", "custom")) { customLabels = true; autoLabels = false; } else if (CheckToken(token, "u", "auto")) { customLabels = false; autoLabels = true; } else { puts("Enter a valid mode (a)ll, (c)ustom or a(u)to."); return false; } } else { customLabels = true; autoLabels = true; } ELabelFlags autoLabelFlags = (autoLabels ? (ELabelFlags)(LFEntryPoint | LFExcepHandler | LFInterHandler | LFSubroutine) : LFNone); ListLabels(customLabels, autoLabelFlags); } else if (CheckToken(token, "rl", "removelabel")) { // Parse arguments token = strtok(NULL, " "); if (token == NULL) { puts("Missing mode (a)ll, (n)ame or a(d)dress."); return false; } if (CheckToken(token, "a", "all")) { m_cpu->RemoveAllLabels(); puts("All labels removed."); } else if (CheckToken(token, "n", "name")) { token = strtok(NULL, " "); if (token == NULL) { puts("Missing label name."); return false; } // Remove label if (m_cpu->RemoveLabel(token)) printf("Label [%s] removed.\n", token); else puts("Enter a valid label name."); } else if (CheckToken(token, "d", "address")) { token = strtok(NULL, " "); if (token == NULL) { puts("Missing label address."); return false; } if (!ParseAddress(m_cpu, token, &addr)) { puts("Enter a valid label address."); return false; } // Remove label m_cpu->FormatAddress(addrStr, addr); if (m_cpu->RemoveLabel(addr)) printf("Label at address %s removed.\n", addrStr); else printf("No label at address %s.\n", addrStr); } else { puts("Enter a valid mode (a)ll, (n)ame or a(d)dress."); return false; } } else if (CheckToken(token, "ac", "addcomment")) // addcomment ADDR TEXT... { // Parse arguments token = strtok(NULL, " "); if (token == NULL) { puts("Missing comment address."); return false; } else if (!ParseAddress(m_cpu, token, &addr)) { puts("Enter a valid comment address."); 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') { puts("Missing comment text."); return false; } // Add comment CComment *comment = m_cpu->AddComment(addr, text); m_cpu->FormatAddress(addrStr, comment->addr); printf("Comment added at %s.\n", addrStr); } else if (CheckToken(token, "rc", "removecomment")) // removecomment ADDR { // Parse arguments token = strtok(NULL, " "); if (token == NULL) { puts("Missing comment address."); return false; } else if (!ParseAddress(m_cpu, token, &addr)) { puts("Enter a valid comment address."); return false; } m_cpu->FormatAddress(addrStr, addr); if (m_cpu->RemoveComment(addr)) printf("Comment at address %s removed.\n", addrStr); else printf("No comment at address %s.\n", addrStr); } else if (CheckToken(token, "t", "trap") || // addtrap (e|i) ID CheckToken(token, "at", "addtrap")) { // Parse arguments token = strtok(NULL, " "); if (token == NULL) { puts("Missing type (e)xception or (i)interrupt"); return false; } if (CheckToken(token, "e", "exception")) { token = strtok(NULL, " "); if (token == NULL) { puts("Missing exception id."); return false; } CException *ex = m_cpu->GetException(token); if (ex == NULL) { puts("Enter a valid exception id."); return false; } if (!ex->trap) { ex->trap = true; printf("Trap added for exceptions of type %s (%s).\n", ex->id, ex->name); } } else if (CheckToken(token, "i", "interrupt")) { token = strtok(NULL, " "); if (token == NULL) { puts("Missing interrupt id."); return false; } CInterrupt *in = m_cpu->GetInterrupt(token); if (in == NULL) { puts("Enter a valid interrupt id."); return false; } if (!in->trap) { in->trap = true; printf("Trap added for interrupts of type %s (%s).\n", in->id, in->name); } } else { puts("Enter valid type (e)xception or (i)interrupt."); return false; } } else if (CheckToken(token, "rt", "removetrap")) // removetrap (e|i) (a|i) ID { // Parse arguments token = strtok(NULL, " "); if (token == NULL) { puts("Missing type (e)xception or (i)interrupt"); return false; } if (CheckToken(token, "e", "exception")) { token = strtok(NULL, " "); if (token == NULL) { puts("Missing mode (a)ll or (i)d."); return false; } if (CheckToken(token, "a", "all")) { bool removed = false; for (vector<CException*>::iterator it = m_cpu->exceps.begin(); it != m_cpu->exceps.end(); it++) { if ((*it)->trap) (*it)->trap = false; removed = true; } if (removed) puts("All exception traps removed."); } else if (CheckToken(token, "i", "id")) { token = strtok(NULL, " "); if (token == NULL) { puts("Missing exception id."); return false; } CException *ex = m_cpu->GetException(token); if (ex != NULL) { if (ex->trap) { ex->trap = false; printf("Trap for exception %s removed.\n", ex->name); } } else puts("Enter a valid exception id."); } else { puts("Enter valid mode (a)ll or (i)d."); return false; } } else if (CheckToken(token, "i", "interrupt")) { token = strtok(NULL, " "); if (token == NULL) { puts("Missing mode (a)ll or (i)d."); return false; } if (CheckToken(token, "a", "all")) { bool removed = false; for (vector<CInterrupt*>::iterator it = m_cpu->inters.begin(); it != m_cpu->inters.end(); it++) { if ((*it)->trap) (*it)->trap = false; removed = true; } if (removed) puts("All interrupt traps removed."); } else if (CheckToken(token, "i", "id")) { token = strtok(NULL, " "); if (token == NULL) { puts("Missing exception id."); return false; } CInterrupt *in = m_cpu->GetInterrupt(token); if (in != NULL) { if (in->trap) { in->trap = false; printf("Trap for interrupt %s removed.\n", in->name); } } else puts("Enter a valid interrupt id."); } else { puts("Enter valid mode (a)ll or (i)d."); return false; } } else { puts("Enter valid type (e)xception or (i)interrupt."); return false; } } else if (CheckToken(token, "w", "memwatch") || // addmemwatch ADDR [(n|r|w|rw) [SIZE [(s|c|m|h|f) [ARGS]]]] CheckToken(token, "aw", "addmemwatch")) { // Parse arguments token = strtok(NULL, " "); if (token == NULL) { puts("Missing address."); return false; } if (!ParseAddress(m_cpu, token, &addr)) { puts("Enter a valid address."); return false; } bool read; bool write; token = strtok(NULL, " "); if (token == NULL || CheckToken(token, "n", "none")) { read = false; write = false; } else if (CheckToken(token, "r", "read")) { read = true; write = false; } else if (CheckToken(token, "w", "write")) { read = false; write = true; } else if (CheckToken(token, "rw", "read/write") || CheckToken(token, "wr", "write/read")) { read = true; write = true; } else { puts("Enter valid read/write flags (n)one, (r)ead, (w)rite or (rw)ead/write."); return false; } unsigned size; token = strtok(NULL, " "); if (token != NULL) { if (!ParseDataSize(token, size)) return false; } else size = 1; // Add mem watch CWatch *watch; token = strtok(NULL, " "); if (token != NULL) { if (CheckToken(token, "s", "simple")) watch = m_cpu->AddSimpleMemWatch(addr, size, read, write); else if (CheckToken(token, "c", "count")) { token = strtok(NULL, " "); if (token == NULL) { puts("Missing watch count."); return false; } int count; if (!ParseInt(token, &count) || count <= 0) { puts("Enter a valid watch count."); return false; } watch = m_cpu->AddCountMemWatch(addr, size, read, write, count); } else if (CheckToken(token, "m", "match")) { vector<UINT64> dataSeq; while ((token = strtok(NULL, " ")) != NULL) { UINT64 data; if (m_cpu->ParseData(token, size, &data)) dataSeq.push_back(data); } if (dataSeq.size() == 0) { const char *sizeStr = GetSizeString(size); printf("Enter a sequence of %s data to match.", sizeStr); return false; } watch = m_cpu->AddMatchMemWatch(addr, size, read, write, dataSeq); } else if (CheckToken(token, "r", "capture")) { token = strtok(NULL, " "); if (token == NULL) { puts("Missing maximum capture length."); return false; } int maxLen; if (!ParseInt(token, &maxLen) || maxLen <= 0) { puts("Enter a valid maximum capture length."); return false; } watch = m_cpu->AddCaptureMemWatch(addr, size, read, write, maxLen); } else if (CheckToken(token, "p", "print")) watch = m_cpu->AddPrintMemWatch(addr, size, read, write); else { puts("Enter a valid watch type (s)imple, (c)ount, (m)atch, captu(r)e or (p)rint."); return false; } } else watch = m_cpu->AddSimpleMemWatch(addr, size, read, write); m_cpu->FormatAddress(addrStr, watch->addr); printf("Memory watch [%s] added at %s.\n", watch->type, addrStr); } else if (CheckToken(token, "lw", "listmemwatches")) { ListMemWatches(); } else if (CheckToken(token, "rw", "removememwatch")) // removememwatch (a|n|d) [NUM|ADDR] { // Parse arguments token = strtok(NULL, " "); if (token == NULL) { puts("Missing mode (a)ll, (n)umber or a(d)dress."); return false; } if (CheckToken(token, "a", "all")) { // Remove all memory watches vector<CWatch*> watches; GetAllMemWatches(watches); for (vector<CWatch*>::iterator it = watches.begin(); it != watches.end(); it++) m_cpu->RemoveWatch(*it); puts("All memory watches removed."); } else if (CheckToken(token, "n", "number")) { token = strtok(NULL, " "); if (token == NULL) { puts("Missing watch number."); return false; } // Remove memory watch by number vector<CWatch*> watches; GetAllMemWatches(watches); int number; if (ParseInt(token, &number) && number >= 0 && number < watches.size()) { m_cpu->RemoveWatch(watches[number]); printf("Memory watch %d removed.\n", number); } else puts("Enter a valid watch number."); } else if (CheckToken(token, "d", "address")) { token = strtok(NULL, " "); if (token == NULL) { puts("Missing address."); return false; } if (!ParseAddress(m_cpu, token, &addr)) { puts("Enter a valid address."); return false; } // Remove watch m_cpu->FormatAddress(addrStr, addr); if (m_cpu->RemoveMemWatch(addr, 1)) printf("Memory watch at address %s removed.\n", addrStr); else printf("No memory watch at address %s.\n", addrStr); } else { puts("Enter a valid mode (a)ll, (n)umber or a(d)dress."); return false; } } else if (CheckToken(token, "pw", "portwatch") || // addportwatch PORT [(i|o|io) [(s|c|m|h|f) [ARGS]]] CheckToken(token, "apw", "addportwatch")) { // Parse arguments token = strtok(NULL, " "); if (token == NULL) { puts("Missing port number."); return false; } if (!m_cpu->ParsePortNum(token, &portNum)) { puts("Enter a valid port number."); return false; } bool input = false; bool output = false; token = strtok(NULL, " "); if (token == NULL || CheckToken(token, "n", "none")) { input = false; output = false; } else if (CheckToken(token, "i", "input")) { input = true; output = false; } else if (CheckToken(token, "o", "output")) { input = false; output = true; } else if (CheckToken(token, "io", "input/output") || CheckToken(token, "oi", "output/input")) { input = true; output = true; } else { puts("Enter valid input/output flags (n)one, (i)nput, (o)utput or (io)nput/output."); return false; } // Add watch CPortIO *port = m_cpu->GetPortIO(portNum); CWatch *watch; token = strtok(NULL, " "); if (token != NULL) { if (CheckToken(token, "s", "simple")) watch = port->AddSimpleWatch(input, output); else if (CheckToken(token, "c", "count")) { token = strtok(NULL, " "); if (token == NULL) { puts("Missing watch count."); return false; } int count; if (!ParseInt(token, &count) || count <= 0) { puts("Enter a valid watch count."); return false; } watch = port->AddCountWatch(input, output, count); } else if (CheckToken(token, "m", "match")) { vector<UINT64> dataSeq; while ((token = strtok(NULL, " ")) != NULL) { UINT64 data; if (m_cpu->ParseData(token, port->dataSize, &data)) dataSeq.push_back(data); } if (dataSeq.size() == 0) { printf("Enter a sequence of %s to match.", GetSizeString(port->dataSize)); return false; } watch = port->AddMatchWatch(input, output, dataSeq); } else if (CheckToken(token, "r", "capture")) { token = strtok(NULL, " "); if (token == NULL) { puts("Missing maximum capture length."); return false; } int maxLen; if (!ParseInt(token, &maxLen) || maxLen <= 0) { puts("Enter a valid maximum capture length."); return false; } watch = port->AddCaptureWatch(input, output, maxLen); } else if (CheckToken(token, "p", "print")) watch = port->AddPrintWatch(input, output); else { puts("Enter a valid I/O watch type (s)imple, (c)ount, (m)atch, captu(r)e or (p)rint."); return false; } } else watch = port->AddSimpleWatch(input, output); m_cpu->FormatPortNum(portNumStr, portNum); printf("Port watch (%s) added for port %u.\n", watch->type, portNumStr); } else if (CheckToken(token, "lpw", "listportwatches")) // listportwatches { ListPortWatches(); } else if (CheckToken(token, "rpw", "removeportwatch")) // removeportwatch (a|n|p) [NUM|PORT] { // Parse arguments token = strtok(NULL, " "); if (token == NULL) { puts("Missing mode (a)ll, (n)umber or (p)ort."); return false; } if (CheckToken(token, "a", "all")) { // Remove all port watches vector<CWatch*> watches; GetAllPortWatches(watches); for (vector<CWatch*>::iterator it = watches.begin(); it != watches.end(); it++) m_cpu->RemoveWatch(*it); puts("All port watches removed."); } else if (CheckToken(token, "n", "number")) { token = strtok(NULL, " "); if (token == NULL) { puts("Missing watch number."); return false; } // Remove port watch by number vector<CWatch*> watches; GetAllPortWatches(watches); int number; if (ParseInt(token, &number) && number >= 0 && number < watches.size()) { m_cpu->RemoveWatch(watches[number]); printf("Port watch %d removed.\n", number); } else puts("Enter a valid port watch number."); } else if (CheckToken(token, "p", "port")) { token = strtok(NULL, " "); if (token == NULL) { puts("Missing port number."); return false; } if (!m_cpu->ParsePortNum(token, &portNum)) { puts("Enter a valid port number."); return false; } // Remove port watch by port number CPortIO *port = m_cpu->GetPortIO(portNum); m_cpu->FormatPortNum(portNumStr, portNum); if (port->RemoveWatch()) printf("Port watch for port %s removed.\n", portNumStr); else printf("No port watch for port %s.\n", portNumStr); } else { puts("Enter a valid mode (a)ll, (n)umber or (p)ort."); return false; } } else if (CheckToken(token, "b", "breakpoint") || // addbreakpoint [ADDR [(s|c) [ARGS]]] CheckToken(token, "ab", "addbreakpoint")) { // Parse arguments token = strtok(NULL, " "); CBreakpoint *bp; if (token != NULL) { if (!ParseAddress(m_cpu, token, &addr)) { puts("Enter a valid address."); return false; } token = strtok(NULL, " "); if (token != NULL) { if (CheckToken(token, "s", "simple")) bp = m_cpu->AddSimpleBreakpoint(addr); else if (CheckToken(token, "c", "count")) { token = strtok(NULL, " "); if (token == NULL) { puts("Missing breakpoint count."); return false; } int count; if (!ParseInt(token, &count) || count <= 0) { puts("Enter a valid breakpoint count."); return false; } bp = m_cpu->AddCountBreakpoint(addr, count); } else { puts("Enter a valid breakpoint type (s)imple or (c)ount."); return false; } } else bp = m_cpu->AddSimpleBreakpoint(addr); } else { if (m_cpu->instrCount == 0) { puts("Cannot set breakpoint at current position as processor not ready."); return false; } bp = m_cpu->AddSimpleBreakpoint(pc); } m_cpu->FormatAddress(addrStr, bp->addr); printf("Breakpoint [%s] added at %s.\n", bp->type, addrStr); } else if (CheckToken(token, "lb", "listbreakpoints")) // listbreakpoints { ListBreakpoints(); } else if (CheckToken(token, "rb", "removebreakpoint")) // removebreakpoint [(a|n|d) [NUM|ADDR]] { // Parse arguments token = strtok(NULL, " "); if (token != NULL) { if (CheckToken(token, "a", "all")) { m_cpu->RemoveAllBreakpoints(); puts("All breakpoints removed."); } else if (CheckToken(token, "n", "number")) { token = strtok(NULL, " "); if (token == NULL) { puts("Missing breakpoint number."); return false; } // Remove breakpoint int number; if (ParseInt(token, &number) && number >= 0 && number < m_cpu->bps.size()) { m_cpu->RemoveBreakpoint(m_cpu->bps[number]); printf("Breakpoint %d removed.\n", number); } else puts("Enter a valid breakpoint number."); } else if (CheckToken(token, "d", "address")) { token = strtok(NULL, " "); if (token == NULL) { puts("Missing breakpoint address."); return false; } if (!ParseAddress(m_cpu, token, &addr)) { puts("Enter a valid breakpoint address."); return false; } // Remove breakpoint m_cpu->FormatAddress(addrStr, addr); if (m_cpu->RemoveBreakpoint(addr)) printf("Breakpoint at address %s removed.\n", addrStr); else printf("No breakpoint at address %s.\n", addrStr); } else puts("Enter a valid mode (a)ll, (n)umber or a(d)dress."); } else { if (m_cpu->RemoveBreakpoint(pc)) puts("Current breakpoint removed."); } } else if (CheckToken(token, "m", "monitor") || // addmonitor REG CheckToken(token, "am", "addmonitor")) { // Parse arguments token = strtok(NULL, " "); if (token == NULL) { puts("Missing register name."); return false; } CRegMonitor *regMon = m_cpu->AddRegMonitor(token); if (regMon == NULL) { puts("Enter a valid register name."); return false; } printf("Monitor added to register %s.\n", regMon->reg->name); } else if (CheckToken(token, "lm", "listmonitors")) // listmonitors { ListMonitors(); } else if (CheckToken(token, "rm", "removemonitor")) // removemonitor (a|n) [REG] { // Parse arguments token = strtok(NULL, " "); if (token == NULL) { puts("Missing mode (a)ll or (n)ame."); return false; } if (CheckToken(token, "a", "all")) { m_cpu->RemoveAllRegMonitors(); puts("All register monitors removed."); } else if (CheckToken(token, "n", "name")) { token = strtok(NULL, " "); if (token == NULL) { puts("Missing register name."); return false; } CRegister *reg = m_cpu->GetRegister(token); if (reg != NULL) { m_cpu->RemoveRegMonitor(reg->name); printf("Monitor for register %s removed.\n", reg->name); } else puts("Enter a valid register name."); } else puts("Enter a valid mode (a)ll or (n)ame."); } else if (CheckToken(token, "l", "list") || // listdisassem [START [END]] CheckToken(token, "ld", "listdisassem")) { // Get start address UINT32 start; token = strtok(NULL, " "); if (token != NULL) { if (!ParseAddress(m_cpu, token, &start)) { puts("Enter a valid start address."); 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 (!ParseAddress(m_cpu, token, &end)) { puts("Enter a valid end address."); 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 [START [END]] { // Get start address UINT32 start; token = strtok(NULL, " "); if (token != NULL) { if (!ParseAddress(m_cpu, token, &start)) { puts("Enter a valid start address."); return false; } } else { // Default is end of last listing start = m_listMem; } // Get end address UINT32 end; token = strtok(NULL, " "); if (token != NULL) { if (!ParseAddress(m_cpu, token, &end)) { puts("Enter a valid end address."); return false; } } else // Default is 8 rows after start end = start + 8 * m_memBytesPerRow; // List the code m_listMem = ListMemory(start, end, m_memBytesPerRow); } else if (CheckToken(token, "p", "print")) // print EXPR { // Parse arguments token = strtok(NULL, " "); if (token == NULL) { puts("Missing argument."); return false; } UINT64 data; char str[255]; const char *result; if (ParseAddress(m_cpu, token, &addr)) { m_cpu->FormatAddress(str, addr, true, LFAll); if (stricmp(token, str) == 0) m_cpu->FormatAddress(str, addr, false, LFNone); result = str; } else if (m_cpu->ParseData(token, 8, &data)) { m_cpu->FormatData(str, 8, data); result = str; } else result = token; printf("%s = %s\n", token, result); } else if (CheckToken(token, "pr", "printregister")) // printregister REG { // Parse arguments token = strtok(NULL, " "); if (token == NULL) { puts("Missing register name."); return false; } CRegister *reg = m_cpu->GetRegister(token); if (reg == NULL) { printf("No register called '%s'.\n", token); return false; } char valStr[50]; reg->GetValue(valStr); printf("Register %s = %s\n", reg->name, valStr); } else if (CheckToken(token, "sr", "setregister")) // setregister REG VALUE { // Parse arguments token = strtok(NULL, " "); if (token == NULL) { puts("Missing register name."); return false; } CRegister *reg = m_cpu->GetRegister(token); if (reg == NULL) { printf("No register called '%s'.\n", token); return false; } token = strtok(NULL, " "); if (token == NULL) { puts("Missing value to set."); return false; } if (!reg->SetValue(token)) { printf("Unable to set value of register %s.\n", reg->name); return false; } char valStr[50]; reg->GetValue(valStr); printf("Set register %s to %s.\n", reg->name, valStr); } else if (CheckToken(token, "py", "printmemory")) // printmemory ADDR SIZE { // Parse arguments token = strtok(NULL, " "); if (token == NULL) { puts("Missing address."); return false; } if (!ParseAddress(m_cpu, token, &addr)) { puts("Enter a valid address."); return false; } token = strtok(NULL, " "); unsigned dataSize = -1; if (token != NULL) { if (!ParseDataSize(token, dataSize)) return false; } else dataSize = 1; const char *sizeStr = GetSizeString(dataSize); UINT64 data = m_cpu->ReadMem(addr, dataSize); char dataStr[50]; m_cpu->FormatData(dataStr, dataSize, data); m_cpu->FormatAddress(addrStr, addr); printf("%s data at %s = %s.\n", sizeStr, addrStr, dataStr); } else if (CheckToken(token, "sy", "setmemory")) // setmemory ADDR SIZE VALUE { // Parse arguments token = strtok(NULL, " "); if (token == NULL) { puts("Missing address."); return false; } if (!ParseAddress(m_cpu, token, &addr)) { puts("Enter a valid address."); return false; } token = strtok(NULL, " "); if (token == NULL) { puts("Missing a size (b)yte, (w)ord, (l)ong or (v)erylong or number (1, 2, 4 or 8)."); return false; } unsigned dataSize; if (!ParseDataSize(token, dataSize)) return false; token = strtok(NULL, " "); if (token == NULL) { puts("Missing value to set."); return false; } const char *sizeStr = GetSizeString(dataSize); UINT64 data; if (!m_cpu->ParseData(token, dataSize, &data)) { printf("Enter a valid %s value.\n", sizeStr); return false; } m_cpu->WriteMem(addr, dataSize, data); char dataStr[50]; m_cpu->FormatData(dataStr, dataSize, data); m_cpu->FormatAddress(addrStr, addr); printf("Set %s data at %s to %s.\n", sizeStr, addrStr, dataStr); } else if (CheckToken(token, "pp", "printio")) // printio PORT { // TODO - read I/O } else if (CheckToken(token, "sp", "sendio")) // sendio PORT VALUE { // TODO - send I/O } else if (CheckToken(token, "cfg", "configure")) // configure ... { // Parse arguments token = strtok(NULL, " "); if (token == NULL) { // If no arguments, then print out current configuration printf("Configuration:\n"); printf(" %-20s %-12s %s\n", "Code Analysis", (m_analyseCode ? "On" : "Off"), "(a)nalysis"); printf(" %-20s %-12s %s\n", "Address Format", GetFmtConfig(m_addrFmt), "a(d)dressfmt"); printf(" %-20s %-12s %s\n", "Port Format", GetFmtConfig(m_portFmt), "(p)ortfmt"); printf(" %-20s %-12s %s\n", "Data Format", GetFmtConfig(m_dataFmt), "da(t)afmt"); printf(" %-20s %-12s %s\n", "Show Labels", (m_showLabels ? "On" : "Off"), "show(l)abels"); printf(" %-20s %-12s %s\n", "Show Opcodes", (m_showOpCodes ? "On" : "Off"), "show(o)pcodes"); printf(" %-20s %-12u %s\n", "Mem Bytes Per Row", m_memBytesPerRow, "mem(b)ytesrow"); return false; } if (CheckToken(token, "a", "analysis")) { token = strtok(NULL, " "); SetBoolConfig(token, m_analyseCode); } else if (CheckToken(token, "d", "addressfmt")) { token = strtok(NULL, " "); SetFmtConfig(token, m_addrFmt); } else if (CheckToken(token, "p", "portfmt")) { token = strtok(NULL, " "); SetFmtConfig(token, m_portFmt); } else if (CheckToken(token, "t", "datafmt")) { token = strtok(NULL, " "); SetFmtConfig(token, m_dataFmt); } else if (CheckToken(token, "l", "showlabels")) { token = strtok(NULL, " "); SetBoolConfig(token, m_showLabels); } else if (CheckToken(token, "o", "showopcodes")) { token = strtok(NULL, " "); SetBoolConfig(token, m_showOpCodes); } else if (CheckToken(token, "b", "membytesrow")) { token = strtok(NULL, " "); SetNumConfig(token, m_memBytesPerRow); } else { puts("Enter a valid option (a)nalysis, a(d)dressfmt, (p)ortfmt, da(t)afmt."); return false; } } else if (CheckToken(token, "ls", "loadstate")) // loadstate FILENAME { // Parse arguments token = strtok(NULL, " "); if (token == NULL) { puts("Missing filename."); return false; } if (LoadState(token)) printf("Debugger state successfully loaded from <%s>\n", token); else printf("Unable to load debugger state from <%s>\n", token); } else if (CheckToken(token, "ss", "savestate")) // savestate FILENAME { // Parse arguments token = strtok(NULL, " "); if (token == NULL) { puts("Missing filename."); return false; } if (SaveState(token)) printf("Debugger state successfully saved to <%s>\n", token); else printf("Unable to save debugger state to <%s>\n", token); } else if (CheckToken(token, "h", "help")) // help { const char *fmt = " %-5s %-15s %s\n"; printf("Debugger Commands:\n"); printf(fmt, "x", "exit", ""); printf(fmt, "n", "next", "[COUNT]"); printf(fmt, "s", "stepover", ""); printf(fmt, "si", "stepinto", ""); printf(fmt, "so", "stepout", ""); printf(fmt, "si", "stepinto", ""); printf(fmt, "si", "continue", "[ADDR]"); printf(fmt, "lc", "listcpus", ""); printf(fmt, "sc", "switchcpu", "(NAME|NUM)"); printf(fmt, "lr", "listregisters", ""); printf(fmt, "le", "listexceptions", ""); printf(fmt, "li", "listinterrupts", ""); printf(fmt, "lo", "listios", ""); printf(fmt, "ln", "listregions", ""); printf(fmt, "al", "addlabel", "ADDR NAME"); printf(fmt, "ll", "listlabels", "[(a)ll|(c)ustom|a(u)to]"); printf(fmt, "rl", "removelabel", "((a)ll|(n)ame|a(d)dress) [NAME|ADDR]"); printf(fmt, "ac", "addcomment", "ADDR TEXT..."); printf(fmt, "rc", "removecomment", "ADDR"); printf(fmt, "t/at", "addtrap", "(e|i) ID"); // TODO - finish } else printf("Unknown command '%s'.\n", token); return false; } bool CConsoleDebugger::CheckToken(const char *str, const char *simple, const char *full) { return (stricmp(str, simple) == 0 || stricmp(str, full) == 0); } void CConsoleDebugger::Truncate(char *dst, size_t maxLen, const char *src) { strncpy(dst, src, maxLen); if (strlen(src) > maxLen) strncpy(&dst[maxLen - 3], "...", 3); dst[maxLen] = '\0'; } void CConsoleDebugger::FormatOpCodes(char *str, int addr, int codesLen) { char *p = str; int i = 0; while (i < codesLen) { UINT8 opCode = (UINT8)m_cpu->ReadMem(addr++, 1); sprintf(p, "%02X", opCode); p += 2; i++; } while (i++ < m_cpu->maxInstrLen) { *p++ = ' '; *p++ = ' '; } *p = '\0'; } bool CConsoleDebugger::GetLabelText(char *str, int maxLen, UINT32 addr) { char labelStr[255]; CLabel *label = m_cpu->GetLabel(addr); if (label != NULL) { Truncate(str, maxLen, label->name); return true; } if (m_analyseCode) { CCodeAnalyser *analyser = m_cpu->GetCodeAnalyser(); CAutoLabel *autoLabel = analyser->analysis->GetAutoLabel(addr); if (autoLabel != NULL && autoLabel->GetLabel(labelStr)) { Truncate(str, maxLen, labelStr); return true; } } str[0] = '\0'; 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 { puts("Enter a valid size (b)yte, (w)ord, (l)ong or (v)erylong or a number 1, 2, 4 or 8."); return false; } } bool CConsoleDebugger::SetBoolConfig(const char *str, bool &cfg) { if (str == NULL) { printf("Current setting: %-12s\n", (cfg ? "On" : "Off")); puts("Change setting with (o)n, o(f)f."); return false; } if (CheckToken(str, "o", "on")) { cfg = true; printf("Changed setting: On\n"); ApplyConfig(); return true; } else if (CheckToken(str, "f", "off")) { cfg = false; printf("Changed setting: Off\n"); ApplyConfig(); return true; } else { puts("Enter a valid setting (o)n, o(f)f."); return false; } } bool CConsoleDebugger::SetNumConfig(const char *str, unsigned &cfg) { if (str == NULL) { printf("Current setting: %-12u\n", cfg); return false; } int number; if (!ParseInt(str, &number)) { puts("Enter a valid number."); return false; } cfg = (unsigned)number; printf("Changed setting: %-12u\n", cfg); ApplyConfig(); return true; } const char *CConsoleDebugger::GetFmtConfig(EFormat fmt) { switch (fmt) { case Hex0x: return "Hex (0x00)"; case HexDollar: return "Hex ($00)"; case HexPostH: return "Hex (00h)"; case Decimal: return "Decimal"; case Binary: return "Binary"; default: return "-"; } } bool CConsoleDebugger::SetFmtConfig(const char *str, EFormat &cfg) { if (str == NULL) { printf("Current setting: %-12s\n", GetFmtConfig(cfg)); puts("Change setting with (h)ex, hex(z)ero, hexdo(l)ar, hex(p)osth, (d)ecimal, (b)inary."); 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 { puts("Enter a valid setting (h)ex, hex(z)ero, hexdo(l)ar, hex(p)osth, (d)ecimal, (b)inary."); return false; } printf("Changed setting: %-12s\n", GetFmtConfig(cfg)); ApplyConfig(); return true; } void CConsoleDebugger::ListCPUs() { puts("CPUs:"); if (cpus.size() == 0) { puts(" None"); return; } int num = 0; for (vector<CCPUDebug*>::iterator it = cpus.begin(); it != cpus.end(); it++) printf(" %d - %s\n", num++, (*it)->name); } void CConsoleDebugger::ListRegisters() { printf("%s Registers:\n", m_cpu->name); if (m_cpu->regs.size() == 0) { puts(" None"); return; } // Get groups vector<const char*> groups; vector<vector<CRegister*> > regsByGroup; size_t totalRows = 0; for (vector<CRegister*>::iterator it = m_cpu->regs.begin(); it != m_cpu->regs.end(); it++) { const char *group = (*it)->group; // TODO - find with stricmp rather than default find if (find(groups.begin(), groups.end(), group) == groups.end()) { groups.push_back(group); regsByGroup.resize(regsByGroup.size() + 1); } int index = find(groups.begin(), groups.end(), group) - groups.begin(); vector<CRegister*> *pRegsInGroup = ®sByGroup[index]; pRegsInGroup->push_back(*it); totalRows = max<size_t>(totalRows, pRegsInGroup->size()); } // Get max label and value widths in each group and print group headers size_t numGroups = groups.size(); vector<size_t> labelWidths(numGroups); vector<size_t> valueWidths(numGroups); vector<size_t> groupWidths(numGroups); char valStr[50]; for (size_t index = 0; index < numGroups; index++) { labelWidths[index] = 0; valueWidths[index] = 0; vector<CRegister*> *pRegsInGroup = ®sByGroup[index]; for (vector<CRegister*>::iterator it = pRegsInGroup->begin(); it != pRegsInGroup->end(); it++) { labelWidths[index] = max<size_t>(labelWidths[index], strlen((*it)->name)); (*it)->GetValue(valStr); valueWidths[index] = max<size_t>(valueWidths[index], strlen(valStr)); } const char *group = groups[index]; groupWidths[index] = max<size_t>(labelWidths[index] + valueWidths[index] + 3, strlen(group) + 1); printf("%-*s", (int)groupWidths[index], group); } puts(""); // Print rows of register values char rowStr[50]; for (size_t row = 0; row < totalRows; row++) { for (size_t index = 0; index < numGroups; index++) { vector<CRegister*> *pRegsInGroup = ®sByGroup[index]; if (row < pRegsInGroup->size()) { CRegister *reg = (*pRegsInGroup)[row]; reg->GetValue(valStr); bool hasMon = m_cpu->GetRegMonitor(reg->name) != NULL; sprintf(rowStr, "%c%-*s %-*s", (hasMon ? '*' : ' '), (int)labelWidths[index], reg->name, (int)valueWidths[index], valStr); } else rowStr[0] = '\0'; printf("%-*s", (int)groupWidths[index], rowStr); } puts(""); } } void CConsoleDebugger::ListExceptions() { printf("%s Exceptions:\n", m_cpu->name); if (m_cpu->exceps.size() == 0) { puts(" None"); return; } char addrStr[20]; UINT32 handlerAddr; for (vector<CException*>::iterator it = m_cpu->exceps.begin(); it != m_cpu->exceps.end(); it++) { if (m_cpu->GetHandlerAddr(*it, handlerAddr)) m_cpu->FormatAddress(addrStr, handlerAddr, true, (m_analyseCode ? LFExcepHandler : LFNone)); else addrStr[0] = '\0'; printf("%c%-12s %-30s %-12s %u\n", ((*it)->trap ? '*' : ' '), (*it)->id, (*it)->name, addrStr, (*it)->count); } } void CConsoleDebugger::ListInterrupts() { printf("%s Interrupts:\n", m_cpu->name); if (m_cpu->inters.size() == 0) { puts(" None"); return; } char addrStr[20]; UINT32 handlerAddr; for (vector<CInterrupt*>::iterator it = m_cpu->inters.begin(); it != m_cpu->inters.end(); it++) { if (m_cpu->GetHandlerAddr(*it, handlerAddr)) m_cpu->FormatAddress(addrStr, handlerAddr, true, (m_analyseCode ? LFInterHandler : LFNone)); else addrStr[0] = '\0'; printf("%c%-12s %-30s %-12s %u\n", ((*it)->trap ? '*' : ' '), (*it)->id, (*it)->name, addrStr, (*it)->count); } } void CConsoleDebugger::ListIOs() { printf("%s I/O Ports:\n", m_cpu->name); if (m_cpu->ios.size() == 0) { puts(" None"); return; } const char *group = NULL; char locStr[255]; char dirChar; char inStr[50]; char outStr[50]; for (vector<CIO*>::iterator it = m_cpu->ios.begin(); it != m_cpu->ios.end(); it++) { // Print group heading if starting a new group if (group == NULL || stricmp((*it)->group, group) != 0) { group = (*it)->group; printf(" %s:\n", group); } // Get location string (memory address or port number) (*it)->GetLocation(locStr); // See whether port was last read or written if ((*it)->inCount + (*it)->outCount > 0) dirChar = ((*it)->last == &(*it)->lastIn ? '<' : '>'); else dirChar = ' '; // Formatt last input if ((*it)->inCount > 0) m_cpu->FormatData(inStr, (*it)->dataSize, (*it)->lastIn); else { inStr[0] = '*'; inStr[1] = '\0'; } size_t inLen = strlen(inStr); int inLPad = 5 - (int)inLen / 2; int inRPad = 5 - (int)inLen + (int)inLen / 2; // Format last output if ((*it)->inCount > 0) m_cpu->FormatData(outStr, (*it)->dataSize, (*it)->lastOut); else { outStr[0] = '*'; outStr[1] = '\0'; } size_t outLen = strlen(outStr); int outLPad = 5 - (int)outLen / 2; int outRPad = 5 - (int)outLen + (int)outLen / 2; // Print details printf(" %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, ""); } } void CConsoleDebugger::ListRegions() { printf("%s Regions:\n", m_cpu->name); if (m_cpu->regions.size() == 0) { puts(" None"); return; } char startStr[255]; char endStr[255]; for (vector<CRegion*>::iterator it = m_cpu->regions.begin(); it != m_cpu->regions.end(); it++) { // Format start and end address m_cpu->FormatAddress(startStr, (*it)->addr); m_cpu->FormatAddress(endStr, (*it)->addrEnd); // Print details printf("%c%s-%s %s\n", ((*it)->isCode ? '*' : ' '), startStr, endStr, (*it)->name); } } void CConsoleDebugger::ListLabels(bool customLabels, ELabelFlags autoLabelFlags) { printf("%s Labels:\n", m_cpu->name); unsigned count = 0; char addrStr[20]; if (customLabels && m_cpu->labels.size() > 0) { puts(" Custom Labels:"); for (vector<CLabel*>::iterator it = m_cpu->labels.begin(); it != m_cpu->labels.end(); it++) { m_cpu->FormatAddress(addrStr, (*it)->addr); printf(" %s %s\n", addrStr, (*it)->name); count++; } } if (m_analyseCode) { char labelStr[255]; CCodeAnalyser *analyser = m_cpu->GetCodeAnalyser(); for (unsigned index = 0; index < CAutoLabel::numLabelFlags; index++) { ELabelFlags flag = CAutoLabel::GetLabelFlag(index); if (!(autoLabelFlags & flag)) continue; vector<CAutoLabel*> withFlag = analyser->analysis->GetAutoLabels(flag); if (withFlag.size() == 0) continue; const char *flagStr = CAutoLabel::GetFlagString(flag); printf(" %ss:\n", flagStr); for (vector<CAutoLabel*>::iterator it = withFlag.begin(); it != withFlag.end(); it++) { if (!(*it)->GetLabel(labelStr, flag)) continue; CBreakpoint *bp = m_cpu->GetBreakpoint((*it)->addr); char bpChr = (bp != NULL ? bp->symbol : ' '); m_cpu->FormatAddress(addrStr, (*it)->addr); printf(" %c%s %s\n", bpChr, addrStr, labelStr); count++; } } } if (count == 0) { puts(" None"); return; } } void CConsoleDebugger::GetAllMemWatches(vector<CWatch*> &watches) { for (vector<CWatch*>::iterator it = m_cpu->memWatches.begin(); it != m_cpu->memWatches.end(); it++) watches.push_back(*it); for (vector<CWatch*>::iterator it = m_cpu->ioWatches.begin(); it != m_cpu->ioWatches.end(); it++) { if (dynamic_cast<CMappedIO*>((*it)->io) != NULL) watches.push_back(*it); } } void CConsoleDebugger::ListMemWatches() { printf("%s Memory Watches:\n", m_cpu->name); vector<CWatch*> watches; GetAllMemWatches(watches); if (watches.size() == 0) { puts(" None"); return; } printf(" %-3s %-8s %-12s %-4s %-20s %s\n", "Num", "Type", "Address", "Trig", "Value", "Info"); char addrStr[20]; const char *trigStr; char valStr[20]; char infoStr[255]; unsigned wNum = 0; for (vector<CWatch*>::iterator it = watches.begin(); it != watches.end(); it++) { m_cpu->FormatAddress(addrStr, (*it)->addr, true); 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'; printf(" %-3u %-8s %-12s %-4s %-20s %s\n", wNum++, (*it)->type, addrStr, trigStr, valStr, infoStr); } } void CConsoleDebugger::GetAllPortWatches(vector<CWatch*> &watches) { for (vector<CWatch*>::iterator it = m_cpu->ioWatches.begin(); it != m_cpu->ioWatches.end(); it++) { if (dynamic_cast<CPortIO*>((*it)->io) != NULL) watches.push_back(*it); } } void CConsoleDebugger::ListPortWatches() { printf("%s I/O Port Watches:\n", m_cpu->name); vector<CWatch*> watches; GetAllPortWatches(watches); if (watches.size() == 0) { puts(" None"); return; } printf(" %-3s %-8s %-12s %-4s %-20s %s\n", "Num", "Type", "Location", "Trig", "Last In/Out", "Info"); char locStr[255]; const char *trigStr; char valStr[20]; char infoStr[255]; unsigned wNum; for (vector<CWatch*>::iterator it = watches.begin(); it != watches.end(); it++) { (*it)->io->GetLocation(locStr); if ((*it)->trigRead && (*it)->trigWrite) trigStr = "I/O"; else if ((*it)->trigRead) trigStr = "I"; else if ((*it)->trigWrite) trigStr = "O"; else trigStr = "-"; if ((*it)->readCount + (*it)->writeCount == 0) { valStr[0] = '-'; valStr[1] = '\0'; } else m_cpu->FormatData(valStr, (*it)->size, (*it)->GetValue()); if (!(*it)->GetInfo(infoStr)) infoStr[0] = '\0'; printf(" %-3u %-8s %-12s %-4s %-20s %s\n", wNum++, (*it)->type, locStr, trigStr, valStr, infoStr); } } void CConsoleDebugger::ListBreakpoints() { printf("%s Breakpoints:\n", m_cpu->name); if (m_cpu->bps.size() == 0) { puts(" None"); return; } char addrStr[20]; char infoStr[255]; unsigned bpNum = 0; for (vector<CBreakpoint*>::iterator it = m_cpu->bps.begin(); it != m_cpu->bps.end(); it++) { m_cpu->FormatAddress(addrStr, (*it)->addr, true, (m_analyseCode ? LFAll : LFNone)); if ((*it)->GetInfo(infoStr)) printf(" %u - %s breakpoint [%s] at %s\n", bpNum++, (*it)->type, infoStr, addrStr); else printf(" %u - %s breakpoint at %s\n", bpNum++, (*it)->type, addrStr); } } void CConsoleDebugger::ListMonitors() { printf("%s Register Monitors:\n", m_cpu->name); if (m_cpu->regMons.size() == 0) { puts(" None"); return; } char valStr[255]; int num = 0; for (vector<CRegMonitor*>::iterator it = m_cpu->regMons.begin(); it != m_cpu->regMons.end(); it++) { (*it)->GetBeforeValue(valStr); printf(" %d - %s change from: %s\n", num++, (*it)->reg->name, valStr); } } UINT32 CConsoleDebugger::ListDisassembly(UINT32 start, UINT32 end, unsigned numInstrs) { UINT32 addr; char startStr[20]; char endStr[20]; char opCodes[50]; char mnemonic[100]; char operands[155]; char addrStr[20]; char labelStr[13]; unsigned instr; UINT32 pc = m_cpu->pc; CCodeAnalyser *analyser = (m_analyseCode ? m_cpu->GetCodeAnalyser() : NULL); // Align address to instruction boundary start -= start%m_cpu->minInstrLen; if (analyser != NULL) { // Use code analyser to find valid start address if (!analyser->analysis->GetNextValidAddr(start)) return start; } else { // In the absence of code analyser, try to align code with current PC address if (m_cpu->instrCount > 0 && pc >= start && pc <= end) { while (start < end) { bool okay = false; addr = start; instr = 0; while (addr < end && instr < numInstrs) { if (addr == pc) { okay = true; break; } int codesLen = m_cpu->GetOpLength(addr); addr += abs(codesLen); instr++; } if (okay) break; start += m_cpu->minInstrLen; } } } // Get true end address addr = start; instr = 0; while (addr < end && instr < numInstrs) { // Get instruction length int codesLen = m_cpu->Disassemble(addr, mnemonic, operands); // Move onto next valid instruction address addr += abs(codesLen); if (analyser != NULL && !analyser->analysis->GetNextValidAddr(addr)) break; instr++; } end = addr; // Format start and end addresses and output title m_cpu->FormatAddress(startStr, start); m_cpu->FormatAddress(endStr, end); printf("%s Code %s - %s:\n", m_cpu->name, startStr, endStr); // Output the disassembly addr = start; instr = 0; while (addr < end) { // Add markers for current PC address and any breakpoints char ind[4]; if (m_cpu->instrCount > 0 && addr == pc) { ind[0] = '>'; ind[1] = '>'; } else { ind[0] = ' '; ind[1] = ' '; } CBreakpoint *bp = m_cpu->GetBreakpoint(addr); ind[2] = (bp != NULL ? bp->symbol : ' '); ind[3] = '\0'; // Format current address m_cpu->FormatAddress(addrStr, addr); // Get labels at address (if any) bool hasLabel = GetLabelText(labelStr, 12, addr); // Get mnemonic, operands and instruction length and print them int codesLen = m_cpu->Disassemble(addr, mnemonic, operands); FormatOpCodes(opCodes, addr, abs(codesLen)); // Get comment at address (if any) CComment *comment = m_cpu->GetComment(addr); // Output line printf("%s", ind); if (m_showLabels) { if (m_labelsOverAddr) printf("%-12s ", (hasLabel ? labelStr : addrStr)); else printf("%s %-12s ", addrStr, labelStr); } else printf("%s ", addrStr); if (m_showOpCodes) printf("[%s] ", opCodes); if (codesLen > 0) printf("%-*s %-20s", (int)m_cpu->maxMnemLen, mnemonic, operands); else printf("???"); if (comment != NULL) printf(" ; %s", comment->text); printf("\n"); // Move onto next valid instruction address addr += abs(codesLen); if (analyser != NULL && !analyser->analysis->GetNextValidAddr(addr)) break; } return end; } UINT32 CConsoleDebugger::ListMemory(UINT32 start, UINT32 end, unsigned bytesPerRow) { char startStr[255]; char endStr[255]; char addrStr[20]; char labelStr[13]; char wChar, dChar; UINT8 data; // Adjust start/end points to be on row boundary start -= (start % bytesPerRow); end += bytesPerRow - (end % bytesPerRow); // Format start and end addresses and output title m_cpu->FormatAddress(startStr, start); m_cpu->FormatAddress(endStr, end); printf("%s Memory %s - %s:\n", m_cpu->name, startStr, endStr); UINT32 addr = start; while (addr < end) { // TODO - check address going out of region or out of range // Format current address m_cpu->FormatAddress(addrStr, addr); // Get labels at address (if any) bool hasLabel = GetLabelText(labelStr, 12, addr); // Output line if (m_showLabels) { if (m_labelsOverAddr) printf(" %-12s", (hasLabel ? labelStr : addrStr)); else printf(" %s %-12s", addrStr, labelStr); } else printf(" %s%c", addrStr); UINT32 lAddr = addr; for (unsigned i = 0; i < bytesPerRow; i++) { CWatch *watch = m_cpu->GetMemWatch(lAddr, 1); // TODO - handling of mapped I/O //CMappedIO *io = m_cpu->GetMappedIO(lAddr); wChar = (watch != NULL ? watch->symbol : ' '); //if (io != NULL) // data = (UINT8)io->last; //else data = (UINT8)m_cpu->ReadMem(lAddr, 1); printf("%c%02X", wChar, data); lAddr++; } printf(" "); lAddr = addr; for (unsigned i = 0; i < bytesPerRow; i++) { // TODO - handling of mapped I/O //CMappedIO *io = m_cpu->GetMappedIO(lAddr); //if (io != NULL) // data = io->last; //else data = (UINT8)m_cpu->ReadMem(lAddr, 1); dChar = (data >= 32 && data <= 126 ? (char)data : '.'); printf("%c", dChar); } printf("\n"); addr += bytesPerRow; } return addr; } void CConsoleDebugger::Attach() { CDebugger::Attach(); ApplyConfig(); Attached(); } void CConsoleDebugger::Detach() { Detaching(); CDebugger::Detach(); } void CConsoleDebugger::ApplyConfig() { for (vector<CCPUDebug*>::iterator it = cpus.begin(); it != cpus.end(); it++) { (*it)->addrFmt = m_addrFmt; (*it)->portFmt = m_portFmt; (*it)->dataFmt = m_dataFmt; } } void CConsoleDebugger::Attached() { // } void CConsoleDebugger::Detaching() { // } void CConsoleDebugger::AnalysisUpdated(CCodeAnalyser *analyser) { // } void CConsoleDebugger::ExceptionTrapped(CException *ex) { char addrStr[255]; ex->cpu->FormatAddress(addrStr, ex->cpu->pc, true, (m_analyseCode ? LFExcepHandler : LFNone)); printf("Exception %s (%s) on %s trapped at %s.\n", ex->id, ex->name, ex->cpu->name, addrStr); } void CConsoleDebugger::InterruptTrapped(CInterrupt *in) { char addrStr[255]; in->cpu->FormatAddress(addrStr, in->cpu->pc, true, (m_analyseCode ? LFInterHandler : LFNone)); printf("Interrupt %s (%s) on %s trapped at %s.\n", in->id, in->name, in->cpu->name, addrStr); } void CConsoleDebugger::MemWatchTriggered(CWatch *watch, UINT32 addr, unsigned dataSize, UINT64 data, bool isRead) { const char *sizeStr = GetSizeString(dataSize); char dataStr[20]; m_cpu->FormatData(dataStr, dataSize, data); const char *rwStr = (isRead ? "read" : "write"); char addrStr[255]; char infoStr[255]; watch->cpu->FormatAddress(addrStr, addr, true); if (watch->GetInfo(infoStr)) printf("%s memory %s (%s) by %s at address %s triggered %s watch.\n", sizeStr, rwStr, dataStr, watch->cpu->name, addrStr, watch->type, infoStr); else printf("%s memory %s (%s) by %s at address %s triggered %s watch.\n", sizeStr, rwStr, dataStr, watch->cpu->name, addrStr, watch->type); } void CConsoleDebugger::IOWatchTriggered(CWatch *watch, CIO *io, UINT64 data, bool isInput) { const char *sizeStr = GetSizeString(io->dataSize); char dataStr[20]; 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]; watch->io->GetLocation(locStr); if (watch->GetInfo(infoStr)) printf("%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); else printf("%s I/O %s (%s) by %s %s %s triggered %s watch.\n", sizeStr, ioStr, dataStr, watch->cpu->name, tfStr, locStr, watch->type); } 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)) printf("%s reached %s breakpoint [%s] at %s.\n", bp->cpu->name, bp->type, infoStr, addrStr); else printf("%s reached %s breakpoint at %s.\n", bp->cpu->name, bp->type, addrStr); } 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); printf("Register %s of %s has changed value at %s to %s.\n", reg->name, reg->cpu->name, addrStr, valStr); } void CConsoleDebugger::ExecutionHalted(CCPUDebug *cpu, EHaltReason reason) { if (reason&HaltUser) { char addrStr[255]; cpu->FormatAddress(addrStr, cpu->pc, true, (m_analyseCode ? LFAll : LFNone)); printf("Execution halted on %s at %s.\n", cpu->name, addrStr); } } void CConsoleDebugger::WriteOut(CCPUDebug *cpu, const char *typeStr, const char *fmtStr, va_list vl) { if (cpu != NULL) printf("%s: ", cpu->name); if (typeStr != NULL) printf(" %s - ", typeStr); vprintf(fmtStr, vl); } void CConsoleDebugger::FlushOut(CCPUDebug *cpu) { fflush(stdout); } } #endif // SUPERMODEL_DEBUGGER