From 52098341dd1c92ba347546f47585d6282ae92c1b Mon Sep 17 00:00:00 2001 From: EliseZeroTwo Date: Sun, 28 Nov 2021 16:12:19 +0100 Subject: [PATCH] feat: add most control flow instructions --- src/gameboy.rs | 10 + src/gameboy/cpu.rs | 29 +- src/gameboy/cpu/alu.rs | 12 +- src/gameboy/cpu/flow.rs | 657 +++++++++++++++++++++++++++++ src/gameboy/cpu/load_store_move.rs | 18 +- src/gameboy/cpu/prefixed.rs | 4 +- 6 files changed, 710 insertions(+), 20 deletions(-) create mode 100644 src/gameboy/cpu/flow.rs diff --git a/src/gameboy.rs b/src/gameboy.rs index 1873ecf..e0b1ecd 100644 --- a/src/gameboy.rs +++ b/src/gameboy.rs @@ -237,4 +237,14 @@ impl Gameboy { } } } + + pub fn cpu_push_stack(&mut self, byte: u8) { + self.registers.sp = self.registers.sp.overflowing_sub(1).0; + self.cpu_write_u8(self.registers.sp, byte) + } + + pub fn cpu_pop_stack(&mut self) { + self.cpu_read_u8(self.registers.sp); + self.registers.sp = self.registers.sp.overflowing_add(1).0; + } } diff --git a/src/gameboy/cpu.rs b/src/gameboy/cpu.rs index f2a131b..2e4cd35 100644 --- a/src/gameboy/cpu.rs +++ b/src/gameboy/cpu.rs @@ -1,4 +1,5 @@ mod alu; +mod flow; mod load_store_move; mod prefixed; @@ -56,7 +57,7 @@ pub struct Registers { // Not actual registers pub cycle: u8, pub hold: Option, - pub opcode_len: Option, + pub opcode_bytecount: Option, pub current_opcode: Option, pub current_prefixed_opcode: Option, pub mem_read_hold: Option, @@ -122,11 +123,16 @@ pub fn tick_cpu(state: &mut Gameboy) { 0x08 => load_store_move::ld_deref_imm_u16_sp, 0x0a => load_store_move::ld_a_deref_bc, 0x11 => load_store_move::ld_de_imm_u16, + 0x18 => flow::jr_i8, 0x1a => load_store_move::ld_a_deref_de, + 0x20 => flow::jr_nz_i8, 0x21 => load_store_move::ld_hl_imm_u16, 0x22 => load_store_move::ld_hl_plus_a, + 0x28 => flow::jr_z_i8, 0x2a => load_store_move::ld_a_hl_plus, + 0x30 => flow::jr_nc_i8, 0x32 => load_store_move::ld_hl_minus_a, + 0x38 => flow::jr_c_i8, 0x3a => load_store_move::ld_a_hl_minus, 0x31 => load_store_move::ld_sp_imm_u16, 0x40 => load_store_move::ld_b_b, @@ -201,15 +207,32 @@ pub fn tick_cpu(state: &mut Gameboy) { 0xAD => alu::xor_a_l, 0xAE => alu::xor_a_deref_hl, 0xAF => alu::xor_a_a, + 0xC0 => flow::ret_nz, + 0xC2 => flow::jp_nz_u16, + 0xC3 => flow::jp_u16, + 0xC4 => flow::call_nz_u16, + 0xC8 => flow::ret_z, + 0xC9 => flow::ret, + 0xCA => flow::jp_z_u16, 0xCB => prefixed::prefixed_handler, + 0xCC => flow::call_z_u16, + 0xCD => flow::call_u16, + 0xD0 => flow::ret_nc, + 0xD2 => flow::jp_nc_u16, + 0xD4 => flow::call_nc_u16, + 0xD8 => flow::ret_c, + 0xD9 => flow::reti, + 0xDA => flow::jp_c_u16, + 0xDC => flow::call_c_u16, 0xDE => alu::sbc_a_imm_u8, + 0xE9 => flow::jp_hl, 0xEE => alu::xor_a_imm_u8, 0xF9 => load_store_move::ld_sp_hl, unknown => panic!("Unrecognized opcode: {:#X}\nRegisters: {:#?}", unknown, state.registers), }(state); if instruction_result == CycleResult::Finished { - match state.registers.opcode_len { + match state.registers.opcode_bytecount { Some(len) => state.registers.pc += len as u16, None => panic!("Forgot to set opcode len for {:#X}", opcode), } @@ -222,7 +245,7 @@ pub fn tick_cpu(state: &mut Gameboy) { state.registers.cycle = 0; state.registers.current_prefixed_opcode = None; state.registers.current_opcode = None; - state.registers.opcode_len = None; + state.registers.opcode_bytecount = None; log::trace!("Cycle finished"); } else { state.registers.cycle += 1; diff --git a/src/gameboy/cpu/alu.rs b/src/gameboy/cpu/alu.rs index 461e883..b736401 100644 --- a/src/gameboy/cpu/alu.rs +++ b/src/gameboy/cpu/alu.rs @@ -35,7 +35,7 @@ macro_rules! define_xor_reg { state.registers.set_subtract(false); state.registers.set_half_carry(false); state.registers.set_carry(false); - state.registers.opcode_len = Some(1); + state.registers.opcode_bytecount = Some(1); CycleResult::Finished } _ => unreachable!(), @@ -58,7 +58,7 @@ macro_rules! define_sbc_reg { state.registers.set_subtract(true); state.registers.set_half_carry(half_carry); state.registers.set_carry(carry); - state.registers.opcode_len = Some(1); + state.registers.opcode_bytecount = Some(1); CycleResult::Finished }, _ => unreachable!(), @@ -88,7 +88,7 @@ pub fn xor_a_deref_hl(state: &mut Gameboy) -> CycleResult { state.registers.set_subtract(false); state.registers.set_half_carry(false); state.registers.set_carry(false); - state.registers.opcode_len = Some(1); + state.registers.opcode_bytecount = Some(1); CycleResult::Finished } _ => unreachable!(), @@ -107,7 +107,7 @@ pub fn xor_a_imm_u8(state: &mut Gameboy) -> CycleResult { state.registers.set_subtract(false); state.registers.set_half_carry(false); state.registers.set_carry(false); - state.registers.opcode_len = Some(2); + state.registers.opcode_bytecount = Some(2); CycleResult::Finished } _ => unreachable!(), @@ -140,7 +140,7 @@ pub fn sbc_a_deref_hl(state: &mut Gameboy) -> CycleResult { state.registers.set_subtract(true); state.registers.set_half_carry(half_carry); state.registers.set_carry(carry); - state.registers.opcode_len = Some(1); + state.registers.opcode_bytecount = Some(1); CycleResult::Finished } _ => unreachable!(), @@ -165,7 +165,7 @@ pub fn sbc_a_imm_u8(state: &mut Gameboy) -> CycleResult { state.registers.set_subtract(true); state.registers.set_half_carry(half_carry); state.registers.set_carry(carry); - state.registers.opcode_len = Some(1); + state.registers.opcode_bytecount = Some(1); CycleResult::Finished } _ => unreachable!(), diff --git a/src/gameboy/cpu/flow.rs b/src/gameboy/cpu/flow.rs new file mode 100644 index 0000000..e5974a9 --- /dev/null +++ b/src/gameboy/cpu/flow.rs @@ -0,0 +1,657 @@ +use super::CycleResult; +use crate::gameboy::Gameboy; + +pub fn jr_nz_i8(state: &mut Gameboy) -> CycleResult { + match state.registers.cycle { + 0 => { + state.cpu_read_u8(state.registers.pc.overflowing_add(1).0); + CycleResult::NeedsMore + } + 1 => { + if state.registers.get_zero() { + state.registers.take_mem(); + state.registers.opcode_bytecount = Some(2); + CycleResult::Finished + } else { + CycleResult::NeedsMore + } + } + 2 => { + let relative = state.registers.take_mem(); + const TC_BITFLAG: u8 = 1 << 7; + if relative & TC_BITFLAG == 0 { + state.registers.pc = state.registers.pc.overflowing_add(relative as u16).0; + } else { + state.registers.pc = + state.registers.pc.overflowing_sub((relative & !TC_BITFLAG) as u16).0; + } + state.registers.opcode_bytecount = Some(0); + CycleResult::Finished + } + _ => unreachable!(), + } +} + +pub fn jr_nc_i8(state: &mut Gameboy) -> CycleResult { + match state.registers.cycle { + 0 => { + state.cpu_read_u8(state.registers.pc.overflowing_add(1).0); + CycleResult::NeedsMore + } + 1 => { + if state.registers.get_carry() { + state.registers.take_mem(); + state.registers.opcode_bytecount = Some(2); + CycleResult::Finished + } else { + CycleResult::NeedsMore + } + } + 2 => { + let relative = state.registers.take_mem(); + const TC_BITFLAG: u8 = 1 << 7; + if relative & TC_BITFLAG == 0 { + state.registers.pc = state.registers.pc.overflowing_add(relative as u16).0; + } else { + state.registers.pc = + state.registers.pc.overflowing_sub((relative & !TC_BITFLAG) as u16).0; + } + state.registers.opcode_bytecount = Some(0); + CycleResult::Finished + } + _ => unreachable!(), + } +} + +pub fn jr_z_i8(state: &mut Gameboy) -> CycleResult { + match state.registers.cycle { + 0 => { + state.cpu_read_u8(state.registers.pc.overflowing_add(1).0); + CycleResult::NeedsMore + } + 1 => { + if !state.registers.get_zero() { + state.registers.take_mem(); + state.registers.opcode_bytecount = Some(2); + CycleResult::Finished + } else { + CycleResult::NeedsMore + } + } + 2 => { + let relative = state.registers.take_mem(); + const TC_BITFLAG: u8 = 1 << 7; + if relative & TC_BITFLAG == 0 { + state.registers.pc = state.registers.pc.overflowing_add(relative as u16).0; + } else { + state.registers.pc = + state.registers.pc.overflowing_sub((relative & !TC_BITFLAG) as u16).0; + } + state.registers.opcode_bytecount = Some(0); + CycleResult::Finished + } + _ => unreachable!(), + } +} + +pub fn jr_c_i8(state: &mut Gameboy) -> CycleResult { + match state.registers.cycle { + 0 => { + state.cpu_read_u8(state.registers.pc.overflowing_add(1).0); + CycleResult::NeedsMore + } + 1 => { + if !state.registers.get_carry() { + state.registers.take_mem(); + state.registers.opcode_bytecount = Some(2); + CycleResult::Finished + } else { + CycleResult::NeedsMore + } + } + 2 => { + let relative = state.registers.take_mem(); + const TC_BITFLAG: u8 = 1 << 7; + if relative & TC_BITFLAG == 0 { + state.registers.pc = state.registers.pc.overflowing_add(relative as u16).0; + } else { + state.registers.pc = + state.registers.pc.overflowing_sub((relative & !TC_BITFLAG) as u16).0; + } + state.registers.opcode_bytecount = Some(0); + CycleResult::Finished + } + _ => unreachable!(), + } +} + +pub fn jr_i8(state: &mut Gameboy) -> CycleResult { + match state.registers.cycle { + 0 => { + state.cpu_read_u8(state.registers.pc.overflowing_add(1).0); + CycleResult::NeedsMore + } + 1 => CycleResult::NeedsMore, + 2 => { + let relative = state.registers.take_mem(); + const TC_BITFLAG: u8 = 1 << 7; + if relative & TC_BITFLAG == 0 { + state.registers.pc = state.registers.pc.overflowing_add(relative as u16).0; + } else { + state.registers.pc = + state.registers.pc.overflowing_sub((relative & !TC_BITFLAG) as u16).0; + } + state.registers.opcode_bytecount = Some(0); + CycleResult::Finished + } + _ => unreachable!(), + } +} + +pub fn jp_u16(state: &mut Gameboy) -> CycleResult { + match state.registers.cycle { + 0 => { + state.cpu_read_u8(state.registers.pc.overflowing_add(1).0); + CycleResult::NeedsMore + } + 1 => { + let lower = state.registers.take_mem() as u16; + state.registers.set_hold(lower); + state.cpu_read_u8(state.registers.pc.overflowing_add(2).0); + CycleResult::NeedsMore + } + 2 => CycleResult::NeedsMore, + 3 => { + let address = (state.registers.take_mem() as u16) << 8 | state.registers.take_hold(); + state.registers.pc = address; + state.registers.opcode_bytecount = Some(0); + CycleResult::Finished + } + _ => unreachable!(), + } +} + +pub fn jp_hl(state: &mut Gameboy) -> CycleResult { + match state.registers.cycle { + 0 => { + state.registers.pc = state.registers.get_hl(); + state.registers.opcode_bytecount = Some(0); + CycleResult::Finished + } + _ => unreachable!(), + } +} + +pub fn jp_nz_u16(state: &mut Gameboy) -> CycleResult { + match state.registers.cycle { + 0 => { + state.cpu_read_u8(state.registers.pc.overflowing_add(1).0); + CycleResult::NeedsMore + } + 1 => { + let lower = state.registers.take_mem() as u16; + state.registers.set_hold(lower); + state.cpu_read_u8(state.registers.pc.overflowing_add(2).0); + CycleResult::NeedsMore + } + 2 => { + if state.registers.get_zero() { + state.registers.take_mem(); + state.registers.opcode_bytecount = Some(3); + CycleResult::Finished + } else { + CycleResult::NeedsMore + } + } + 3 => { + let address = (state.registers.take_mem() as u16) << 8 | state.registers.take_hold(); + state.registers.pc = address; + state.registers.opcode_bytecount = Some(0); + CycleResult::Finished + } + _ => unreachable!(), + } +} + +pub fn jp_nc_u16(state: &mut Gameboy) -> CycleResult { + match state.registers.cycle { + 0 => { + state.cpu_read_u8(state.registers.pc.overflowing_add(1).0); + CycleResult::NeedsMore + } + 1 => { + let lower = state.registers.take_mem() as u16; + state.registers.set_hold(lower); + state.cpu_read_u8(state.registers.pc.overflowing_add(2).0); + CycleResult::NeedsMore + } + 2 => { + if state.registers.get_carry() { + state.registers.take_mem(); + state.registers.opcode_bytecount = Some(3); + CycleResult::Finished + } else { + CycleResult::NeedsMore + } + } + 3 => { + let address = (state.registers.take_mem() as u16) << 8 | state.registers.take_hold(); + state.registers.pc = address; + state.registers.opcode_bytecount = Some(0); + CycleResult::Finished + } + _ => unreachable!(), + } +} + +pub fn jp_z_u16(state: &mut Gameboy) -> CycleResult { + match state.registers.cycle { + 0 => { + state.cpu_read_u8(state.registers.pc.overflowing_add(1).0); + CycleResult::NeedsMore + } + 1 => { + let lower = state.registers.take_mem() as u16; + state.registers.set_hold(lower); + state.cpu_read_u8(state.registers.pc.overflowing_add(2).0); + CycleResult::NeedsMore + } + 2 => { + if !state.registers.get_zero() { + state.registers.take_mem(); + state.registers.opcode_bytecount = Some(3); + CycleResult::Finished + } else { + CycleResult::NeedsMore + } + } + 3 => { + let address = (state.registers.take_mem() as u16) << 8 | state.registers.take_hold(); + state.registers.pc = address; + state.registers.opcode_bytecount = Some(0); + CycleResult::Finished + } + _ => unreachable!(), + } +} + +pub fn jp_c_u16(state: &mut Gameboy) -> CycleResult { + match state.registers.cycle { + 0 => { + state.cpu_read_u8(state.registers.pc.overflowing_add(1).0); + CycleResult::NeedsMore + } + 1 => { + let lower = state.registers.take_mem() as u16; + state.registers.set_hold(lower); + state.cpu_read_u8(state.registers.pc.overflowing_add(2).0); + CycleResult::NeedsMore + } + 2 => { + if !state.registers.get_carry() { + state.registers.take_mem(); + state.registers.opcode_bytecount = Some(3); + CycleResult::Finished + } else { + CycleResult::NeedsMore + } + } + 3 => { + let address = (state.registers.take_mem() as u16) << 8 | state.registers.take_hold(); + state.registers.pc = address; + state.registers.opcode_bytecount = Some(0); + CycleResult::Finished + } + _ => unreachable!(), + } +} + +pub fn call_u16(state: &mut Gameboy) -> CycleResult { + match state.registers.cycle { + 0 => { + state.cpu_read_u8(state.registers.pc.overflowing_add(1).0); + CycleResult::NeedsMore + } + 1 => { + let lower = state.registers.take_mem() as u16; + state.registers.set_hold(lower); + state.cpu_read_u8(state.registers.pc.overflowing_add(2).0); + CycleResult::NeedsMore + } + 2 => CycleResult::NeedsMore, + 3 => { + state.cpu_push_stack((state.registers.pc.overflowing_add(3).0 >> 8) as u8); + CycleResult::NeedsMore + } + 4 => { + state.cpu_push_stack(state.registers.pc.overflowing_add(3).0 as u8); + CycleResult::NeedsMore + } + 5 => { + let address = (state.registers.take_mem() as u16) << 8 | state.registers.take_hold(); + state.registers.pc = address; + state.registers.opcode_bytecount = Some(0); + CycleResult::Finished + } + _ => unreachable!(), + } +} + +pub fn call_nz_u16(state: &mut Gameboy) -> CycleResult { + match state.registers.cycle { + 0 => { + state.cpu_read_u8(state.registers.pc.overflowing_add(1).0); + CycleResult::NeedsMore + } + 1 => { + let lower = state.registers.take_mem() as u16; + state.registers.set_hold(lower); + state.cpu_read_u8(state.registers.pc.overflowing_add(2).0); + CycleResult::NeedsMore + } + 2 => { + if state.registers.get_zero() { + state.registers.take_mem(); + state.registers.opcode_bytecount = Some(3); + CycleResult::Finished + } else { + CycleResult::NeedsMore + } + } + 3 => { + state.cpu_push_stack((state.registers.pc.overflowing_add(3).0 >> 8) as u8); + CycleResult::NeedsMore + } + 4 => { + state.cpu_push_stack(state.registers.pc.overflowing_add(3).0 as u8); + CycleResult::NeedsMore + } + 5 => { + let address = (state.registers.take_mem() as u16) << 8 | state.registers.take_hold(); + state.registers.pc = address; + state.registers.opcode_bytecount = Some(0); + CycleResult::Finished + } + _ => unreachable!(), + } +} + +pub fn call_nc_u16(state: &mut Gameboy) -> CycleResult { + match state.registers.cycle { + 0 => { + state.cpu_read_u8(state.registers.pc.overflowing_add(1).0); + CycleResult::NeedsMore + } + 1 => { + let lower = state.registers.take_mem() as u16; + state.registers.set_hold(lower); + state.cpu_read_u8(state.registers.pc.overflowing_add(2).0); + CycleResult::NeedsMore + } + 2 => { + if state.registers.get_carry() { + state.registers.take_mem(); + state.registers.opcode_bytecount = Some(3); + CycleResult::Finished + } else { + CycleResult::NeedsMore + } + } + 3 => { + state.cpu_push_stack((state.registers.pc.overflowing_add(3).0 >> 8) as u8); + CycleResult::NeedsMore + } + 4 => { + state.cpu_push_stack(state.registers.pc.overflowing_add(3).0 as u8); + CycleResult::NeedsMore + } + 5 => { + let address = (state.registers.take_mem() as u16) << 8 | state.registers.take_hold(); + state.registers.pc = address; + state.registers.opcode_bytecount = Some(0); + CycleResult::Finished + } + _ => unreachable!(), + } +} + +pub fn call_z_u16(state: &mut Gameboy) -> CycleResult { + match state.registers.cycle { + 0 => { + state.cpu_read_u8(state.registers.pc.overflowing_add(1).0); + CycleResult::NeedsMore + } + 1 => { + let lower = state.registers.take_mem() as u16; + state.registers.set_hold(lower); + state.cpu_read_u8(state.registers.pc.overflowing_add(2).0); + CycleResult::NeedsMore + } + 2 => { + if !state.registers.get_zero() { + state.registers.take_mem(); + state.registers.opcode_bytecount = Some(3); + CycleResult::Finished + } else { + CycleResult::NeedsMore + } + } + 3 => { + state.cpu_push_stack((state.registers.pc.overflowing_add(3).0 >> 8) as u8); + CycleResult::NeedsMore + } + 4 => { + state.cpu_push_stack(state.registers.pc.overflowing_add(3).0 as u8); + CycleResult::NeedsMore + } + 5 => { + let address = (state.registers.take_mem() as u16) << 8 | state.registers.take_hold(); + state.registers.pc = address; + state.registers.opcode_bytecount = Some(0); + CycleResult::Finished + } + _ => unreachable!(), + } +} + +pub fn call_c_u16(state: &mut Gameboy) -> CycleResult { + match state.registers.cycle { + 0 => { + state.cpu_read_u8(state.registers.pc.overflowing_add(1).0); + CycleResult::NeedsMore + } + 1 => { + let lower = state.registers.take_mem() as u16; + state.registers.set_hold(lower); + state.cpu_read_u8(state.registers.pc.overflowing_add(2).0); + CycleResult::NeedsMore + } + 2 => { + if !state.registers.get_carry() { + state.registers.take_mem(); + state.registers.opcode_bytecount = Some(3); + CycleResult::Finished + } else { + CycleResult::NeedsMore + } + } + 3 => { + state.cpu_push_stack((state.registers.pc.overflowing_add(3).0 >> 8) as u8); + CycleResult::NeedsMore + } + 4 => { + state.cpu_push_stack(state.registers.pc.overflowing_add(3).0 as u8); + CycleResult::NeedsMore + } + 5 => { + let address = (state.registers.take_mem() as u16) << 8 | state.registers.take_hold(); + state.registers.pc = address; + state.registers.opcode_bytecount = Some(0); + CycleResult::Finished + } + _ => unreachable!(), + } +} + +pub fn ret(state: &mut Gameboy) -> CycleResult { + match state.registers.cycle { + 0 => { + state.cpu_pop_stack(); + CycleResult::NeedsMore + } + 1 => { + let lsb = state.registers.take_mem() as u16; + state.registers.set_hold(lsb); + state.cpu_pop_stack(); + CycleResult::NeedsMore + } + 2 => CycleResult::NeedsMore, + 3 => { + let address = (state.registers.take_mem() as u16) << 8 | state.registers.take_hold(); + state.registers.pc = address; + state.registers.opcode_bytecount = Some(0); + CycleResult::Finished + } + _ => unreachable!(), + } +} + +pub fn reti(state: &mut Gameboy) -> CycleResult { + match state.registers.cycle { + 0 => { + state.cpu_pop_stack(); + CycleResult::NeedsMore + } + 1 => { + let lsb = state.registers.take_mem() as u16; + state.registers.set_hold(lsb); + state.cpu_pop_stack(); + CycleResult::NeedsMore + } + 2 => CycleResult::NeedsMore, + 3 => { + state.interrupts.ime = true; + let address = (state.registers.take_mem() as u16) << 8 | state.registers.take_hold(); + state.registers.pc = address; + state.registers.opcode_bytecount = Some(0); + CycleResult::Finished + } + _ => unreachable!(), + } +} + +pub fn ret_nz(state: &mut Gameboy) -> CycleResult { + match state.registers.cycle { + 0 => CycleResult::NeedsMore, + 1 => { + if state.registers.get_zero() { + state.registers.opcode_bytecount = Some(1); + CycleResult::Finished + } else { + state.cpu_pop_stack(); + CycleResult::NeedsMore + } + } + 2 => { + let lsb = state.registers.take_mem() as u16; + state.registers.set_hold(lsb); + state.cpu_pop_stack(); + CycleResult::NeedsMore + } + 3 => CycleResult::NeedsMore, + 4 => { + let address = (state.registers.take_mem() as u16) << 8 | state.registers.take_hold(); + state.registers.pc = address; + state.registers.opcode_bytecount = Some(0); + CycleResult::Finished + } + _ => unreachable!(), + } +} + +pub fn ret_nc(state: &mut Gameboy) -> CycleResult { + match state.registers.cycle { + 0 => CycleResult::NeedsMore, + 1 => { + if state.registers.get_carry() { + state.registers.opcode_bytecount = Some(1); + CycleResult::Finished + } else { + state.cpu_pop_stack(); + CycleResult::NeedsMore + } + } + 2 => { + let lsb = state.registers.take_mem() as u16; + state.registers.set_hold(lsb); + state.cpu_pop_stack(); + CycleResult::NeedsMore + } + 3 => CycleResult::NeedsMore, + 4 => { + let address = (state.registers.take_mem() as u16) << 8 | state.registers.take_hold(); + state.registers.pc = address; + state.registers.opcode_bytecount = Some(0); + CycleResult::Finished + } + _ => unreachable!(), + } +} + +pub fn ret_z(state: &mut Gameboy) -> CycleResult { + match state.registers.cycle { + 0 => CycleResult::NeedsMore, + 1 => { + if !state.registers.get_zero() { + state.registers.opcode_bytecount = Some(1); + CycleResult::Finished + } else { + state.cpu_pop_stack(); + CycleResult::NeedsMore + } + } + 2 => { + let lsb = state.registers.take_mem() as u16; + state.registers.set_hold(lsb); + state.cpu_pop_stack(); + CycleResult::NeedsMore + } + 3 => CycleResult::NeedsMore, + 4 => { + let address = (state.registers.take_mem() as u16) << 8 | state.registers.take_hold(); + state.registers.pc = address; + state.registers.opcode_bytecount = Some(0); + CycleResult::Finished + } + _ => unreachable!(), + } +} + +pub fn ret_c(state: &mut Gameboy) -> CycleResult { + match state.registers.cycle { + 0 => CycleResult::NeedsMore, + 1 => { + if !state.registers.get_carry() { + state.registers.opcode_bytecount = Some(1); + CycleResult::Finished + } else { + state.cpu_pop_stack(); + CycleResult::NeedsMore + } + } + 2 => { + let lsb = state.registers.take_mem() as u16; + state.registers.set_hold(lsb); + state.cpu_pop_stack(); + CycleResult::NeedsMore + } + 3 => CycleResult::NeedsMore, + 4 => { + let address = (state.registers.take_mem() as u16) << 8 | state.registers.take_hold(); + state.registers.pc = address; + state.registers.opcode_bytecount = Some(0); + CycleResult::Finished + } + _ => unreachable!(), + } +} diff --git a/src/gameboy/cpu/load_store_move.rs b/src/gameboy/cpu/load_store_move.rs index 0310dd6..4ef504b 100644 --- a/src/gameboy/cpu/load_store_move.rs +++ b/src/gameboy/cpu/load_store_move.rs @@ -22,7 +22,7 @@ macro_rules! define_ld_reg_imm_u16 { reg &= 0xFF; reg |= (state.registers.take_mem() as u16) << 8; state.registers.[](reg); - state.registers.opcode_len = Some(3); + state.registers.opcode_bytecount = Some(3); CycleResult::Finished }, _ => unreachable!(), @@ -47,7 +47,7 @@ pub fn ld_sp_hl(state: &mut Gameboy) -> CycleResult { 1 => { state.registers.sp &= 0xFF; state.registers.sp |= (state.registers.h as u16) << 8; - state.registers.opcode_len = Some(3); + state.registers.opcode_bytecount = Some(3); CycleResult::Finished } _ => unreachable!(), @@ -75,7 +75,7 @@ pub fn ld_deref_imm_u16_sp(state: &mut Gameboy) -> CycleResult { 3 => { let addr = state.registers.take_hold().overflowing_add(1).0; state.cpu_write_u8(addr, (state.registers.sp >> 8) as u8); - state.registers.opcode_len = Some(3); + state.registers.opcode_bytecount = Some(3); CycleResult::Finished } _ => unreachable!(), @@ -90,7 +90,7 @@ macro_rules! define_ld_reg_reg { 0 => { let res = state.registers.$rreg; state.registers.$lreg = res; - state.registers.opcode_len = Some(1); + state.registers.opcode_bytecount = Some(1); CycleResult::Finished }, _ => unreachable!(), @@ -167,7 +167,7 @@ macro_rules! define_ld_reg_deref { }, 1 => { state.registers.$lreg = state.registers.take_mem(); - state.registers.opcode_len = Some(1); + state.registers.opcode_bytecount = Some(1); CycleResult::Finished }, _ => unreachable!(), @@ -196,7 +196,7 @@ pub fn ld_hl_minus_a(state: &mut Gameboy) -> CycleResult { 1 => { let reg = state.registers.get_hl().overflowing_sub(1).0; state.registers.set_hl(reg); - state.registers.opcode_len = Some(1); + state.registers.opcode_bytecount = Some(1); CycleResult::Finished } _ => unreachable!(), @@ -213,7 +213,7 @@ pub fn ld_a_hl_minus(state: &mut Gameboy) -> CycleResult { state.registers.a = state.registers.take_mem(); let reg = state.registers.get_hl().overflowing_sub(1).0; state.registers.set_hl(reg); - state.registers.opcode_len = Some(1); + state.registers.opcode_bytecount = Some(1); CycleResult::Finished } _ => unreachable!(), @@ -229,7 +229,7 @@ pub fn ld_hl_plus_a(state: &mut Gameboy) -> CycleResult { 1 => { let reg = state.registers.get_hl().overflowing_add(1).0; state.registers.set_hl(reg); - state.registers.opcode_len = Some(1); + state.registers.opcode_bytecount = Some(1); CycleResult::Finished } _ => unreachable!(), @@ -246,7 +246,7 @@ pub fn ld_a_hl_plus(state: &mut Gameboy) -> CycleResult { state.registers.a = state.registers.take_mem(); let reg = state.registers.get_hl().overflowing_add(1).0; state.registers.set_hl(reg); - state.registers.opcode_len = Some(1); + state.registers.opcode_bytecount = Some(1); CycleResult::Finished } _ => unreachable!(), diff --git a/src/gameboy/cpu/prefixed.rs b/src/gameboy/cpu/prefixed.rs index 55925a6..e3304d6 100644 --- a/src/gameboy/cpu/prefixed.rs +++ b/src/gameboy/cpu/prefixed.rs @@ -99,7 +99,7 @@ macro_rules! define_bit_reg { state.registers.set_zero(state.registers.$reg & (1 << $bit) == 0); state.registers.set_subtract(false); state.registers.set_half_carry(true); - state.registers.opcode_len = Some(2); + state.registers.opcode_bytecount = Some(2); CycleResult::Finished } _ => unreachable!(), @@ -144,7 +144,7 @@ macro_rules! define_bit_deref_hl { state.registers.set_zero(mem_read & (1 << $bit) == 0); state.registers.set_subtract(false); state.registers.set_half_carry(true); - state.registers.opcode_len = Some(2); + state.registers.opcode_bytecount = Some(2); CycleResult::Finished } _ => unreachable!(),