Merging the 68k debugger from another branch

This commit is contained in:
dingusdev 2020-11-26 20:00:08 -07:00
commit 0c202b0c2d
4 changed files with 273 additions and 35 deletions

View File

@ -16,10 +16,14 @@ if (UNIX AND NOT APPLE)
endif()
endif()
find_package(PkgConfig REQUIRED)
pkg_check_modules(CAPSTONE REQUIRED capstone>=4.0.1)
include_directories(${CAPSTONE_INCLUDE_DIRS})
link_directories(${CAPSTONE_LIBRARY_DIRS})
add_subdirectory("${PROJECT_SOURCE_DIR}/cpu/ppc/")
add_subdirectory("${PROJECT_SOURCE_DIR}/debugger/")
add_subdirectory("${PROJECT_SOURCE_DIR}/devices/")
add_subdirectory("${PROJECT_SOURCE_DIR}/execution")
add_subdirectory("${PROJECT_SOURCE_DIR}/machines/")
add_subdirectory("${PROJECT_SOURCE_DIR}/thirdparty/loguru/")
@ -36,8 +40,6 @@ set(BUILD_TOOLS OFF CACHE BOOL "Build Cubeb tools")
add_subdirectory(thirdparty/cubeb EXCLUDE_FROM_ALL)
set(CLI11_ROOT ${PROJECT_SOURCE_DIR}/thirdparty/CLI11)
include_directories("${PROJECT_SOURCE_DIR}" "${PROJECT_SOURCE_DIR}/devices"
"${PROJECT_SOURCE_DIR}/cpu/ppc"
"${PROJECT_SOURCE_DIR}/debugger"
@ -58,7 +60,6 @@ file(GLOB TEST_SOURCES "${PROJECT_SOURCE_DIR}/cpu/ppc/test/*.cpp")
add_executable(dingusppc ${SOURCES} $<TARGET_OBJECTS:debugger>
$<TARGET_OBJECTS:cpu_ppc>
$<TARGET_OBJECTS:devices>
$<TARGET_OBJECTS:execution>
$<TARGET_OBJECTS:machines>
$<TARGET_OBJECTS:loguru>)
@ -68,15 +69,13 @@ target_link_libraries(dingusppc "${PROJECT_SOURCE_DIR}/thirdparty/SDL2/lib/x64/S
cubeb)
else()
#target_link_libraries(dingusppc libsoundio_static ${LIBSOUNDIO_LIBS} ${SDL2_LIBRARIES})
target_link_libraries(dingusppc cubeb ${SDL2_LIBRARIES} ${CMAKE_DL_LIBS} ${CMAKE_THREAD_LIBS_INIT})
target_include_directories(dingusppc PRIVATE ${CLI11_ROOT})
target_link_libraries(dingusppc cubeb ${SDL2_LIBRARIES} ${CAPSTONE_LIBRARIES} ${CMAKE_DL_LIBS} ${CMAKE_THREAD_LIBS_INIT})
endif()
add_executable(testppc ${TEST_SOURCES} $<TARGET_OBJECTS:debugger>
$<TARGET_OBJECTS:cpu_ppc>
$<TARGET_OBJECTS:devices>
$<TARGET_OBJECTS:execution>
$<TARGET_OBJECTS:machines>
$<TARGET_OBJECTS:loguru>)
@ -86,19 +85,9 @@ target_link_libraries(testppc "${PROJECT_SOURCE_DIR}/thirdparty/SDL2/lib/x64/SDL
cubeb)
else()
#target_link_libraries(testppc libsoundio_static ${LIBSOUNDIO_LIBS} ${SDL2_LIBRARIES})
target_link_libraries(testppc cubeb ${SDL2_LIBRARIES} ${CMAKE_DL_LIBS} ${CMAKE_THREAD_LIBS_INIT})
target_link_libraries(testppc cubeb ${SDL2_LIBRARIES} ${CAPSTONE_LIBRARIES} ${CMAKE_DL_LIBS} ${CMAKE_THREAD_LIBS_INIT})
endif()
file(GLOB BENCH_SOURCES "${PROJECT_SOURCE_DIR}/benchmark/*.cpp")
add_executable(bench1 ${BENCH_SOURCES} $<TARGET_OBJECTS:debugger>
$<TARGET_OBJECTS:cpu_ppc>
$<TARGET_OBJECTS:devices>
$<TARGET_OBJECTS:execution>
$<TARGET_OBJECTS:machines>
$<TARGET_OBJECTS:loguru>)
target_link_libraries(bench1 cubeb ${SDL2_LIBRARIES} ${CMAKE_DL_LIBS} ${CMAKE_THREAD_LIBS_INIT})
add_custom_command(
TARGET testppc POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy

View File

@ -554,6 +554,7 @@ extern void ppc_main_opcode(void);
extern void ppc_exec(void);
extern void ppc_exec_single(void);
extern void ppc_exec_until(uint32_t goal_addr);
extern void ppc_exec_dbg(uint32_t start_addr, uint32_t size);
/* debugging support API */
void print_gprs(void); /* print content of the general purpose registers */

View File

@ -86,9 +86,17 @@ static PPCOpcode OpcodeGrabber[] = {
ppc_illegalop, ppc_illegalop, ppc_illegalop, ppc_opcode63};
/** Lookup tables for branch instructions. */
static PPCOpcode SubOpcode16Grabber[] = {ppc_bc, ppc_bcl, ppc_bca, ppc_bcla};
static PPCOpcode SubOpcode16Grabber[] = {
dppc_interpreter::ppc_bc,
dppc_interpreter::ppc_bcl,
dppc_interpreter::ppc_bca,
dppc_interpreter::ppc_bcla};
static PPCOpcode SubOpcode18Grabber[] = {ppc_b, ppc_bl, ppc_ba, ppc_bla};
static PPCOpcode SubOpcode18Grabber[] = {
dppc_interpreter::ppc_b,
dppc_interpreter::ppc_bl,
dppc_interpreter::ppc_ba,
dppc_interpreter::ppc_bla};
/** Instructions decoding tables for integer,
single floating-point, and double-floating point ops respectively */
@ -352,7 +360,7 @@ void ppc_exec_single()
}
/** Execute PPC code until goal_addr is reached. */
void ppc_exec_until(uint32_t goal_addr)
void ppc_exec_until(volatile uint32_t goal_addr)
{
uint32_t bb_start_la, page_start, delta;
uint8_t* pc_real;
@ -411,6 +419,68 @@ again:
}
}
/** Execute PPC code until control is reached the specified region. */
void ppc_exec_dbg(volatile uint32_t start_addr, volatile uint32_t size)
{
uint32_t bb_start_la, page_start, delta;
uint8_t* pc_real;
/* start new basic block */
glob_bb_start_la = bb_start_la = ppc_state.pc;
bb_kind = BB_end_kind::BB_NONE;
if (setjmp(exc_env)) {
/* reaching here means we got a low-level exception */
#ifdef NEW_TBR_UPDATE_ALGO
cycles_count += ((ppc_state.pc - glob_bb_start_la) >> 2) + 1;
UPDATE_TBR_DEC
#else
timebase_counter += ((ppc_state.pc - glob_bb_start_la) >> 2) + 1;
#endif
glob_bb_start_la = bb_start_la = ppc_next_instruction_address;
pc_real = quickinstruction_translate(bb_start_la);
page_start = bb_start_la & 0xFFFFF000;
ppc_state.pc = bb_start_la;
bb_kind = BB_end_kind::BB_NONE;
//printf("DBG Exec: got exception, continue at %X\n", ppc_state.pc);
goto again;
}
/* initial MMU translation for the current code page. */
pc_real = quickinstruction_translate(bb_start_la);
/* set current code page limits */
page_start = bb_start_la & 0xFFFFF000;
again:
while (ppc_state.pc < start_addr || ppc_state.pc >= start_addr + size) {
ppc_main_opcode();
if (bb_kind != BB_end_kind::BB_NONE) {
#ifdef NEW_TBR_UPDATE_ALGO
cycles_count += ((ppc_state.pc - bb_start_la) >> 2) + 1;
UPDATE_TBR_DEC
#else
timebase_counter += ((ppc_state.pc - bb_start_la) >> 2) + 1;
#endif
glob_bb_start_la = bb_start_la = ppc_next_instruction_address;
if ((ppc_next_instruction_address & 0xFFFFF000) != page_start) {
page_start = bb_start_la & 0xFFFFF000;
pc_real = quickinstruction_translate(bb_start_la);
} else {
pc_real += (int)bb_start_la - (int)ppc_state.pc;
ppc_set_cur_instruction(pc_real);
}
ppc_state.pc = bb_start_la;
bb_kind = BB_end_kind::BB_NONE;
//printf("DBG Exec: new basic block at %X, start_addr=%X\n", ppc_state.pc, start_addr);
} else {
ppc_state.pc += 4;
pc_real += 4;
ppc_set_cur_instruction(pc_real);
}
}
}
uint64_t instr_count, old_instr_count;

