feat: add inc/dec u8 opcodes and ldh opcodes

This commit is contained in:
EliseZeroTwo 2021-11-28 17:37:20 +01:00
parent c0733e8fec
commit 06c7197468
No known key found for this signature in database
GPG key ID: E6D56A6F7B7991DE
3 changed files with 274 additions and 0 deletions

View file

@ -120,28 +120,44 @@ pub fn tick_cpu(state: &mut Gameboy) {
let instruction_result: CycleResult = match opcode { let instruction_result: CycleResult = match opcode {
0x01 => load_store_move::ld_bc_imm_u16, 0x01 => load_store_move::ld_bc_imm_u16,
0x04 => alu::inc_b,
0x05 => alu::dec_b,
0x06 => load_store_move::ld_b_imm_u8, 0x06 => load_store_move::ld_b_imm_u8,
0x08 => load_store_move::ld_deref_imm_u16_sp, 0x08 => load_store_move::ld_deref_imm_u16_sp,
0x0a => load_store_move::ld_a_deref_bc, 0x0a => load_store_move::ld_a_deref_bc,
0x0c => alu::inc_c,
0x0d => alu::dec_c,
0x0e => load_store_move::ld_c_imm_u8, 0x0e => load_store_move::ld_c_imm_u8,
0x11 => load_store_move::ld_de_imm_u16, 0x11 => load_store_move::ld_de_imm_u16,
0x14 => alu::inc_d,
0x15 => alu::dec_d,
0x16 => load_store_move::ld_d_imm_u8, 0x16 => load_store_move::ld_d_imm_u8,
0x18 => flow::jr_i8, 0x18 => flow::jr_i8,
0x1a => load_store_move::ld_a_deref_de, 0x1a => load_store_move::ld_a_deref_de,
0x1c => alu::inc_e,
0x1d => alu::dec_e,
0x1e => load_store_move::ld_e_imm_u8, 0x1e => load_store_move::ld_e_imm_u8,
0x20 => flow::jr_nz_i8, 0x20 => flow::jr_nz_i8,
0x21 => load_store_move::ld_hl_imm_u16, 0x21 => load_store_move::ld_hl_imm_u16,
0x22 => load_store_move::ld_hl_plus_a, 0x22 => load_store_move::ld_hl_plus_a,
0x24 => alu::inc_h,
0x25 => alu::dec_h,
0x26 => load_store_move::ld_h_imm_u8, 0x26 => load_store_move::ld_h_imm_u8,
0x28 => flow::jr_z_i8, 0x28 => flow::jr_z_i8,
0x2a => load_store_move::ld_a_hl_plus, 0x2a => load_store_move::ld_a_hl_plus,
0x2c => alu::inc_l,
0x2d => alu::dec_l,
0x2e => load_store_move::ld_l_imm_u8, 0x2e => load_store_move::ld_l_imm_u8,
0x30 => flow::jr_nc_i8, 0x30 => flow::jr_nc_i8,
0x31 => load_store_move::ld_sp_imm_u16, 0x31 => load_store_move::ld_sp_imm_u16,
0x32 => load_store_move::ld_hl_minus_a, 0x32 => load_store_move::ld_hl_minus_a,
0x34 => alu::inc_deref_hl,
0x35 => alu::dec_deref_hl,
0x36 => load_store_move::ld_deref_hl_imm_u8, 0x36 => load_store_move::ld_deref_hl_imm_u8,
0x38 => flow::jr_c_i8, 0x38 => flow::jr_c_i8,
0x3a => load_store_move::ld_a_hl_minus, 0x3a => load_store_move::ld_a_hl_minus,
0x3c => alu::inc_a,
0x3d => alu::dec_a,
0x3e => load_store_move::ld_a_imm_u8, 0x3e => load_store_move::ld_a_imm_u8,
0x40 => load_store_move::ld_b_b, 0x40 => load_store_move::ld_b_b,
0x41 => load_store_move::ld_b_c, 0x41 => load_store_move::ld_b_c,
@ -233,9 +249,15 @@ pub fn tick_cpu(state: &mut Gameboy) {
0xDA => flow::jp_c_u16, 0xDA => flow::jp_c_u16,
0xDC => flow::call_c_u16, 0xDC => flow::call_c_u16,
0xDE => alu::sbc_a_imm_u8, 0xDE => alu::sbc_a_imm_u8,
0xE0 => load_store_move::ldh_imm_u8_a,
0xE2 => load_store_move::ldh_deref_c_a,
0xE9 => flow::jp_hl, 0xE9 => flow::jp_hl,
0xEA => load_store_move::ld_deref_imm_u16_a,
0xEE => alu::xor_a_imm_u8, 0xEE => alu::xor_a_imm_u8,
0xF0 => load_store_move::ldh_a_imm_u8,
0xF2 => load_store_move::ldh_a_deref_c,
0xF9 => load_store_move::ld_sp_hl, 0xF9 => load_store_move::ld_sp_hl,
0xFA => load_store_move::ld_a_deref_imm_u16,
unknown => panic!("Unrecognized opcode: {:#X}\nRegisters: {:#?}", unknown, state.registers), unknown => panic!("Unrecognized opcode: {:#X}\nRegisters: {:#?}", unknown, state.registers),
}(state); }(state);

View file

@ -24,6 +24,36 @@ pub fn sub_with_carry(lhs: u8, rhs: u8, carry: bool) -> CarryResult {
CarryResult { result, carry, half_carry } CarryResult { result, carry, half_carry }
} }
pub fn add_with_carry(lhs: u8, rhs: u8, carry: bool) -> CarryResult {
let carry_u8 = carry as u8;
let (first_res, first_carry) = lhs.overflowing_add(rhs);
let (result, second_carry) = first_res.overflowing_add(carry_u8);
let carry = first_carry || second_carry;
let (first_hc_res, first_half_carry) = (lhs & 0xF).overflowing_add(rhs & 0xF);
let (_, second_half_carry) = first_hc_res.overflowing_add(carry_u8);
let half_carry = first_half_carry || second_half_carry;
CarryResult { result, carry, half_carry }
}
pub fn add(lhs: u8, rhs: u8) -> CarryResult {
let (result, carry) = lhs.overflowing_add(rhs);
let (_, half_carry) = (lhs & 0xF).overflowing_add(rhs & 0xF);
CarryResult { result, carry, half_carry }
}
pub fn sub(lhs: u8, rhs: u8) -> CarryResult {
let (result, carry) = lhs.overflowing_add(rhs);
let (_, half_carry) = (lhs & 0xF).overflowing_add(rhs & 0xF);
CarryResult { result, carry, half_carry }
}
macro_rules! define_xor_reg { macro_rules! define_xor_reg {
($reg:ident) => { ($reg:ident) => {
paste::paste! { paste::paste! {
@ -171,3 +201,107 @@ pub fn sbc_a_imm_u8(state: &mut Gameboy) -> CycleResult {
_ => unreachable!(), _ => unreachable!(),
} }
} }
macro_rules! define_inc_reg {
($reg:ident) => {
paste::paste! {
pub fn [<inc_ $reg>](state: &mut Gameboy) -> CycleResult {
match state.registers.cycle {
0 => {
let CarryResult { result, half_carry, .. } = add(
state.registers.$reg,
1,
);
state.registers.set_zero(result == 0);
state.registers.set_subtract(false);
state.registers.set_half_carry(half_carry);
state.registers.opcode_bytecount = Some(1);
CycleResult::Finished
},
_ => unimplemented!(),
}
}
}
};
}
define_inc_reg!(b);
define_inc_reg!(c);
define_inc_reg!(d);
define_inc_reg!(e);
define_inc_reg!(h);
define_inc_reg!(l);
define_inc_reg!(a);
pub fn inc_deref_hl(state: &mut Gameboy) -> CycleResult {
match state.registers.cycle {
0 => {
state.cpu_read_u8(state.registers.get_hl());
CycleResult::NeedsMore
}
1 => {
let CarryResult { result, half_carry, .. } = add(state.registers.take_mem(), 1);
state.registers.set_zero(result == 0);
state.registers.set_subtract(false);
state.registers.set_half_carry(half_carry);
state.registers.opcode_bytecount = Some(1);
CycleResult::NeedsMore
}
2 => CycleResult::Finished,
_ => unimplemented!(),
}
}
macro_rules! define_dec_reg {
($reg:ident) => {
paste::paste! {
pub fn [<dec_ $reg>](state: &mut Gameboy) -> CycleResult {
match state.registers.cycle {
0 => {
let CarryResult { result, half_carry, .. } = sub(
state.registers.$reg,
1,
);
state.registers.set_zero(result == 0);
state.registers.set_subtract(false);
state.registers.set_half_carry(half_carry);
state.registers.opcode_bytecount = Some(1);
CycleResult::Finished
},
_ => unimplemented!(),
}
}
}
};
}
define_dec_reg!(b);
define_dec_reg!(c);
define_dec_reg!(d);
define_dec_reg!(e);
define_dec_reg!(h);
define_dec_reg!(l);
define_dec_reg!(a);
pub fn dec_deref_hl(state: &mut Gameboy) -> CycleResult {
match state.registers.cycle {
0 => {
state.cpu_read_u8(state.registers.get_hl());
CycleResult::NeedsMore
}
1 => {
let CarryResult { result, half_carry, .. } = sub(state.registers.take_mem(), 1);
state.registers.set_zero(result == 0);
state.registers.set_subtract(false);
state.registers.set_half_carry(half_carry);
state.registers.opcode_bytecount = Some(1);
CycleResult::NeedsMore
}
2 => CycleResult::Finished,
_ => unimplemented!(),
}
}

View file

@ -262,3 +262,121 @@ pub fn ld_deref_hl_imm_u8(state: &mut Gameboy) -> CycleResult {
_ => unreachable!(), _ => unreachable!(),
} }
} }
pub fn ldh_a_imm_u8(state: &mut Gameboy) -> CycleResult {
match state.registers.cycle {
0 => {
state.cpu_read_u8(state.registers.pc.overflowing_add(1).0);
CycleResult::NeedsMore
}
1 => {
let imm = state.registers.take_mem();
let addr = 0xFF00u16 | imm as u16;
state.cpu_read_u8(addr);
CycleResult::NeedsMore
}
2 => {
state.registers.a = state.registers.take_mem();
state.registers.opcode_bytecount = Some(2);
CycleResult::Finished
}
_ => unreachable!(),
}
}
pub fn ldh_imm_u8_a(state: &mut Gameboy) -> CycleResult {
match state.registers.cycle {
0 => {
state.cpu_read_u8(state.registers.pc.overflowing_add(1).0);
CycleResult::NeedsMore
}
1 => {
let imm = state.registers.take_mem();
let addr = 0xFF00u16 | imm as u16;
state.cpu_write_u8(addr, state.registers.a);
state.registers.opcode_bytecount = Some(2);
CycleResult::NeedsMore
}
2 => CycleResult::Finished,
_ => unreachable!(),
}
}
pub fn ldh_a_deref_c(state: &mut Gameboy) -> CycleResult {
match state.registers.cycle {
0 => {
let imm = state.registers.c;
let addr = 0xFF00u16 | imm as u16;
state.cpu_read_u8(addr);
CycleResult::NeedsMore
}
1 => {
state.registers.a = state.registers.take_mem();
state.registers.opcode_bytecount = Some(1);
CycleResult::Finished
}
_ => unreachable!(),
}
}
pub fn ldh_deref_c_a(state: &mut Gameboy) -> CycleResult {
match state.registers.cycle {
0 => {
let addr = 0xFF00u16 | state.registers.c as u16;
state.cpu_write_u8(addr, state.registers.a);
state.registers.opcode_bytecount = Some(1);
CycleResult::NeedsMore
}
1 => CycleResult::Finished,
_ => unreachable!(),
}
}
pub fn ld_a_deref_imm_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 lsb = state.registers.take_mem() as u16;
state.cpu_read_u8(state.registers.pc.overflowing_add(2).0);
state.registers.set_hold(lsb);
CycleResult::NeedsMore
}
2 => {
let addr = (state.registers.take_mem() as u16) << 8 | state.registers.take_hold();
state.cpu_read_u8(addr);
CycleResult::NeedsMore
}
3 => {
state.registers.a = state.registers.take_mem();
state.registers.opcode_bytecount = Some(3);
CycleResult::Finished
}
_ => unreachable!(),
}
}
pub fn ld_deref_imm_u16_a(state: &mut Gameboy) -> CycleResult {
match state.registers.cycle {
0 => {
state.cpu_read_u8(state.registers.pc.overflowing_add(1).0);
CycleResult::NeedsMore
}
1 => {
let lsb = state.registers.take_mem() as u16;
state.cpu_read_u8(state.registers.pc.overflowing_add(2).0);
state.registers.set_hold(lsb);
CycleResult::NeedsMore
}
2 => {
let addr = (state.registers.take_mem() as u16) << 8 | state.registers.take_hold();
state.cpu_write_u8(addr, state.registers.a);
state.registers.opcode_bytecount = Some(3);
CycleResult::NeedsMore
}
3 => CycleResult::Finished,
_ => unreachable!(),
}
}