Files
mpw/bin/debugger.cpp
Kelvin Sherlock a6b58bf382 the loader "tool" was also doing macsbug decoding, so unify that as well.
This also fixes the interrupt bug with the debug shell, where it
manifested as a stack underflow warning.
2026-08-12 23:12:18 -04:00

1288 lines
24 KiB
C++

/*
* Copyright (c) 2013, Kelvin W Sherlock
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <cstdint>
#include <cctype>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <signal.h>
#include <string>
#include <vector>
#include <array>
#include <unordered_set>
#include <unordered_map>
#include <map>
#include <deque>
#include <bitset>
#include <readline/readline.h>
#include "loader.h"
#include "address_map.h"
#include "debugger.h"
#include "debugger_internal.h"
#include "template.h"
#include <cpu/defs.h>
#include <cpu/CpuModule.h>
#include <macos/traps.h>
#include <macos/sysequ.h>
#include <macos/errors.h>
#include <mpw/mpw.h>
#include <toolbox/loader.h>
#include <toolbox/mm.h>
namespace {
using namespace Debug::Internal;
const uint32_t kGlobalSize = 0x10000;
const uint32_t kBackTraceSize = 50;
bool sigInt = false;
bool memBreak = false;
void sigIntHandler(int)
{
sigInt = true;
}
AddressMap brkMap; // address breaks
AddressMap rbrkMap; // read breaks.
AddressMap wbrkMap; // write breaks.
ToolMap tbrkMap; // tool breaks.
std::unordered_map<std::string, Debug::Template> TemplateTable;
struct BackTraceInfo {
uint32_t a[8];
uint32_t d[8];
uint32_t pc;
uint16_t csr;
BackTraceInfo(bool populate = false) {
if (populate)
{
for (unsigned i = 0; i < 8; ++i) a[i] = cpuGetAReg(i);
for (unsigned i = 0; i < 8; ++i) d[i] = cpuGetDReg(i);
pc = cpuGetPC();
csr = cpuGetSR();
}
}
};
std::deque<BackTraceInfo> BackTrace;
// TODO -- state indicator for code/data
uint32_t disasm(uint32_t pc, uint16_t *op = nullptr)
{
static char strings[4][256];
if (pc >= Flags.memorySize)
{
if (op) *op = 0;
return pc;
}
uint16_t opcode = Debug::ReadWord(pc);
if (op) *op = opcode;
if ((opcode & 0xf000) == 0xa000)
{
const char *name;
name = TrapName(opcode);
if (name)
{
printf("$%08X %-51s ; %04X\n", pc, name, opcode);
}
else
{
printf("$%08X Tool #$%04X ; %04X\n",
pc, opcode, opcode);
}
pc += 2;
return pc;
}
for (unsigned j = 0; j < 4; ++j) strings[j][0] = 0;
uint32_t newpc = cpuDisOpcode(pc, strings[0], strings[1], strings[2], strings[3]);
// replace jsr address w/ jsr macsbug name, if possible.
// jsr offset(pc)
uint32_t address = 0;
switch (opcode)
{
case 0x4EBA: // jsr offset(pc)
{
int16_t offset = Debug::ReadWord(pc + 2);
address = pc + 2 + offset;
break;
}
case 0x4EB9: // jsr address
{
address = Debug::ReadLong(pc + 2);
break;
}
case 0x4EF9: // jmp address
{
address = Debug::ReadLong(pc + 2);
break;
}
case 0x4EAD: // jsr offset(a5)
{
// check if address is a jmp address (see above)
// and follow it. a5 should never change.
int16_t offset = Debug::ReadWord(pc + 2);
address = cpuGetAReg(5) + offset;
if (Debug::ReadWord(address) == 0x4EF9)
address = Debug::ReadLong(address + 2);
else address = 0;
break;
}
// consider checking branches?
}
if (address) {
auto iter = SymbolTableInvert.find(address);
if (iter != SymbolTableInvert.end())
{
strncpy(strings[3], iter->second.c_str(), 40);
strings[3][40] = 0;
}
}
printf("%s %-10s %-40s ; %s\n", strings[0], strings[2], strings[3], strings[1]);
int mboffset = Loader::Native::endOfModule(opcode);
std::string s;
if (mboffset && Loader::Native::validMacsBugSymbol(pc + mboffset, s, &newpc))
printf(" %s\n\n", s.c_str());
return newpc;
}
bool step(bool trace)
{
// return false to break (toolbreak, address break, etc.)
uint16_t op;
memBreak = false;
//uint32_t prevPC = cpuGetPC();
// backtracing. store contents before the instruction.
if (BackTrace.size() == kBackTraceSize)
{
BackTrace.pop_front();
}
BackTrace.emplace_back(true);
//BackTrace.back().pc = prevPC;
cpuExecuteInstruction();
uint32_t pc = cpuGetPC();
if (trace) disasm(pc, &op);
else op = Debug::ReadWord(pc);
if (Flags.traceMacsbug && !trace) {
std::string s;
int mboffset = Loader::Native::endOfModule(op);
if (mboffset && Loader::Native::validMacsBugSymbol(pc + mboffset, s))
fprintf(stderr, "%s\n", s.c_str());
}
// will this also be set by an interrupt?
if (cpuGetStop())
{
if (!trace) disasm(pc);
printf("CPU stopped\n");
return false;
}
if (sigInt)
{
if (!trace) disasm(pc);
printf("^C break\n");
sigInt = false;
return false;
}
if (memBreak)
{
if (!trace) disasm(pc);
printf("Memory break\n");
memBreak = false;
return false;
}
// check for pc breaks
if (brkMap.lookup(pc))
{
if (!trace) disasm(pc);
printf("Address break: $%08x\n", pc);
return false;
}
// todo -- instruction break for rts /rtn
// check for toolbreaks.
if ((op & 0xf000) == 0xa000)
{
if (tbrkMap.lookup(op))
{
if (!trace) disasm(pc);
printf("Tool break: $%04x\n", op);
return false;
}
}
if (pc > Flags.memorySize)
{
printf("PC out of range\n");
return false;
}
uint32_t sp = cpuGetAReg(7);
if (sp < Flags.stackRange.first)
{
printf("Stack overflow error\n");
return false;
}
if (sp > Flags.stackRange.second)
{
printf("Stack underflow error\n");
return false;
}
return true;
}
static void LogWrite(int size, uint32_t value)
{
fprintf(stdout, " write %d bytes", size);
switch(size)
{
case 1:
fprintf(stdout, " [%02x]\n", value);
break;
case 2:
fprintf(stdout, " [%04x]\n", value);
break;
case 3:
fprintf(stdout, " [%06x]\n", value);
break;
case 4:
fprintf(stdout, " [%08x]\n", value);
break;
default:
fprintf(stdout, "\n");
break;
}
}
void MemoryLogger(uint32_t address, int size, int readWrite, uint32_t value)
{
if (address < kGlobalSize && Flags.traceGlobals)
{
const char *name = GlobalName(address);
if (!name) name = "unknown";
fprintf(stdout, "%-20s %08x - ", name, address);
if (readWrite)
{
LogWrite(size, value);
}
else
{
fprintf(stdout, " read %d bytes\n", size);
}
}
// check for memory breaks.
if (readWrite)
{
// todo -- what if writing 1 byte 4-bit address?
// todo -- if not single stepping at time of break, should
// disasm the prev pc before printing.
if (!wbrkMap.lookup(address)) return;
printf("Memory Break $%08x - ", address);
LogWrite(size, value);
// todo -- print previous value, old value.
memBreak = true;
}
else
{
if (!rbrkMap.lookup(address)) return;
printf("Memory Break $%08x - read %d bytes\n", address, size);
memBreak = true;
}
}
}
#pragma mark - Debugger
namespace Debug {
std::string ReadPString(uint32_t address)
{
std::string tmp;
unsigned size = ReadByte(address++);
tmp.reserve(size);
for (unsigned i = 0; i < size; ++i)
tmp.push_back(ReadByte(address++));
return tmp;
}
std::string ReadCString(uint32_t address)
{
std::string tmp;
for (;;)
{
char c = ReadByte(address++);
if (!c) break;
tmp.push_back(c);
}
return tmp;
}
uint32_t ReadLong(uint32_t address)
{
uint32_t tmp = 0;
for (unsigned i = 0; i < 4; ++i)
{
if (address < Flags.memorySize)
tmp = (tmp << 8) | Flags.memory[address++];
}
return tmp;
}
uint16_t ReadWord(uint32_t address)
{
uint16_t tmp = 0;
for (unsigned i = 0; i < 2; ++i)
{
if (address < Flags.memorySize)
tmp = (tmp << 8) | Flags.memory[address++];
}
return tmp;
}
uint8_t ReadByte(uint32_t address)
{
if (address < Flags.memorySize)
return Flags.memory[address];
return 0;
}
void Help()
{
fputs(
"help\n"
"break expression -- set cpu break\n"
"mbreak expression -- set memory break (r+w)\n"
"rbreak expression -- set memory break (r)\n"
"wbreak expression -- set memory break (w)\n"
"tbreak expression -- set tool break\n"
"step\n"
"continue\n"
"bt | backtrace -- print cpu backtrace\n"
"\n"
"print expression\n"
"list expression\n"
"dump expression\n"
"register=expression\n"
"\n"
"expression;h -- print hexdump\n"
"expression;i -- print information\n"
"expression;l -- print assembly listing\n"
"expression;t template -- print template\n"
"* -- print registers\n"
"\n"
"registers: a0-7, d0-7, pc, sp, fp, csr\n"
"\n",
stdout);
}
void Print(uint32_t data)
{
// 32-bit unsigned int
printf("$%08x", data);
// 32-but unsigned
printf(" %12u", data);
// 32-bit signed int
int32_t negValue = 0;
if (data & 0x80000000)
negValue = (int32_t)data;
if ((data & 0xffff8000) == 0x8000)
negValue = (int16_t)data;
if (negValue != 0)
printf(" %12d", negValue);
else printf(" ");
// print binary value
{
std::string bins;
bins.reserve(32);
bins.push_back('%');
if (data > 0xffff)
{
for (unsigned i = 0, mask = 0x80000000; i < 16; ++i, mask >>= 1)
bins.push_back( data & mask ? '1' : '0');
}
if (data > 0xff)
{
for (unsigned i = 0, mask = 0x8000; i < 8; ++i, mask >>= 1)
bins.push_back( data & mask ? '1' : '0');
}
for (unsigned i = 0, mask = 0x80; i < 8; ++i, mask >>= 1)
bins.push_back( data & mask ? '1' : '0');
printf(" %33s", bins.c_str());
}
// 4-cc code? 2-cc code? 1-cc code?
char tmp[5];
int bits = 0;
tmp[0] = (data >> 24) & 0xff;
tmp[1] = (data >> 16) & 0xff;
tmp[2] = (data >> 8) & 0xff;
tmp[3] = (data >> 0) & 0xff;
tmp[4] = 0;
if (isprint(tmp[0])) bits |= (1 << 0);
if (isprint(tmp[1])) bits |= (1 << 1);
if (isprint(tmp[2])) bits |= (1 << 2);
if (isprint(tmp[3])) bits |= (1 << 3);
switch(bits)
{
case 0x0f:
printf(" '%s'", tmp);
break;
case 0x0e:
if (data <= 0xffffff)
printf(" '%s'", tmp + 1);
break;
case 0x0c:
if (data <= 0xffff)
printf(" '%s'", tmp + 2);
break;
case 0x08:
if (data <= 0xff)
printf(" '%s'", tmp + 3);
break;
}
printf("\n");
}
// grr... need separate count/range options.
void List(uint32_t pc, int count)
{
// TODO -- if no address, use previous address.
if (pc & 0x01)
{
printf("address is not aligned: $%08x\n", pc);
return;
}
for (int i = 0; i < count; ++i)
{
if (pc >= Flags.memorySize) break;
pc = disasm(pc);
}
}
void List(uint32_t pc, uint32_t endpc)
{
if ((int32_t)endpc < (int32_t)pc) return;
if (pc & 0x01)
{
printf("address is not aligned: $%08x\n", pc);
return;
}
while (pc <= endpc)
{
if (pc >= Flags.memorySize) break;
pc = disasm(pc);
}
}
const char *srBits(uint16_t sr)
{
static char srbits[20];
srbits[0] = sr & (1 << 15) ? 'T' : ' ';
srbits[1] = sr & (1 << 14) ? 'T' : ' ';
srbits[2] = sr & (1 << 13) ? 'S' : ' ';
srbits[3] = sr & (1 << 12) ? 'M' : ' ';
srbits[4] = ' ';
srbits[5] = sr & (1 << 10) ? 'I' : ' ';
srbits[6] = sr & (1 << 9) ? 'I' : ' ';
srbits[7] = sr & (1 << 8) ? 'I' : ' ';
srbits[8] = ' ';
srbits[9] = ' ';
srbits[10] = ' ';
srbits[11] = sr & (1 << 4) ? 'X' : ' ';
srbits[12] = sr & (1 << 3) ? 'N' : ' ';
srbits[13] = sr & (1 << 2) ? 'Z' : ' ';
srbits[14] = sr & (1 << 1) ? 'V' : ' ';
srbits[15] = sr & (1 << 0) ? 'C' : ' ';
srbits[16] = 0;
return srbits;
}
void PrintRegisters(const BackTraceInfo &i)
{
const char *srbits = srBits(i.csr);
printf(" 0 1 2 3 4 5 6 7\n");
printf("D: %08x %08x %08x %08x %08x %08x %08x %08x\n",
i.d[0], i.d[1], i.d[2], i.d[3],
i.d[4], i.d[5], i.d[6], i.d[7]
);
printf("A: %08x %08x %08x %08x %08x %08x %08x %08x\n",
i.a[0], i.a[1], i.a[2], i.a[3],
i.a[4], i.a[5], i.a[6], i.a[7]
);
printf("PC: %08x CSR: %04x %s\n", i.pc, i.csr, srbits);
}
void PrintRegisters()
{
uint16_t sr = cpuGetSR();
const char *srbits = srBits(sr);
printf(" 0 1 2 3 4 5 6 7\n");
printf("D: %08x %08x %08x %08x %08x %08x %08x %08x\n",
cpuGetDReg(0), cpuGetDReg(1), cpuGetDReg(2), cpuGetDReg(3),
cpuGetDReg(4), cpuGetDReg(5), cpuGetDReg(6), cpuGetDReg(7)
);
printf("A: %08x %08x %08x %08x %08x %08x %08x %08x\n",
cpuGetAReg(0), cpuGetAReg(1), cpuGetAReg(2), cpuGetAReg(3),
cpuGetAReg(4), cpuGetAReg(5), cpuGetAReg(6), cpuGetAReg(7)
);
printf("PC: %08X CSR: %04x %s\n", cpuGetPC(), sr, srbits);
}
void btdiff(const BackTraceInfo &prev, const BackTraceInfo &current)
{
bool nl = false;
for (unsigned i = 0; i < 8; ++i)
if (current.d[i] != prev.d[i]) {
printf(" D%u: %08x\n", i, current.d[i]);
nl = true;
}
for (unsigned i = 0; i < 8; ++i)
if (current.a[i] != prev.a[i]) {
printf(" A%u: %08x\n", i, current.a[i]);
nl = true;
}
// pc always changes, but it's included in the listing.
//printf("PC: %08x\n", current.pc);
if (current.csr != prev.csr) {
printf(" CSR: %04x %s\n", current.csr, srBits(current.csr));
nl = true;
}
if (nl) printf("\n");
}
void PrintBackTrace()
{
// backtrace.
auto iter = BackTrace.cbegin();
auto end = BackTrace.cend();
if (iter == end) return;
const BackTraceInfo *prev;
{
const BackTraceInfo &info = *iter;
// print all registers for the first entry.
// for subsequent entries, print changed registers.
//disasm(info.pc);
PrintRegisters(info);
printf("\n");
prev = &info;
++iter;
}
for ( ; iter != end; ++iter)
{
const BackTraceInfo &current = *iter;
disasm(prev->pc);
btdiff(*prev, current);
//
prev = &current;
}
// print current registers.
{
BackTraceInfo current(true);
disasm(prev->pc);
btdiff(*prev, current);
}
// finally, print the current instruction.
printf("---------\n");
disasm(cpuGetPC());
}
void ToolBreak(int32_t tool)
{
bool remove = false;
if (tool < 0)
{
tool = -tool;
remove = true;
}
if (tool >= 0xa000 && tool <= 0xafff)
{
if (remove) tbrkMap.remove(tool);
else tbrkMap.add(tool);
}
else
{
fprintf(stderr, "Invalid tool: $%04x\n", tool);
}
}
void ToolBreak()
{
// list all tool breaks.
if (!tbrkMap.size())
{
printf("No tool breaks\n");
return;
}
std::vector<unsigned> v;
v.reserve(tbrkMap.size());
for (auto kv : tbrkMap)
{
v.push_back(kv.first);
}
std::sort(v.begin(), v.end());
for (auto trap : v)
{
const char *name = TrapName(trap);
if (!name) name = "";
printf("$%04x %s\n", trap, name);
}
}
void Break(int32_t address)
{
// 24-bit only, - address to remove.
bool remove = false;
if (address < 0)
{
address = -address;
remove = true;
}
// 24-bit address only.
if ((address & 0xff000000) == 0)
{
if (remove) brkMap.remove(address);
else brkMap.add(address);
}
else
{
fprintf(stderr, "Invalid address: $%08x\n", address);
}
}
void Break()
{
// list all tool breaks.
if (!brkMap.size())
{
printf("No breaks\n");
return;
}
std::vector<unsigned> v;
v.reserve(brkMap.size());
for (auto kv : brkMap)
{
v.push_back(kv.first);
}
std::sort(v.begin(), v.end());
for (auto address : v)
{
printf("$%08x\n", address);
}
}
void ReadBreak()
{}
void WriteBreak()
{}
void ReadWriteBreak()
{
}
void ReadBreak(int32_t address)
{
// sanity check address?
if (address < 0) rbrkMap.remove(-address);
else rbrkMap.add(address);
}
void WriteBreak(int32_t address)
{
// sanity check address?
if (address < 0) wbrkMap.remove(-address);
else wbrkMap.add(address);
}
void ReadWriteBreak(int32_t address)
{
ReadBreak(address);
WriteBreak(address);
}
void Step(const Command &cmd)
{
// TODO - step n to step specified # of instructions.
// TODO -- step @address to step until address?
// disasm 1 line, execute it.
int count = 0;
if (cmd.argc == 1) count = (int)cmd.argv[0];
if (count < 1) count = 1;
// TODO -- move to common function...
for (int i = 0; i < count; ++i)
{
if (!step(true)) break;
}
}
void Continue(const Command &cmd)
{
while (step(false)) ;
}
void SetARegister(unsigned reg, uint32_t value)
{
if (reg > 7) return;
if (reg == 7)
{
// sp/7 must be aligned.
if (value & 0x01)
{
fprintf(stderr, "Address is not aligned: $%08x\n", value);
return;
}
}
if (value > Flags.memorySize)
{
fprintf(stderr, "Warning: address exceeeds memory size: $%08x\n", value);
}
cpuSetAReg(reg, value);
}
void SetDRegister(unsigned reg, uint32_t value)
{
if (reg > 7) return;
cpuSetDReg(reg, value);
}
void SetXRegister(unsigned reg, uint32_t value)
{
if (reg == 0)
{
if (value & 0x01)
{
fprintf(stderr, "Address is not aligned: $%08x\n", value);
return;
}
if (value > Flags.memorySize)
{
fprintf(stderr, "Warning: address exceeeds memory size: $%08x\n", value);
}
//cpuSetPC(value);
cpuInitializeFromNewPC(value);
return;
}
if (reg == 1)
{
cpuSetSR(value & 0xffff);
}
}
// todo -- return a range
uint32_t VariableGet(const std::string &s)
{
auto iter = SymbolTable.find(s);
if (iter == SymbolTable.end()) return 0;
return iter->second.first;
}
// TODO -- take a Token and construct a pair, if it's a range.
// var = expr : expr or var = expr@count
void VariableSet(const std::string &key, uint32_t value)
{
SymbolTable.emplace(key, std::make_pair(value, 0));
}
void Info(uint32_t address)
{
// print info on the value.
Print(address);
// 1. as a pointer.
MM::Native::MemoryInfo(address);
// 2. (todo) - check SymbolTable for procedure address.
for (const auto &kv : SymbolTable)
{
const auto &name = kv.first;
auto range = kv.second;
//printf("%s: %x %x\n", name.c_str(), range.first, range.second);
// range end may be 0
if ((address == range.first) || (address >= range.first && address < range.second))
{
uint32_t offset = address - range.first;
if (offset)
printf("Routine: %s+$%x\n", name.c_str(), offset);
else
printf("Routine: %s\n", name.c_str());
break;
}
}
// 2. as a tool trap.
if (address >= 0xa000 && address <= 0xafff)
{
const char *cp = TrapName(address);
if (cp)
printf("Tool: %s\n", cp);
}
// 3. as a global
if (address <= 0xffff)
{
const char *cp = GlobalName(address);
if (cp)
printf("Global: %s\n", cp);
}
// 4 as an error
// almost all are negative 16-bit values,
// but may also be a positive 16-bit value.
uint16_t error = 0;
if (address <= 0xffff) error = address;
if ((address & 0xffff8000) == 0xffff8000) error = address;
if (error)
{
const char *cp = ErrorName(error);
// find returns an iterator to the hash bucket, which may contain
// other errors.
auto range = ErrorTableInvert.equal_range(error);
for(auto iter = range.first; iter != range.second; ++iter) {
printf("Error: %s", iter->second.c_str());
if (cp) printf(" %s", cp);
printf("\n");
cp = nullptr;
}
}
}
void ApplyTemplate(int32_t address, const std::string &name)
{
// find the template..
auto iter = TemplateTable.find(name);
if (iter == TemplateTable.end()) {
fprintf(stderr, "Unknown template: %s\n", name.c_str());
return;
}
FieldEntry *e = iter->second;
ApplyTemplate(address, e);
}
namespace {
/*
* returns a list of possible matches.
* Item[0] is the longest match.
*/
char **mpw_attempted_completion_function(const char* text, int _start, int _end)
{
std::string s(text);
rl_attempted_completion_over = 1; // suppress filename completion if no results.
// returns iter to first element _not less_ than key
// ie, >= key.
auto iter = SymbolTable.lower_bound(s);
unsigned count = 0;
unsigned length = s.length();
auto begin = iter;
for (;;)
{
if (iter == SymbolTable.end()) break;
if (iter->first.compare(0, length, s) != 0) break;
++count;
++iter;
}
auto end = iter;
if (!count) return NULL;
if (count > 100) return NULL;
if (count == 1)
{
char **buffer = (char **)malloc(3 * sizeof(char *));
buffer[0] = strdup(begin->first.c_str()); // longest substring match
buffer[1] = strdup(begin->first.c_str()); // the match
buffer[2] = NULL;
return buffer;
}
char **buffer = (char **)malloc((count + 2) * sizeof(char *));
unsigned i = 0;
auto min_length = begin->first.length();
// item 0 is the longest match. (fill in later.)
buffer[i++] = NULL;
for (iter = begin; iter != end; ++iter)
{
buffer[i++] = strdup(iter->first.c_str());
min_length = std::min(min_length, iter->first.length());
}
buffer[i] = NULL;
// assume the first is the longest, then search until not true.
buffer[0] = strdup(begin->first.c_str());
for (unsigned i = length; ; ++i)
{
if (i >= min_length)
{
buffer[0][i] = 0;
break;
}
char c = buffer[0][i];
if (!c) break;
bool nomatch = false;
for (int j = 1; ; ++j)
{
char *cp = buffer[j];
if (!cp) break;
if (cp[i] != c) nomatch = true;
}
if (nomatch)
{
buffer[0][i] = 0;
break;
}
}
return buffer;
}
void readline_init()
{
rl_readline_name = (char *)"mpw";
rl_attempted_completion_function = mpw_attempted_completion_function;
}
}
// TODO -- RUN command - reload, re-initialize, re-execute
// TODO -- parser calls commands directly (except trace/step/run/etc)
void Shell()
{
char *cp;
readline_init();
add_history("!Andy, it still has history!");
Loader::Native::LoadDebugNames(SymbolTable);
LoadTrapFile(MPW::RootDirPathForFile("Errors.text"), ErrorTable);
LoadTrapFile(MPW::RootDirPathForFile("Globals.text"), GlobalTable);
LoadTrapFile(MPW::RootDirPathForFile("Traps.text"), TrapTable);
LoadTemplateFile(MPW::RootDirPathForFile("Templates.text"), TemplateTable);
// load the error code to error mnemonic
ErrorTableInvert.reserve(ErrorTable.size());
for (const auto &kv : ErrorTable) {
ErrorTableInvert.emplace(std::make_pair(kv.second, kv.first));
}
// address to function name.
SymbolTableInvert.reserve(SymbolTable.size());
for (const auto &kv : SymbolTable) {
SymbolTableInvert.emplace(std::make_pair(kv.second.first, kv.first));
}
// start it up
printf("MPW Debugger shell\n\n");
disasm(cpuGetPC());
signal(SIGINT, sigIntHandler);
memorySetLoggingFunc(MemoryLogger);
for(;;)
{
cp = readline("] ");
if (!cp)
{
printf("\n");
break; // prompt for exit?
}
// parse the command...
const char *iter = cp;
while (*iter && isspace(*iter)) ++iter;
if (*iter)
{
Command cmd;
std::memset(&cmd, 0, sizeof(cmd));
if (ParseLine(iter, &cmd))
{
switch(cmd.action)
{
case cmdNull:
break;
case cmdStep:
Step(cmd);
break;
case cmdContinue:
Continue(cmd);
break;
default:
Help();
break;
}
}
// don't add if same as previous entry.
HIST_ENTRY *he = current_history();
if (he == nullptr || strcmp(he->line, cp) != 0)
add_history(cp);
}
free(cp);
}
}
} // namespace Debugger