diff --git a/src/lib.rs b/src/lib.rs index ef1a3bf..3ab2e8a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,4 @@ #![warn(clippy::all, clippy::pedantic)] mod register; +mod run; diff --git a/src/register.rs b/src/register.rs index 259276b..4659f3d 100644 --- a/src/register.rs +++ b/src/register.rs @@ -11,6 +11,18 @@ pub enum Flag { Carry, } +#[derive(Debug, Clone, Copy)] +pub enum Single { + A, + B, + C, + D, + E, + F, + H, + L, +} + // Combined represents a pair of two registers taht can be acted on as a single // 16 bit register. #[derive(Debug, Clone, Copy)] @@ -101,6 +113,34 @@ impl Registers { self.flags |= bit << pos; } + pub fn get_single_register(&self, register: Single) -> u8 { + match register { + Single::A => self.a, + Single::B => self.b, + Single::C => self.c, + Single::D => self.d, + Single::E => self.e, + Single::F => self.f, + Single::H => self.h, + Single::L => self.l, + } + } + + pub fn set_single_register(&mut self, register: Single, value: u8) { + let register_ref = match register { + Single::A => &mut self.a, + Single::B => &mut self.b, + Single::C => &mut self.c, + Single::D => &mut self.d, + Single::E => &mut self.e, + Single::F => &mut self.f, + Single::H => &mut self.h, + Single::L => &mut self.l, + }; + + *register_ref = value; + } + /// Get the value of a pair of registers pub fn get_combined_register(&self, registers: Combined) -> u16 { let (left_register, right_register) = self.get_register_values_for_combined(registers); diff --git a/src/run.rs b/src/run.rs index 4b95629..38ad94d 100644 --- a/src/run.rs +++ b/src/run.rs @@ -16,6 +16,11 @@ pub enum Instruction { value: u8, register: register::Single, }, + // 3.3.1.2, excluding the (hl) instructions + LDr1r2 { + dst: register::Single, + src: register::Single, + }, } struct RunnableInstruction { @@ -37,6 +42,56 @@ impl RunnableInstruction { 0x1E => Self::make_ldnn_data(register::Single::E, data), 0x26 => Self::make_ldnn_data(register::Single::H, data), 0x2E => Self::make_ldnn_data(register::Single::L, data), + + 0x7F => Self::make_ldr1r2_data(register::Single::A, register::Single::A, data), + 0x78 => Self::make_ldr1r2_data(register::Single::A, register::Single::B, data), + 0x79 => Self::make_ldr1r2_data(register::Single::A, register::Single::C, data), + 0x7A => Self::make_ldr1r2_data(register::Single::A, register::Single::D, data), + 0x7B => Self::make_ldr1r2_data(register::Single::A, register::Single::E, data), + 0x7C => Self::make_ldr1r2_data(register::Single::A, register::Single::H, data), + 0x7D => Self::make_ldr1r2_data(register::Single::A, register::Single::L, data), + // 0x7E skipped + 0x40 => Self::make_ldr1r2_data(register::Single::B, register::Single::B, data), + 0x41 => Self::make_ldr1r2_data(register::Single::B, register::Single::C, data), + 0x42 => Self::make_ldr1r2_data(register::Single::B, register::Single::D, data), + 0x43 => Self::make_ldr1r2_data(register::Single::B, register::Single::E, data), + 0x44 => Self::make_ldr1r2_data(register::Single::B, register::Single::H, data), + 0x45 => Self::make_ldr1r2_data(register::Single::B, register::Single::L, data), + // 0x46 skiipped + 0x48 => Self::make_ldr1r2_data(register::Single::C, register::Single::B, data), + 0x49 => Self::make_ldr1r2_data(register::Single::C, register::Single::C, data), + 0x4A => Self::make_ldr1r2_data(register::Single::C, register::Single::D, data), + 0x4B => Self::make_ldr1r2_data(register::Single::C, register::Single::E, data), + 0x4C => Self::make_ldr1r2_data(register::Single::C, register::Single::H, data), + 0x4D => Self::make_ldr1r2_data(register::Single::C, register::Single::L, data), + // 0x4E skipped + 0x50 => Self::make_ldr1r2_data(register::Single::D, register::Single::B, data), + 0x51 => Self::make_ldr1r2_data(register::Single::D, register::Single::C, data), + 0x52 => Self::make_ldr1r2_data(register::Single::D, register::Single::D, data), + 0x53 => Self::make_ldr1r2_data(register::Single::D, register::Single::E, data), + 0x54 => Self::make_ldr1r2_data(register::Single::D, register::Single::H, data), + 0x55 => Self::make_ldr1r2_data(register::Single::D, register::Single::L, data), + // 0x56 skipped + 0x58 => Self::make_ldr1r2_data(register::Single::E, register::Single::B, data), + 0x59 => Self::make_ldr1r2_data(register::Single::E, register::Single::C, data), + 0x5A => Self::make_ldr1r2_data(register::Single::E, register::Single::D, data), + 0x5B => Self::make_ldr1r2_data(register::Single::E, register::Single::E, data), + 0x5C => Self::make_ldr1r2_data(register::Single::E, register::Single::H, data), + 0x5D => Self::make_ldr1r2_data(register::Single::E, register::Single::L, data), + // 0x5E skipped + 0x60 => Self::make_ldr1r2_data(register::Single::H, register::Single::B, data), + 0x61 => Self::make_ldr1r2_data(register::Single::H, register::Single::C, data), + 0x62 => Self::make_ldr1r2_data(register::Single::H, register::Single::D, data), + 0x63 => Self::make_ldr1r2_data(register::Single::H, register::Single::E, data), + 0x64 => Self::make_ldr1r2_data(register::Single::H, register::Single::H, data), + 0x65 => Self::make_ldr1r2_data(register::Single::H, register::Single::L, data), + // 0x66 skipped + 0x68 => Self::make_ldr1r2_data(register::Single::L, register::Single::B, data), + 0x69 => Self::make_ldr1r2_data(register::Single::L, register::Single::C, data), + 0x6A => Self::make_ldr1r2_data(register::Single::L, register::Single::D, data), + 0x6B => Self::make_ldr1r2_data(register::Single::L, register::Single::E, data), + 0x6C => Self::make_ldr1r2_data(register::Single::L, register::Single::H, data), + 0x6D => Self::make_ldr1r2_data(register::Single::L, register::Single::L, data), _ => panic!("invalid opcode {opcode:0x}"), } } @@ -53,6 +108,20 @@ impl RunnableInstruction { &data[2..], ) } + + fn make_ldr1r2_data( + dst: register::Single, + src: register::Single, + data: &[u8], + ) -> (RunnableInstruction, &[u8]) { + ( + RunnableInstruction { + instruction: Instruction::LDr1r2 { dst, src }, + cycles: 4, + }, + &data[1..], + ) + } } impl Processor { @@ -61,6 +130,11 @@ impl Processor { Instruction::LDnnn { value, register } => { self.registers.set_single_register(register, value); } + Instruction::LDr1r2 { dst, src } => { + dbg!(dst, src); + let src_value = self.registers.get_single_register(src); + self.registers.set_single_register(dst, src_value); + } } self.num_cycles += u64::from(instruction.cycles); @@ -93,4 +167,66 @@ mod tests { processor.registers.get_single_register(expected_register) ); } + + // lol going from a register to itself is kind of a weird thing to test + #[test_case(0x7F, register::Single::A, register::Single::A)] + #[test_case(0x78, register::Single::A, register::Single::B)] + #[test_case(0x79, register::Single::A, register::Single::C)] + #[test_case(0x7A, register::Single::A, register::Single::D)] + #[test_case(0x7B, register::Single::A, register::Single::E)] + #[test_case(0x7C, register::Single::A, register::Single::H)] + #[test_case(0x7D, register::Single::A, register::Single::L)] + #[test_case(0x40, register::Single::B, register::Single::B)] + #[test_case(0x41, register::Single::B, register::Single::C)] + #[test_case(0x42, register::Single::B, register::Single::D)] + #[test_case(0x43, register::Single::B, register::Single::E)] + #[test_case(0x44, register::Single::B, register::Single::H)] + #[test_case(0x45, register::Single::B, register::Single::L)] + #[test_case(0x48, register::Single::C, register::Single::B)] + #[test_case(0x49, register::Single::C, register::Single::C)] + #[test_case(0x4A, register::Single::C, register::Single::D)] + #[test_case(0x4B, register::Single::C, register::Single::E)] + #[test_case(0x4C, register::Single::C, register::Single::H)] + #[test_case(0x4D, register::Single::C, register::Single::L)] + #[test_case(0x50, register::Single::D, register::Single::B)] + #[test_case(0x51, register::Single::D, register::Single::C)] + #[test_case(0x52, register::Single::D, register::Single::D)] + #[test_case(0x53, register::Single::D, register::Single::E)] + #[test_case(0x54, register::Single::D, register::Single::H)] + #[test_case(0x55, register::Single::D, register::Single::L)] + #[test_case(0x58, register::Single::E, register::Single::B)] + #[test_case(0x59, register::Single::E, register::Single::C)] + #[test_case(0x5A, register::Single::E, register::Single::D)] + #[test_case(0x5B, register::Single::E, register::Single::E)] + #[test_case(0x5C, register::Single::E, register::Single::H)] + #[test_case(0x5D, register::Single::E, register::Single::L)] + #[test_case(0x60, register::Single::H, register::Single::B)] + #[test_case(0x61, register::Single::H, register::Single::C)] + #[test_case(0x62, register::Single::H, register::Single::D)] + #[test_case(0x63, register::Single::H, register::Single::E)] + #[test_case(0x64, register::Single::H, register::Single::H)] + #[test_case(0x65, register::Single::H, register::Single::L)] + #[test_case(0x68, register::Single::L, register::Single::B)] + #[test_case(0x69, register::Single::L, register::Single::C)] + #[test_case(0x6A, register::Single::L, register::Single::D)] + #[test_case(0x6B, register::Single::L, register::Single::E)] + #[test_case(0x6C, register::Single::L, register::Single::H)] + #[test_case(0x6D, register::Single::L, register::Single::L)] + fn test_load_register( + ld_opcode: u8, + dst_register: register::Single, + src_register: register::Single, + ) { + let mut processor = Processor::default(); + processor.registers.set_single_register(src_register, 0x45); + + let data = [ld_opcode, 0x00]; + let (ins, extra_data) = RunnableInstruction::from_data(&data); + + // assert our extra data after the instruction is returned + assert_eq!(extra_data, &[0x00]); + + processor.run(&ins); + assert_eq!(0x45, processor.registers.get_single_register(dst_register)); + } }