feat: add most control flow instructions
This commit is contained in:
parent
b8cdc65ca7
commit
52098341dd
6 changed files with 710 additions and 20 deletions
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<u16>,
|
||||
pub opcode_len: Option<u8>,
|
||||
pub opcode_bytecount: Option<u8>,
|
||||
pub current_opcode: Option<u8>,
|
||||
pub current_prefixed_opcode: Option<u8>,
|
||||
pub mem_read_hold: Option<u8>,
|
||||
|
@ -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;
|
||||
|
|
|
@ -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!(),
|
||||
|
|
657
src/gameboy/cpu/flow.rs
Normal file
657
src/gameboy/cpu/flow.rs
Normal file
|
@ -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!(),
|
||||
}
|
||||
}
|
|
@ -22,7 +22,7 @@ macro_rules! define_ld_reg_imm_u16 {
|
|||
reg &= 0xFF;
|
||||
reg |= (state.registers.take_mem() as u16) << 8;
|
||||
state.registers.[<set_ $reg>](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!(),
|
||||
|
|
|
@ -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!(),
|
||||
|
|
Loading…
Reference in a new issue