diff --git a/src/run.rs b/src/run.rs index 49d4948..774b1c8 100644 --- a/src/run.rs +++ b/src/run.rs @@ -107,6 +107,16 @@ impl Processor { self.registers.set_combined_register(dst, dst_address + 1); } + Instruction::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 } => { + 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); + } } self.num_cycles += u64::from(instruction.cycles); @@ -464,4 +474,34 @@ mod tests { .get_combined_register(register::Combined::HL) ); } + + #[test] + fn test_load_to_io_register_zone_by_immediate() { + let mut processor = Processor::default(); + processor.registers.a = 0xAF; + + let data = [0xE0, 0x05, 0x06]; + let (ins, extra_data) = + RunnableInstruction::from_data(&data).expect("could not parse instruction"); + + assert_eq!(extra_data, &[0x06]); + processor.run(&ins); + + assert_eq!(Some(0xAF), processor.memory.get(0xFF05)); + } + + #[test] + fn test_load_from_io_register_zone_by_immediate() { + let mut processor = Processor::default(); + processor.memory.set(0xFF05, 0xAF); + + let data = [0xF0, 0x05, 0x06]; + let (ins, extra_data) = + RunnableInstruction::from_data(&data).expect("could not parse instruction"); + + assert_eq!(extra_data, &[0x06]); + processor.run(&ins); + + assert_eq!(0xAF, processor.registers.a); + } } diff --git a/src/run/instructions.rs b/src/run/instructions.rs index a902f84..af6d47a 100644 --- a/src/run/instructions.rs +++ b/src/run/instructions.rs @@ -34,6 +34,16 @@ pub enum Instruction { // A register whose value will be used to get the address relative to FF00 offset_register: register::Single, }, + // 3.3.1.20 + LDFromMemoryRelativeToIORegisterStartByImmediate { + dst: register::Single, + offset: u8, + }, + // 3.3.1.19 + LDToMemoryRelativeToIORegisterStartByImmediate { + offset: u8, + src: register::Single, + }, LDFromImmediateAddress { src_address: u16, dst: register::Single, diff --git a/src/run/parse/load8/memory.rs b/src/run/parse/load8/memory.rs index 0e1c553..2105a9e 100644 --- a/src/run/parse/load8/memory.rs +++ b/src/run/parse/load8/memory.rs @@ -68,6 +68,13 @@ fn parse_load_from_address_to_register(data: &[u8]) -> ParseResult { register::Single::A, data, ), + 0xF0 => make_ld_from_memory_relative_to_io_register_start_by_immediate( + register::Single::A, + data, + ), + 0xE0 => { + make_ld_to_memory_relative_to_io_register_start_by_immediate(register::Single::A, data) + } _ => Err(Error::UnknownOpcode(opcode)), } @@ -227,6 +234,46 @@ fn make_ld_to_memory_relative_to_io_register_start( .ok_or(Error::NoData) } +fn make_ld_to_memory_relative_to_io_register_start_by_immediate( + src: register::Single, + data: &[u8], +) -> ParseResult { + let opcode = parse::get_opcode_from_data(data)?; + let value = data.get(1).ok_or(Error::NotEnoughArgs(opcode))?; + + Ok(( + RunnableInstruction { + instruction: Instruction::LDToMemoryRelativeToIORegisterStartByImmediate { + src, + offset: *value, + }, + // guaranteed to succeed given we got the value + cycles: 12, + }, + &data[2..], + )) +} + +fn make_ld_from_memory_relative_to_io_register_start_by_immediate( + dst: register::Single, + data: &[u8], +) -> ParseResult { + let opcode = parse::get_opcode_from_data(data)?; + let value = data.get(1).ok_or(Error::NotEnoughArgs(opcode))?; + + Ok(( + RunnableInstruction { + instruction: Instruction::LDFromMemoryRelativeToIORegisterStartByImmediate { + offset: *value, + dst, + }, + cycles: 12, + }, + // guaranteed to succeed given we got the value + &data[2..], + )) +} + fn make_ld_to_address_then_do_arithmetic< F: Fn(register::Combined, register::Single) -> Instruction, >(