diff --git a/riscemu/CPU.py b/riscemu/CPU.py index ec95ce6..12ef50c 100644 --- a/riscemu/CPU.py +++ b/riscemu/CPU.py @@ -25,6 +25,7 @@ class CPU: self.exit = False self.exit_code = 0 self.conf = conf + self.active_debug = False # if a debugging session is currently runnign # setup MMU, registers and syscall handlers self.mmu = MMU(conf) @@ -66,7 +67,30 @@ class CPU: self.__run() - def __run(self): + def continue_from_debugger(self, verbose=True): + """ + dalled from the debugger to continue running + """ + self.__run(verbose) + + def step(self): + if self.exit: + print(FMT_CPU + "Program exited with code {}".format(self.exit_code) + FMT_NONE) + else: + try: + self.cycle += 1 + ins = self.mmu.read_ins(self.pc) + print("Running instruction {} from 0x{:08X}".format(ins, self.pc)) + self.pc += 1 + self.__run_instruction(ins) + except LaunchDebuggerException: + print(FMT_CPU + "[CPU] Returning to debugger!" + FMT_NONE) + except RiscemuBaseException as ex: + self.pc -= 1 + print(ex.message()) + + + def __run(self, verbose=False): if self.pc <= 0: return False ins = None @@ -74,14 +98,22 @@ class CPU: while not self.exit: self.cycle += 1 ins = self.mmu.read_ins(self.pc) + if verbose: + print(FMT_CPU + " Running 0x{:08X}:{} {}".format(self.pc, FMT_NONE, ins)) self.pc += 1 self.__run_instruction(ins) except RiscemuBaseException as ex: - print(FMT_ERROR + "[CPU] excpetion caught at 0x{:08X}: {}:".format(self.pc-1, ins) + FMT_NONE) - print(" " + ex.message()) + if not isinstance(ex, LaunchDebuggerException): + print(FMT_ERROR + "[CPU] excpetion caught at 0x{:08X}: {}:".format(self.pc-1, ins) + FMT_NONE) + print(ex.message()) + self.pc -= 1 + + if self.active_debug: + print(FMT_CPU + "[CPU] Returning to debugger!" + FMT_NONE) + return if self.conf.debug_on_exception: launch_debug_session(self, self.mmu, self.regs, - "Exception encountered, launching debug:".format(self.pc-1)) + "Exception encountered, launching debug:") print() print(FMT_CPU + "Program exited with code {}".format(self.exit_code) + FMT_NONE) diff --git a/riscemu/Exceptions.py b/riscemu/Exceptions.py index 07348c8..d1605f7 100644 --- a/riscemu/Exceptions.py +++ b/riscemu/Exceptions.py @@ -134,3 +134,9 @@ class NumberFormatException(RiscemuBaseException): self.__class__.__name__, self.msg ) + + +# this exception is not printed and simply signals that an interactive debugging session is +class LaunchDebuggerException(RiscemuBaseException): + def message(self): + return "" diff --git a/riscemu/debug.py b/riscemu/debug.py index c739791..84e0e83 100644 --- a/riscemu/debug.py +++ b/riscemu/debug.py @@ -1,31 +1,46 @@ import typing +from .Registers import Registers if typing.TYPE_CHECKING: from . import * def launch_debug_session(cpu: 'CPU', mmu: 'MMU', reg: 'Registers', prompt=""): - if not cpu.conf.debug_instruction: + if not cpu.conf.debug_instruction or cpu.active_debug: return import code import readline import rlcompleter - # setup some aliases + cpu.active_debug = True + + # setup some aliases: registers = reg regs = reg memory = mmu mem = mmu syscall_interface = cpu.syscall_int + # setup helper functions: def dump(what, *args, **kwargs): if isinstance(what, Registers): regs.dump(*args, **kwargs) else: mmu.dump(what, *args, **kwargs) + def ins(): + print("Current instruction at 0x{:08X}:".format(cpu.pc)) + return mmu.read_ins(cpu.pc) + + def cont(verbose=True): + cpu.continue_from_debugger(verbose) + + def step(): + cpu.step() + sess_vars = globals() sess_vars.update(locals()) readline.set_completer(rlcompleter.Completer(sess_vars).complete) readline.parse_and_bind("tab: complete") - code.InteractiveConsole(sess_vars).interact(banner=prompt, exitmsg="Resuming simulation") + code.InteractiveConsole(sess_vars).interact(banner=prompt, exitmsg="Exiting debugger") + cpu.active_debug = False diff --git a/riscemu/instructions/RV32I.py b/riscemu/instructions/RV32I.py index 2bb9ea4..ef56378 100644 --- a/riscemu/instructions/RV32I.py +++ b/riscemu/instructions/RV32I.py @@ -271,7 +271,10 @@ class RV32I(InstructionSet): def instruction_sbreak(self, ins: 'LoadedInstruction'): ASSERT_LEN(ins.args, 0) - launch_debug_session(self.cpu, self.mmu, self.regs, "Debug instruction encountered at 0x{:08X}".format(self.pc)) + if self.cpu.active_debug: + print("Debug instruction encountered at 0x{:08X}".format(self.pc-1)) + raise LaunchDebuggerException() + launch_debug_session(self.cpu, self.mmu, self.regs, "Debug instruction encountered at 0x{:08X}".format(self.pc-1)) def instruction_nop(self, ins: 'LoadedInstruction'): ASSERT_LEN(ins.args, 0)