From 4508ac653fff5558e516a1778cbd3c0f30f53abc Mon Sep 17 00:00:00 2001 From: pcasaretto Date: Sat, 20 Apr 2024 18:52:16 -0300 Subject: [PATCH] conditional jmps --- src/instructions/and.rs | 46 +++++++++---- src/instructions/jmp.rs | 140 ++++++++++++++++++++++++++++++++++++++++ src/instructions/mod.rs | 11 +++- src/instructions/sbc.rs | 24 +++---- 4 files changed, 192 insertions(+), 29 deletions(-) diff --git a/src/instructions/and.rs b/src/instructions/and.rs index 3c8aa62..c39f789 100644 --- a/src/instructions/and.rs +++ b/src/instructions/and.rs @@ -19,23 +19,21 @@ pub fn and(target: RegisterTarget) -> impl Fn(&mut Gameboy) -> u8 { } } -pub fn and_mem_at_r16(reg: Register16bTarget) -> impl Fn(&mut Gameboy) -> u8 { - move |gameboy: &mut Gameboy| { - let addr = gameboy.cpu.registers.get_u16(reg); - let value = gameboy.bus.read_byte(addr); - let a = gameboy.cpu.registers.get_u8(RegisterTarget::A); +pub fn and_mem_at_r16(gameboy: &mut Gameboy) -> u8 { + let addr = gameboy.cpu.registers.get_u16(Register16bTarget::HL); + let value = gameboy.bus.read_byte(addr); + let a = gameboy.cpu.registers.get_u8(RegisterTarget::A); - let result = a & value; + let result = a & value; - gameboy.cpu.registers.set_u8(RegisterTarget::A, result); + gameboy.cpu.registers.set_u8(RegisterTarget::A, result); - gameboy.cpu.registers.f.zero = result == 0; - gameboy.cpu.registers.f.subtract = false; - gameboy.cpu.registers.f.half_carry = true; - gameboy.cpu.registers.f.carry = false; - const TICKS: u8 = 8; - TICKS - } + gameboy.cpu.registers.f.zero = result == 0; + gameboy.cpu.registers.f.subtract = false; + gameboy.cpu.registers.f.half_carry = true; + gameboy.cpu.registers.f.carry = false; + const TICKS: u8 = 8; + TICKS } pub fn and_d8(gameboy: &mut Gameboy) -> u8 { @@ -118,4 +116,24 @@ mod tests { and(RegisterTarget::B)(&mut gameboy); assert!(!gameboy.cpu.registers.f.subtract); } + + #[test] + fn test_and_mem_at_r16() { + let mut gameboy = Gameboy::default(); + gameboy.cpu.registers.set_u16(Register16bTarget::HL, 0xC050); + gameboy.cpu.registers.set_u8(RegisterTarget::A, 0b1000_1010); + gameboy.bus.write_byte(0xC050, 0b1010_1010); + and_mem_at_r16(&mut gameboy); + assert_eq!(gameboy.cpu.registers.a, 0b1000_1010); + } + + #[test] + fn test_and_d8() { + let mut gameboy = Gameboy::default(); + gameboy.cpu.registers.pc = 0xC050; + gameboy.cpu.registers.set_u8(RegisterTarget::A, 0b1010_1010); + gameboy.bus.write_byte(0xC051, 0b1100_1010); + and_d8(&mut gameboy); + assert_eq!(gameboy.cpu.registers.a, 0b1000_1010); + } } diff --git a/src/instructions/jmp.rs b/src/instructions/jmp.rs index 94c73b9..ae86ffb 100644 --- a/src/instructions/jmp.rs +++ b/src/instructions/jmp.rs @@ -8,6 +8,58 @@ pub fn jmp_a16(gameboy: &mut Gameboy) -> u8 { return TICKS; } +pub fn jp_nz_a16(gameboy: &mut Gameboy) -> u8 { + if gameboy.cpu.registers.f.zero { + gameboy.cpu.registers.pc = gameboy.cpu.registers.pc.wrapping_add(3); + const TICKS: u8 = 12; + return TICKS; + } + let low = gameboy.read_next_byte(); + let high = gameboy.read_next_byte(); + gameboy.cpu.registers.pc = u16::from_be_bytes([high, low]); + const TICKS: u8 = 16; + return TICKS; +} + +pub fn jp_z_a16(gameboy: &mut Gameboy) -> u8 { + if !gameboy.cpu.registers.f.zero { + gameboy.cpu.registers.pc = gameboy.cpu.registers.pc.wrapping_add(3); + const TICKS: u8 = 12; + return TICKS; + } + let low = gameboy.read_next_byte(); + let high = gameboy.read_next_byte(); + gameboy.cpu.registers.pc = u16::from_be_bytes([high, low]); + const TICKS: u8 = 16; + return TICKS; +} + +pub fn jp_nc_a16(gameboy: &mut Gameboy) -> u8 { + if gameboy.cpu.registers.f.carry { + gameboy.cpu.registers.pc = gameboy.cpu.registers.pc.wrapping_add(3); + const TICKS: u8 = 12; + return TICKS; + } + let low = gameboy.read_next_byte(); + let high = gameboy.read_next_byte(); + gameboy.cpu.registers.pc = u16::from_be_bytes([high, low]); + const TICKS: u8 = 16; + return TICKS; +} + +pub fn jp_c_a16(gameboy: &mut Gameboy) -> u8 { + if !gameboy.cpu.registers.f.carry { + gameboy.cpu.registers.pc = gameboy.cpu.registers.pc.wrapping_add(3); + const TICKS: u8 = 12; + return TICKS; + } + let low = gameboy.read_next_byte(); + let high = gameboy.read_next_byte(); + gameboy.cpu.registers.pc = u16::from_be_bytes([high, low]); + const TICKS: u8 = 16; + return TICKS; +} + pub fn jr_z(gameboy: &mut Gameboy) -> u8 { if !gameboy.cpu.registers.f.zero { gameboy.cpu.registers.pc = gameboy.cpu.registers.pc.wrapping_add(2); @@ -103,6 +155,94 @@ mod tests { assert_eq!(gameboy.cpu.registers.pc, 0x0201); } + #[test] + fn test_jp_nc_a16_flag_set() { + let mut gameboy = Gameboy::default(); + gameboy.cpu.registers.pc = 0xC050; + gameboy.cpu.registers.f.carry = true; + gameboy.bus.memory[0xC051] = 0x01; + gameboy.bus.memory[0xC052] = 0x02; + jp_nc_a16(&mut gameboy); + assert_eq!(gameboy.cpu.registers.pc, 0xC053); + } + + #[test] + fn test_jp_nc_a16_flag_unset() { + let mut gameboy = Gameboy::default(); + gameboy.cpu.registers.pc = 0xC050; + gameboy.cpu.registers.f.carry = false; + gameboy.bus.memory[0xC051] = 0x01; + gameboy.bus.memory[0xC052] = 0x02; + jp_nc_a16(&mut gameboy); + assert_eq!(gameboy.cpu.registers.pc, 0x0201); + } + + #[test] + fn test_jp_c_a16_flag_set() { + let mut gameboy = Gameboy::default(); + gameboy.cpu.registers.pc = 0xC050; + gameboy.cpu.registers.f.carry = false; + gameboy.bus.memory[0xC051] = 0x01; + gameboy.bus.memory[0xC052] = 0x02; + jp_c_a16(&mut gameboy); + assert_eq!(gameboy.cpu.registers.pc, 0xC053); + } + + #[test] + fn test_jp_c_a16_flag_unset() { + let mut gameboy = Gameboy::default(); + gameboy.cpu.registers.pc = 0xC050; + gameboy.cpu.registers.f.carry = true; + gameboy.bus.memory[0xC051] = 0x01; + gameboy.bus.memory[0xC052] = 0x02; + jp_c_a16(&mut gameboy); + assert_eq!(gameboy.cpu.registers.pc, 0x0201); + } + + #[test] + fn test_jp_nz_a16_flag_set() { + let mut gameboy = Gameboy::default(); + gameboy.cpu.registers.pc = 0xC050; + gameboy.cpu.registers.f.zero = true; + gameboy.bus.memory[0xC051] = 0x01; + gameboy.bus.memory[0xC052] = 0x02; + jp_nz_a16(&mut gameboy); + assert_eq!(gameboy.cpu.registers.pc, 0xC053); + } + + #[test] + fn test_jp_nz_a16_flag_unset() { + let mut gameboy = Gameboy::default(); + gameboy.cpu.registers.pc = 0xC050; + gameboy.cpu.registers.f.zero = false; + gameboy.bus.memory[0xC051] = 0x01; + gameboy.bus.memory[0xC052] = 0x02; + jp_nz_a16(&mut gameboy); + assert_eq!(gameboy.cpu.registers.pc, 0x0201); + } + + #[test] + fn test_jp_z_a16_flag_set() { + let mut gameboy = Gameboy::default(); + gameboy.cpu.registers.pc = 0xC050; + gameboy.cpu.registers.f.zero = false; + gameboy.bus.memory[0xC051] = 0x01; + gameboy.bus.memory[0xC052] = 0x02; + jp_z_a16(&mut gameboy); + assert_eq!(gameboy.cpu.registers.pc, 0xC053); + } + + #[test] + fn test_jp_z_a16_flag_unset() { + let mut gameboy = Gameboy::default(); + gameboy.cpu.registers.pc = 0xC050; + gameboy.cpu.registers.f.zero = true; + gameboy.bus.memory[0xC051] = 0x01; + gameboy.bus.memory[0xC052] = 0x02; + jp_z_a16(&mut gameboy); + assert_eq!(gameboy.cpu.registers.pc, 0x0201); + } + #[test] fn test_jr() { let mut gameboy = Gameboy::default(); diff --git a/src/instructions/mod.rs b/src/instructions/mod.rs index 1a32787..39d94ce 100644 --- a/src/instructions/mod.rs +++ b/src/instructions/mod.rs @@ -103,10 +103,10 @@ pub fn from_byte(byte: u8) -> Box { 0x2C => Box::new(pai(inc::inc_r(RegisterTarget::L))), 0x3C => Box::new(pai(inc::inc_r(RegisterTarget::A))), + 0x35 => Box::new(pai(dec::dec_mem_at_hl)), 0x05 => Box::new(pai(dec::dec_r(RegisterTarget::B))), 0x15 => Box::new(pai(dec::dec_r(RegisterTarget::D))), 0x25 => Box::new(pai(dec::dec_r(RegisterTarget::H))), - 0x35 => Box::new(pai(dec::dec_mem_at_hl)), 0x0D => Box::new(pai(dec::dec_r(RegisterTarget::C))), 0x1D => Box::new(pai(dec::dec_r(RegisterTarget::E))), 0x2D => Box::new(pai(dec::dec_r(RegisterTarget::L))), @@ -234,7 +234,7 @@ pub fn from_byte(byte: u8) -> Box { 0xA3 => Box::new(pai(and::and(RegisterTarget::E))), 0xA4 => Box::new(pai(and::and(RegisterTarget::H))), 0xA5 => Box::new(pai(and::and(RegisterTarget::L))), - 0xA6 => Box::new(pai(and::and_mem_at_r16(Register16bTarget::HL))), + 0xA6 => Box::new(pai(and::and_mem_at_r16)), 0xA7 => Box::new(pai(and::and(RegisterTarget::A))), 0xA8 => Box::new(pai(xor::xor(RegisterTarget::B))), @@ -269,6 +269,11 @@ pub fn from_byte(byte: u8) -> Box { 0xE1 => Box::new(pai(stack::pop(Register16bTarget::HL))), 0xF1 => Box::new(pai(stack::pop(Register16bTarget::AF))), + 0xC2 => Box::new(jmp::jp_nz_a16), + 0xD2 => Box::new(jmp::jp_nc_a16), + 0xCA => Box::new(jmp::jp_z_a16), + 0xDA => Box::new(jmp::jp_c_a16), + 0xC4 => Box::new(call::call_nz_a16), 0xD4 => Box::new(call::call_nc_a16), @@ -285,7 +290,7 @@ pub fn from_byte(byte: u8) -> Box { 0xCD => Box::new(call::call_a16), 0xC9 => Box::new(call::ret), - 0xE9 => Box::new(pai(jmp::jp_hl)), + 0xE9 => Box::new(jmp::jp_hl), 0xC6 => Box::new(pai(add::add_d8)), 0xD6 => Box::new(pai(sub::sub_d8)), diff --git a/src/instructions/sbc.rs b/src/instructions/sbc.rs index d18fc68..558c3e7 100644 --- a/src/instructions/sbc.rs +++ b/src/instructions/sbc.rs @@ -178,7 +178,7 @@ mod tests { cpu: CPU { registers: Registers { a: 4, - pc: 0x0012, + pc: 0xC051, f: FlagsRegister::from(0), ..Default::default() }, @@ -186,7 +186,7 @@ mod tests { }, ..Default::default() }; - gameboy.bus.memory[0x0012] = 1; + gameboy.bus.memory[0xC051] = 1; sbc_n8(&mut gameboy); assert_eq!(gameboy.cpu.registers.a, 3); } @@ -196,7 +196,7 @@ mod tests { let mut gameboy = Gameboy { cpu: CPU { registers: Registers { - pc: 0x0012, + pc: 0xC051, a: 4, f: FlagsRegister::from(0), ..Default::default() @@ -205,7 +205,7 @@ mod tests { }, ..Default::default() }; - gameboy.bus.memory[0x0012] = 5; + gameboy.bus.memory[0xC051] = 5; sbc_n8(&mut gameboy); assert_eq!(gameboy.cpu.registers.a, 255); } @@ -216,7 +216,7 @@ mod tests { cpu: CPU { registers: Registers { a: 4, - pc: 0x0012, + pc: 0xC051, f: FlagsRegister::from(0), ..Default::default() }, @@ -224,7 +224,7 @@ mod tests { }, ..Default::default() }; - gameboy.bus.memory[0x0012] = 5; + gameboy.bus.memory[0xC051] = 5; sbc_n8(&mut gameboy); assert!(gameboy.cpu.registers.f.carry); } @@ -234,7 +234,7 @@ mod tests { let mut gameboy = Gameboy { cpu: CPU { registers: Registers { - pc: 0x0012, + pc: 0xC051, a: 5, f: FlagsRegister::from(0), ..Default::default() @@ -243,7 +243,7 @@ mod tests { }, ..Default::default() }; - gameboy.bus.memory[0x0012] = 5; + gameboy.bus.memory[0xC051] = 5; sbc_n8(&mut gameboy); assert!(gameboy.cpu.registers.f.zero); } @@ -253,7 +253,7 @@ mod tests { let mut gameboy = Gameboy { cpu: CPU { registers: Registers { - pc: 0x0012, + pc: 0xC051, a: 5, f: FlagsRegister::from(0), ..Default::default() @@ -262,7 +262,7 @@ mod tests { }, ..Default::default() }; - gameboy.bus.memory[0x0012] = 5; + gameboy.bus.memory[0xC051] = 5; sbc_n8(&mut gameboy); assert!(gameboy.cpu.registers.f.subtract); } @@ -272,7 +272,7 @@ mod tests { let mut gameboy = Gameboy { cpu: CPU { registers: Registers { - pc: 0x0012, + pc: 0xC051, a: 0b00010000, f: FlagsRegister::from(0), ..Default::default() @@ -281,7 +281,7 @@ mod tests { }, ..Default::default() }; - gameboy.bus.memory[0x0012] = 1; + gameboy.bus.memory[0xC051] = 1; sbc_n8(&mut gameboy); assert!(gameboy.cpu.registers.f.half_carry); }