mirror of
https://github.com/dingusdev/dingusppc.git
synced 2024-11-17 00:07:20 +00:00
ebb51addd7
Part of the "debugger: Fix interrupt signal." commit.
817 lines
26 KiB
C++
817 lines
26 KiB
C++
/*
|
|
DingusPPC - The Experimental PowerPC Macintosh emulator
|
|
Copyright (C) 2018-24 divingkatae and maximum
|
|
(theweirdo) spatium
|
|
|
|
(Contact divingkatae#1017 or powermax#2286 on Discord for more info)
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <cpu/ppc/ppcdisasm.h>
|
|
#include <cpu/ppc/ppcemu.h>
|
|
#include <cpu/ppc/ppcmmu.h>
|
|
#include <devices/common/hwinterrupt.h>
|
|
#include <devices/common/ofnvram.h>
|
|
#include "memaccess.h"
|
|
#include <utils/profiler.h>
|
|
|
|
#include <array>
|
|
#include <cstring>
|
|
#include <fstream>
|
|
#include <iomanip>
|
|
#include <iostream>
|
|
#include <loguru.hpp>
|
|
#include <map>
|
|
#include <memory>
|
|
#include <sstream>
|
|
#include <stdio.h>
|
|
#include <string>
|
|
|
|
#ifdef DEBUG_CPU_INT
|
|
#include <machines/machinebase.h>
|
|
#include <devices/common/viacuda.h>
|
|
#endif
|
|
|
|
#ifdef ENABLE_68K_DEBUGGER // optionally defined in CMakeLists.txt
|
|
#include <capstone/capstone.h>
|
|
#endif
|
|
|
|
using namespace std;
|
|
|
|
static uint32_t str2addr(string& addr_str) {
|
|
try {
|
|
return (uint32_t)stoul(addr_str, NULL, 0);
|
|
} catch (invalid_argument& exc) {
|
|
throw invalid_argument(string("Cannot convert ") + addr_str);
|
|
}
|
|
}
|
|
|
|
static uint32_t str2num(string& num_str) {
|
|
try {
|
|
return (uint32_t)stol(num_str, NULL, 0);
|
|
} catch (invalid_argument& exc) {
|
|
throw invalid_argument(string("Cannot convert ") + num_str);
|
|
}
|
|
}
|
|
|
|
static void show_help() {
|
|
cout << "Debugger commands:" << endl;
|
|
cout << " step [N] -- execute single instruction" << endl;
|
|
cout << " N is an optional step count" << endl;
|
|
cout << " si [N] -- shortcut for step" << endl;
|
|
cout << " next -- same as step but treats subroutine calls" << endl;
|
|
cout << " as single instructions." << endl;
|
|
cout << " ni -- shortcut for next" << endl;
|
|
cout << " until X -- execute until address X is reached" << endl;
|
|
cout << " go -- exit debugger and continue emulator execution" << endl;
|
|
cout << " regs -- dump content of the GRPs" << endl;
|
|
cout << " mregs -- dump content of the MMU registers" << endl;
|
|
cout << " set R=X -- assign value X to register R" << endl;
|
|
cout << " if R=loglevel, set the internal" << endl;
|
|
cout << " log level to X whose range is -2...9" << endl;
|
|
cout << " dump NT,X -- dump N memory cells of size T at address X" << endl;
|
|
cout << " T can be b(byte), w(word), d(double)," << endl;
|
|
cout << " q(quad) or c(character)." << endl;
|
|
cout << " profile C N -- run subcommand C on profile N" << endl;
|
|
cout << " supported subcommands:" << endl;
|
|
cout << " 'show' - show profile report" << endl;
|
|
cout << " 'reset' - reset profile variables" << endl;
|
|
#ifdef PROFILER
|
|
cout << " profiler -- show stats related to the processor" << endl;
|
|
#endif
|
|
cout << " disas N,X -- disassemble N instructions starting at address X" << endl;
|
|
cout << " X can be any number or a known register name" << endl;
|
|
cout << " disas with no arguments defaults to disas 1,pc" << endl;
|
|
cout << " da N,X -- shortcut for disas" << endl;
|
|
#ifdef ENABLE_68K_DEBUGGER
|
|
cout << " context X -- switch to the debugging context X." << endl;
|
|
cout << " X can be either 'ppc' (default) or '68k'" << endl;
|
|
cout << " Use 68k for debugging emulated 68k code only." << endl;
|
|
#endif
|
|
cout << " printenv -- print current NVRAM settings." << endl;
|
|
cout << " setenv V N -- set NVRAM variable V to value N." << endl;
|
|
cout << " quit -- quit the debugger" << endl << endl;
|
|
cout << "Pressing ENTER will repeat last command." << endl;
|
|
}
|
|
|
|
#ifdef ENABLE_68K_DEBUGGER
|
|
|
|
static void disasm_68k(uint32_t count, uint32_t address) {
|
|
csh cs_handle;
|
|
uint8_t code[10];
|
|
size_t code_size;
|
|
uint64_t dis_addr;
|
|
|
|
if (cs_open(CS_ARCH_M68K, CS_MODE_M68K_040, &cs_handle) != CS_ERR_OK) {
|
|
cout << "Capstone initialization error" << endl;
|
|
return;
|
|
}
|
|
|
|
cs_insn* insn = cs_malloc(cs_handle);
|
|
|
|
for (; power_on && count > 0; count--) {
|
|
/* prefetch opcode bytes (a 68k instruction can occupy 2...10 bytes) */
|
|
for (int i = 0; i < sizeof(code); i++) {
|
|
code[i] = mem_read_dbg(address + i, 1);
|
|
}
|
|
|
|
const uint8_t *code_ptr = code;
|
|
code_size = sizeof(code);
|
|
dis_addr = address;
|
|
|
|
// catch and handle F-Traps (Nanokernel calls) ourselves because
|
|
// Capstone will likely return no meaningful assembly for them
|
|
if ((code[0] & 0xF0) == 0xF0) {
|
|
goto print_bin;
|
|
}
|
|
|
|
if (cs_disasm_iter(cs_handle, &code_ptr, &code_size, &dis_addr, insn)) {
|
|
cout << uppercase << hex << insn->address << " ";
|
|
cout << setfill(' ');
|
|
cout << setw(10) << left << insn->mnemonic << insn->op_str << endl;
|
|
address = dis_addr;
|
|
} else {
|
|
print_bin:
|
|
cout << uppercase << hex << address << " ";
|
|
cout << setfill(' ');
|
|
cout << setw(10) << left << "dc.w" << "$" << hex <<
|
|
((code[0] << 8) | code[1]) << endl;
|
|
address += 2;
|
|
}
|
|
}
|
|
|
|
cs_free(insn, 1);
|
|
cs_close(&cs_handle);
|
|
}
|
|
|
|
/* emulator opcode table size --> 512 KB */
|
|
#define EMU_68K_TABLE_SIZE 0x80000
|
|
|
|
/** Execute one emulated 68k instruction. */
|
|
void exec_single_68k()
|
|
{
|
|
uint32_t emu_table_virt, cur_68k_pc, cur_instr_tab_entry, ppc_pc;
|
|
|
|
/* PPC r24 contains 68k PC advanced by two bytes
|
|
as part of instruction prefetching */
|
|
cur_68k_pc = get_reg(string("R24")) - 2;
|
|
|
|
/* PPC r29 contains base address of the emulator opcode table */
|
|
emu_table_virt = get_reg(string("R29")) & 0xFFF80000;
|
|
|
|
/* calculate address of the current opcode table entry as follows:
|
|
get_word(68k_PC) * entry_size + table_base */
|
|
cur_instr_tab_entry = mmu_read_vmem<uint16_t>(cur_68k_pc) * 8 + emu_table_virt;
|
|
|
|
/* grab the PPC PC too */
|
|
ppc_pc = get_reg(string("PC"));
|
|
|
|
//printf("cur_instr_tab_entry = %X\n", cur_instr_tab_entry);
|
|
|
|
/* because the first two PPC instructions for each emulated 68k opcode
|
|
are resided in the emulator opcode table, we need to execute them
|
|
one by one until the execution goes outside the opcode table. */
|
|
while (power_on && ppc_pc >= cur_instr_tab_entry && ppc_pc < cur_instr_tab_entry + 8) {
|
|
ppc_exec_single();
|
|
ppc_pc = get_reg(string("PC"));
|
|
}
|
|
|
|
/* Getting here means we're outside the emualtor opcode table.
|
|
Execute PPC code until we hit the opcode table again. */
|
|
ppc_exec_dbg(emu_table_virt, EMU_68K_TABLE_SIZE - 1);
|
|
}
|
|
|
|
/** Execute emulated 68k code until target_addr is reached. */
|
|
void exec_until_68k(uint32_t target_addr)
|
|
{
|
|
uint32_t emu_table_virt, ppc_pc;
|
|
|
|
emu_table_virt = get_reg(string("R29")) & 0xFFF80000;
|
|
|
|
while (power_on && target_addr != (get_reg(string("R24")) - 2)) {
|
|
ppc_pc = static_cast<uint32_t>(get_reg(string("PC")));
|
|
|
|
if (ppc_pc >= emu_table_virt && ppc_pc < (emu_table_virt + EMU_68K_TABLE_SIZE - 1)) {
|
|
ppc_exec_single();
|
|
} else {
|
|
ppc_exec_dbg(emu_table_virt, EMU_68K_TABLE_SIZE - 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
void print_68k_regs()
|
|
{
|
|
int i;
|
|
string reg;
|
|
|
|
for (i = 0; i < 8; i++) {
|
|
reg = "R" + to_string(i + 8);
|
|
cout << "D" << dec << i << " : " << uppercase << hex << get_reg(reg) << endl;
|
|
}
|
|
|
|
for (i = 0; i < 7; i++) {
|
|
reg = "R" + to_string(i + 16);
|
|
cout << "A" << dec << i << " : " << uppercase << hex << get_reg(reg) << endl;
|
|
}
|
|
|
|
cout << "A7 : " << uppercase << hex << get_reg(string("R1")) << endl;
|
|
|
|
cout << "PC: " << uppercase << hex << get_reg(string("R24")) - 2 << endl;
|
|
|
|
cout << "SR: " << uppercase << hex << ((get_reg("R25") & 0xFF) << 8) << endl;
|
|
|
|
cout << "CCR: " << uppercase << hex << get_reg(string("R26")) << endl;
|
|
}
|
|
|
|
#endif // ENABLE_68K_DEBUGGER
|
|
|
|
static void dump_mem(string& params) {
|
|
int cell_size, chars_per_line;
|
|
bool is_char;
|
|
uint32_t count, addr;
|
|
uint64_t val;
|
|
string num_type_str, addr_str;
|
|
size_t separator_pos;
|
|
|
|
separator_pos = params.find_first_of(",");
|
|
if (separator_pos == std::string::npos) {
|
|
cout << "dump: not enough arguments specified." << endl;
|
|
return;
|
|
}
|
|
|
|
num_type_str = params.substr(0, params.find_first_of(","));
|
|
addr_str = params.substr(params.find_first_of(",") + 1);
|
|
|
|
is_char = false;
|
|
|
|
switch (num_type_str.back()) {
|
|
case 'b':
|
|
case 'B':
|
|
cell_size = 1;
|
|
break;
|
|
case 'w':
|
|
case 'W':
|
|
cell_size = 2;
|
|
break;
|
|
case 'd':
|
|
case 'D':
|
|
cell_size = 4;
|
|
break;
|
|
case 'q':
|
|
case 'Q':
|
|
cell_size = 8;
|
|
break;
|
|
case 'c':
|
|
case 'C':
|
|
cell_size = 1;
|
|
is_char = true;
|
|
break;
|
|
default:
|
|
cout << "Invalid data type " << num_type_str << endl;
|
|
return;
|
|
}
|
|
|
|
try {
|
|
num_type_str = num_type_str.substr(0, num_type_str.length() - 1);
|
|
count = str2addr(num_type_str);
|
|
} catch (invalid_argument& exc) {
|
|
cout << exc.what() << endl;
|
|
return;
|
|
}
|
|
|
|
try {
|
|
addr = str2addr(addr_str);
|
|
} catch (invalid_argument& exc) {
|
|
try {
|
|
/* number conversion failed, trying reg name */
|
|
addr = get_reg(addr_str);
|
|
} catch (invalid_argument& exc) {
|
|
cout << exc.what() << endl;
|
|
return;
|
|
}
|
|
}
|
|
|
|
cout << "Dumping memory at address " << hex << addr << ":" << endl;
|
|
|
|
chars_per_line = 0;
|
|
|
|
try {
|
|
for (int i = 0; i < count; addr += cell_size, i++) {
|
|
if (chars_per_line + cell_size * 2 > 80) {
|
|
cout << endl;
|
|
chars_per_line = 0;
|
|
}
|
|
val = mem_read_dbg(addr, cell_size);
|
|
if (is_char) {
|
|
cout << (char)val;
|
|
chars_per_line += cell_size;
|
|
} else {
|
|
cout << setw(cell_size * 2) << setfill('0') << right;
|
|
cout << uppercase << hex << val << " ";
|
|
chars_per_line += cell_size * 2 + 2;
|
|
}
|
|
}
|
|
} catch (invalid_argument& exc) {
|
|
cout << exc.what() << endl;
|
|
return;
|
|
}
|
|
|
|
cout << endl << endl;
|
|
}
|
|
|
|
static void disasm(uint32_t count, uint32_t address) {
|
|
PPCDisasmContext ctx;
|
|
|
|
ctx.instr_addr = address;
|
|
ctx.simplified = true;
|
|
|
|
for (int i = 0; power_on && i < count; i++) {
|
|
ctx.instr_code = READ_DWORD_BE_A(mmu_translate_imem(ctx.instr_addr));
|
|
cout << uppercase << hex << ctx.instr_addr;
|
|
cout << " " << disassemble_single(&ctx) << endl;
|
|
}
|
|
}
|
|
|
|
static void print_gprs() {
|
|
string reg_name;
|
|
int i;
|
|
|
|
for (i = 0; i < 32; i++) {
|
|
reg_name = "R" + to_string(i);
|
|
|
|
cout << right << std::setw(3) << setfill(' ') << reg_name << " : " <<
|
|
setw(8) << setfill('0') << right << uppercase << hex << get_reg(reg_name) << setfill(' ');
|
|
|
|
if (i & 1) {
|
|
cout << endl;
|
|
} else {
|
|
cout << "\t\t";
|
|
}
|
|
}
|
|
|
|
array<string,6> sprs = {"PC", "LR", "CR", "CTR", "XER", "MSR"};
|
|
|
|
for (auto &spr : sprs) {
|
|
cout << right << std::setw(3) << setfill(' ') << spr << " : " <<
|
|
setw(8) << setfill('0') << uppercase << hex << get_reg(spr) << setfill(' ');
|
|
|
|
if (i & 1) {
|
|
cout << endl;
|
|
} else {
|
|
cout << "\t\t";
|
|
}
|
|
|
|
i++;
|
|
}
|
|
}
|
|
|
|
extern bool is_601;
|
|
|
|
static void print_mmu_regs()
|
|
{
|
|
printf("MSR : 0x%08X\n", ppc_state.msr);
|
|
|
|
printf("\nBAT registers:\n");
|
|
|
|
for (int i = 0; i < 4; i++) {
|
|
printf("IBAT%dU : 0x%08X, IBAT%dL : 0x%08X\n",
|
|
i, ppc_state.spr[528+i*2],
|
|
i, ppc_state.spr[529+i*2]);
|
|
}
|
|
|
|
if (!is_601) {
|
|
for (int i = 0; i < 4; i++) {
|
|
printf("DBAT%dU : 0x%08X, DBAT%dL : 0x%08X\n",
|
|
i, ppc_state.spr[536+i*2],
|
|
i, ppc_state.spr[537+i*2]);
|
|
}
|
|
}
|
|
|
|
printf("\n");
|
|
printf("SDR1 : 0x%08X\n", ppc_state.spr[SPR::SDR1]);
|
|
printf("\nSegment registers:\n");
|
|
|
|
for (int i = 0; i < 16; i++) {
|
|
printf("SR%-2d : 0x%08X\n", i, ppc_state.sr[i]);
|
|
}
|
|
}
|
|
|
|
#ifndef _WIN32
|
|
|
|
#include <signal.h>
|
|
#include <termios.h>
|
|
#include <unistd.h>
|
|
#include <sys/ioctl.h>
|
|
|
|
static struct sigaction old_act_sigint, new_act_sigint;
|
|
static struct sigaction old_act_sigterm, new_act_sigterm;
|
|
static struct termios orig_termios;
|
|
|
|
static void mysig_handler(int signum)
|
|
{
|
|
// restore original terminal state
|
|
tcsetattr(STDIN_FILENO, TCSANOW, &orig_termios);
|
|
|
|
// restore original signal handler for SIGINT
|
|
signal(SIGINT, old_act_sigint.sa_handler);
|
|
signal(SIGTERM, old_act_sigterm.sa_handler);
|
|
|
|
LOG_F(INFO, "Old terminal state restored, SIG#=%d", signum);
|
|
|
|
// re-post signal
|
|
raise(signum);
|
|
}
|
|
#endif
|
|
|
|
void enter_debugger() {
|
|
string inp, cmd, addr_str, expr_str, reg_expr, last_cmd, reg_value_str,
|
|
inst_string, inst_num_str, profile_name, sub_cmd;
|
|
uint32_t addr, inst_grab;
|
|
std::stringstream ss;
|
|
int log_level, context;
|
|
size_t separator_pos;
|
|
bool did_message = false;
|
|
|
|
unique_ptr<OfConfigUtils> ofnvram = unique_ptr<OfConfigUtils>(new OfConfigUtils);
|
|
|
|
context = 1; /* start with the PowerPC context */
|
|
|
|
#ifndef _WIN32
|
|
struct winsize win_size_previous;
|
|
ioctl(0, TIOCGWINSZ, &win_size_previous);
|
|
#endif
|
|
|
|
while (1) {
|
|
if (power_off_reason == po_shut_down) {
|
|
power_off_reason = po_shutting_down;
|
|
break;
|
|
}
|
|
power_on = true;
|
|
|
|
if (power_off_reason == po_starting_up) {
|
|
power_off_reason = po_none;
|
|
cmd = "go";
|
|
}
|
|
else if (power_off_reason == po_disassemble_on) {
|
|
inp = "si 1000000000";
|
|
ss.str("");
|
|
ss.clear();
|
|
ss.str(inp);
|
|
ss >> cmd;
|
|
}
|
|
else if (power_off_reason == po_disassemble_off) {
|
|
power_off_reason = po_none;
|
|
cmd = "go";
|
|
}
|
|
else
|
|
{
|
|
if (power_off_reason == po_enter_debugger) {
|
|
power_off_reason = po_entered_debugger;
|
|
}
|
|
if (!did_message) {
|
|
cout << "Welcome to the DingusPPC command line debugger." << endl;
|
|
cout << "Please enter a command or 'help'." << endl << endl;
|
|
did_message = true;
|
|
}
|
|
|
|
printf("%08X: dingusdbg> ", ppc_state.pc);
|
|
|
|
while (power_on) {
|
|
/* reset string stream */
|
|
ss.str("");
|
|
ss.clear();
|
|
|
|
cmd = "";
|
|
std::cin.clear();
|
|
getline(cin, inp, '\n');
|
|
ss.str(inp);
|
|
ss >> cmd;
|
|
|
|
#ifndef _WIN32
|
|
struct winsize win_size_current;
|
|
ioctl(0, TIOCGWINSZ, &win_size_current);
|
|
if (win_size_current.ws_col != win_size_previous.ws_col || win_size_current.ws_row != win_size_previous.ws_row) {
|
|
win_size_previous = win_size_current;
|
|
if (cmd.empty()) {
|
|
continue;
|
|
}
|
|
}
|
|
#endif
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (power_off_reason == po_signal_interrupt) {
|
|
power_off_reason = po_enter_debugger;
|
|
// ignore command if interrupt happens because the input line is probably incomplete.
|
|
last_cmd = "";
|
|
continue;
|
|
}
|
|
|
|
if (cmd.empty() && !last_cmd.empty()) {
|
|
cmd = last_cmd;
|
|
cout << cmd << endl;
|
|
}
|
|
if (cmd == "help") {
|
|
cmd = "";
|
|
show_help();
|
|
} else if (cmd == "quit") {
|
|
cmd = "";
|
|
break;
|
|
} else if (cmd == "profile") {
|
|
cmd = "";
|
|
ss >> sub_cmd;
|
|
ss >> profile_name;
|
|
|
|
if (sub_cmd == "show") {
|
|
gProfilerObj->print_profile(profile_name);
|
|
} else if (sub_cmd == "reset") {
|
|
gProfilerObj->reset_profile(profile_name);
|
|
} else {
|
|
cout << "Unknown/empty subcommand " << sub_cmd << endl;
|
|
}
|
|
}
|
|
else if (cmd == "regs") {
|
|
cmd = "";
|
|
if (context == 2) {
|
|
#ifdef ENABLE_68K_DEBUGGER
|
|
print_68k_regs();
|
|
#endif
|
|
} else {
|
|
print_gprs();
|
|
}
|
|
} else if (cmd == "mregs") {
|
|
cmd = "";
|
|
print_mmu_regs();
|
|
} else if (cmd == "set") {
|
|
ss >> expr_str;
|
|
|
|
separator_pos = expr_str.find_first_of("=");
|
|
if (separator_pos == std::string::npos) {
|
|
cout << "set: not enough arguments specified." << endl;
|
|
continue;
|
|
}
|
|
|
|
try {
|
|
reg_expr = expr_str.substr(0, expr_str.find_first_of("="));
|
|
addr_str = expr_str.substr(expr_str.find_first_of("=") + 1);
|
|
if (reg_expr == "loglevel") {
|
|
log_level = str2num(addr_str);
|
|
if (log_level < -2 || log_level > 9) {
|
|
cout << "Log level must be in the range -2...9!" << endl;
|
|
continue;
|
|
}
|
|
loguru::g_stderr_verbosity = log_level;
|
|
} else {
|
|
addr = str2addr(addr_str);
|
|
set_reg(reg_expr, addr);
|
|
}
|
|
} catch (invalid_argument& exc) {
|
|
cout << exc.what() << endl;
|
|
}
|
|
} else if (cmd == "step" || cmd == "si") {
|
|
int count;
|
|
|
|
expr_str = "";
|
|
ss >> expr_str;
|
|
if (expr_str.length() > 0) {
|
|
try {
|
|
count = str2num(expr_str);
|
|
} catch (invalid_argument& exc) {
|
|
cout << exc.what() << endl;
|
|
count = 1;
|
|
}
|
|
} else {
|
|
count = 1;
|
|
}
|
|
|
|
if (context == 2) {
|
|
#ifdef ENABLE_68K_DEBUGGER
|
|
for (; --count >= 0;) {
|
|
exec_single_68k();
|
|
}
|
|
#endif
|
|
} else {
|
|
for (; --count >= 0;) {
|
|
ppc_exec_single();
|
|
}
|
|
}
|
|
} else if (cmd == "next" || cmd == "ni") {
|
|
addr_str = "PC";
|
|
addr = (uint32_t)get_reg(addr_str) + 4;
|
|
ppc_exec_until(addr);
|
|
} else if (cmd == "until") {
|
|
ss >> addr_str;
|
|
try {
|
|
addr = str2addr(addr_str);
|
|
if (context == 2) {
|
|
#ifdef ENABLE_68K_DEBUGGER
|
|
exec_until_68k(addr);
|
|
#endif
|
|
} else {
|
|
ppc_exec_until(addr);
|
|
}
|
|
} catch (invalid_argument& exc) {
|
|
cout << exc.what() << endl;
|
|
}
|
|
} else if (cmd == "go") {
|
|
cmd = "";
|
|
power_on = true;
|
|
ppc_exec();
|
|
} else if (cmd == "disas" || cmd == "da") {
|
|
expr_str = "";
|
|
ss >> expr_str;
|
|
if (expr_str.length() > 0) {
|
|
separator_pos = expr_str.find_first_of(",");
|
|
if (separator_pos == std::string::npos) {
|
|
cout << "disas: not enough arguments specified." << endl;
|
|
continue;
|
|
}
|
|
|
|
inst_num_str = expr_str.substr(0, expr_str.find_first_of(","));
|
|
try {
|
|
inst_grab = str2num(inst_num_str);
|
|
} catch (invalid_argument& exc) {
|
|
cout << exc.what() << endl;
|
|
continue;
|
|
}
|
|
|
|
addr_str = expr_str.substr(expr_str.find_first_of(",") + 1);
|
|
try {
|
|
addr = str2addr(addr_str);
|
|
} catch (invalid_argument& exc) {
|
|
try {
|
|
/* number conversion failed, trying reg name */
|
|
if (context == 2 && (addr_str == "pc" || addr_str == "PC")) {
|
|
addr_str = "R24";
|
|
addr = get_reg(addr_str) - 2;
|
|
} else {
|
|
addr = get_reg(addr_str);
|
|
}
|
|
} catch (invalid_argument& exc) {
|
|
cout << exc.what() << endl;
|
|
continue;
|
|
}
|
|
}
|
|
try {
|
|
if (context == 2) {
|
|
#ifdef ENABLE_68K_DEBUGGER
|
|
disasm_68k(inst_grab, addr);
|
|
#endif
|
|
} else {
|
|
disasm(inst_grab, addr);
|
|
}
|
|
} catch (invalid_argument& exc) {
|
|
cout << exc.what() << endl;
|
|
}
|
|
} else {
|
|
/* disas without arguments defaults to disas 1,pc */
|
|
try {
|
|
if (context == 2) {
|
|
#ifdef ENABLE_68K_DEBUGGER
|
|
addr_str = "R24";
|
|
addr = get_reg(addr_str);
|
|
disasm_68k(1, addr - 2);
|
|
#endif
|
|
} else {
|
|
addr_str = "PC";
|
|
addr = (uint32_t)get_reg(addr_str);
|
|
disasm(1, addr);
|
|
}
|
|
} catch (invalid_argument& exc) {
|
|
cout << exc.what() << endl;
|
|
}
|
|
}
|
|
} else if (cmd == "dump") {
|
|
expr_str = "";
|
|
ss >> expr_str;
|
|
dump_mem(expr_str);
|
|
#ifdef ENABLE_68K_DEBUGGER
|
|
} else if (cmd == "context") {
|
|
cmd = "";
|
|
expr_str = "";
|
|
ss >> expr_str;
|
|
if (expr_str == "ppc" || expr_str == "PPC") {
|
|
context = 1;
|
|
} else if (expr_str == "68k" || expr_str == "68K") {
|
|
context = 2;
|
|
} else {
|
|
cout << "Unknown debugging context: " << expr_str << endl;
|
|
}
|
|
#endif
|
|
} else if (cmd == "printenv") {
|
|
cmd = "";
|
|
if (ofnvram->init())
|
|
continue;
|
|
ofnvram->printenv();
|
|
} else if (cmd == "setenv") {
|
|
cmd = "";
|
|
string var_name, value;
|
|
ss >> var_name;
|
|
std::istream::sentry se(ss); // skip white space
|
|
getline(ss, value); // get everything up to eol
|
|
if (ofnvram->init())
|
|
continue;
|
|
ofnvram->setenv(var_name, value);
|
|
#ifndef _WIN32
|
|
} else if (cmd == "nvedit") {
|
|
cmd = "";
|
|
cout << "===== press CNTRL-C to save =====" << endl;
|
|
|
|
// save original terminal state
|
|
tcgetattr(STDIN_FILENO, &orig_termios);
|
|
struct termios new_termios = orig_termios;
|
|
|
|
new_termios.c_cflag &= ~(CSIZE | PARENB);
|
|
new_termios.c_cflag |= CS8;
|
|
|
|
new_termios.c_lflag &= ~(ISIG | NOFLSH | ICANON | ECHOCTL);
|
|
new_termios.c_lflag |= NOFLSH | ECHONL;
|
|
|
|
// new_termios.c_iflag &= ~(ICRNL | IGNCR);
|
|
// new_termios.c_iflag |= INLCR;
|
|
|
|
// new_termios.c_oflag &= ~(ONOCR | ONLCR);
|
|
// new_termios.c_oflag |= OPOST | OCRNL | ONLRET;
|
|
|
|
tcsetattr(STDIN_FILENO, TCSANOW, &new_termios);
|
|
|
|
// save original signal handler for SIGINT
|
|
// then redirect SIGINT to new handler
|
|
memset(&new_act_sigint, 0, sizeof(new_act_sigint));
|
|
new_act_sigint.sa_handler = mysig_handler;
|
|
sigaction(SIGINT, &new_act_sigint, &old_act_sigint);
|
|
|
|
// save original signal handler for SIGTERM
|
|
// then redirect SIGTERM to new handler
|
|
memset(&new_act_sigterm, 0, sizeof(new_act_sigterm));
|
|
new_act_sigterm.sa_handler = mysig_handler;
|
|
sigaction(SIGTERM, &new_act_sigterm, &old_act_sigterm);
|
|
|
|
getline(cin, inp, '\x03');
|
|
|
|
// restore original terminal state
|
|
tcsetattr(STDIN_FILENO, TCSANOW, &orig_termios);
|
|
|
|
// restore original signal handler for SIGINT
|
|
signal(SIGINT, old_act_sigint.sa_handler);
|
|
|
|
// restore original signal handler for SIGTERM
|
|
signal(SIGTERM, old_act_sigterm.sa_handler);
|
|
|
|
if (ofnvram->init())
|
|
continue;
|
|
ofnvram->setenv("nvramrc", inp);
|
|
#endif
|
|
#ifdef DEBUG_CPU_INT
|
|
} else if (cmd == "amicint") {
|
|
cmd = "";
|
|
string value;
|
|
int irq_id;
|
|
ss >> value;
|
|
try {
|
|
irq_id = str2num(value);
|
|
} catch (invalid_argument& exc) {
|
|
cout << exc.what() << endl;
|
|
continue;
|
|
}
|
|
InterruptCtrl* int_ctrl = dynamic_cast<InterruptCtrl*>(
|
|
gMachineObj->get_comp_by_type(HWCompType::INT_CTRL));
|
|
int_ctrl->ack_int(irq_id, 1);
|
|
} else if (cmd == "viaint") {
|
|
cmd = "";
|
|
string value;
|
|
int irq_bit;
|
|
ss >> value;
|
|
try {
|
|
irq_bit = str2num(value);
|
|
} catch (invalid_argument& exc) {
|
|
cout << exc.what() << endl;
|
|
continue;
|
|
}
|
|
ViaCuda* via_obj = dynamic_cast<ViaCuda*>(gMachineObj->get_comp_by_name("ViaCuda"));
|
|
ppc_state.pc -= 4;
|
|
via_obj->assert_int(irq_bit);
|
|
#endif
|
|
} else {
|
|
if (!cmd.empty()) {
|
|
cout << "Unknown command: " << cmd << endl;
|
|
cmd = "";
|
|
}
|
|
}
|
|
last_cmd = cmd;
|
|
}
|
|
}
|