From 830031b0b56a7da694724eeb341e1d0dc9ab3e49 Mon Sep 17 00:00:00 2001 From: paul moore Date: Mon, 1 Jan 2024 17:12:40 -0800 Subject: [PATCH] add fin command --- build.rs | 1 - src/debugger.rs | 10 +++++++++- src/execute.rs | 38 ++++++++++++++++++++++++++++++-------- src/shell.rs | 5 +++++ src/syntax.rs | 6 ++++++ 5 files changed, 50 insertions(+), 10 deletions(-) diff --git a/build.rs b/build.rs index ae6c717..eef1d8c 100644 --- a/build.rs +++ b/build.rs @@ -2,7 +2,6 @@ fn main() { println!("cargo:rerun-if-changed=sim65/6502.c"); cc::Build::new() .file("sim65/6502.c") - // .include("common") .define("DB65", "1") .compile("sim65"); } diff --git a/src/debugger.rs b/src/debugger.rs index 0a7f9b9..9b4bcad 100644 --- a/src/debugger.rs +++ b/src/debugger.rs @@ -37,6 +37,7 @@ pub(crate) enum FrameType { #[derive(Debug)] pub struct StackFrame { pub(crate) frame_type: FrameType, + pub(crate) stop_on_pop: bool, } #[derive(Debug, Clone)] pub struct BreakPoint { @@ -211,7 +212,14 @@ impl Debugger { self.execute(0) // 0 = forever } } - + pub fn finish(&mut self) -> Result { + for i in (0..self.stack_frames.len()).rev() { + if let FrameType::Jsr(_) = self.stack_frames[i].frame_type { + self.stack_frames[i].stop_on_pop = true; + } + } + self.execute(0) // 0 = forever + } pub fn next(&mut self) -> Result { let next_inst = Cpu::read_byte(Cpu::read_pc()); diff --git a/src/execute.rs b/src/execute.rs index 24c7d3f..8145f16 100644 --- a/src/execute.rs +++ b/src/execute.rs @@ -18,6 +18,7 @@ pub enum StopReason { Count, Next, Bug(BugType), + Finish, } #[derive(Debug, Clone)] pub enum BugType { @@ -35,18 +36,17 @@ impl Debugger { let reason = 'main_loop: loop { let pc = Cpu::read_pc(); - /* - Stack tracking code + + /*============================================================== + Stack tracking code if we hit a jsr, we push the return address and the stack pointer onto our own tracking stack. If we hit a rts, we pop the frame Also tracks push and pulls + ===============================================================*/ - Does not deal with interrupts since sim65 does not support them - - Includes stack balance check logic - */ let inst = Cpu::read_byte(pc); + let mut finish = false; match inst { 0x20 => { // jsr @@ -57,6 +57,7 @@ impl Debugger { let addr = lo as u16 | ((hi as u16) << 8); self.stack_frames.push(StackFrame { frame_type: FrameType::Jsr((addr, pc + 3, sp, 0)), + stop_on_pop: false, }); } @@ -64,6 +65,10 @@ impl Debugger { // rts if let Some(frame) = self.stack_frames.pop() { let sp = Cpu::read_sp(); + if frame.stop_on_pop { + // defer til after we execute the rts + finish = true; + } if self.enable_stack_check { if let FrameType::Jsr((_addr, _ret_addr, fsp, _)) = frame.frame_type { if sp + 2 != fsp { @@ -88,6 +93,7 @@ impl Debugger { let ac = Cpu::read_ac(); self.stack_frames.push(StackFrame { frame_type: FrameType::Pha(ac), + stop_on_pop: false, }); } @@ -104,6 +110,7 @@ impl Debugger { let sr = Cpu::read_sr(); self.stack_frames.push(StackFrame { frame_type: FrameType::Php(sr), + stop_on_pop: false, }); } 0x40 => { @@ -115,29 +122,41 @@ impl Debugger { // Now execute the instruction self.ticks += Cpu::execute_insn() as usize; + // PVExit called? if let Some(exit_code) = Cpu::exit_done() { self.run_done = false; break StopReason::Exit(exit_code); } + if Cpu::was_paracall() { - // keep our stack tracker clean + // a PV call opos the stack but we do not see an rts + // so we have a dangling stack frame - pop it self.stack_frames.pop().ok_or(anyhow!("stack underflow"))?; } + // invalid memory read check if self.enable_mem_check { if let Some(addr) = Cpu::get_memcheck() { break StopReason::Bug(BugType::Memcheck(addr)); } } + + // limited number of instructions? if counting { count -= 1; if count == 0 { break StopReason::Count; } } - // did we hit a breakpoint? + // did we just pop a stop_on_pop frame? + if finish { + break StopReason::Finish; + } + let pc = Cpu::read_pc(); + + // did we step over a function call? if let Some(next) = self.next_bp { // next stepping bp if next == pc { @@ -168,6 +187,8 @@ impl Debugger { } } } + + // did we hit a breakpoint? if let Some(bp) = self.break_points.get(&pc) { if bp.temp { self.break_points.remove(&pc); @@ -175,6 +196,7 @@ impl Debugger { break StopReason::BreakPoint(pc); } + // post instruction clean up Cpu::post_inst_reset(); }; Cpu::post_inst_reset(); // will have been missed on a break diff --git a/src/shell.rs b/src/shell.rs index c77ac6b..f5e7801 100644 --- a/src/shell.rs +++ b/src/shell.rs @@ -253,6 +253,10 @@ impl Shell { self.debugger .enable_stack_check(*args.get_one::("stackcheck").unwrap()); } + Some(("finish", _)) => { + let reason = self.debugger.finish()?; + self.stop(reason); + } Some((name, _matches)) => unimplemented!("{name}"), None => unreachable!("subcommand required"), } @@ -330,6 +334,7 @@ impl Shell { let wp = self.debugger.get_watch(addr).unwrap(); println!("watch #{} 0x{:04x} ({}) ", wp.number, wp.addr, wp.symbol); } + StopReason::Finish => {} } // disassemble the current instruction let inst_addr = self.debugger.read_pc(); diff --git a/src/syntax.rs b/src/syntax.rs index c5eb374..1a53d32 100644 --- a/src/syntax.rs +++ b/src/syntax.rs @@ -152,4 +152,10 @@ pub fn syntax() -> Command { .about("enable features") .help_template(APPLET_TEMPLATE), ) + .subcommand( + Command::new("finish") + .alias("fin") + .about("run till current function returns") + .help_template(APPLET_TEMPLATE), + ) }