feat: add more opcodes and stub serial output
This commit is contained in:
parent
1d4dc8bc22
commit
8c5c1c4b23
7 changed files with 493 additions and 13 deletions
|
@ -4,6 +4,7 @@ mod joypad;
|
|||
mod mapper;
|
||||
mod memory;
|
||||
mod ppu;
|
||||
mod serial;
|
||||
mod sound;
|
||||
mod timer;
|
||||
|
||||
|
@ -14,7 +15,7 @@ use memory::Memory;
|
|||
use ppu::Ppu;
|
||||
use timer::Timer;
|
||||
|
||||
use self::{cpu::Registers, mapper::NoMBC, sound::Sound};
|
||||
use self::{cpu::Registers, mapper::NoMBC, serial::Serial, sound::Sound};
|
||||
|
||||
pub struct DmaState {
|
||||
pub base: u8,
|
||||
|
@ -42,13 +43,14 @@ pub struct Gameboy {
|
|||
timer: Timer,
|
||||
pub registers: Registers,
|
||||
pub joypad: Joypad,
|
||||
pub serial: Serial,
|
||||
pub dma: DmaState,
|
||||
pub sound: Sound,
|
||||
|
||||
pub single_step: bool,
|
||||
pub breakpoints: [bool; u16::MAX as usize],
|
||||
pub mem_read_breakpoints: [bool; u16::MAX as usize],
|
||||
pub mem_write_breakpoints: [bool; u16::MAX as usize],
|
||||
pub breakpoints: [bool; u16::MAX as usize + 1],
|
||||
pub mem_read_breakpoints: [bool; u16::MAX as usize + 1],
|
||||
pub mem_write_breakpoints: [bool; u16::MAX as usize + 1],
|
||||
trigger_bp: bool,
|
||||
}
|
||||
|
||||
|
@ -60,14 +62,15 @@ impl Gameboy {
|
|||
interrupts: Interrupts::new(),
|
||||
timer: Timer::new(),
|
||||
joypad: Joypad::new(),
|
||||
serial: Serial::new(),
|
||||
dma: DmaState::new(),
|
||||
ppu: Ppu::new(),
|
||||
registers: Registers::default(),
|
||||
sound: Sound::default(),
|
||||
single_step: false,
|
||||
breakpoints: [false; u16::MAX as usize],
|
||||
mem_read_breakpoints: [false; u16::MAX as usize],
|
||||
mem_write_breakpoints: [false; u16::MAX as usize],
|
||||
breakpoints: [false; u16::MAX as usize + 1],
|
||||
mem_read_breakpoints: [false; u16::MAX as usize + 1],
|
||||
mem_write_breakpoints: [false; u16::MAX as usize + 1],
|
||||
trigger_bp: false,
|
||||
}
|
||||
}
|
||||
|
@ -258,7 +261,8 @@ impl Gameboy {
|
|||
fn cpu_read_io(&self, address: u16) -> u8 {
|
||||
match address {
|
||||
0xFF00 => self.joypad.cpu_read(),
|
||||
0xFF01..=0xFF02 => unimplemented!("Serial"),
|
||||
0xFF01 => self.serial.sb,
|
||||
0xFF02 => self.serial.sc,
|
||||
0xFF03 => 0, // Unused
|
||||
0xFF04 => self.timer.div,
|
||||
0xFF05 => self.timer.tima,
|
||||
|
@ -317,7 +321,8 @@ impl Gameboy {
|
|||
fn cpu_write_io(&mut self, address: u16, value: u8) {
|
||||
match address {
|
||||
0xFF00 => self.joypad.cpu_write(value),
|
||||
0xFF01..=0xFF02 => unimplemented!("Serial"),
|
||||
0xFF01 => self.serial.sb = value,
|
||||
0xFF02 => self.serial.sc = value,
|
||||
0xFF03 => {} // Unused
|
||||
0xFF04 => self.timer.div = value,
|
||||
0xFF05 => self.timer.tima = value,
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
mod alu;
|
||||
mod flow;
|
||||
mod load_store_move;
|
||||
mod misc;
|
||||
mod prefixed;
|
||||
|
||||
use super::Gameboy;
|
||||
|
@ -177,13 +178,16 @@ pub fn tick_cpu(state: &mut Gameboy) {
|
|||
};
|
||||
|
||||
let result: CycleResult = match opcode {
|
||||
0x00 => misc::nop,
|
||||
0x01 => load_store_move::ld_bc_imm_u16,
|
||||
0x03 => alu::inc_bc,
|
||||
0x04 => alu::inc_b,
|
||||
0x05 => alu::dec_b,
|
||||
0x06 => load_store_move::ld_b_imm_u8,
|
||||
0x08 => load_store_move::ld_deref_imm_u16_sp,
|
||||
0x09 => alu::add_hl_bc,
|
||||
0x0a => load_store_move::ld_a_deref_bc,
|
||||
0x0b => alu::dec_bc,
|
||||
0x0c => alu::inc_c,
|
||||
0x0d => alu::dec_c,
|
||||
0x0e => load_store_move::ld_c_imm_u8,
|
||||
|
@ -194,7 +198,9 @@ pub fn tick_cpu(state: &mut Gameboy) {
|
|||
0x16 => load_store_move::ld_d_imm_u8,
|
||||
0x17 => alu::rla,
|
||||
0x18 => flow::jr_i8,
|
||||
0x19 => alu::add_hl_de,
|
||||
0x1a => load_store_move::ld_a_deref_de,
|
||||
0x1b => alu::dec_de,
|
||||
0x1c => alu::inc_e,
|
||||
0x1d => alu::dec_e,
|
||||
0x1e => load_store_move::ld_e_imm_u8,
|
||||
|
@ -206,10 +212,13 @@ pub fn tick_cpu(state: &mut Gameboy) {
|
|||
0x25 => alu::dec_h,
|
||||
0x26 => load_store_move::ld_h_imm_u8,
|
||||
0x28 => flow::jr_z_i8,
|
||||
0x29 => alu::add_hl_hl,
|
||||
0x2a => load_store_move::ld_a_hl_plus,
|
||||
0x2b => alu::dec_hl,
|
||||
0x2c => alu::inc_l,
|
||||
0x2d => alu::dec_l,
|
||||
0x2e => load_store_move::ld_l_imm_u8,
|
||||
0x2f => alu::cpl,
|
||||
0x30 => flow::jr_nc_i8,
|
||||
0x31 => load_store_move::ld_sp_imm_u16,
|
||||
0x32 => load_store_move::ld_hl_minus_a,
|
||||
|
@ -217,11 +226,15 @@ pub fn tick_cpu(state: &mut Gameboy) {
|
|||
0x34 => alu::inc_deref_hl,
|
||||
0x35 => alu::dec_deref_hl,
|
||||
0x36 => load_store_move::ld_deref_hl_imm_u8,
|
||||
0x37 => alu::scf,
|
||||
0x38 => flow::jr_c_i8,
|
||||
0x39 => alu::add_hl_sp,
|
||||
0x3a => load_store_move::ld_a_hl_minus,
|
||||
0x3b => alu::dec_sp,
|
||||
0x3c => alu::inc_a,
|
||||
0x3d => alu::dec_a,
|
||||
0x3e => load_store_move::ld_a_imm_u8,
|
||||
0x3f => alu::ccf,
|
||||
0x40 => load_store_move::ld_b_b,
|
||||
0x41 => load_store_move::ld_b_c,
|
||||
0x42 => load_store_move::ld_b_d,
|
||||
|
@ -317,6 +330,14 @@ pub fn tick_cpu(state: &mut Gameboy) {
|
|||
0x9D => alu::sbc_a_l,
|
||||
0x9E => alu::sbc_a_deref_hl,
|
||||
0x9F => alu::sbc_a_a,
|
||||
0xA0 => alu::and_a_b,
|
||||
0xA1 => alu::and_a_c,
|
||||
0xA2 => alu::and_a_d,
|
||||
0xA3 => alu::and_a_e,
|
||||
0xA4 => alu::and_a_h,
|
||||
0xA5 => alu::and_a_l,
|
||||
0xA6 => alu::and_a_deref_hl,
|
||||
0xA7 => alu::and_a_a,
|
||||
0xA8 => alu::xor_a_b,
|
||||
0xA9 => alu::xor_a_c,
|
||||
0xAA => alu::xor_a_d,
|
||||
|
@ -325,6 +346,14 @@ pub fn tick_cpu(state: &mut Gameboy) {
|
|||
0xAD => alu::xor_a_l,
|
||||
0xAE => alu::xor_a_deref_hl,
|
||||
0xAF => alu::xor_a_a,
|
||||
0xB0 => alu::or_a_b,
|
||||
0xB1 => alu::or_a_c,
|
||||
0xB2 => alu::or_a_d,
|
||||
0xB3 => alu::or_a_e,
|
||||
0xB4 => alu::or_a_h,
|
||||
0xB5 => alu::or_a_l,
|
||||
0xB6 => alu::or_a_deref_hl,
|
||||
0xB7 => alu::or_a_a,
|
||||
0xB8 => alu::cp_a_b,
|
||||
0xB9 => alu::cp_a_c,
|
||||
0xBA => alu::cp_a_d,
|
||||
|
@ -340,6 +369,7 @@ pub fn tick_cpu(state: &mut Gameboy) {
|
|||
0xC4 => flow::call_nz_u16,
|
||||
0xC5 => load_store_move::push_bc,
|
||||
0xC6 => alu::add_a_imm_u8,
|
||||
0xC7 => flow::rst_0x0,
|
||||
0xC8 => flow::ret_z,
|
||||
0xC9 => flow::ret,
|
||||
0xCA => flow::jp_z_u16,
|
||||
|
@ -347,31 +377,42 @@ pub fn tick_cpu(state: &mut Gameboy) {
|
|||
0xCC => flow::call_z_u16,
|
||||
0xCD => flow::call_u16,
|
||||
0xCE => alu::adc_a_imm_u8,
|
||||
0xCF => flow::rst_0x08,
|
||||
0xD0 => flow::ret_nc,
|
||||
0xD1 => load_store_move::pop_de,
|
||||
0xD2 => flow::jp_nc_u16,
|
||||
0xD4 => flow::call_nc_u16,
|
||||
0xD5 => load_store_move::push_de,
|
||||
0xD6 => alu::sub_a_imm_u8,
|
||||
0xD7 => flow::rst_0x10,
|
||||
0xD8 => flow::ret_c,
|
||||
0xD9 => flow::reti,
|
||||
0xDA => flow::jp_c_u16,
|
||||
0xDC => flow::call_c_u16,
|
||||
0xDE => alu::sbc_a_imm_u8,
|
||||
0xDF => flow::rst_0x18,
|
||||
0xE0 => load_store_move::ldh_imm_u8_a,
|
||||
0xE1 => load_store_move::pop_hl,
|
||||
0xE2 => load_store_move::ldh_deref_c_a,
|
||||
0xE5 => load_store_move::push_hl,
|
||||
0xE6 => alu::and_a_imm_u8,
|
||||
0xE7 => flow::rst_0x20,
|
||||
0xE9 => flow::jp_hl,
|
||||
0xEA => load_store_move::ld_deref_imm_u16_a,
|
||||
0xEE => alu::xor_a_imm_u8,
|
||||
0xEF => flow::rst_0x28,
|
||||
0xF0 => load_store_move::ldh_a_imm_u8,
|
||||
0xF1 => load_store_move::pop_af,
|
||||
0xF2 => load_store_move::ldh_a_deref_c,
|
||||
0xF3 => misc::di,
|
||||
0xF5 => load_store_move::push_af,
|
||||
0xF6 => alu::or_a_imm_u8,
|
||||
0xF7 => flow::rst_0x30,
|
||||
0xF9 => load_store_move::ld_sp_hl,
|
||||
0xFA => load_store_move::ld_a_deref_imm_u16,
|
||||
0xFB => misc::ei,
|
||||
0xFE => alu::cp_a_imm_u8,
|
||||
0xFF => flow::rst_0x38,
|
||||
unknown => {
|
||||
panic!("Unrecognized opcode: {:#X}\nRegisters: {:#x?}", unknown, state.registers)
|
||||
}
|
||||
|
|
|
@ -197,7 +197,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_bytecount = Some(1);
|
||||
state.registers.opcode_bytecount = Some(2);
|
||||
CycleResult::Finished
|
||||
}
|
||||
_ => unreachable!(),
|
||||
|
@ -272,7 +272,7 @@ pub fn add_a_imm_u8(state: &mut Gameboy) -> CycleResult {
|
|||
state.registers.set_subtract(false);
|
||||
state.registers.set_half_carry(half_carry);
|
||||
state.registers.set_carry(carry);
|
||||
state.registers.opcode_bytecount = Some(1);
|
||||
state.registers.opcode_bytecount = Some(2);
|
||||
CycleResult::Finished
|
||||
}
|
||||
_ => unreachable!(),
|
||||
|
@ -353,7 +353,7 @@ pub fn adc_a_imm_u8(state: &mut Gameboy) -> CycleResult {
|
|||
state.registers.set_subtract(false);
|
||||
state.registers.set_half_carry(half_carry);
|
||||
state.registers.set_carry(carry);
|
||||
state.registers.opcode_bytecount = Some(1);
|
||||
state.registers.opcode_bytecount = Some(2);
|
||||
CycleResult::Finished
|
||||
}
|
||||
_ => unreachable!(),
|
||||
|
@ -428,7 +428,7 @@ pub fn sub_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_bytecount = Some(1);
|
||||
state.registers.opcode_bytecount = Some(2);
|
||||
CycleResult::Finished
|
||||
}
|
||||
_ => unreachable!(),
|
||||
|
@ -608,6 +608,49 @@ pub fn inc_sp(state: &mut Gameboy) -> CycleResult {
|
|||
}
|
||||
}
|
||||
|
||||
macro_rules! define_dec_u16_reg {
|
||||
($lreg:ident, $rreg:ident) => {
|
||||
paste::paste! {
|
||||
pub fn [<dec_ $lreg $rreg>](state: &mut Gameboy) -> CycleResult {
|
||||
match state.registers.cycle {
|
||||
0 => {
|
||||
let (res, carry) = state.registers.$rreg.overflowing_sub(1);
|
||||
state.registers.$rreg = res;
|
||||
state.registers.set_hold(carry as u16);
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
1 => {
|
||||
if state.registers.take_hold() != 0 {
|
||||
let (res, _) = state.registers.$lreg.overflowing_sub(1);
|
||||
state.registers.$lreg = res;
|
||||
}
|
||||
state.registers.opcode_bytecount = Some(1);
|
||||
CycleResult::Finished
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
define_dec_u16_reg!(b, c);
|
||||
define_dec_u16_reg!(d, e);
|
||||
define_dec_u16_reg!(h, l);
|
||||
|
||||
pub fn dec_sp(state: &mut Gameboy) -> CycleResult {
|
||||
match state.registers.cycle {
|
||||
0 => CycleResult::NeedsMore,
|
||||
1 => {
|
||||
let (res, _) = state.registers.sp.overflowing_sub(1);
|
||||
state.registers.sp = res;
|
||||
state.registers.opcode_bytecount = Some(1);
|
||||
CycleResult::Finished
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! define_cp_reg_reg {
|
||||
($lreg:ident, $rreg:ident) => {
|
||||
paste::paste! {
|
||||
|
@ -676,3 +719,252 @@ pub fn cp_a_deref_hl(state: &mut Gameboy) -> CycleResult {
|
|||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! define_or_reg {
|
||||
($reg:ident) => {
|
||||
paste::paste! {
|
||||
pub fn [<or_a_ $reg>](state: &mut Gameboy) -> CycleResult {
|
||||
match state.registers.cycle {
|
||||
0 => {
|
||||
let result = state.registers.a | state.registers.$reg;
|
||||
|
||||
state.registers.a = result;
|
||||
state.registers.set_zero(result == 0);
|
||||
state.registers.set_subtract(false);
|
||||
state.registers.set_half_carry(false);
|
||||
state.registers.set_carry(false);
|
||||
state.registers.opcode_bytecount = Some(1);
|
||||
CycleResult::Finished
|
||||
},
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
define_or_reg!(a);
|
||||
define_or_reg!(b);
|
||||
define_or_reg!(c);
|
||||
define_or_reg!(d);
|
||||
define_or_reg!(e);
|
||||
define_or_reg!(h);
|
||||
define_or_reg!(l);
|
||||
|
||||
pub fn or_a_deref_hl(state: &mut Gameboy) -> CycleResult {
|
||||
match state.registers.cycle {
|
||||
0 => {
|
||||
state.cpu_read_u8(state.registers.get_hl());
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
1 => {
|
||||
let result = state.registers.a | state.registers.take_mem();
|
||||
|
||||
state.registers.a = result;
|
||||
state.registers.set_zero(result == 0);
|
||||
state.registers.set_subtract(false);
|
||||
state.registers.set_half_carry(false);
|
||||
state.registers.set_carry(false);
|
||||
state.registers.opcode_bytecount = Some(1);
|
||||
CycleResult::Finished
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn or_a_imm_u8(state: &mut Gameboy) -> CycleResult {
|
||||
match state.registers.cycle {
|
||||
0 => {
|
||||
state.cpu_read_u8(state.registers.pc + 1);
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
1 => {
|
||||
let result = state.registers.a | state.registers.take_mem();
|
||||
|
||||
state.registers.a = result;
|
||||
state.registers.set_zero(result == 0);
|
||||
state.registers.set_subtract(false);
|
||||
state.registers.set_half_carry(false);
|
||||
state.registers.set_carry(false);
|
||||
state.registers.opcode_bytecount = Some(2);
|
||||
CycleResult::Finished
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! define_and_reg {
|
||||
($reg:ident) => {
|
||||
paste::paste! {
|
||||
pub fn [<and_a_ $reg>](state: &mut Gameboy) -> CycleResult {
|
||||
match state.registers.cycle {
|
||||
0 => {
|
||||
let result = state.registers.a & state.registers.$reg;
|
||||
|
||||
state.registers.a = result;
|
||||
state.registers.set_zero(result == 0);
|
||||
state.registers.set_subtract(false);
|
||||
state.registers.set_half_carry(true);
|
||||
state.registers.set_carry(false);
|
||||
state.registers.opcode_bytecount = Some(1);
|
||||
CycleResult::Finished
|
||||
},
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
define_and_reg!(a);
|
||||
define_and_reg!(b);
|
||||
define_and_reg!(c);
|
||||
define_and_reg!(d);
|
||||
define_and_reg!(e);
|
||||
define_and_reg!(h);
|
||||
define_and_reg!(l);
|
||||
|
||||
pub fn and_a_deref_hl(state: &mut Gameboy) -> CycleResult {
|
||||
match state.registers.cycle {
|
||||
0 => {
|
||||
state.cpu_read_u8(state.registers.get_hl());
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
1 => {
|
||||
let result = state.registers.a & state.registers.take_mem();
|
||||
|
||||
state.registers.a = result;
|
||||
state.registers.set_zero(result == 0);
|
||||
state.registers.set_subtract(false);
|
||||
state.registers.set_half_carry(true);
|
||||
state.registers.set_carry(false);
|
||||
state.registers.opcode_bytecount = Some(1);
|
||||
CycleResult::Finished
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn and_a_imm_u8(state: &mut Gameboy) -> CycleResult {
|
||||
match state.registers.cycle {
|
||||
0 => {
|
||||
state.cpu_read_u8(state.registers.pc + 1);
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
1 => {
|
||||
let result = state.registers.a & state.registers.take_mem();
|
||||
|
||||
state.registers.a = result;
|
||||
state.registers.set_zero(result == 0);
|
||||
state.registers.set_subtract(false);
|
||||
state.registers.set_half_carry(true);
|
||||
state.registers.set_carry(false);
|
||||
state.registers.opcode_bytecount = Some(2);
|
||||
CycleResult::Finished
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cpl(state: &mut Gameboy) -> CycleResult {
|
||||
match state.registers.cycle {
|
||||
0 => {
|
||||
state.registers.a = !state.registers.a;
|
||||
state.registers.set_subtract(true);
|
||||
state.registers.set_half_carry(true);
|
||||
state.registers.opcode_bytecount = Some(1);
|
||||
CycleResult::Finished
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ccf(state: &mut Gameboy) -> CycleResult {
|
||||
match state.registers.cycle {
|
||||
0 => {
|
||||
state.registers.set_subtract(false);
|
||||
state.registers.set_half_carry(false);
|
||||
state.registers.set_carry(!state.registers.get_carry());
|
||||
state.registers.opcode_bytecount = Some(1);
|
||||
CycleResult::Finished
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn scf(state: &mut Gameboy) -> CycleResult {
|
||||
match state.registers.cycle {
|
||||
0 => {
|
||||
state.registers.set_subtract(false);
|
||||
state.registers.set_half_carry(false);
|
||||
state.registers.set_carry(true);
|
||||
state.registers.opcode_bytecount = Some(1);
|
||||
CycleResult::Finished
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! define_add_hl_u16_reg {
|
||||
($lreg:ident, $rreg:ident) => {
|
||||
paste::paste! {
|
||||
pub fn [<add_hl_ $lreg $rreg>](state: &mut Gameboy) -> CycleResult {
|
||||
match state.registers.cycle {
|
||||
0 => {
|
||||
let CarryResult { result, carry, .. } = add(state.registers.l, state.registers.$rreg);
|
||||
state.registers.l = result;
|
||||
state.registers.set_hold(carry as u16);
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
1 => {
|
||||
if state.registers.take_hold() != 0 {
|
||||
let CarryResult { result, carry, half_carry } = add(state.registers.h, state.registers.$lreg);
|
||||
state.registers.h = result;
|
||||
state.registers.set_half_carry(half_carry);
|
||||
state.registers.set_carry(carry);
|
||||
} else {
|
||||
state.registers.set_half_carry(false);
|
||||
state.registers.set_carry(false);
|
||||
}
|
||||
state.registers.set_subtract(false);
|
||||
state.registers.opcode_bytecount = Some(1);
|
||||
CycleResult::Finished
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
define_add_hl_u16_reg!(b, c);
|
||||
define_add_hl_u16_reg!(d, e);
|
||||
define_add_hl_u16_reg!(h, l);
|
||||
|
||||
pub fn add_hl_sp(state: &mut Gameboy) -> CycleResult {
|
||||
match state.registers.cycle {
|
||||
0 => {
|
||||
let CarryResult { result, carry, .. } =
|
||||
add(state.registers.l, state.registers.sp as u8);
|
||||
state.registers.l = result;
|
||||
state.registers.set_hold(carry as u16);
|
||||
CycleResult::NeedsMore
|
||||
}
|
||||
1 => {
|
||||
if state.registers.take_hold() != 0 {
|
||||
let CarryResult { result, carry, half_carry } =
|
||||
add(state.registers.h, (state.registers.sp >> 8) as u8);
|
||||
state.registers.h = result;
|
||||
state.registers.set_half_carry(half_carry);
|
||||
state.registers.set_carry(carry);
|
||||
} else {
|
||||
state.registers.set_half_carry(false);
|
||||
state.registers.set_carry(false);
|
||||
}
|
||||
state.registers.set_subtract(false);
|
||||
state.registers.opcode_bytecount = Some(1);
|
||||
CycleResult::Finished
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -197,6 +197,7 @@ pub fn jp_nz_u16(state: &mut Gameboy) -> CycleResult {
|
|||
2 => {
|
||||
if state.registers.get_zero() {
|
||||
state.registers.take_mem();
|
||||
state.registers.take_hold();
|
||||
state.registers.opcode_bytecount = Some(3);
|
||||
CycleResult::Finished
|
||||
} else {
|
||||
|
@ -228,6 +229,7 @@ pub fn jp_nc_u16(state: &mut Gameboy) -> CycleResult {
|
|||
2 => {
|
||||
if state.registers.get_carry() {
|
||||
state.registers.take_mem();
|
||||
state.registers.take_hold();
|
||||
state.registers.opcode_bytecount = Some(3);
|
||||
CycleResult::Finished
|
||||
} else {
|
||||
|
@ -259,6 +261,7 @@ pub fn jp_z_u16(state: &mut Gameboy) -> CycleResult {
|
|||
2 => {
|
||||
if !state.registers.get_zero() {
|
||||
state.registers.take_mem();
|
||||
state.registers.take_hold();
|
||||
state.registers.opcode_bytecount = Some(3);
|
||||
CycleResult::Finished
|
||||
} else {
|
||||
|
@ -290,6 +293,7 @@ pub fn jp_c_u16(state: &mut Gameboy) -> CycleResult {
|
|||
2 => {
|
||||
if !state.registers.get_carry() {
|
||||
state.registers.take_mem();
|
||||
state.registers.take_hold();
|
||||
state.registers.opcode_bytecount = Some(3);
|
||||
CycleResult::Finished
|
||||
} else {
|
||||
|
@ -352,6 +356,7 @@ pub fn call_nz_u16(state: &mut Gameboy) -> CycleResult {
|
|||
2 => {
|
||||
if state.registers.get_zero() {
|
||||
state.registers.take_mem();
|
||||
state.registers.take_hold();
|
||||
state.registers.opcode_bytecount = Some(3);
|
||||
CycleResult::Finished
|
||||
} else {
|
||||
|
@ -391,6 +396,7 @@ pub fn call_nc_u16(state: &mut Gameboy) -> CycleResult {
|
|||
2 => {
|
||||
if state.registers.get_carry() {
|
||||
state.registers.take_mem();
|
||||
state.registers.take_hold();
|
||||
state.registers.opcode_bytecount = Some(3);
|
||||
CycleResult::Finished
|
||||
} else {
|
||||
|
@ -430,6 +436,7 @@ pub fn call_z_u16(state: &mut Gameboy) -> CycleResult {
|
|||
2 => {
|
||||
if !state.registers.get_zero() {
|
||||
state.registers.take_mem();
|
||||
state.registers.take_hold();
|
||||
state.registers.opcode_bytecount = Some(3);
|
||||
CycleResult::Finished
|
||||
} else {
|
||||
|
@ -469,6 +476,7 @@ pub fn call_c_u16(state: &mut Gameboy) -> CycleResult {
|
|||
2 => {
|
||||
if !state.registers.get_carry() {
|
||||
state.registers.take_mem();
|
||||
state.registers.take_hold();
|
||||
state.registers.opcode_bytecount = Some(3);
|
||||
CycleResult::Finished
|
||||
} else {
|
||||
|
@ -655,3 +663,38 @@ pub fn ret_c(state: &mut Gameboy) -> CycleResult {
|
|||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! define_rst {
|
||||
($addr:literal) => {
|
||||
paste::paste! {
|
||||
pub fn [<rst_ $addr>](state: &mut Gameboy) -> CycleResult {
|
||||
match state.registers.cycle {
|
||||
0 => CycleResult::NeedsMore,
|
||||
1 => {
|
||||
state.cpu_push_stack((state.registers.pc >> 8) as u8);
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
2 => {
|
||||
state.cpu_push_stack((state.registers.pc & 0xFF) as u8);
|
||||
CycleResult::NeedsMore
|
||||
},
|
||||
3 => {
|
||||
state.registers.pc = $addr;
|
||||
state.registers.opcode_bytecount = Some(0);
|
||||
CycleResult::Finished
|
||||
},
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
define_rst!(0x0);
|
||||
define_rst!(0x08);
|
||||
define_rst!(0x10);
|
||||
define_rst!(0x18);
|
||||
define_rst!(0x20);
|
||||
define_rst!(0x28);
|
||||
define_rst!(0x30);
|
||||
define_rst!(0x38);
|
||||
|
|
34
src/gameboy/cpu/misc.rs
Normal file
34
src/gameboy/cpu/misc.rs
Normal file
|
@ -0,0 +1,34 @@
|
|||
use super::CycleResult;
|
||||
use crate::gameboy::Gameboy;
|
||||
|
||||
pub fn nop(state: &mut Gameboy) -> CycleResult {
|
||||
match state.registers.cycle {
|
||||
0 => {
|
||||
state.registers.opcode_bytecount = Some(1);
|
||||
CycleResult::Finished
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn di(state: &mut Gameboy) -> CycleResult {
|
||||
match state.registers.cycle {
|
||||
0 => {
|
||||
state.interrupts.ime = false;
|
||||
state.registers.opcode_bytecount = Some(1);
|
||||
CycleResult::Finished
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ei(state: &mut Gameboy) -> CycleResult {
|
||||
match state.registers.cycle {
|
||||
0 => {
|
||||
state.interrupts.ime = true;
|
||||
state.registers.opcode_bytecount = Some(1);
|
||||
CycleResult::Finished
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
|
@ -24,6 +24,13 @@ pub fn prefixed_handler(state: &mut Gameboy) -> CycleResult {
|
|||
0x14 => rl_h,
|
||||
0x15 => rl_l,
|
||||
0x17 => rl_a,
|
||||
0x30 => swap_b,
|
||||
0x31 => swap_c,
|
||||
0x32 => swap_d,
|
||||
0x33 => swap_e,
|
||||
0x34 => swap_h,
|
||||
0x35 => swap_l,
|
||||
0x37 => swap_a,
|
||||
0x40 => bit_0_b,
|
||||
0x41 => bit_0_c,
|
||||
0x42 => bit_0_d,
|
||||
|
@ -204,3 +211,33 @@ define_rl_reg!(e);
|
|||
define_rl_reg!(h);
|
||||
define_rl_reg!(l);
|
||||
define_rl_reg!(a);
|
||||
|
||||
macro_rules! define_swap_reg {
|
||||
($reg:ident) => {
|
||||
paste::paste! {
|
||||
pub fn [<swap_ $reg>](state: &mut Gameboy) -> CycleResult {
|
||||
match state.registers.cycle {
|
||||
1 => {
|
||||
state.registers.$reg = (state.registers.$reg >> 4) | (state.registers.$reg << 4);
|
||||
|
||||
state.registers.set_zero(state.registers.$reg == 0);
|
||||
state.registers.set_subtract(false);
|
||||
state.registers.set_half_carry(false);
|
||||
state.registers.set_carry(false);
|
||||
state.registers.opcode_bytecount = Some(2);
|
||||
CycleResult::Finished
|
||||
},
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
define_swap_reg!(b);
|
||||
define_swap_reg!(c);
|
||||
define_swap_reg!(d);
|
||||
define_swap_reg!(e);
|
||||
define_swap_reg!(h);
|
||||
define_swap_reg!(l);
|
||||
define_swap_reg!(a);
|
||||
|
|
28
src/gameboy/serial.rs
Normal file
28
src/gameboy/serial.rs
Normal file
|
@ -0,0 +1,28 @@
|
|||
pub struct Serial {
|
||||
pub sb: u8,
|
||||
pub sc: u8,
|
||||
}
|
||||
|
||||
impl Serial {
|
||||
pub fn new() -> Serial {
|
||||
Self { sb: 0, sc: 0 }
|
||||
}
|
||||
|
||||
pub fn set_transfer_in_process(&mut self, value: bool) {
|
||||
self.sc &= !(1 << 7);
|
||||
self.sc |= (value as u8) << 7;
|
||||
}
|
||||
|
||||
pub fn get_transfer_in_process(&mut self) -> bool {
|
||||
(self.sc >> 7) & 0b1 == 1
|
||||
}
|
||||
|
||||
pub fn is_conductor(&self) -> bool {
|
||||
self.sc & 0b1 == 1
|
||||
}
|
||||
|
||||
pub fn set_side(&mut self, conductor: bool) {
|
||||
self.sc &= !0b1;
|
||||
self.sc |= conductor as u8;
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue