diff --git a/src/breakpoint.rs b/src/breakpoint.rs index 0be1df3..cf0474e 100644 --- a/src/breakpoint.rs +++ b/src/breakpoint.rs @@ -1,12 +1,13 @@ use std::collections::{HashMap, VecDeque}; -// use crate::breakpoint_shell::{BreakPointShell, BreakPointShellCommand}; +use crate::op::Operation; pub enum Command { Ignore, Step, Cont, Kill, + Next, } pub enum State { @@ -27,7 +28,7 @@ pub trait BreakPointHandler { pub struct BreakPointManager { handlers: Vec>, - breakpoints: HashMap, + breakpoints: HashMap>, steppers: Vec, } @@ -43,37 +44,56 @@ impl BreakPointManager { // FIXME handler cannot use borrowed values adding more specific lifetimes could work but maybe // it is better to not own it at all? pub fn register_breakpoint(&mut self, adr: usize, bp: impl BreakPointHandler + 'static) { - self.handlers.insert(adr, Box::new(bp)); let i = self.handlers.len(); - self.breakpoints.insert(adr, i); + self.handlers.push(Box::new(bp)); + self.add_addr_breakpoint(adr, i); } - pub fn inform(&mut self, adr: usize) -> Command { + fn add_addr_breakpoint(&mut self, adr: usize, hndlr: usize) { + match self.breakpoints.get_mut(&adr) { + Some(bps) => bps.push(hndlr), + None => { + let mut bps = Vec::new(); + bps.push(hndlr); + self.breakpoints.insert(adr, bps); + } + } + } + + pub fn inform(&mut self, adr: usize, op: &Operation) -> Command { let mut new_steppers = Vec::new(); + let mut new_bps = Vec::new(); new_steppers.reserve(self.steppers.len()); + for hdl_i in self.steppers.drain(0..) { + println!("hit breakpoint {adr:#08x}"); let cmd = self.handlers[hdl_i].handler(Trigger::Step(adr)); match cmd { Command::Step => new_steppers.push(hdl_i), Command::Kill => return Command::Kill, Command::Cont | Command::Ignore => (), + Command::Next => new_bps.push(hdl_i), } } self.steppers = new_steppers; + new_bps.iter().for_each(|hdl| self.add_addr_breakpoint(adr+op.offs, *hdl)); - if let Some(hdl_i) = self.breakpoints.get(&adr) { - println!("hit breakpoint {adr:#08x}"); - let cmd = self.handlers[*hdl_i].handler(Trigger::Address(adr)); - match cmd { - Command::Step => self.steppers.push(*hdl_i), - Command::Kill => return Command::Kill, - Command::Cont | Command::Ignore => (), + if let Some(hdlrs) = self.breakpoints.get(&adr) { + let mut new_bps = Vec::new(); + for hdl_i in hdlrs { + println!("hit breakpoint {adr:#08x}"); + let cmd = self.handlers[*hdl_i].handler(Trigger::Address(adr)); + match cmd { + Command::Step => self.steppers.push(*hdl_i), + Command::Kill => return Command::Kill, + Command::Cont | Command::Ignore => (), + Command::Next => new_bps.push(*hdl_i), + } } - } else { - return Command::Ignore; + new_bps.iter().for_each(|hdl| self.add_addr_breakpoint(adr+op.offs, *hdl)); } - Command::Ignore // FIXME + Command::Ignore } } diff --git a/src/breakpoint_shell.rs b/src/breakpoint_shell.rs index f4a06f2..edd6434 100644 --- a/src/breakpoint_shell.rs +++ b/src/breakpoint_shell.rs @@ -18,8 +18,13 @@ struct BreakPointShellParser { pub enum ShellCommand { #[command(visible_alias="s")] Step, + #[command(visible_alias="c")] + Continue, + #[command(visible_alias="n")] + Next, #[command(visible_alias="q")] Quit, + // TODO UP } impl BreakPointShell { @@ -57,6 +62,8 @@ impl BreakPointHandler for BreakPointShell { match parser.cmd { ShellCommand::Step => break Command::Step, ShellCommand::Quit => break Command::Kill, + ShellCommand::Continue => break Command::Cont, + ShellCommand::Next => break Command::Next, } } } diff --git a/src/emu.rs b/src/emu.rs index fd9c5b7..fac71dd 100644 --- a/src/emu.rs +++ b/src/emu.rs @@ -121,7 +121,7 @@ impl GameBoy { let (dst_ref, dst_adr) = match dst { Loc::Mem(ref src) => match src { AddrLoc::Val => { - let adr = self.next16(pc) as usize; + let adr = self.read16(pc) as usize; (&mut self.mem[adr], adr) } _ => todo!(), @@ -155,7 +155,7 @@ impl GameBoy { let dst_ref = match dst { Loc::Reg(reg) => { // FIXME assuming DD is a typo for DE... - let reg_loc = reg as usize; + let reg_loc = reg.into(); &mut self.reg[reg_loc..=reg_loc + 1] } Loc::Val => panic!("value as dst is not allowed!"), @@ -172,24 +172,29 @@ impl GameBoy { dst_ref[1] = h; } - fn next16(&self, adr: usize) -> u16 { - let ladr = self.mem[adr + 1] as u16; - let hadr = self.mem[adr + 2] as u16; + fn read16(&self, adr: usize) -> u16 { + let ladr = self.mem[adr] as u16; + let hadr = self.mem[adr+1] as u16; (hadr << 8) + ladr } + fn store16(&mut self, loc: Loc, _loc_val: u8, h: u8, l: u8) { + match loc { + Loc::Reg(dst_reg) => { + let dst_reg: usize = dst_reg.into(); + self.reg[dst_reg] = h; + self.reg[dst_reg + 1] = l; + } + _ => todo!() + } + } + fn step(&mut self) { use Instr::*; let mut pc = self.pc as usize; let instr = self.mem[pc]; - match self.bp_manager.inform(pc) { - BpCommand::Ignore | BpCommand::Cont => (), - BpCommand::Step => todo!(), - BpCommand::Kill => panic!("kill from breakpoint"), - }; - let win = &self.mem[pc..cmp::min(RAM_SIZE, pc + 3)]; let op = match Operation::try_from(instr) { Ok(op) => op, @@ -198,6 +203,12 @@ impl GameBoy { } }; + match self.bp_manager.inform(pc, &op) { + BpCommand::Ignore | BpCommand::Cont | BpCommand::Next => (), + BpCommand::Step => todo!(), + BpCommand::Kill => panic!("kill from breakpoint"), + }; + println!("pc:{pc:#08x}:{win:02x?} {op:?}"); match op.inst { @@ -209,7 +220,7 @@ impl GameBoy { } Nop => (), Jp => { - let new_pc = self.next16(pc); + let new_pc = self.read16(pc+1); self.pc = new_pc; println!("jp {pc:#06x}->{new_pc:#06x}"); return; @@ -224,21 +235,44 @@ impl GameBoy { } Call => { let sp = self.sp as usize; - self.mem[sp - 1] = (pc >> 8) as u8; - self.mem[sp - 2] = (pc & 0xFF) as u8; - let new_pc = self.next16(pc); - let old_pc = self.pc; + self.mem[sp - 1] = ((pc+op.offs) >> 8) as u8; + self.mem[sp - 2] = ((pc+op.offs) & 0xFF) as u8; + let new_pc = self.read16(pc+1); self.pc = new_pc; self.sp -= 2; - println!("call {new_pc:#06x} <- {old_pc:#06x}"); + println!("call {new_pc:#06x}"); return; } Ret => { - let new_pc = self.next16((self.sp as usize) - 1); - let old_pc = self.pc; + let new_pc = self.read16(self.sp as usize); self.pc = new_pc; self.sp += 2; - println!("ret {new_pc:#06x} <- {old_pc:#06x}"); + println!("ret {new_pc:#06x}"); + return; + } + Psh(reg) => { + let reg: usize = reg.into(); + let sp = self.sp as usize; + self.mem[sp - 1] = self.reg[reg]; + self.mem[sp - 2] = self.reg[reg + 1]; + self.sp -= 2; + println!("push Reg({reg:#04b})"); + } + Pop(reg) => { + let reg: usize = reg.into(); + let sp = self.sp as usize; + self.reg[reg + 1] = self.mem[sp]; + self.reg[reg] = self.mem[sp + 1]; + self.sp += 2; + println!("pop Reg({reg:#04b})"); + } + Inc(reg) => { + self.reg[reg] += 1; + println!("Inc Reg({reg:#04b})"); + } + Dec(reg) => { + self.reg[reg as usize] -= 1; + println!("Dec Reg({reg:#04b})"); } Ld(dst, src) => self.op_ld(dst, src), Ld16(dst, src) => self.op_ld16(dst, src), diff --git a/src/op.rs b/src/op.rs index 1427d70..cb3682c 100644 --- a/src/op.rs +++ b/src/op.rs @@ -1,3 +1,5 @@ +use std::fmt; + pub const HD_MSK: u8 = 0b1100_0000; pub const T1_MSK: u8 = 0b0011_1000; pub const T2_MSK: u8 = 0b0000_0111; @@ -15,11 +17,18 @@ pub enum Instr { // Misc Nop, + Psh(Register), // double (16bit) register, eg B => BC + Pop(Register), // Load Ld(Loc, Loc), Ld16(Loc, Loc), + // Arith + // TODO can we do Inc16/Inc8 with one via Register::WIDE + Inc(Register), + Dec(Register), + // CPU ctl Di, @@ -47,16 +56,85 @@ pub enum Loc { #[derive(Debug, Copy, Clone)] pub enum Register { - A = 0x07, - F = 0x08, - B = 0x00, - C = 0x01, - D = 0x02, - E = 0x03, - H = 0x04, - L = 0x05, + A, + F, + B, + C, + D, + E, + H, + L, - SP = 0x09, + AF, + BC, + DE, + HL, + + SP, +} + +impl From for usize { + fn from(v: Register) -> usize { + use Register::*; + match v { + A | AF => 0x07, + B | BC => 0x00, + D | DE => 0x02, + H | HL => 0x04, + F => 0x08, + C => 0x01, + E => 0x03, + L => 0x05, + SP => 0x09 + } + } +} + +impl Register { + // this is from 16bit arithmetic operation instructions + fn from16_rep(v: u8) -> Register { + use Register::*; + match v { + 0b00 => BC, + 0b01 => DE, + 0b10 => HL, + 0b11 => SP, + _ => panic!("unknown register {v}") + } + } + + fn repr_psh(v: u8) -> Register { + use Register::*; + match v { + 0b00 => B, + 0b01 => D, + 0b10 => H, + 0b11 => A, + _ => panic!("unknown register {v}") + } + } +} + +impl std::ops::Index for [T] { + type Output = T; + + fn index(&self, r: Register) -> &Self::Output { + let i: usize = r.into(); + &self[i] + } +} + +impl std::ops::IndexMut for [T] { + fn index_mut(&mut self, r: Register) -> &mut Self::Output { + let i: usize = r.into(); + &mut self[i] + } +} + +impl fmt::Binary for Register { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Binary::fmt(&(*self as usize), f) + } } impl Loc { @@ -89,6 +167,10 @@ impl TryFrom for Register { } } +fn payload(v: u8) -> u8 { + (v & 0b110_000) >> 4 +} + impl TryFrom for Operation { type Error = String; @@ -105,6 +187,18 @@ impl TryFrom for Operation { 0b11_110_011 => (Di, 1, 1), 0b11_101_010 => (Ld(Mem(AddrLoc::Val), Reg(Register::A)), 4, 3), 0b11_100_000 => (Ld(HMem(AddrLoc::Val), Reg(Register::A)), 3, 2), + _ if (value & (HD_MSK | 0b1_000 | T2_MSK) == 0b00_000_011) => { + (Inc(Register::from16_rep(payload(value))), 2, 1) + } + _ if (value & (HD_MSK | 0b1_000 | T2_MSK) == 0b00_001_011) => { + (Dec(Register::from16_rep(payload(value))), 2, 1) + } + _ if (value & (HD_MSK | 0b1_000 | T2_MSK) == 0b11_000_101) => { + (Psh(Register::repr_psh(payload(value))), 4, 1) + } + _ if (value & (HD_MSK | 0b1_000 | T2_MSK) == 0b11_000_001) => { + (Pop(Register::repr_psh(payload(value))), 3, 1) + } _ if (value & (HD_MSK | T2_MSK)) == 0b00_000_110 => { let reg = Register::try_from((value & T1_MSK) >> 3)?; (Ld(Reg(reg), Val), 2, 2) @@ -115,7 +209,7 @@ impl TryFrom for Operation { (Ld(Reg(dst), Reg(src)), 1, 1) } _ if (value & (HD_MSK | 0b1_000 | T2_MSK)) == 0b00_000_001 => { - let reg = match (value & 0b110_000) >> 4 { + let reg = match payload(value) { 0b00 => Register::B, 0b01 => Register::D, 0b10 => Register::H,