mpw/bin/debugger.cpp

1316 lines
24 KiB
C++
Raw Permalink Normal View History

2013-07-30 05:06:19 +00:00
/*
* 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:
2013-07-30 05:06:19 +00:00
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
2013-07-30 05:06:19 +00:00
* 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.
2013-07-30 05:06:19 +00:00
*
* 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>
2013-07-04 04:29:09 +00:00
#include <cstdlib>
2015-02-17 00:23:12 +00:00
#include <algorithm>
2013-07-04 04:29:09 +00:00
2013-07-04 16:52:35 +00:00
#include <signal.h>
#include <string>
#include <vector>
2013-07-04 16:12:44 +00:00
#include <array>
2013-07-04 04:29:09 +00:00
#include <unordered_set>
2013-07-04 16:12:44 +00:00
#include <unordered_map>
2013-08-19 01:07:24 +00:00
#include <map>
#include <deque>
2013-07-04 16:12:44 +00:00
2013-07-04 04:29:09 +00:00
#include <bitset>
#include <readline/readline.h>
#include "loader.h"
2013-07-05 17:54:15 +00:00
#include "address_map.h"
#include "debugger.h"
2014-12-30 19:09:07 +00:00
#include "debugger_internal.h"
2015-01-06 01:28:56 +00:00
#include "template.h"
2013-07-04 04:29:09 +00:00
#include <cpu/defs.h>
#include <cpu/CpuModule.h>
2013-07-04 04:29:09 +00:00
#include <macos/traps.h>
#include <macos/sysequ.h>
#include <macos/errors.h>
2013-07-04 04:29:09 +00:00
2013-07-08 03:39:53 +00:00
#include <mpw/mpw.h>
2014-12-30 19:09:07 +00:00
#include <toolbox/loader.h>
#include <toolbox/mm.h>
2013-07-04 16:52:35 +00:00
namespace {
2014-12-30 19:09:07 +00:00
using namespace Debug::Internal;
2013-07-13 17:42:19 +00:00
const uint32_t kGlobalSize = 0x10000;
2015-01-06 01:28:56 +00:00
const uint32_t kBackTraceSize = 50;
2013-07-13 17:42:19 +00:00
2013-07-04 16:52:35 +00:00
bool sigInt = false;
2013-07-13 17:42:19 +00:00
bool memBreak = false;
2013-07-04 16:52:35 +00:00
void sigIntHandler(int)
{
sigInt = true;
}
2013-07-05 17:54:15 +00:00
AddressMap brkMap; // address breaks
AddressMap rbrkMap; // read breaks.
AddressMap wbrkMap; // write breaks.
ToolMap tbrkMap; // tool breaks.
2013-07-04 16:12:44 +00:00
2015-01-06 01:28:56 +00:00
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;
2015-01-06 01:28:56 +00:00
2013-07-04 04:29:09 +00:00
2013-07-05 17:54:44 +00:00
void printMacsbug(uint32_t pc, uint32_t opcode, uint32_t *newPC = nullptr)
2013-07-05 17:54:44 +00:00
{
// pc is actually pc after the opcode.
2013-07-05 17:54:44 +00:00
unsigned mboffset;
switch(opcode)
{
case 0x4E75: // rts
case 0x4ED0: // jmp (a0)
mboffset = 2;
break;
case 0x4E74: // rtd #
mboffset = 4;
break;
2013-07-05 17:54:44 +00:00
default:
return;
break;
}
pc += mboffset;
// check for MacsBug name after rts.
std::string s;
unsigned b = Debug::ReadByte(pc);
2013-09-02 02:27:09 +00:00
if (b >= 0x80 && b <= 0x9f)
2013-07-05 17:54:44 +00:00
{
b -= 0x80;
pc++;
2013-09-02 02:27:09 +00:00
if (!b) b = Debug::ReadByte(pc++);
2013-07-05 17:54:44 +00:00
s.reserve(b);
for (unsigned i = 0; i < b; ++i)
{
s.push_back(Debug::ReadByte(pc++));
2013-07-05 17:54:44 +00:00
}
printf(" %s\n\n", s.c_str());
// word-align
pc = pc + 1 & ~0x01;
// and possibly a zero-word after it.
if (Debug::ReadWord(pc) == 0x0000) pc += 2;
if (newPC) *newPC = pc;
2013-07-05 17:54:44 +00:00
}
}
2013-07-13 17:46:54 +00:00
// TODO -- state indicator for code/data
2013-07-04 16:13:31 +00:00
uint32_t disasm(uint32_t pc, uint16_t *op = nullptr)
{
2013-07-04 04:29:09 +00:00
2013-07-04 16:13:31 +00:00
static char strings[4][256];
2013-07-04 16:13:31 +00:00
if (pc >= Flags.memorySize)
{
2013-07-04 16:13:31 +00:00
if (op) *op = 0;
return pc;
}
uint16_t opcode = Debug::ReadWord(pc);
2013-07-04 16:13:31 +00:00
if (op) *op = opcode;
2013-07-04 16:13:31 +00:00
if ((opcode & 0xf000) == 0xa000)
2013-07-04 04:29:09 +00:00
{
2013-07-04 16:13:31 +00:00
const char *name;
name = TrapName(opcode);
if (name)
{
printf("$%08X %-51s ; %04X\n", pc, name, opcode);
}
else
{
printf("$%08X Tool #$%04X ; %04X\n",
2013-07-04 16:13:31 +00:00
pc, opcode, opcode);
}
pc += 2;
return pc;
2013-07-04 04:29:09 +00:00
}
2013-07-04 16:13:31 +00:00
for (unsigned j = 0; j < 4; ++j) strings[j][0] = 0;
2013-07-04 04:29:09 +00:00
2013-07-05 17:54:44 +00:00
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;
}
}
2013-07-04 16:13:31 +00:00
printf("%s %-10s %-40s ; %s\n", strings[0], strings[2], strings[3], strings[1]);
2013-07-04 04:29:09 +00:00
printMacsbug(pc, opcode, &newpc);
2013-07-05 17:54:44 +00:00
return newpc;
2013-07-04 04:29:09 +00:00
}
2013-07-04 16:52:35 +00:00
bool step(bool trace)
{
// return false to break (toolbreak, address break, etc.)
uint16_t op;
2013-07-13 17:42:19 +00:00
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;
2013-07-04 16:52:35 +00:00
cpuExecuteInstruction();
2013-07-04 16:52:35 +00:00
uint32_t pc = cpuGetPC();
2013-09-01 19:32:39 +00:00
2013-07-04 16:52:35 +00:00
if (trace) disasm(pc, &op);
else op = Debug::ReadWord(pc);
2013-07-04 16:52:35 +00:00
2013-07-05 17:54:44 +00:00
if (Flags.traceMacsbug && !trace)
printMacsbug(pc, op);
2013-07-04 16:52:35 +00:00
// will this also be set by an interrupt?
if (cpuGetStop())
{
if (!trace) disasm(pc);
printf("CPU stopped\n");
return false;
2013-07-04 16:52:35 +00:00
}
if (sigInt)
{
if (!trace) disasm(pc);
printf("^C break\n");
sigInt = false;
return false;
2013-07-04 16:52:35 +00:00
}
2013-07-13 17:42:19 +00:00
if (memBreak)
{
2013-09-01 19:32:39 +00:00
if (!trace) disasm(pc);
2013-07-13 17:42:19 +00:00
printf("Memory break\n");
memBreak = false;
return false;
}
2013-07-04 16:52:35 +00:00
// check for pc breaks
2013-07-05 17:54:15 +00:00
if (brkMap.lookup(pc))
2013-07-04 16:52:35 +00:00
{
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)
{
2013-07-05 17:54:15 +00:00
if (tbrkMap.lookup(op))
2013-07-04 16:52:35 +00:00
{
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;
}
2013-09-01 19:32:39 +00:00
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;
2013-09-01 19:32:39 +00:00
default:
fprintf(stdout, "\n");
break;
}
}
2013-07-13 17:42:19 +00:00
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)
{
2013-09-01 19:32:39 +00:00
LogWrite(size, value);
2013-07-13 17:42:19 +00:00
}
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;
2013-09-01 19:32:39 +00:00
printf("Memory Break $%08x - ", address);
LogWrite(size, value);
2013-07-13 17:42:19 +00:00
// todo -- print previous value, old value.
memBreak = true;
}
else
{
if (!rbrkMap.lookup(address)) return;
2013-09-01 19:32:39 +00:00
printf("Memory Break $%08x - read %d bytes\n", address, size);
2013-07-13 17:42:19 +00:00
memBreak = true;
}
}
2013-07-04 16:52:35 +00:00
}
#pragma mark - Debugger
namespace Debug {
2013-07-04 16:13:31 +00:00
2015-01-06 01:28:56 +00:00
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()
{
printf("help\n");
printf("break expression\n");
2013-07-04 04:29:09 +00:00
printf("step\n");
printf("continue\n");
printf("\n");
printf("print expression\n");
printf("list expression\n");
printf("dump expression\n");
printf("register=expression\n");
printf("bt | backtrace -- print cpu backtrace\n");
printf("expression;h -- print hexdump\n");
printf("expression;i -- print information\n");
printf("expression;l -- print assembly listing\n");
printf("\n");
printf("registers: a0-7, d0-7, pc, sp, fp, csr\n");
printf("\n");
}
void Print(uint32_t data)
{
2013-07-10 03:26:21 +00:00
// 32-bit unsigned int
2013-07-19 23:58:30 +00:00
printf("$%08x", data);
// 32-but unsigned
printf(" %12u", data);
2013-07-10 03:26:21 +00:00
// 32-bit signed int
2013-07-19 23:58:30 +00:00
int32_t negValue = 0;
if (data & 0x80000000)
2013-07-19 23:58:30 +00:00
negValue = (int32_t)data;
2013-07-10 03:26:21 +00:00
if ((data & 0xffff8000) == 0x8000)
2013-07-19 23:58:30 +00:00
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());
}
2013-07-10 03:26:21 +00:00
// 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;
2013-07-10 03:26:21 +00:00
case 0x08:
if (data <= 0xff)
printf(" '%s'", tmp + 3);
break;
}
2013-07-19 23:58:30 +00:00
2013-07-13 17:46:54 +00:00
2013-07-10 03:26:21 +00:00
printf("\n");
}
2013-07-04 16:13:31 +00:00
// grr... need separate count/range options.
void List(uint32_t pc, int count)
2013-07-04 04:29:09 +00:00
{
// TODO -- if no address, use previous address.
if (pc & 0x01)
2013-07-04 04:29:09 +00:00
{
printf("address is not aligned: $%08x\n", pc);
return;
}
2013-07-04 04:29:09 +00:00
for (int i = 0; i < count; ++i)
{
if (pc >= Flags.memorySize) break;
pc = disasm(pc);
2013-07-04 04:29:09 +00:00
}
}
void List(uint32_t pc, uint32_t endpc)
{
2015-02-07 00:56:57 +00:00
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);
}
2013-07-04 04:29:09 +00:00
}
const char *srBits(uint16_t sr)
2013-07-04 04:29:09 +00:00
{
static char srbits[20];
2013-07-04 16:13:31 +00:00
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]
);
2015-01-06 01:28:56 +00:00
printf("PC: %08x CSR: %04x %s\n", i.pc, i.csr, srbits);
}
void PrintRegisters()
{
uint16_t sr = cpuGetSR();
const char *srbits = srBits(sr);
2013-07-04 16:13:31 +00:00
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),
2013-07-04 04:29:09 +00:00
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),
2013-07-04 04:29:09 +00:00
cpuGetAReg(4), cpuGetAReg(5), cpuGetAReg(6), cpuGetAReg(7)
);
2013-07-04 16:13:31 +00:00
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());
}