diff --git a/src/run.rs b/src/run.rs index 1894d21..892d041 100644 --- a/src/run.rs +++ b/src/run.rs @@ -6,6 +6,8 @@ use crate::{ }; use thiserror::Error; +use self::instructions::{load16::SixteenBitLoadInstruction, load8::EightBitLoadInstruction}; + mod instructions; mod parse; @@ -36,45 +38,53 @@ macro_rules! assert_ok { } impl Processor { - #[allow(clippy::too_many_lines)] fn run(&mut self, instruction: &RunnableInstruction) { - match instruction.instruction { - Instruction::LD8BitImmediateToRegister { value, register } => { + match &instruction.instruction { + Instruction::EightBitLoad(load_instruction) => self.run_8_bit_load(load_instruction), + Instruction::SixteenBitLoad(load_instruction) => self.run_16_bit_load(load_instruction), + } + + self.num_cycles += u64::from(instruction.cycles); + } + + fn run_8_bit_load(&mut self, instruction: &EightBitLoadInstruction) { + match *instruction { + EightBitLoadInstruction::LDImmediateToRegister { value, register } => { self.registers.set_single_8bit_register(register, value); } - Instruction::LD8bitr1r2 { dst, src } => { + EightBitLoadInstruction::LDBetweenRegisters { dst, src } => { let src_value = self.registers.get_single_8bit_register(src); self.registers.set_single_8bit_register(dst, src_value); } - Instruction::LDFromRegisterAddress { src, dst } => { + EightBitLoadInstruction::LDFromRegisterAddress { src, dst } => { let load_res = self.load_from_register_address_to_register(dst, src); assert_ok!(load_res); } - Instruction::LDFromImmediateAddress { src_address, dst } => { + EightBitLoadInstruction::LDFromImmediateAddress { src_address, dst } => { let load_res = self.load_from_address_to_register(dst, src_address.into()); assert_ok!(load_res); } - Instruction::LDToRegisterAddress { src, dst } => { + EightBitLoadInstruction::LDToRegisterAddress { src, dst } => { let load_res = self.load_from_register_to_register_address(dst, src); assert_ok!(load_res); } - Instruction::LDToImmediateAddress { src, dst_address } => { + EightBitLoadInstruction::LDToImmediateAddress { src, dst_address } => { let load_res = self.load_from_register_to_address(dst_address.into(), src); assert_ok!(load_res); } - Instruction::LDnToHLAddress { value } => { + EightBitLoadInstruction::LDnToHLAddress { value } => { let dest_address = self.registers.get_combined_register(register::Combined::HL); let load_res = self.load_8bit_immediate_to_address(dest_address.into(), value); assert_ok!(load_res); } - Instruction::LDFromMemoryRelativeToIORegisterStart { + EightBitLoadInstruction::LDFromMemoryRelativeToIORegisterStart { offset_register, dst, } => { @@ -85,7 +95,7 @@ impl Processor { assert_ok!(load_res); } - Instruction::LDToMemoryRelativeToIORegisterStart { + EightBitLoadInstruction::LDToMemoryRelativeToIORegisterStart { src, offset_register, } => { @@ -96,7 +106,7 @@ impl Processor { assert_ok!(load_res); } - Instruction::LDFromRegisterAddressThenDec { dst, src } => { + EightBitLoadInstruction::LDFromRegisterAddressThenDec { dst, src } => { let load_res = self.load_from_register_address_to_register(dst, src); assert_ok!(load_res); @@ -104,7 +114,7 @@ impl Processor { self.registers.set_combined_register(src, src_address - 1); } - Instruction::LDFromRegisterAddressThenInc { dst, src } => { + EightBitLoadInstruction::LDFromRegisterAddressThenInc { dst, src } => { let load_res = self.load_from_register_address_to_register(dst, src); assert_ok!(load_res); @@ -112,7 +122,7 @@ impl Processor { self.registers.set_combined_register(src, src_address + 1); } - Instruction::LDToRegisterAddressThenDec { dst, src } => { + EightBitLoadInstruction::LDToRegisterAddressThenDec { dst, src } => { let dst_address = self.registers.get_combined_register(dst); let load_res = self.load_from_register_to_address(dst_address.into(), src); assert_ok!(load_res); @@ -120,7 +130,7 @@ impl Processor { self.registers.set_combined_register(dst, dst_address - 1); } - Instruction::LDToRegisterAddressThenInc { dst, src } => { + EightBitLoadInstruction::LDToRegisterAddressThenInc { dst, src } => { let dst_address = self.registers.get_combined_register(dst); let load_res = self.load_from_register_to_address(dst_address.into(), src); assert_ok!(load_res); @@ -128,28 +138,38 @@ impl Processor { self.registers.set_combined_register(dst, dst_address + 1); } - Instruction::LDToMemoryRelativeToIORegisterStartByImmediate { src, offset } => { + EightBitLoadInstruction::LDToMemoryRelativeToIORegisterStartByImmediate { + src, + offset, + } => { let dst_address = memory::IO_REGISTER_START_ADDRESS + usize::from(offset); let load_res = self.load_from_register_to_address(dst_address, src); assert_ok!(load_res); } - Instruction::LDFromMemoryRelativeToIORegisterStartByImmediate { dst, offset } => { + EightBitLoadInstruction::LDFromMemoryRelativeToIORegisterStartByImmediate { + dst, + offset, + } => { let src_address = memory::IO_REGISTER_START_ADDRESS + usize::from(offset); let load_res = self.load_from_address_to_register(dst, src_address); assert_ok!(load_res); } + } + } - Instruction::LD16bitImmediateToRegister { dst, value } => { + fn run_16_bit_load(&mut self, instruction: &SixteenBitLoadInstruction) { + match *instruction { + SixteenBitLoadInstruction::LDImmediateToRegister { dst, value } => { self.registers.set_16bit_register(dst, value); } - Instruction::LD16Bitr1r2 { dst, src } => { + SixteenBitLoadInstruction::LDBetweenRegisters { dst, src } => { let value = self.registers.get_16bit_register(src); self.registers.set_16bit_register(dst, value); } - Instruction::LDEffectiveAddress { dst, offset } => { + SixteenBitLoadInstruction::LDEffectiveAddress { dst, offset } => { let current_sp = self .registers .get_16bit_register(register::SixteenBit::Single( @@ -197,7 +217,7 @@ impl Processor { self.registers.set_combined_register(dst, new_sp); } - Instruction::Push { src } => { + SixteenBitLoadInstruction::Push { src } => { let current_sp = self .registers .get_16bit_register(register::SixteenBit::Single( @@ -219,7 +239,7 @@ impl Processor { ); } - Instruction::Pop { dst } => { + SixteenBitLoadInstruction::Pop { dst } => { let current_sp = self .registers .get_16bit_register(register::SixteenBit::Single( @@ -244,8 +264,6 @@ impl Processor { ); } } - - self.num_cycles += u64::from(instruction.cycles); } fn load_from_register_to_register_address( diff --git a/src/run/instructions.rs b/src/run/instructions.rs index 9e34374..689309f 100644 --- a/src/run/instructions.rs +++ b/src/run/instructions.rs @@ -1,122 +1,20 @@ +//! The `instructions` module holds structural definitions for all of the instructions that can run on the gameboy. + use super::parse::{self, ParseResult}; -use super::register; + +pub mod load16; +pub mod load8; // these are indexed with the instruction numbers in this manual // http://marc.rawer.de/Gameboy/Docs/GBCPUman.pdf +#[derive(Debug, Clone)] pub enum Instruction { - // 3.3.1.1 - LD8BitImmediateToRegister { - value: u8, - register: register::SingleEightBit, - }, - // 3.3.1.2, excluding the (hl) instructions, 3.3.1.3, excluding the (nn) instructions - // and 3.3.1.4 excluding the (nn) instructions - LD8bitr1r2 { - dst: register::SingleEightBit, - src: register::SingleEightBit, - }, - // 3.3.1.2/3.3.1.3, only the loading from (nn) instructions - LDFromRegisterAddress { - // src, unlike some of the other instructions, is a 16bit combined register that will be - // dereferenced to get the value to place into the dst register - src: register::Combined, - dst: register::SingleEightBit, - }, - // 3.3.1.5 - load to register relative to $FF00 (the address of the i/o registers) - LDFromMemoryRelativeToIORegisterStart { - // A register whose value will be used to get the address relative to FF00 - offset_register: register::SingleEightBit, - dst: register::SingleEightBit, - }, - // 3.3.1.6 - load to memory relative to $FF00 (the address of the i/o registers) - LDToMemoryRelativeToIORegisterStart { - src: register::SingleEightBit, - // A register whose value will be used to get the address relative to FF00 - offset_register: register::SingleEightBit, - }, - // 3.3.1.20 - LDFromMemoryRelativeToIORegisterStartByImmediate { - dst: register::SingleEightBit, - offset: u8, - }, - // 3.3.1.19 - LDToMemoryRelativeToIORegisterStartByImmediate { - offset: u8, - src: register::SingleEightBit, - }, - LDFromImmediateAddress { - src_address: u16, - dst: register::SingleEightBit, - }, - // 3.3.1.2, only the loading to (hl) instructions - // 3.3.1.4, only the loading to (nn) instructions - LDToRegisterAddress { - src: register::SingleEightBit, - // dst, unlike some other instructions, is a 16bit combined register that holds - // the address we will write the value to - dst: register::Combined, - }, - // 3.3.1.4, only the loading to (nn) immediate instrution - LDToImmediateAddress { - src: register::SingleEightBit, - dst_address: u16, - }, - // 3.3.1.2, but the (hl), n instruction [why is this in this section? it's so out of place] - LDnToHLAddress { - value: u8, - }, - // 3.3.1.{7,8,9} - LDFromRegisterAddressThenDec { - // The src, unlike some other destination instructions, refers to a register - // whose address will be dereferenced (and then decremented after the load) - src: register::Combined, - dst: register::SingleEightBit, - }, - // 3.3.1.{13,14,15} - LDFromRegisterAddressThenInc { - // The src, unlike some other destination instructions, refers to a register - // whose address will be dereferenced (and then incremented after the load) - src: register::Combined, - dst: register::SingleEightBit, - }, - // 3.3.1.{10,11,12} - LDToRegisterAddressThenDec { - src: register::SingleEightBit, - // The destination, unlike some other destination instructions, refers to a register - // whose address will be dereferenced (and then decremented after the load) - dst: register::Combined, - }, - // 3.3.1.{16,17,18} - LDToRegisterAddressThenInc { - src: register::SingleEightBit, - // The destination, unlike some other destination instructions, refers to a register - // whose address will be dereferenced (and then incremented after the load) - dst: register::Combined, - }, - // 3.3.2.1 - LD16bitImmediateToRegister { - value: u16, - dst: register::SixteenBit, - }, - // 3.3.2.2 - LD16Bitr1r2 { - dst: register::SixteenBit, - src: register::SixteenBit, - }, - // 3.3.3.4 - LDEffectiveAddress { - dst: register::Combined, - offset: i8, - }, - // 3.3.3.5 - Push { - src: register::Combined, - }, - Pop { - dst: register::Combined, - }, + EightBitLoad(load8::EightBitLoadInstruction), + SixteenBitLoad(load16::SixteenBitLoadInstruction), } +/// `RunnableInstruction` is an instruction that can run on the processor, and has any metadata needed to do so +#[derive(Debug, Clone)] pub struct RunnableInstruction { pub(super) instruction: Instruction, pub(super) cycles: u8, diff --git a/src/run/instructions/load16.rs b/src/run/instructions/load16.rs new file mode 100644 index 0000000..35696c8 --- /dev/null +++ b/src/run/instructions/load16.rs @@ -0,0 +1,30 @@ +use crate::register; + +/// `SixteenBitLoadInstruction` represents one of the Gameboy's instructions for loading +/// a sixteen bit value either into a register or into memory +#[derive(Debug, Clone)] +pub enum SixteenBitLoadInstruction { + // 3.3.2.1 + LDImmediateToRegister { + value: u16, + dst: register::SixteenBit, + }, + // 3.3.2.2 + LDBetweenRegisters { + dst: register::SixteenBit, + src: register::SixteenBit, + }, + // 3.3.3.4 + LDEffectiveAddress { + dst: register::Combined, + offset: i8, + }, + // 3.3.3.5 + Push { + src: register::Combined, + }, + // 3.3.3.6 + Pop { + dst: register::Combined, + }, +} diff --git a/src/run/instructions/load8.rs b/src/run/instructions/load8.rs new file mode 100644 index 0000000..d30896a --- /dev/null +++ b/src/run/instructions/load8.rs @@ -0,0 +1,96 @@ +use crate::register; + +/// `EightBitLoadInstruction` represents one of the Gameboy's instructions for loading +/// an eight bit value either into a register or into memory +#[derive(Debug, Clone)] +pub enum EightBitLoadInstruction { + // 3.3.1.1 + LDImmediateToRegister { + value: u8, + register: register::SingleEightBit, + }, + // 3.3.1.2, excluding the (hl) instructions, 3.3.1.3, excluding the (nn) instructions + // and 3.3.1.4 excluding the (nn) instructions + LDBetweenRegisters { + dst: register::SingleEightBit, + src: register::SingleEightBit, + }, + // 3.3.1.2/3.3.1.3, only the loading from (nn) instructions + LDFromRegisterAddress { + // src, unlike some of the other instructions, is a 16bit combined register that will be + // dereferenced to get the value to place into the dst register + src: register::Combined, + dst: register::SingleEightBit, + }, + // 3.3.1.5 - load to register relative to $FF00 (the address of the i/o registers) + LDFromMemoryRelativeToIORegisterStart { + // A register whose value will be used to get the address relative to FF00 + offset_register: register::SingleEightBit, + dst: register::SingleEightBit, + }, + // 3.3.1.6 - load to memory relative to $FF00 (the address of the i/o registers) + LDToMemoryRelativeToIORegisterStart { + src: register::SingleEightBit, + // A register whose value will be used to get the address relative to FF00 + offset_register: register::SingleEightBit, + }, + // 3.3.1.20 + LDFromMemoryRelativeToIORegisterStartByImmediate { + dst: register::SingleEightBit, + offset: u8, + }, + // 3.3.1.19 + LDToMemoryRelativeToIORegisterStartByImmediate { + offset: u8, + src: register::SingleEightBit, + }, + LDFromImmediateAddress { + src_address: u16, + dst: register::SingleEightBit, + }, + // 3.3.1.2, only the loading to (hl) instructions + // 3.3.1.4, only the loading to (nn) instructions + LDToRegisterAddress { + src: register::SingleEightBit, + // dst, unlike some other instructions, is a 16bit combined register that holds + // the address we will write the value to + dst: register::Combined, + }, + // 3.3.1.4, only the loading to (nn) immediate instrution + LDToImmediateAddress { + src: register::SingleEightBit, + dst_address: u16, + }, + // 3.3.1.2, but the (hl), n instruction [why is this in this section? it's so out of place] + LDnToHLAddress { + value: u8, + }, + // 3.3.1.{7,8,9} + LDFromRegisterAddressThenDec { + // The src, unlike some other destination instructions, refers to a register + // whose address will be dereferenced (and then decremented after the load) + src: register::Combined, + dst: register::SingleEightBit, + }, + // 3.3.1.{13,14,15} + LDFromRegisterAddressThenInc { + // The src, unlike some other destination instructions, refers to a register + // whose address will be dereferenced (and then incremented after the load) + src: register::Combined, + dst: register::SingleEightBit, + }, + // 3.3.1.{10,11,12} + LDToRegisterAddressThenDec { + src: register::SingleEightBit, + // The destination, unlike some other destination instructions, refers to a register + // whose address will be dereferenced (and then decremented after the load) + dst: register::Combined, + }, + // 3.3.1.{16,17,18} + LDToRegisterAddressThenInc { + src: register::SingleEightBit, + // The destination, unlike some other destination instructions, refers to a register + // whose address will be dereferenced (and then incremented after the load) + dst: register::Combined, + }, +} diff --git a/src/run/parse.rs b/src/run/parse.rs index 0070d7e..1553570 100644 --- a/src/run/parse.rs +++ b/src/run/parse.rs @@ -1,3 +1,5 @@ +//! The `parse` module holds functions that will parse streams of data into an [`crate::instructions::Instruction`] + use super::instructions::RunnableInstruction; use thiserror::Error; diff --git a/src/run/parse/load16/immediate.rs b/src/run/parse/load16/immediate.rs index 4223054..112c1f6 100644 --- a/src/run/parse/load16/immediate.rs +++ b/src/run/parse/load16/immediate.rs @@ -1,4 +1,5 @@ use crate::register; +use crate::run::instructions::load16::SixteenBitLoadInstruction; use crate::run::{ instructions::{Instruction, RunnableInstruction}, parse::{self, Error, OpcodeParser, ParseResult}, @@ -43,7 +44,9 @@ fn make_load_immediate_data(dst: register::SixteenBit, data: &[u8]) -> ParseResu let value = u16::from_le_bytes([args[0], args[1]]); Ok(( RunnableInstruction { - instruction: Instruction::LD16bitImmediateToRegister { value, dst }, + instruction: Instruction::SixteenBitLoad( + SixteenBitLoadInstruction::LDImmediateToRegister { value, dst }, + ), cycles: 12, }, // guaranteed to succeed given we already took the first two elements as args @@ -71,10 +74,12 @@ fn make_load_effective_address(dst: register::Combined, data: &[u8]) -> ParseRes Ok(( RunnableInstruction { - instruction: Instruction::LDEffectiveAddress { - dst, - offset: signed_value, - }, + instruction: Instruction::SixteenBitLoad( + SixteenBitLoadInstruction::LDEffectiveAddress { + dst, + offset: signed_value, + }, + ), cycles: 12, }, &data[2..], diff --git a/src/run/parse/load16/stack.rs b/src/run/parse/load16/stack.rs index 660c795..54e5029 100644 --- a/src/run/parse/load16/stack.rs +++ b/src/run/parse/load16/stack.rs @@ -1,4 +1,5 @@ use crate::register; +use crate::run::instructions::load16::SixteenBitLoadInstruction; use crate::run::instructions::{Instruction, RunnableInstruction}; use crate::run::parse::{self, Error, OpcodeParser, ParseResult}; @@ -39,11 +40,15 @@ fn make_stack_operation_data( .map(|remaining_data| { let instruction = match operation { Operation::Push => RunnableInstruction { - instruction: Instruction::Push { src: reg }, + instruction: Instruction::SixteenBitLoad(SixteenBitLoadInstruction::Push { + src: reg, + }), cycles: 16, }, Operation::Pop => RunnableInstruction { - instruction: Instruction::Pop { dst: reg }, + instruction: Instruction::SixteenBitLoad(SixteenBitLoadInstruction::Pop { + dst: reg, + }), cycles: 12, }, }; diff --git a/src/run/parse/load16/transfer.rs b/src/run/parse/load16/transfer.rs index 887665f..36805ce 100644 --- a/src/run/parse/load16/transfer.rs +++ b/src/run/parse/load16/transfer.rs @@ -1,4 +1,5 @@ use crate::register; +use crate::run::instructions::load16::SixteenBitLoadInstruction; use crate::run::instructions::Instruction; use crate::run::{ instructions::RunnableInstruction, @@ -12,7 +13,7 @@ impl OpcodeParser for Between16BitRegisterParser { fn parse_opcode(data: &[u8]) -> ParseResult { let opcode = parse::get_opcode_from_data(data)?; match opcode { - 0xF9 => make_ld_r1_r2_data( + 0xF9 => make_ld_between_register_data( register::SixteenBit::Single(register::SingleSixteenBit::StackPointer), register::SixteenBit::Combined(register::Combined::HL), data, @@ -22,7 +23,7 @@ impl OpcodeParser for Between16BitRegisterParser { } } -fn make_ld_r1_r2_data( +fn make_ld_between_register_data( dst: register::SixteenBit, src: register::SixteenBit, data: &[u8], @@ -31,7 +32,9 @@ fn make_ld_r1_r2_data( .map(|remaining_data| { ( RunnableInstruction { - instruction: Instruction::LD16Bitr1r2 { dst, src }, + instruction: Instruction::SixteenBitLoad( + SixteenBitLoadInstruction::LDBetweenRegisters { dst, src }, + ), cycles: 4, }, remaining_data, diff --git a/src/run/parse/load8/immediate.rs b/src/run/parse/load8/immediate.rs index f2e6999..6e06f5f 100644 --- a/src/run/parse/load8/immediate.rs +++ b/src/run/parse/load8/immediate.rs @@ -1,4 +1,5 @@ use crate::register; +use crate::run::instructions::load8::EightBitLoadInstruction; use crate::run::instructions::Instruction; use crate::run::parse::Error; use crate::run::{ @@ -34,10 +35,12 @@ fn make_load_immediate_data(register: register::SingleEightBit, data: &[u8]) -> Ok(( RunnableInstruction { - instruction: Instruction::LD8BitImmediateToRegister { - register, - value: *value, - }, + instruction: Instruction::EightBitLoad( + EightBitLoadInstruction::LDImmediateToRegister { + register, + value: *value, + }, + ), // TODO: I don't love that the cycles are in a parsing function, // but ultimately I want the cycles to be as close to the opcode as possible // so that it's easier to change. Maybe I can change this once I end up with diff --git a/src/run/parse/load8/memory.rs b/src/run/parse/load8/memory.rs index 4ecf7d7..57b3daa 100644 --- a/src/run/parse/load8/memory.rs +++ b/src/run/parse/load8/memory.rs @@ -1,4 +1,5 @@ use crate::register; +use crate::run::instructions::load8::EightBitLoadInstruction; use crate::run::instructions::{Instruction, RunnableInstruction}; use crate::run::parse::{self, Error, OpcodeParser, ParseResult}; @@ -12,7 +13,7 @@ impl OpcodeParser for Memory8BitLoadParser { parse_load_from_register_to_address, parse_load_from_address_to_register, parse_load_immediate_instructions, - parse_load_from_register_to_address_then_do_arithmetic, + parse_load_from_register_to_address_and_do_arithmetic, ]; for parse_func in parse_funcs { @@ -127,31 +128,31 @@ fn parse_load_immediate_instructions(data: &[u8]) -> ParseResult { } } -fn parse_load_from_register_to_address_then_do_arithmetic(data: &[u8]) -> ParseResult { +fn parse_load_from_register_to_address_and_do_arithmetic(data: &[u8]) -> ParseResult { let opcode = parse::get_opcode_from_data(data)?; match opcode { - 0x32 => make_ld_to_address_then_do_arithmetic( + 0x32 => make_ld_to_address_and_do_arithmetic( register::Combined::HL, register::SingleEightBit::A, - |dst, src| Instruction::LDToRegisterAddressThenDec { dst, src }, + |dst, src| EightBitLoadInstruction::LDToRegisterAddressThenDec { dst, src }, data, ), - 0x22 => make_ld_to_address_then_do_arithmetic( + 0x22 => make_ld_to_address_and_do_arithmetic( register::Combined::HL, register::SingleEightBit::A, - |dst, src| Instruction::LDToRegisterAddressThenInc { dst, src }, + |dst, src| EightBitLoadInstruction::LDToRegisterAddressThenInc { dst, src }, data, ), - 0x3A => make_ld_from_address_then_do_arithmetic( + 0x3A => make_ld_from_address_and_do_arithmetic( register::SingleEightBit::A, register::Combined::HL, - |dst, src| Instruction::LDFromRegisterAddressThenDec { dst, src }, + |dst, src| EightBitLoadInstruction::LDFromRegisterAddressThenDec { dst, src }, data, ), - 0x2A => make_ld_from_address_then_do_arithmetic( + 0x2A => make_ld_from_address_and_do_arithmetic( register::SingleEightBit::A, register::Combined::HL, - |dst, src| Instruction::LDFromRegisterAddressThenInc { dst, src }, + |dst, src| EightBitLoadInstruction::LDFromRegisterAddressThenInc { dst, src }, data, ), _ => Err(Error::UnknownOpcode(opcode)), @@ -167,7 +168,9 @@ fn make_ld_from_register_address( .map(|remaining_data| { ( RunnableInstruction { - instruction: Instruction::LDFromRegisterAddress { src, dst }, + instruction: Instruction::EightBitLoad( + EightBitLoadInstruction::LDFromRegisterAddress { src, dst }, + ), cycles: 8, }, remaining_data, @@ -185,7 +188,9 @@ fn make_ld_to_register_address( .map(|remaining_data| { ( RunnableInstruction { - instruction: Instruction::LDToRegisterAddress { src, dst }, + instruction: Instruction::EightBitLoad( + EightBitLoadInstruction::LDToRegisterAddress { src, dst }, + ), cycles: 8, }, remaining_data, @@ -202,7 +207,9 @@ fn make_ld_from_immediate_address(dst: register::SingleEightBit, data: &[u8]) -> let src_address = u16::from_le_bytes([args[0], args[1]]); Ok(( RunnableInstruction { - instruction: Instruction::LDFromImmediateAddress { src_address, dst }, + instruction: Instruction::EightBitLoad( + EightBitLoadInstruction::LDFromImmediateAddress { src_address, dst }, + ), cycles: 16, }, // This can't fail if get(1..=2) passed @@ -218,7 +225,10 @@ fn make_ld_to_immediate_address(src: register::SingleEightBit, data: &[u8]) -> P let dst_address = u16::from_le_bytes([args[0], args[1]]); Ok(( RunnableInstruction { - instruction: Instruction::LDToImmediateAddress { src, dst_address }, + instruction: Instruction::EightBitLoad(EightBitLoadInstruction::LDToImmediateAddress { + src, + dst_address, + }), cycles: 16, }, // This can't fail if get(1..=2) passed @@ -231,7 +241,9 @@ fn make_ld_n_to_hl_address(data: &[u8]) -> ParseResult { let value = data.get(1).ok_or(Error::NotEnoughArgs(opcode))?; Ok(( RunnableInstruction { - instruction: Instruction::LDnToHLAddress { value: *value }, + instruction: Instruction::EightBitLoad(EightBitLoadInstruction::LDnToHLAddress { + value: *value, + }), cycles: 12, }, // This can't fail if get(1) succeeeded @@ -248,10 +260,12 @@ fn make_ld_from_memory_relative_to_io_register_start( .map(|remaining_data| { ( RunnableInstruction { - instruction: Instruction::LDFromMemoryRelativeToIORegisterStart { - offset_register, - dst, - }, + instruction: Instruction::EightBitLoad( + EightBitLoadInstruction::LDFromMemoryRelativeToIORegisterStart { + offset_register, + dst, + }, + ), cycles: 8, }, // guaranteed to succeed given we found the opcode @@ -270,10 +284,12 @@ fn make_ld_to_memory_relative_to_io_register_start( .map(|remaining_data| { ( RunnableInstruction { - instruction: Instruction::LDToMemoryRelativeToIORegisterStart { - src, - offset_register, - }, + instruction: Instruction::EightBitLoad( + EightBitLoadInstruction::LDToMemoryRelativeToIORegisterStart { + src, + offset_register, + }, + ), cycles: 8, }, // guaranteed to succeed given we found the opcode @@ -292,10 +308,12 @@ fn make_ld_to_memory_relative_to_io_register_start_by_immediate( Ok(( RunnableInstruction { - instruction: Instruction::LDToMemoryRelativeToIORegisterStartByImmediate { - src, - offset: *value, - }, + instruction: Instruction::EightBitLoad( + EightBitLoadInstruction::LDToMemoryRelativeToIORegisterStartByImmediate { + src, + offset: *value, + }, + ), // guaranteed to succeed given we got the value cycles: 12, }, @@ -312,10 +330,12 @@ fn make_ld_from_memory_relative_to_io_register_start_by_immediate( Ok(( RunnableInstruction { - instruction: Instruction::LDFromMemoryRelativeToIORegisterStartByImmediate { - offset: *value, - dst, - }, + instruction: Instruction::EightBitLoad( + EightBitLoadInstruction::LDFromMemoryRelativeToIORegisterStartByImmediate { + offset: *value, + dst, + }, + ), cycles: 12, }, // guaranteed to succeed given we got the value @@ -323,34 +343,45 @@ fn make_ld_from_memory_relative_to_io_register_start_by_immediate( )) } -fn make_ld_to_address_then_do_arithmetic< - F: Fn(register::Combined, register::SingleEightBit) -> Instruction, +/// `make_ld_to_address_and_do_arithmetic` will make one of the instructions that load to memory and either increment +/// or decerement. The instruction itself will be produced by the given `make` function. +fn make_ld_to_address_and_do_arithmetic< + F: Fn(register::Combined, register::SingleEightBit) -> EightBitLoadInstruction, >( dst: register::Combined, src: register::SingleEightBit, make: F, data: &[u8], ) -> ParseResult { - make_load_then_do_arithmetic(|| make(dst, src), data) + make_load_and_do_arithmetic(|| make(dst, src), data) } -fn make_ld_from_address_then_do_arithmetic< - F: Fn(register::SingleEightBit, register::Combined) -> Instruction, +/// `make_ld_to_address_and_do_arithmetic` will make one of the instructions that load from memory and either increment +/// or decerement. The instruction itself will be produced by the given `make` function. +fn make_ld_from_address_and_do_arithmetic< + F: Fn(register::SingleEightBit, register::Combined) -> EightBitLoadInstruction, >( dst: register::SingleEightBit, src: register::Combined, make: F, data: &[u8], ) -> ParseResult { - make_load_then_do_arithmetic(|| make(dst, src), data) + make_load_and_do_arithmetic(|| make(dst, src), data) } -fn make_load_then_do_arithmetic Instruction>(make: F, data: &[u8]) -> ParseResult { +/// `make_load_and_do_arithmetic` will parse a `RunnableInstruction` from one of the load+arithmetic isntructions, +/// which will be produced by the given `make` function +fn make_load_and_do_arithmetic EightBitLoadInstruction>( + make: F, + data: &[u8], +) -> ParseResult { data.get(1..) .map(|remaining_data| { + let load_instruction = make(); + ( RunnableInstruction { - instruction: make(), + instruction: Instruction::EightBitLoad(load_instruction), cycles: 8, }, remaining_data, diff --git a/src/run/parse/load8/transfer.rs b/src/run/parse/load8/transfer.rs index 9197fad..bc94438 100644 --- a/src/run/parse/load8/transfer.rs +++ b/src/run/parse/load8/transfer.rs @@ -1,4 +1,5 @@ use crate::register; +use crate::run::instructions::load8::EightBitLoadInstruction; use crate::run::instructions::Instruction; use crate::run::{ instructions::RunnableInstruction, @@ -17,68 +18,68 @@ impl OpcodeParser for Between8BitRegisterParser { let opcode = parse::get_opcode_from_data(data)?; match opcode { - 0x7f => make_ldr1r2_data(A, A, data), - 0x78 => make_ldr1r2_data(A, B, data), - 0x79 => make_ldr1r2_data(A, C, data), - 0x7A => make_ldr1r2_data(A, D, data), - 0x7B => make_ldr1r2_data(A, E, data), - 0x7C => make_ldr1r2_data(A, H, data), - 0x7D => make_ldr1r2_data(A, L, data), + 0x7f => make_load_between_register_data(A, A, data), + 0x78 => make_load_between_register_data(A, B, data), + 0x79 => make_load_between_register_data(A, C, data), + 0x7A => make_load_between_register_data(A, D, data), + 0x7B => make_load_between_register_data(A, E, data), + 0x7C => make_load_between_register_data(A, H, data), + 0x7D => make_load_between_register_data(A, L, data), - 0x47 => make_ldr1r2_data(B, A, data), - 0x40 => make_ldr1r2_data(B, B, data), - 0x41 => make_ldr1r2_data(B, C, data), - 0x42 => make_ldr1r2_data(B, D, data), - 0x43 => make_ldr1r2_data(B, E, data), - 0x44 => make_ldr1r2_data(B, H, data), - 0x45 => make_ldr1r2_data(B, L, data), + 0x47 => make_load_between_register_data(B, A, data), + 0x40 => make_load_between_register_data(B, B, data), + 0x41 => make_load_between_register_data(B, C, data), + 0x42 => make_load_between_register_data(B, D, data), + 0x43 => make_load_between_register_data(B, E, data), + 0x44 => make_load_between_register_data(B, H, data), + 0x45 => make_load_between_register_data(B, L, data), - 0x4F => make_ldr1r2_data(C, A, data), - 0x48 => make_ldr1r2_data(C, B, data), - 0x49 => make_ldr1r2_data(C, C, data), - 0x4A => make_ldr1r2_data(C, D, data), - 0x4B => make_ldr1r2_data(C, E, data), - 0x4C => make_ldr1r2_data(C, H, data), - 0x4D => make_ldr1r2_data(C, L, data), + 0x4F => make_load_between_register_data(C, A, data), + 0x48 => make_load_between_register_data(C, B, data), + 0x49 => make_load_between_register_data(C, C, data), + 0x4A => make_load_between_register_data(C, D, data), + 0x4B => make_load_between_register_data(C, E, data), + 0x4C => make_load_between_register_data(C, H, data), + 0x4D => make_load_between_register_data(C, L, data), - 0x57 => make_ldr1r2_data(D, A, data), - 0x50 => make_ldr1r2_data(D, B, data), - 0x51 => make_ldr1r2_data(D, C, data), - 0x52 => make_ldr1r2_data(D, D, data), - 0x53 => make_ldr1r2_data(D, E, data), - 0x54 => make_ldr1r2_data(D, H, data), - 0x55 => make_ldr1r2_data(D, L, data), + 0x57 => make_load_between_register_data(D, A, data), + 0x50 => make_load_between_register_data(D, B, data), + 0x51 => make_load_between_register_data(D, C, data), + 0x52 => make_load_between_register_data(D, D, data), + 0x53 => make_load_between_register_data(D, E, data), + 0x54 => make_load_between_register_data(D, H, data), + 0x55 => make_load_between_register_data(D, L, data), - 0x5F => make_ldr1r2_data(E, A, data), - 0x58 => make_ldr1r2_data(E, B, data), - 0x59 => make_ldr1r2_data(E, C, data), - 0x5A => make_ldr1r2_data(E, D, data), - 0x5B => make_ldr1r2_data(E, E, data), - 0x5C => make_ldr1r2_data(E, H, data), - 0x5D => make_ldr1r2_data(E, L, data), + 0x5F => make_load_between_register_data(E, A, data), + 0x58 => make_load_between_register_data(E, B, data), + 0x59 => make_load_between_register_data(E, C, data), + 0x5A => make_load_between_register_data(E, D, data), + 0x5B => make_load_between_register_data(E, E, data), + 0x5C => make_load_between_register_data(E, H, data), + 0x5D => make_load_between_register_data(E, L, data), - 0x67 => make_ldr1r2_data(H, A, data), - 0x60 => make_ldr1r2_data(H, B, data), - 0x61 => make_ldr1r2_data(H, C, data), - 0x62 => make_ldr1r2_data(H, D, data), - 0x63 => make_ldr1r2_data(H, E, data), - 0x64 => make_ldr1r2_data(H, H, data), - 0x65 => make_ldr1r2_data(H, L, data), + 0x67 => make_load_between_register_data(H, A, data), + 0x60 => make_load_between_register_data(H, B, data), + 0x61 => make_load_between_register_data(H, C, data), + 0x62 => make_load_between_register_data(H, D, data), + 0x63 => make_load_between_register_data(H, E, data), + 0x64 => make_load_between_register_data(H, H, data), + 0x65 => make_load_between_register_data(H, L, data), - 0x6F => make_ldr1r2_data(L, A, data), - 0x68 => make_ldr1r2_data(L, B, data), - 0x69 => make_ldr1r2_data(L, C, data), - 0x6A => make_ldr1r2_data(L, D, data), - 0x6B => make_ldr1r2_data(L, E, data), - 0x6c => make_ldr1r2_data(L, H, data), - 0x6D => make_ldr1r2_data(L, L, data), + 0x6F => make_load_between_register_data(L, A, data), + 0x68 => make_load_between_register_data(L, B, data), + 0x69 => make_load_between_register_data(L, C, data), + 0x6A => make_load_between_register_data(L, D, data), + 0x6B => make_load_between_register_data(L, E, data), + 0x6c => make_load_between_register_data(L, H, data), + 0x6D => make_load_between_register_data(L, L, data), _ => Err(Error::UnknownOpcode(opcode)), } } } -fn make_ldr1r2_data( +fn make_load_between_register_data( dst: register::SingleEightBit, src: register::SingleEightBit, data: &[u8], @@ -87,7 +88,9 @@ fn make_ldr1r2_data( .map(|remaining_data| { ( RunnableInstruction { - instruction: Instruction::LD8bitr1r2 { dst, src }, + instruction: Instruction::EightBitLoad( + EightBitLoadInstruction::LDBetweenRegisters { dst, src }, + ), cycles: 4, }, remaining_data,