From 0638bcf635f6194ca2245d48d21911c8f10cdadb Mon Sep 17 00:00:00 2001 From: 45Tatami Date: Sat, 13 Apr 2024 21:13:35 +0200 Subject: [PATCH] basic flag setting and add some testcases --- src/emu.rs | 162 ++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 111 insertions(+), 51 deletions(-) diff --git a/src/emu.rs b/src/emu.rs index 131fff9..1a7fb5b 100644 --- a/src/emu.rs +++ b/src/emu.rs @@ -21,30 +21,7 @@ pub struct GameBoy { bp_manager: BreakPointManager, } -fn add(a: u8, b: u8) -> u8 { - if std::u8::MAX - b < a { - ((a as u16 + b as u16) - std::u8::MAX as u16) as u8 - 1 - } else { - a + b - } -} - -fn sub(a: u8, b: u8) -> u8 { - if a < b { - std::u8::MAX - (b - a) + 1 - } else { - a - b - } -} - -fn add_inplace(a: &mut u8, b: u8) { - *a = add(*a, b) -} - -fn sub_inplace(a: &mut u8, b: u8) { - *a = sub(*a, b) -} - +#[derive(Clone, Copy, Debug)] enum Flag { Z, N, H ,CY } @@ -53,12 +30,36 @@ fn flag_set(f: Flag, v: u8) -> bool { use Flag::*; match f { Z => v << 0 >> 7 == 1, - N => v << 1 >> 6 == 1, - H => v << 2 >> 5 == 1, - CY => v << 3 >> 4 == 1, + N => v << 1 >> 7 == 1, + H => v << 2 >> 7 == 1, + CY => v << 3 >> 7 == 1, } } +fn add_noflag(a: u8, b: u8) -> u8 { + if std::u8::MAX - b < a { + ((a as u16 + b as u16) - std::u8::MAX as u16) as u8 - 1 + } else { + a + b + } +} + +fn sub_noflag(a: u8, b: u8) -> u8 { + if a < b { + std::u8::MAX - (b - a) + 1 + } else { + a - b + } +} + +fn add_inplace(a: &mut u8, b: u8) { + *a = add_noflag(*a, b) +} + +fn sub_inplace(a: &mut u8, b: u8) { + *a = sub_noflag(*a, b) +} + impl GameBoy { pub fn new() -> GameBoy { GameBoy { @@ -73,6 +74,31 @@ impl GameBoy { } + fn add(&mut self, a: u8, b: u8) -> u8 { + let val = add_noflag(a, b); + println!("{}", val); + + self.set_flag(Flag::Z, val == 0); + self.set_flag(Flag::N, false); + self.set_flag(Flag::H, false); // FIXME + self.set_flag(Flag::CY, false); // FIXME + + val + } + + fn sub(&mut self, a: u8, b: u8) -> u8 { + let val = sub_noflag(a, b); + + self.set_flag(Flag::Z, val == 0); + self.set_flag(Flag::N, true); + self.set_flag(Flag::H, false); // FIXME + self.set_flag(Flag::CY, false); // FIXME + + val + } + + + fn set(adr: u16, val: u8) { // check for special registers } @@ -80,12 +106,13 @@ impl GameBoy { fn set_flag(&mut self, f: Flag, v: bool) { let r = &mut self.reg[Register::F]; let v = v as u8; + //println!("setting {f:?}: {v}"); use Flag::*; match f { - Z => *r = v << 7, - N => *r = v << 6, - H => *r = v << 5, - CY => *r = v << 4 + Z => *r = (v << 7) | (*r & 0b0111_1111), + N => *r = (v << 6) | (*r & 0b1011_1111), + H => *r = (v << 5) | (*r & 0b1101_1111), + CY => *r = (v << 4) | (*r & 0b1110_1111), } } @@ -278,7 +305,8 @@ impl GameBoy { BpCommand::Kill => panic!("kill from breakpoint"), }; - println!("pc:{pc:#08x}:{win:02x?} {op:?}"); + let regs = &self.reg[..8]; + println!("\u{1B}[1;31mpc:{pc:#08x}:{win:02x?} {op:?} {regs:?}\u{1B}[0m"); match op.inst { Unimpl(instr) => { @@ -318,6 +346,8 @@ impl GameBoy { println!("jr {new_pc:#06x} <- {old_pc:#06x} + {e}"); // TODO remember cycle count 3 for jump, 2 for not!!! return; + } else { + println!("jr cond not met"); } } Call => { @@ -355,14 +385,14 @@ impl GameBoy { } Inc(dst) => { match dst { - ValSrc::Reg(reg) => add_inplace(&mut self.reg[reg], 1), + ValSrc::Reg(reg) => self.reg[reg] = self.add(self.reg[reg], 1), _ => todo!() } println!("Inc {dst:?}"); } Dec(dst) => { match dst { - ValSrc::Reg(reg) => sub_inplace(&mut self.reg[reg], 1), + ValSrc::Reg(reg) => self.reg[reg] = self.sub(self.reg[reg], 1), _ => todo!() } println!("Dec {dst:?}"); @@ -402,25 +432,55 @@ impl GameBoy { #[cfg(test)] mod tests { - // #[test] - // fn add_overflow() { - // use crate::emu::add; - // assert_eq!(add(1, 1), 2); + #[test] + fn add_overflow() { + let mut gb = crate::emu::GameBoy::new(); - // assert_eq!(add(std::u8::MAX, 1), 0); - // assert_eq!(add(std::u8::MAX, 2), 1); - // assert_eq!(add(1, std::u8::MAX), 0); + assert_eq!(gb.add(1, 1), 2); - // assert_eq!(add(std::u8::MAX - 3, 3), std::u8::MAX); - // assert_eq!(add(3, std::u8::MAX - 3), std::u8::MAX); - // } + assert_eq!(gb.add(std::u8::MAX, 1), 0); + assert_eq!(gb.add(std::u8::MAX, 2), 1); + assert_eq!(gb.add(1, std::u8::MAX), 0); - // #[test] - // fn sub_underflow() { - // use crate::emu::sub; - // assert_eq!(sub(1, 1), 0); - // assert_eq!(sub(1, 2), std::u8::MAX); - // assert_eq!(sub(1, 3), std::u8::MAX - 1); - // } + assert_eq!(gb.add(std::u8::MAX - 3, 3), std::u8::MAX); + assert_eq!(gb.add(3, std::u8::MAX - 3), std::u8::MAX); + } + + #[test] + fn sub_underflow() { + let mut gb = crate::emu::GameBoy::new(); + + assert_eq!(gb.sub(1, 1), 0); + assert_eq!(gb.sub(1, 2), std::u8::MAX); + assert_eq!(gb.sub(1, 3), std::u8::MAX - 1); + } + + #[test] + fn flag_check() { + let mut gb = crate::emu::GameBoy::new(); + + use crate::emu::*; + + for f in [Flag::Z, Flag::N, Flag::H, Flag::CY] { + assert!(!flag_set(f, gb.reg[Register::F])); + gb.set_flag(f, true); + assert!(flag_set(f, gb.reg[Register::F])); + gb.set_flag(f, false); + assert!(!flag_set(f, gb.reg[Register::F])); + } + } + + #[test] + fn flag_set() { + use crate::emu::*; + + let mut gb = GameBoy::new(); + + assert!(!flag_set(Flag::Z, gb.reg[Register::F])); + gb.add(std::u8::MAX - 1 , 1); + assert!(!flag_set(Flag::Z, gb.reg[Register::F])); + gb.add(std::u8::MAX, 1); + assert!(flag_set(Flag::Z, gb.reg[Register::F])); + } }