From f3895493ae16a38dbf6d9309bcc93da812ad69cf Mon Sep 17 00:00:00 2001 From: Ricky Zhang Date: Fri, 1 Sep 2017 10:47:22 -0400 Subject: [PATCH] Add break point function for Basilisk II in cxmon. To enter break point, you need to run a m68k program in Macintosh guest OS. The program executes a new emul_op instruction 0x7138. The program will be provided in cxmon/utils folder in a separate commit. Once emulation is suspended, you can enter below new cxmon command to manipluate break points: ba [address] Add a break point br [breakpoints#] Remove a break point. If # is 0, remove all break points. bd [breakpoints#] Disable a break point. If # is 0, disable all break points. be [breakpoints#] Enable a break point. If # is 0, enable all break points. bi List all break points bs "file" Save all break points to a file bl "file" Load break points from a file Once emulation PC reach break point address, it automatically suspends and traps into cxmon. Signed-off-by: Ricky Zhang --- BasiliskII/src/emul_op.cpp | 17 ++ BasiliskII/src/include/emul_op.h | 1 + BasiliskII/src/uae_cpu/newcpu.cpp | 5 - BasiliskII/src/uae_cpu/newcpu.h | 79 ++++++--- cxmon/src/mon.cpp | 10 ++ cxmon/src/mon.h | 8 + cxmon/src/mon_cmd.cpp | 286 ++++++++++++++++++++++++++++++ cxmon/src/mon_cmd.h | 7 + 8 files changed, 386 insertions(+), 27 deletions(-) diff --git a/BasiliskII/src/emul_op.cpp b/BasiliskII/src/emul_op.cpp index f6b91dde..6051f9e2 100644 --- a/BasiliskII/src/emul_op.cpp +++ b/BasiliskII/src/emul_op.cpp @@ -564,6 +564,23 @@ void EmulOp(uint16 opcode, M68kRegisters *r) r->a[0] = ReadMacInt32(0x2b6); break; + case M68K_EMUL_OP_SUSPEND: { + printf("*** Suspend\n"); + printf("d0 %08x d1 %08x d2 %08x d3 %08x\n" + "d4 %08x d5 %08x d6 %08x d7 %08x\n" + "a0 %08x a1 %08x a2 %08x a3 %08x\n" + "a4 %08x a5 %08x a6 %08x a7 %08x\n" + "sr %04x\n", + r->d[0], r->d[1], r->d[2], r->d[3], r->d[4], r->d[5], r->d[6], r->d[7], + r->a[0], r->a[1], r->a[2], r->a[3], r->a[4], r->a[5], r->a[6], r->a[7], + r->sr); +#ifdef ENABLE_MON + char *arg[4] = {"mon", "-m", "-r", NULL}; + mon(3, arg); +#endif + break; + } + default: printf("FATAL: EMUL_OP called with bogus opcode %08x\n", opcode); printf("d0 %08x d1 %08x d2 %08x d3 %08x\n" diff --git a/BasiliskII/src/include/emul_op.h b/BasiliskII/src/include/emul_op.h index b6d3a1e6..e7684127 100644 --- a/BasiliskII/src/include/emul_op.h +++ b/BasiliskII/src/include/emul_op.h @@ -90,6 +90,7 @@ enum { M68K_EMUL_OP_SOUNDIN_CLOSE, M68K_EMUL_OP_DEBUGUTIL, M68K_EMUL_OP_IDLE_TIME, + M68K_EMUL_OP_SUSPEND, M68K_EMUL_OP_MAX // highest number }; diff --git a/BasiliskII/src/uae_cpu/newcpu.cpp b/BasiliskII/src/uae_cpu/newcpu.cpp index 71aa89fb..eb14efd6 100644 --- a/BasiliskII/src/uae_cpu/newcpu.cpp +++ b/BasiliskII/src/uae_cpu/newcpu.cpp @@ -43,11 +43,6 @@ extern int intlev(void); // From baisilisk_glue.cpp B2_mutex *spcflags_lock = NULL; #endif -#if ENABLE_MON -#include "mon.h" -#include "mon_disass.h" -#endif - bool quit_program = false; struct flag_struct regflags; diff --git a/BasiliskII/src/uae_cpu/newcpu.h b/BasiliskII/src/uae_cpu/newcpu.h index 1d07c36b..0359e7b7 100644 --- a/BasiliskII/src/uae_cpu/newcpu.h +++ b/BasiliskII/src/uae_cpu/newcpu.h @@ -30,7 +30,13 @@ #include "m68k.h" #include "readcpu.h" #include "spcflags.h" - + +#if ENABLE_MON +#include "mon.h" +#include "mon_disass.h" +#endif + + extern int areg_byteinc[]; extern int imm8_table[]; @@ -74,6 +80,7 @@ struct comptbl { #endif extern void REGPARAM2 op_illg (uae_u32) REGPARAM; +extern void m68k_dumpstate(uaecptr *nextpc); typedef char flagtype; @@ -147,8 +154,6 @@ static __inline__ uae_u32 get_ilong_prefetch (uae_s32 o) } #endif -#define m68k_incpc(o) (regs.pc_p += (o)) - static __inline__ void fill_prefetch_0 (void) { #if USE_PREFETCH_BUFFER @@ -175,6 +180,55 @@ static __inline__ void fill_prefetch_2 (void) #define fill_prefetch_2 fill_prefetch_0 #endif +static __inline__ uaecptr m68k_getpc (void) +{ +#if REAL_ADDRESSING || DIRECT_ADDRESSING + return get_virtual_address(regs.pc_p); +#else + return regs.pc + ((char *)regs.pc_p - (char *)regs.pc_oldp); +#endif +} + +static __inline__ void m68k_setpc (uaecptr newpc) +{ +#if ENABLE_MON + uae_u32 previous_pc = m68k_getpc(); +#endif + +#if REAL_ADDRESSING || DIRECT_ADDRESSING + regs.pc_p = get_real_address(newpc); +#else + regs.pc_p = regs.pc_oldp = get_real_address(newpc); + regs.pc = newpc; +#endif + +#if ENABLE_MON + if(isBreakPoint(newpc)) { + printf("Stopped at break point address: %08lx. Last PC: %08lx\n", newpc, previous_pc); + m68k_dumpstate(NULL); + char *arg[4] = {"mon", "-m", "-r", NULL}; + mon(3, arg); + } +#endif // end of #if ENABLE_MON +} + +static __inline__ void m68k_incpc (uae_s32 delta) +{ +#if ENABLE_MON + uae_u32 previous_pc = m68k_getpc(); +#endif + regs.pc_p += (delta); +#if ENABLE_MON + uaecptr next_pc = m68k_getpc(); + if(isBreakPoint(next_pc)) { + printf("Stopped at break point address: %08lx. Last PC: %08lx\n", next_pc, previous_pc); + m68k_dumpstate(NULL); + char *arg[4] = {"mon", "-m", "-r", NULL}; + mon(3, arg); + } +#endif // end of #if ENABLE_MON +} + /* These are only used by the 68020/68881 code, and therefore don't * need to handle prefetch. */ static __inline__ uae_u32 next_ibyte (void) @@ -198,25 +252,6 @@ static __inline__ uae_u32 next_ilong (void) return r; } -static __inline__ void m68k_setpc (uaecptr newpc) -{ -#if REAL_ADDRESSING || DIRECT_ADDRESSING - regs.pc_p = get_real_address(newpc); -#else - regs.pc_p = regs.pc_oldp = get_real_address(newpc); - regs.pc = newpc; -#endif -} - -static __inline__ uaecptr m68k_getpc (void) -{ -#if REAL_ADDRESSING || DIRECT_ADDRESSING - return get_virtual_address(regs.pc_p); -#else - return regs.pc + ((char *)regs.pc_p - (char *)regs.pc_oldp); -#endif -} - #define m68k_setpc_fast m68k_setpc #define m68k_setpc_bcc m68k_setpc #define m68k_setpc_rte m68k_setpc diff --git a/cxmon/src/mon.cpp b/cxmon/src/mon.cpp index f3c6c4e7..7b2b8700 100644 --- a/cxmon/src/mon.cpp +++ b/cxmon/src/mon.cpp @@ -55,6 +55,9 @@ extern "C" { #define VERSION "3" #endif +// Break points +BREAK_POINT_SET active_break_points; +BREAK_POINT_SET disabled_break_points; // Buffer we're operating on bool mon_use_real_mem = false; @@ -1043,6 +1046,13 @@ void mon_init() mon_add_command("i", ascii_dump, "i [start [end]] ASCII memory dump\n"); mon_add_command("m", memory_dump, "m [start [end]] Hex/ASCII memory dump\n"); mon_add_command("b", binary_dump, "b [start [end]] Binary memory dump\n"); + mon_add_command("ba", break_point_add, "ba [address] Add a break point\n"); + mon_add_command("br", break_point_remove, "br [breakpoints#] Remove a break point. If # is 0, remove all break points.\n"); + mon_add_command("bd", break_point_disable, "bd [breakpoints#] Disable a break point. If # is 0, disable all break points.\n"); + mon_add_command("be", break_point_enable, "be [breakpoints#] Enable a break point. If # is 0, enable all break points.\n"); + mon_add_command("bi", break_point_info, "bi List all break points\n"); + mon_add_command("bs", break_point_save, "bs \"file\" Save all break points to a file\n"); + mon_add_command("bl", break_point_load, "bl \"file\" Load break points from a file\n"); mon_add_command("d", disassemble_ppc, "d [start [end]] Disassemble PowerPC code\n"); mon_add_command("d65", disassemble_6502, "d65 [start [end]] Disassemble 6502 code\n"); mon_add_command("d68", disassemble_680x0, "d68 [start [end]] Disassemble 680x0 code\n"); diff --git a/cxmon/src/mon.h b/cxmon/src/mon.h index becc8a86..5970ccbb 100644 --- a/cxmon/src/mon.h +++ b/cxmon/src/mon.h @@ -22,6 +22,7 @@ #define MON_H #include +#include /* @@ -80,6 +81,10 @@ extern uint32 mon_mem_size; // Size of mon buffer (if mon_use_real_mem = fals extern bool mon_macos_mode; // Flag: enable features in the disassembler for working with MacOS code +typedef std::set BREAK_POINT_SET; +extern BREAK_POINT_SET active_break_points; +extern BREAK_POINT_SET disabled_break_points; + // Add command to mon extern void mon_add_command(const char *name, void (*func)(), const char *help_text); @@ -97,4 +102,7 @@ extern void mon_write_half(uintptr adr, uint32 w); extern uint32 mon_read_word(uintptr adr); extern void mon_write_word(uintptr adr, uint32 l); +// Check if break point is set +#define isBreakPoint(address) (active_break_points.find(address) != active_break_points.end()) + #endif diff --git a/cxmon/src/mon_cmd.cpp b/cxmon/src/mon_cmd.cpp index 4cb6a92b..d5eb5ee8 100644 --- a/cxmon/src/mon_cmd.cpp +++ b/cxmon/src/mon_cmd.cpp @@ -22,6 +22,7 @@ #include #include +#include #include "mon.h" #include "mon_cmd.h" @@ -32,6 +33,9 @@ #endif +static const char* STR_ACTIVE_BREAK_POINTS = "Active Break Points:\n"; +static const char* STR_DISABLED_BREAK_POINTS = "Disabled Break Points:\n"; + /* * range_args = [expression] [[COMMA] expression] END * @@ -302,6 +306,288 @@ void binary_dump(void) } +/* + * Add Break Point + */ +void break_point_add(void) +{ + uintptr address; + + if (mon_token == T_END || + !mon_expression(&address)) { + fprintf(monerr, "Expect break point in hexadecimal.\n"); + return; + } + + if (mon_token != T_END) { + mon_error("Too many arguments"); + return; + } + + BREAK_POINT_SET::iterator it; + // Save break point + if((it = disabled_break_points.find(address)) == disabled_break_points.end()) + active_break_points.insert(address); + else { + disabled_break_points.erase(it); + active_break_points.insert(address); + } +} + + +/* + * Remove Break Point + */ +void break_point_remove(void) +{ + uintptr index; + + if (mon_token == T_END || + !mon_expression(&index)) { + fprintf(monerr, "Expect index number of break point in hexadecimal.\n"); + return; + } + + if (mon_token != T_END) { + mon_error("Too many arguments"); + return; + } + + if (index > active_break_points.size()) { + mon_error("Illegal Index Number!"); + return; + } + + BREAK_POINT_SET::iterator it; + + if (0 == index) { + active_break_points.clear(); + printf("Removed all break points!\n"); + return; + } + + int pos = 1; + for (it = active_break_points.begin(); it != active_break_points.end(); it++) + if (pos++ == index) + break; + // Remove break point + printf("Removed break point %4x at address %08lx\n", index, *it); + active_break_points.erase(it); +} + + +/* + * Disable Break Point + */ +void break_point_disable(void) +{ + uintptr index; + + if (mon_token == T_END || + !mon_expression(&index)) { + fprintf(monerr, "Expect index number of break point in hexadecimal.\n"); + return; + } + + if (mon_token != T_END) { + mon_error("Too many arguments"); + return; + } + + if (index > active_break_points.size()) { + mon_error("Illegal Index Number!"); + return; + } + + BREAK_POINT_SET::iterator it; + + if (0 == index) { + for (it = active_break_points.begin(); it != active_break_points.end(); it++) + disabled_break_points.insert(*it); + active_break_points.clear(); + printf("Disabled all break points!\n"); + return; + } + + int pos = 1; + for (it = active_break_points.begin(); it != active_break_points.end(); it++) + if (pos++ == index) + break; + // Add to disable break points + printf("Disabled break point %4x at address %08lx\n", index, *it); + disabled_break_points.insert(*it); + // Remove break point + active_break_points.erase(it); +} + + +/* + * Enable Break Point + */ +void break_point_enable(void) +{ + uintptr index; + + if (mon_token == T_END || + !mon_expression(&index)) { + fprintf(monerr, "Expect index number of break point in hexadecimal.\n"); + return; + } + + if (mon_token != T_END) { + mon_error("Too many arguments"); + return; + } + + if (index > disabled_break_points.size()) { + mon_error("Illegal Index Number!"); + return; + } + + BREAK_POINT_SET::iterator it; + + if (0 == index) { + for (it = disabled_break_points.begin(); it != disabled_break_points.end(); it++) + active_break_points.insert(*it); + disabled_break_points.clear(); + printf("Enabled all break points!\n"); + return; + } + + int pos = 1; + for (it = disabled_break_points.begin(); it != disabled_break_points.end(); it++) + if (pos++ == index) + break; + // Add to active break points + printf("Disabled break point %4x at address %08lx\n", index, *it); + active_break_points.insert(*it); + // Remove break point + disabled_break_points.erase(it); +} + + +/* + * List all Active Break Points + */ +void break_point_info(void) +{ + if (mon_token != T_END) { + mon_error("Too many arguments"); + return; + } + + BREAK_POINT_SET::iterator it; + int pos; + + if (!active_break_points.empty()) { + pos = 1; + printf(STR_ACTIVE_BREAK_POINTS); + for (it = active_break_points.begin(); it != active_break_points.end(); it++) + printf("\tBreak point %4x at address %08lx\n", pos++, *it); + } + + if (!disabled_break_points.empty()) { + putchar('\n'); + printf(STR_DISABLED_BREAK_POINTS); + pos = 1; + for (it = disabled_break_points.begin(); it != disabled_break_points.end(); it++) + printf("\tBreak point %4x at address %08lx\n", pos++, *it); + } +} + + +/* + * Save all Active Break Points to a file + */ +void break_point_save(void) +{ + FILE *file; + + if (mon_token == T_END) { + mon_error("Missing file name"); + return; + } + if (mon_token != T_STRING) { + mon_error("'\"' around file name expected"); + return; + } + mon_get_token(); + if (mon_token != T_END) { + mon_error("Too many arguments"); + return; + } + + if (!(file = fopen(mon_string, "w"))) + mon_error("Unable to create file"); + else { + BREAK_POINT_SET::iterator it; + + fprintf(file, STR_ACTIVE_BREAK_POINTS); + for (it = active_break_points.begin(); it != active_break_points.end(); it++) + fprintf(file, "%x\n", *it); + + fprintf(file, STR_DISABLED_BREAK_POINTS); + for (it = disabled_break_points.begin(); it != disabled_break_points.end(); it++) + fprintf(file, "%x\n", *it); + + fclose(file); + } +} + + +/* + * Load Break Point from a file + */ +void break_point_load(void) +{ + FILE *file; + + if (mon_token == T_END) { + mon_error("Missing file name"); + return; + } + if (mon_token != T_STRING) { + mon_error("'\"' around file name expected"); + return; + } + mon_get_token(); + if (mon_token != T_END) { + mon_error("Too many arguments"); + return; + } + + if (!(file = fopen(mon_string, "r"))) + mon_error("Unable to create file"); + else{ + char line_buff[1024]; + bool isDisabledBreakPoints = false;; + + if(fgets(line_buff, sizeof(line_buff), file) == NULL || + strcmp(line_buff, STR_ACTIVE_BREAK_POINTS) != 0) { + mon_error("Invalid break point file format!"); + return; + } + + while(fgets(line_buff, sizeof(line_buff), file) != NULL) { + if(strcmp(line_buff, STR_DISABLED_BREAK_POINTS) == 0) { + isDisabledBreakPoints = true; + continue; + } + uintptr address; + std::stringstream ss; + ss << std::hex << line_buff; + ss >> address; + if(isDisabledBreakPoints) + disabled_break_points.insert(address); + else + active_break_points.insert(address); + } + + fclose(file); + } +} + + /* * Disassemble * d [start [end]] diff --git a/cxmon/src/mon_cmd.h b/cxmon/src/mon_cmd.h index d50d90eb..b9c344ba 100644 --- a/cxmon/src/mon_cmd.h +++ b/cxmon/src/mon_cmd.h @@ -28,6 +28,13 @@ extern void shell_command(void); extern void memory_dump(void); extern void ascii_dump(void); extern void binary_dump(void); +extern void break_point_add(void); +extern void break_point_remove(void); +extern void break_point_disable(void); +extern void break_point_enable(void); +extern void break_point_info(void); +extern void break_point_save(void); +extern void break_point_load(void); extern void disassemble_ppc(void); extern void disassemble_6502(void); extern void disassemble_680x0(void);