View File

@ -19,9 +19,6 @@ 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 <fstream>
#include <iomanip>
#include <iostream>
@ -30,6 +27,10 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
#include <stdio.h>
#include <string>
#include <thirdparty/loguru/loguru.hpp>
#include <capstone/capstone.h>
#include "../cpu/ppc/ppcdisasm.h"
#include "../cpu/ppc/ppcemu.h"
#include "../cpu/ppc/ppcmmu.h"
using namespace std;
@ -71,6 +72,9 @@ static void show_help() {
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 << " 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;
cout << " quit -- quit the debugger" << endl << endl;
cout << "Pressing ENTER will repeat last command." << endl;
}
@ -88,6 +92,113 @@ static void disasm(uint32_t count, uint32_t address) {
}
}
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 (; 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;
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 {
cout << "DS.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()
{
string reg;
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 */
reg = "R24";
cur_68k_pc = get_reg(reg) - 2;
/* PPC r29 contains base address of the emulator opcode table */
reg = "R29";
emu_table_virt = get_reg(reg) & 0xFFF80000;
/* calculate address of the current opcode table entry as follows:
get_word(68k_PC) * entry_size + table_base */
cur_instr_tab_entry = mem_grab_word(cur_68k_pc) * 8 + emu_table_virt;
/* grab the PPC PC too */
reg = "PC";
ppc_pc = get_reg(reg);
//printf("cur_instr_tab_entry = %X\n", cur_instr_tab_entry);
/* because the first two PPC instructions for each emulated 68k once
are resided in the emulator opcode table, we need to execute them
one by one until the execution goes outside the opcode table. */
while (ppc_pc >= cur_instr_tab_entry && ppc_pc < cur_instr_tab_entry + 8) {
ppc_exec_single();
reg = "PC";
ppc_pc = get_reg(reg);
LOG_F(9, "Tracing within emulator table, PC = %X\n", ppc_pc);
}
/* Getting here means we're outside the emualtor opcode table.
Execute PPC code until we hit the opcode table again. */
LOG_F(9, "Tracing outside the emulator table, PC = %X\n", ppc_pc);
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)
{
string reg;
uint32_t emu_table_virt, ppc_pc;
reg = "R29";
emu_table_virt = get_reg(reg) & 0xFFF80000;
reg = "R24";
while (target_addr != (get_reg(reg) - 2)) {
reg = "PC";
ppc_pc = get_reg(reg);
if (ppc_pc >= emu_table_virt && ppc_pc < (emu_table_virt + EMU_68K_TABLE_SIZE - 1)) {
LOG_F(9, "Tracing within emulator table, PC = %X\n", ppc_pc);
ppc_exec_single();
} else {
LOG_F(9, "Tracing outside the emulator table, PC = %X\n", ppc_pc);
ppc_exec_dbg(emu_table_virt, EMU_68K_TABLE_SIZE - 1);
}
reg = "R24";
}
}
static void dump_mem(string& params) {
int cell_size, chars_per_line;
bool is_char;
@ -169,7 +280,8 @@ static void dump_mem(string& params) {
cout << (char)val;
chars_per_line += cell_size;
} else {
cout << setw(cell_size * 2) << setfill('0') << uppercase << hex << val << " ";
cout << setw(cell_size * 2) << setfill('0') << right;
cout << uppercase << hex << val << " ";
chars_per_line += cell_size * 2 + 2;
}
}
@ -181,18 +293,47 @@ static void dump_mem(string& params) {
cout << endl << endl;
}
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;
}
reg = "R1";
cout << "A7 : " << uppercase << hex << get_reg(reg) << endl;
reg = "R24";
cout << "PC: " << uppercase << hex << get_reg(reg) - 2 << endl;
reg = "R25";
cout << "SR: " << uppercase << hex << ((get_reg(reg) & 0xFF) << 8) << endl;
reg = "R26";
cout << "CCR: " << uppercase << hex << get_reg(reg) << endl;
}
void enter_debugger() {
string inp, cmd, addr_str, expr_str, reg_expr, last_cmd, reg_value_str, inst_string, inst_num_str;
uint32_t addr, inst_grab;
std::stringstream ss;
int log_level;
int log_level, context;
size_t separator_pos;
context = 1; /* start with the PowerPC context */
cout << "Welcome to the DingusPPC command line debugger." << endl;
cout << "Please enter a command or 'help'." << endl << endl;
while (1) {
cout << "ppcdbg> ";
cout << "dingusdbg> ";
/* reset string stream */
ss.str("");
@ -220,7 +361,11 @@ void enter_debugger() {
}
#endif
else if (cmd == "regs") {
print_gprs();
if (context == 2) {
print_68k_regs();
} else {
print_gprs();
}
} else if (cmd == "set") {
ss >> expr_str;
@ -248,7 +393,11 @@ void enter_debugger() {
cout << exc.what() << endl;
}
} else if (cmd == "step" || cmd == "si") {
ppc_exec_single();
if (context == 2) {
exec_single_68k();
} else {
ppc_exec_single();
}
} else if (cmd == "next" || cmd == "ni") {
addr_str = "PC";
addr = get_reg(addr_str) + 4;
@ -257,7 +406,11 @@ void enter_debugger() {
ss >> addr_str;
try {
addr = str2addr(addr_str);
ppc_exec_until(addr);
if (context == 2) {
exec_until_68k(addr);
} else {
ppc_exec_until(addr);
}
} catch (invalid_argument& exc) {
cout << exc.what() << endl;
}
@ -278,27 +431,52 @@ void enter_debugger() {
} catch (invalid_argument& exc) {
try {
/* number conversion failed, trying reg name */
addr = get_reg(addr_str);
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 {
disasm(inst_grab, addr);
if (context == 2) {
disasm_68k(inst_grab, addr);
} else {
disasm(inst_grab, addr);
}
} catch (invalid_argument& exc) {
cout << exc.what() << endl;
}
} else {
/* disas without arguments defaults to disas 1,pc */
addr_str = "PC";
addr = get_reg(addr_str);
disasm(1, addr);
if (context == 2) {
addr_str = "R24";
addr = get_reg(addr_str);
disasm_68k(1, addr - 2);
} else {
addr_str = "PC";
addr = get_reg(addr_str);
disasm(1, addr);
}
}
} else if (cmd == "dump") {
expr_str = "";
ss >> expr_str;
dump_mem(expr_str);
} else if (cmd == "context") {
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;
}
} else {
cout << "Unknown command: " << cmd << endl;
continue;