diff --git a/src/cpu.rs b/src/cpu.rs index 9c5f171..93b3137 100644 --- a/src/cpu.rs +++ b/src/cpu.rs @@ -24,14 +24,13 @@ impl Processor { .memory .get_from(pc.into()) .expect("program counter out of range"); - let (instruction, rest_memory) = - parse::next_instruction(memory_view).expect("invalid instruction"); - let (next_pc, _carry) = - pc.overflowing_add(u16::try_from(memory_view.len() - rest_memory.len()).unwrap()); + + let (instruction, bytes_read) = + parse::next_instruction(&memory_view).expect("invalid instruction"); self.run_instruction(&instruction); - // wow this code sucks; I may change the parse code to return a number of bytes read, - // rather than a memory view. + + let (next_pc, _carry) = pc.overflowing_add(bytes_read); self.registers.program_counter = next_pc; } diff --git a/src/cpu/instructions.rs b/src/cpu/instructions.rs index 07982b2..14c6dcc 100644 --- a/src/cpu/instructions.rs +++ b/src/cpu/instructions.rs @@ -1,6 +1,8 @@ //! The `instructions` module holds structural definitions for all of the instructions that can run on the gameboy. -use super::parse::{self, ParseResult}; +use crate::memory::View; + +use super::parse::{self, Error}; pub mod arith8; pub mod load16; @@ -26,9 +28,12 @@ impl RunnableInstruction { /// `from_data` will produce an instruction from the given data and return the data after /// processing that operation. /// + /// This is only available for testing in order to get instructions that can be run standalone + /// /// # Errors /// Returns an error if the instruction couldn't be parsed. - pub fn from_data(data: &[u8]) -> ParseResult { - parse::next_instruction(data) + pub fn from_data(data: &[u8]) -> Result<(RunnableInstruction, &[u8]), Error> { + parse::next_instruction(&View::new_from_data(data, 0)) + .map(|(ins, offset)| (ins, &data[offset.into()..])) } } diff --git a/src/cpu/parse.rs b/src/cpu/parse.rs index b154121..105323d 100644 --- a/src/cpu/parse.rs +++ b/src/cpu/parse.rs @@ -1,5 +1,7 @@ //! The `parse` module holds functions that will parse streams of data into an [`crate::instructions::Instruction`] +use crate::memory::View; + use super::instructions::RunnableInstruction; use thiserror::Error; @@ -17,20 +19,21 @@ pub enum Error { NotEnoughArgs(u8), } -/// `ParseResult` is the result of a Parse operation -pub(super) type ParseResult<'a> = Result<(RunnableInstruction, &'a [u8]), Error>; +/// `ParseResult` is the result of a Parse operation, which includes the parsed instruction and the number of bytes read. +pub(super) type ParseOutput = (RunnableInstruction, u16); +pub(super) type ParseResult = Result; /// `OpcodeParser` takes input data, parses out an opcode (and its associated arguments) if it can, and returns /// the remaining data after reading it. trait OpcodeParser { /// Parse an opcode and all of its data from the given data buffer. Returns either an error, or /// The parsed instruction, and a slice of all the data after the instruction - fn parse_opcode(data: &[u8]) -> ParseResult; + fn parse_opcode(data: &View) -> ParseResult; } /// `next_instruction` will parse the next instruction from the given data stream. Returns either an error, /// or the parsed instruction, and a slice of all the data after the instruction -pub fn next_instruction(data: &[u8]) -> ParseResult { +pub fn next_instruction(data: &View) -> ParseResult { let parse_funcs = &[ load8::immediate::Immediate8BitLoadParser::parse_opcode, load8::transfer::Between8BitRegisterParser::parse_opcode, @@ -49,11 +52,10 @@ pub fn next_instruction(data: &[u8]) -> ParseResult { } } - let opcode = get_opcode_from_data(data)?; - + let opcode = get_opcode_from_data(data); Err(Error::UnknownOpcode(opcode)) } -fn get_opcode_from_data(data: &[u8]) -> Result { - data.first().copied().ok_or(Error::NoData) +fn get_opcode_from_data(data: &View) -> u8 { + data.get() } diff --git a/src/cpu/parse/arith8/add.rs b/src/cpu/parse/arith8/add.rs index 6db7681..aa7ffbb 100644 --- a/src/cpu/parse/arith8/add.rs +++ b/src/cpu/parse/arith8/add.rs @@ -1,116 +1,111 @@ use crate::{ cpu::{ instructions::{arith8::EightBitArithmeticInstruction, Instruction, RunnableInstruction}, - parse::{self, Error, OpcodeParser, ParseResult}, + parse::{self, Error, OpcodeParser, ParseOutput, ParseResult}, }, + memory::{GetViewTuple, View}, register, }; pub struct EightBitAddParser; impl OpcodeParser for EightBitAddParser { - fn parse_opcode(data: &[u8]) -> ParseResult { - let opcode = parse::get_opcode_from_data(data)?; + fn parse_opcode(data: &View) -> ParseResult { + let opcode = parse::get_opcode_from_data(data); match opcode { - 0x87 => build_add_register_to_a_data(register::SingleEightBit::A, data), - 0x80 => build_add_register_to_a_data(register::SingleEightBit::B, data), - 0x81 => build_add_register_to_a_data(register::SingleEightBit::C, data), - 0x82 => build_add_register_to_a_data(register::SingleEightBit::D, data), - 0x83 => build_add_register_to_a_data(register::SingleEightBit::E, data), - 0x84 => build_add_register_to_a_data(register::SingleEightBit::H, data), - 0x85 => build_add_register_to_a_data(register::SingleEightBit::L, data), - 0x86 => build_add_hl_address_to_a_data(data), - 0xC6 => build_add_immediate_to_a_data(data), - 0x88 => build_add_register_to_a_with_carry_data(register::SingleEightBit::B, data), - 0x89 => build_add_register_to_a_with_carry_data(register::SingleEightBit::C, data), - 0x8A => build_add_register_to_a_with_carry_data(register::SingleEightBit::D, data), - 0x8B => build_add_register_to_a_with_carry_data(register::SingleEightBit::E, data), - 0x8C => build_add_register_to_a_with_carry_data(register::SingleEightBit::H, data), - 0x8D => build_add_register_to_a_with_carry_data(register::SingleEightBit::L, data), - 0xCE => build_add_immediate_to_a_with_carry_data(data), + 0x87 => Ok(build_add_register_to_a_data(register::SingleEightBit::A)), + 0x80 => Ok(build_add_register_to_a_data(register::SingleEightBit::B)), + 0x81 => Ok(build_add_register_to_a_data(register::SingleEightBit::C)), + 0x82 => Ok(build_add_register_to_a_data(register::SingleEightBit::D)), + 0x83 => Ok(build_add_register_to_a_data(register::SingleEightBit::E)), + 0x84 => Ok(build_add_register_to_a_data(register::SingleEightBit::H)), + 0x85 => Ok(build_add_register_to_a_data(register::SingleEightBit::L)), + 0x86 => Ok(build_add_hl_address_to_a_data()), + 0xC6 => Ok(build_add_immediate_to_a_data(data)), + 0x88 => Ok(build_add_register_to_a_with_carry_data( + register::SingleEightBit::B, + )), + 0x89 => Ok(build_add_register_to_a_with_carry_data( + register::SingleEightBit::C, + )), + 0x8A => Ok(build_add_register_to_a_with_carry_data( + register::SingleEightBit::D, + )), + 0x8B => Ok(build_add_register_to_a_with_carry_data( + register::SingleEightBit::E, + )), + 0x8C => Ok(build_add_register_to_a_with_carry_data( + register::SingleEightBit::H, + )), + 0x8D => Ok(build_add_register_to_a_with_carry_data( + register::SingleEightBit::L, + )), + 0xCE => Ok(build_add_immediate_to_a_with_carry_data(data)), _ => Err(Error::UnknownOpcode(opcode)), } } } -fn build_add_register_to_a_data(src: register::SingleEightBit, data: &[u8]) -> ParseResult { - data.get(1..) - .map(|remaining_data| { - ( - RunnableInstruction { - instruction: Instruction::EightBitArithmetic( - EightBitArithmeticInstruction::AddSingleRegisterToA { src }, - ), - cycles: 4, - }, - remaining_data, - ) - }) - .ok_or(Error::NoData) +fn build_add_register_to_a_data(src: register::SingleEightBit) -> ParseOutput { + ( + RunnableInstruction { + instruction: Instruction::EightBitArithmetic( + EightBitArithmeticInstruction::AddSingleRegisterToA { src }, + ), + cycles: 4, + }, + 1, + ) } -fn build_add_register_to_a_with_carry_data( - src: register::SingleEightBit, - data: &[u8], -) -> ParseResult { - data.get(1..) - .map(|remaining_data| { - ( - RunnableInstruction { - instruction: Instruction::EightBitArithmetic( - EightBitArithmeticInstruction::AddSingleRegisterToAWithCarry { src }, - ), - cycles: 4, - }, - remaining_data, - ) - }) - .ok_or(Error::NoData) +fn build_add_register_to_a_with_carry_data(src: register::SingleEightBit) -> ParseOutput { + ( + RunnableInstruction { + instruction: Instruction::EightBitArithmetic( + EightBitArithmeticInstruction::AddSingleRegisterToAWithCarry { src }, + ), + cycles: 4, + }, + 1, + ) } -fn build_add_hl_address_to_a_data(data: &[u8]) -> ParseResult { - data.get(1..) - .map(|remaining_data| { - ( - RunnableInstruction { - instruction: Instruction::EightBitArithmetic( - EightBitArithmeticInstruction::AddHLAddressToA, - ), - cycles: 8, - }, - remaining_data, - ) - }) - .ok_or(Error::NoData) +fn build_add_hl_address_to_a_data() -> ParseOutput { + ( + RunnableInstruction { + instruction: Instruction::EightBitArithmetic( + EightBitArithmeticInstruction::AddHLAddressToA, + ), + cycles: 8, + }, + 1, + ) } -fn build_add_immediate_to_a_data(data: &[u8]) -> ParseResult { - let opcode = parse::get_opcode_from_data(data)?; - let n = data.get(1).copied().ok_or(Error::NotEnoughArgs(opcode))?; +fn build_add_immediate_to_a_data(data: &View) -> ParseOutput { + let (_opcode, n) = data.get_tuple(); - Ok(( + ( RunnableInstruction { instruction: Instruction::EightBitArithmetic( EightBitArithmeticInstruction::AddImmediateToA { n }, ), cycles: 8, }, - // Guaranteed to exist because the above succeeded - &data[2..], - )) + 2, + ) } -fn build_add_immediate_to_a_with_carry_data(data: &[u8]) -> ParseResult { - let opcode = parse::get_opcode_from_data(data)?; - let n = data.get(1).copied().ok_or(Error::NotEnoughArgs(opcode))?; +fn build_add_immediate_to_a_with_carry_data(data: &View) -> ParseOutput { + let (_opcode, n) = data.get_tuple(); - Ok(( + ( RunnableInstruction { instruction: Instruction::EightBitArithmetic( EightBitArithmeticInstruction::AddImmediateToAWithCarry { n }, ), cycles: 8, }, - &data[2..], - )) + 2, + ) } diff --git a/src/cpu/parse/load16/immediate.rs b/src/cpu/parse/load16/immediate.rs index bf24e81..be647e7 100644 --- a/src/cpu/parse/load16/immediate.rs +++ b/src/cpu/parse/load16/immediate.rs @@ -1,64 +1,63 @@ -use crate::register; use crate::cpu::instructions::load16::SixteenBitLoadInstruction; +use crate::cpu::parse::ParseOutput; use crate::cpu::{ instructions::{Instruction, RunnableInstruction}, parse::{self, Error, OpcodeParser, ParseResult}, }; +use crate::memory::{GetViewTuple, View}; +use crate::register; #[allow(clippy::module_name_repetitions)] pub struct Immediate16BitLoadParser; impl OpcodeParser for Immediate16BitLoadParser { - fn parse_opcode(data: &[u8]) -> ParseResult { - let opcode = parse::get_opcode_from_data(data)?; + fn parse_opcode(data: &View) -> ParseResult { + let opcode = parse::get_opcode_from_data(data); match opcode { - 0x01 => make_load_immediate_data( + 0x01 => Ok(make_load_immediate_data( register::SixteenBit::Combined(register::Combined::BC), data, - ), - 0x11 => make_load_immediate_data( + )), + 0x11 => Ok(make_load_immediate_data( register::SixteenBit::Combined(register::Combined::DE), data, - ), - 0x21 => make_load_immediate_data( + )), + 0x21 => Ok(make_load_immediate_data( register::SixteenBit::Combined(register::Combined::HL), data, - ), - 0x31 => make_load_immediate_data( + )), + 0x31 => Ok(make_load_immediate_data( register::SixteenBit::Single(register::SingleSixteenBit::StackPointer), data, - ), - 0xF8 => make_load_effective_address(register::Combined::HL, data), + )), + 0xF8 => Ok(make_load_effective_address(register::Combined::HL, data)), _ => Err(Error::UnknownOpcode(opcode)), } } } -fn make_load_immediate_data(dst: register::SixteenBit, data: &[u8]) -> ParseResult { - let opcode = parse::get_opcode_from_data(data)?; - let args = data.get(1..=2).ok_or(Error::NotEnoughArgs(opcode))?; +fn make_load_immediate_data(dst: register::SixteenBit, data: &View) -> ParseOutput { + let (_opcode, lower_bytes, upper_bytes) = data.get_tuple(); // manual doesn't state this should be LE, but some inspection of games and googling // indicates it should be. - let value = u16::from_le_bytes([args[0], args[1]]); - Ok(( + let value = u16::from_le_bytes([lower_bytes, upper_bytes]); + ( RunnableInstruction { instruction: Instruction::SixteenBitLoad( SixteenBitLoadInstruction::LoadImmediateToRegister { value, dst }, ), cycles: 12, }, - // guaranteed to succeed given we already took the first two elements as args - &data[3..], - )) + 3, + ) } -fn make_load_effective_address(dst: register::Combined, data: &[u8]) -> ParseResult { - let opcode = parse::get_opcode_from_data(data)?; - let unsigned_value = data.get(1).ok_or(Error::NotEnoughArgs(opcode))?; +fn make_load_effective_address(dst: register::Combined, data: &View) -> ParseOutput { + let (_opcode, unsigned_value) = data.get_tuple(); let signed_value = { - match i8::try_from(*unsigned_value) { + match i8::try_from(unsigned_value) { Ok(value) => value, // Convert this from a two's complement unsigned to a signed Err(_err) => { @@ -72,7 +71,7 @@ fn make_load_effective_address(dst: register::Combined, data: &[u8]) -> ParseRes } }; - Ok(( + ( RunnableInstruction { instruction: Instruction::SixteenBitLoad( SixteenBitLoadInstruction::LoadEffectiveAddress { @@ -82,6 +81,6 @@ fn make_load_effective_address(dst: register::Combined, data: &[u8]) -> ParseRes ), cycles: 12, }, - &data[2..], - )) + 2, + ) } diff --git a/src/cpu/parse/load16/stack.rs b/src/cpu/parse/load16/stack.rs index 351bd17..37bb57f 100644 --- a/src/cpu/parse/load16/stack.rs +++ b/src/cpu/parse/load16/stack.rs @@ -1,7 +1,8 @@ -use crate::register; use crate::cpu::instructions::load16::SixteenBitLoadInstruction; use crate::cpu::instructions::{Instruction, RunnableInstruction}; -use crate::cpu::parse::{self, Error, OpcodeParser, ParseResult}; +use crate::cpu::parse::{self, Error, OpcodeParser, ParseOutput, ParseResult}; +use crate::memory::View; +use crate::register; #[allow(clippy::module_name_repetitions)] pub struct StackLoadParser; @@ -13,47 +14,59 @@ enum Operation { } impl OpcodeParser for StackLoadParser { - fn parse_opcode(data: &[u8]) -> ParseResult { - let opcode = parse::get_opcode_from_data(data)?; + fn parse_opcode(data: &View) -> ParseResult { + let opcode = parse::get_opcode_from_data(data); match opcode { - 0xF5 => make_stack_operation_data(Operation::Push, register::Combined::AF, data), - 0xC5 => make_stack_operation_data(Operation::Push, register::Combined::BC, data), - 0xD5 => make_stack_operation_data(Operation::Push, register::Combined::DE, data), - 0xE5 => make_stack_operation_data(Operation::Push, register::Combined::HL, data), + 0xF5 => Ok(make_stack_operation_data( + Operation::Push, + register::Combined::AF, + )), + 0xC5 => Ok(make_stack_operation_data( + Operation::Push, + register::Combined::BC, + )), + 0xD5 => Ok(make_stack_operation_data( + Operation::Push, + register::Combined::DE, + )), + 0xE5 => Ok(make_stack_operation_data( + Operation::Push, + register::Combined::HL, + )), - 0xF1 => make_stack_operation_data(Operation::Pop, register::Combined::AF, data), - 0xC1 => make_stack_operation_data(Operation::Pop, register::Combined::BC, data), - 0xD1 => make_stack_operation_data(Operation::Pop, register::Combined::DE, data), - 0xE1 => make_stack_operation_data(Operation::Pop, register::Combined::HL, data), + 0xF1 => Ok(make_stack_operation_data( + Operation::Pop, + register::Combined::AF, + )), + 0xC1 => Ok(make_stack_operation_data( + Operation::Pop, + register::Combined::BC, + )), + 0xD1 => Ok(make_stack_operation_data( + Operation::Pop, + register::Combined::DE, + )), + 0xE1 => Ok(make_stack_operation_data( + Operation::Pop, + register::Combined::HL, + )), _ => Err(Error::UnknownOpcode(opcode)), } } } -fn make_stack_operation_data( - operation: Operation, - reg: register::Combined, - data: &[u8], -) -> ParseResult { - data.get(1..) - .map(|remaining_data| { - let instruction = match operation { - Operation::Push => RunnableInstruction { - instruction: Instruction::SixteenBitLoad(SixteenBitLoadInstruction::Push { - src: reg, - }), - cycles: 16, - }, - Operation::Pop => RunnableInstruction { - instruction: Instruction::SixteenBitLoad(SixteenBitLoadInstruction::Pop { - dst: reg, - }), - cycles: 12, - }, - }; +fn make_stack_operation_data(operation: Operation, reg: register::Combined) -> ParseOutput { + let instruction = match operation { + Operation::Push => RunnableInstruction { + instruction: Instruction::SixteenBitLoad(SixteenBitLoadInstruction::Push { src: reg }), + cycles: 16, + }, + Operation::Pop => RunnableInstruction { + instruction: Instruction::SixteenBitLoad(SixteenBitLoadInstruction::Pop { dst: reg }), + cycles: 12, + }, + }; - (instruction, remaining_data) - }) - .ok_or(Error::NoData) + (instruction, 1) } diff --git a/src/cpu/parse/load16/transfer.rs b/src/cpu/parse/load16/transfer.rs index 0133e6c..0ec9dd8 100644 --- a/src/cpu/parse/load16/transfer.rs +++ b/src/cpu/parse/load16/transfer.rs @@ -1,23 +1,24 @@ -use crate::register; use crate::cpu::instructions::load16::SixteenBitLoadInstruction; use crate::cpu::instructions::Instruction; +use crate::cpu::parse::ParseOutput; use crate::cpu::{ instructions::RunnableInstruction, parse::{self, Error, OpcodeParser, ParseResult}, }; +use crate::memory::View; +use crate::register; pub struct Between16BitRegisterParser; impl OpcodeParser for Between16BitRegisterParser { - /// Parses an opcode that transfers an 8bit values between single 8 bit registers. - fn parse_opcode(data: &[u8]) -> ParseResult { - let opcode = parse::get_opcode_from_data(data)?; + /// Parses an opcode that transfers an 16bit values between single 16 bit registers. + fn parse_opcode(data: &View) -> ParseResult { + let opcode = parse::get_opcode_from_data(data); match opcode { - 0xF9 => make_load_between_register_data( + 0xF9 => Ok(make_load_between_register_data( register::SixteenBit::Single(register::SingleSixteenBit::StackPointer), register::SixteenBit::Combined(register::Combined::HL), - data, - ), + )), _ => Err(Error::UnknownOpcode(opcode)), } } @@ -26,19 +27,14 @@ impl OpcodeParser for Between16BitRegisterParser { fn make_load_between_register_data( dst: register::SixteenBit, src: register::SixteenBit, - data: &[u8], -) -> ParseResult { - data.get(1..) - .map(|remaining_data| { - ( - RunnableInstruction { - instruction: Instruction::SixteenBitLoad( - SixteenBitLoadInstruction::LoadBetweenRegisters { dst, src }, - ), - cycles: 4, - }, - remaining_data, - ) - }) - .ok_or(Error::NoData) +) -> ParseOutput { + ( + RunnableInstruction { + instruction: Instruction::SixteenBitLoad( + SixteenBitLoadInstruction::LoadBetweenRegisters { dst, src }, + ), + cycles: 4, + }, + 1, + ) } diff --git a/src/cpu/parse/load8/immediate.rs b/src/cpu/parse/load8/immediate.rs index fbb4783..97d9cfc 100644 --- a/src/cpu/parse/load8/immediate.rs +++ b/src/cpu/parse/load8/immediate.rs @@ -1,44 +1,42 @@ -use crate::register; use crate::cpu::instructions::load8::EightBitLoadInstruction; use crate::cpu::instructions::Instruction; +use crate::cpu::parse::ParseOutput; use crate::cpu::{ instructions::RunnableInstruction, parse::{self, Error, OpcodeParser, ParseResult}, }; +use crate::memory::{GetViewTuple, View}; +use crate::register; #[allow(clippy::module_name_repetitions)] pub struct Immediate8BitLoadParser; impl OpcodeParser for Immediate8BitLoadParser { /// Parses an opcode that will transfer an immediate 8 bit value into a single 8 bit register. - fn parse_opcode(data: &[u8]) -> ParseResult { - let opcode = parse::get_opcode_from_data(data)?; + fn parse_opcode(data: &View) -> ParseResult { + let opcode = parse::get_opcode_from_data(data); match opcode { - 0x3E => make_load_immediate_data(register::SingleEightBit::A, data), - 0x06 => make_load_immediate_data(register::SingleEightBit::B, data), - 0x0E => make_load_immediate_data(register::SingleEightBit::C, data), - 0x16 => make_load_immediate_data(register::SingleEightBit::D, data), - 0x1E => make_load_immediate_data(register::SingleEightBit::E, data), - 0x26 => make_load_immediate_data(register::SingleEightBit::H, data), - 0x2E => make_load_immediate_data(register::SingleEightBit::L, data), + 0x3E => Ok(make_load_immediate_data(register::SingleEightBit::A, data)), + 0x06 => Ok(make_load_immediate_data(register::SingleEightBit::B, data)), + 0x0E => Ok(make_load_immediate_data(register::SingleEightBit::C, data)), + 0x16 => Ok(make_load_immediate_data(register::SingleEightBit::D, data)), + 0x1E => Ok(make_load_immediate_data(register::SingleEightBit::E, data)), + 0x26 => Ok(make_load_immediate_data(register::SingleEightBit::H, data)), + 0x2E => Ok(make_load_immediate_data(register::SingleEightBit::L, data)), _ => Err(Error::UnknownOpcode(opcode)), } } } -fn make_load_immediate_data(register: register::SingleEightBit, data: &[u8]) -> ParseResult { - let opcode = parse::get_opcode_from_data(data)?; - let value = data.get(1).ok_or(Error::NotEnoughArgs(opcode))?; +fn make_load_immediate_data(register: register::SingleEightBit, data: &View) -> ParseOutput { + let (_opcode, value) = data.get_tuple(); - Ok(( + ( RunnableInstruction { instruction: Instruction::EightBitLoad( - EightBitLoadInstruction::LoadImmediateToRegister { - register, - value: *value, - }, + EightBitLoadInstruction::LoadImmediateToRegister { register, 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 @@ -46,7 +44,6 @@ fn make_load_immediate_data(register: register::SingleEightBit, data: &[u8]) -> // more of the instructions complete cycles: 8, }, - // This can't fail if the previous .get(1) succeeded - &data[2..], - )) + 2, + ) } diff --git a/src/cpu/parse/load8/memory.rs b/src/cpu/parse/load8/memory.rs index 58cee3d..2e8a595 100644 --- a/src/cpu/parse/load8/memory.rs +++ b/src/cpu/parse/load8/memory.rs @@ -1,14 +1,15 @@ -use crate::register; use crate::cpu::instructions::load8::EightBitLoadInstruction; use crate::cpu::instructions::{Instruction, RunnableInstruction}; -use crate::cpu::parse::{self, Error, OpcodeParser, ParseResult}; +use crate::cpu::parse::{self, Error, OpcodeParser, ParseOutput, ParseResult}; +use crate::memory::{GetViewTuple, View}; +use crate::register; #[allow(clippy::module_name_repetitions)] pub struct Memory8BitLoadParser; impl OpcodeParser for Memory8BitLoadParser { // Parses a single 8 bit instruction to load an 8 bit value to/from memory - fn parse_opcode(data: &[u8]) -> ParseResult { + fn parse_opcode(data: &View) -> ParseResult { let parse_funcs = &[ parse_load_from_register_to_address, parse_load_from_address_to_register, @@ -24,155 +25,158 @@ impl OpcodeParser for Memory8BitLoadParser { } } - let opcode = parse::get_opcode_from_data(data)?; - + let opcode = parse::get_opcode_from_data(data); Err(Error::UnknownOpcode(opcode)) } } -fn parse_load_from_register_to_address(data: &[u8]) -> ParseResult { - let opcode = parse::get_opcode_from_data(data)?; +fn parse_load_from_register_to_address(data: &View) -> ParseResult { + let opcode = parse::get_opcode_from_data(data); match opcode { - 0x02 => { - make_load_to_register_address(register::Combined::BC, register::SingleEightBit::A, data) - } - 0x12 => { - make_load_to_register_address(register::Combined::DE, register::SingleEightBit::A, data) - } - 0x70 => { - make_load_to_register_address(register::Combined::HL, register::SingleEightBit::B, data) - } - 0x71 => { - make_load_to_register_address(register::Combined::HL, register::SingleEightBit::C, data) - } - 0x72 => { - make_load_to_register_address(register::Combined::HL, register::SingleEightBit::D, data) - } - 0x73 => { - make_load_to_register_address(register::Combined::HL, register::SingleEightBit::E, data) - } - 0x74 => { - make_load_to_register_address(register::Combined::HL, register::SingleEightBit::H, data) - } - 0x75 => { - make_load_to_register_address(register::Combined::HL, register::SingleEightBit::L, data) - } - 0x77 => { - make_load_to_register_address(register::Combined::HL, register::SingleEightBit::A, data) - } + 0x02 => Ok(make_load_to_register_address( + register::Combined::BC, + register::SingleEightBit::A, + )), + 0x12 => Ok(make_load_to_register_address( + register::Combined::DE, + register::SingleEightBit::A, + )), + 0x70 => Ok(make_load_to_register_address( + register::Combined::HL, + register::SingleEightBit::B, + )), + 0x71 => Ok(make_load_to_register_address( + register::Combined::HL, + register::SingleEightBit::C, + )), + 0x72 => Ok(make_load_to_register_address( + register::Combined::HL, + register::SingleEightBit::D, + )), + 0x73 => Ok(make_load_to_register_address( + register::Combined::HL, + register::SingleEightBit::E, + )), + 0x74 => Ok(make_load_to_register_address( + register::Combined::HL, + register::SingleEightBit::H, + )), + 0x75 => Ok(make_load_to_register_address( + register::Combined::HL, + register::SingleEightBit::L, + )), + 0x77 => Ok(make_load_to_register_address( + register::Combined::HL, + register::SingleEightBit::A, + )), _ => Err(Error::UnknownOpcode(opcode)), } } -fn parse_load_from_address_to_register(data: &[u8]) -> ParseResult { - let opcode = parse::get_opcode_from_data(data)?; +fn parse_load_from_address_to_register(data: &View) -> ParseResult { + let opcode = parse::get_opcode_from_data(data); match opcode { - 0x0A => make_load_from_register_address( + 0x0A => Ok(make_load_from_register_address( register::SingleEightBit::A, register::Combined::BC, - data, - ), - 0x1A => make_load_from_register_address( + )), + 0x1A => Ok(make_load_from_register_address( register::SingleEightBit::A, register::Combined::DE, - data, - ), - 0x46 => make_load_from_register_address( + )), + 0x46 => Ok(make_load_from_register_address( register::SingleEightBit::B, register::Combined::HL, - data, - ), - 0x4E => make_load_from_register_address( + )), + 0x4E => Ok(make_load_from_register_address( register::SingleEightBit::C, register::Combined::HL, - data, - ), - 0x56 => make_load_from_register_address( + )), + 0x56 => Ok(make_load_from_register_address( register::SingleEightBit::D, register::Combined::HL, - data, - ), - 0x5E => make_load_from_register_address( + )), + 0x5E => Ok(make_load_from_register_address( register::SingleEightBit::E, register::Combined::HL, - data, - ), - 0x66 => make_load_from_register_address( + )), + 0x66 => Ok(make_load_from_register_address( register::SingleEightBit::H, register::Combined::HL, - data, - ), - 0x6E => make_load_from_register_address( + )), + 0x6E => Ok(make_load_from_register_address( register::SingleEightBit::L, register::Combined::HL, - data, - ), - 0x7E => make_load_from_register_address( + )), + 0x7E => Ok(make_load_from_register_address( register::SingleEightBit::A, register::Combined::HL, - data, - ), + )), - 0xF2 => make_load_from_memory_relative_to_io_register_start( + 0xF2 => Ok(make_load_from_memory_relative_to_io_register_start( register::SingleEightBit::C, register::SingleEightBit::A, - data, - ), - 0xE2 => make_load_to_memory_relative_to_io_register_start( + )), + 0xE2 => Ok(make_load_to_memory_relative_to_io_register_start( register::SingleEightBit::C, register::SingleEightBit::A, - data, + )), + 0xF0 => Ok( + make_load_from_memory_relative_to_io_register_start_by_immediate( + register::SingleEightBit::A, + data, + ), ), - 0xF0 => make_load_from_memory_relative_to_io_register_start_by_immediate( - register::SingleEightBit::A, - data, - ), - 0xE0 => make_load_to_memory_relative_to_io_register_start_by_immediate( - register::SingleEightBit::A, - data, + 0xE0 => Ok( + make_load_to_memory_relative_to_io_register_start_by_immediate( + register::SingleEightBit::A, + data, + ), ), _ => Err(Error::UnknownOpcode(opcode)), } } -fn parse_load_immediate_instructions(data: &[u8]) -> ParseResult { - let opcode = parse::get_opcode_from_data(data)?; +fn parse_load_immediate_instructions(data: &View) -> ParseResult { + let opcode = parse::get_opcode_from_data(data); match opcode { - 0x36 => make_load_n_to_hl_address(data), - 0xFA => make_load_from_immediate_address(register::SingleEightBit::A, data), - 0xEA => make_load_to_immediate_address(register::SingleEightBit::A, data), + 0x36 => Ok(make_load_n_to_hl_address(data)), + 0xFA => Ok(make_load_from_immediate_address( + register::SingleEightBit::A, + data, + )), + 0xEA => Ok(make_load_to_immediate_address( + register::SingleEightBit::A, + data, + )), _ => Err(Error::UnknownOpcode(opcode)), } } -fn parse_load_from_register_to_address_and_do_arithmetic(data: &[u8]) -> ParseResult { - let opcode = parse::get_opcode_from_data(data)?; +fn parse_load_from_register_to_address_and_do_arithmetic(data: &View) -> ParseResult { + let opcode = parse::get_opcode_from_data(data); match opcode { - 0x32 => make_load_to_address_and_do_arithmetic( + 0x32 => Ok(make_load_to_address_and_do_arithmetic( register::Combined::HL, register::SingleEightBit::A, |dst, src| EightBitLoadInstruction::LoadToRegisterAddressThenDec { dst, src }, - data, - ), - 0x22 => make_load_to_address_and_do_arithmetic( + )), + 0x22 => Ok(make_load_to_address_and_do_arithmetic( register::Combined::HL, register::SingleEightBit::A, |dst, src| EightBitLoadInstruction::LoadToRegisterAddressThenInc { dst, src }, - data, - ), - 0x3A => make_load_from_address_and_do_arithmetic( + )), + 0x3A => Ok(make_load_from_address_and_do_arithmetic( register::SingleEightBit::A, register::Combined::HL, |dst, src| EightBitLoadInstruction::LoadFromRegisterAddressThenDec { dst, src }, - data, - ), - 0x2A => make_load_from_address_and_do_arithmetic( + )), + 0x2A => Ok(make_load_from_address_and_do_arithmetic( register::SingleEightBit::A, register::Combined::HL, |dst, src| EightBitLoadInstruction::LoadFromRegisterAddressThenInc { dst, src }, - data, - ), + )), _ => Err(Error::UnknownOpcode(opcode)), } } @@ -180,184 +184,155 @@ fn parse_load_from_register_to_address_and_do_arithmetic(data: &[u8]) -> ParseRe fn make_load_from_register_address( dst: register::SingleEightBit, src: register::Combined, - data: &[u8], -) -> ParseResult { - data.get(1..) - .map(|remaining_data| { - ( - RunnableInstruction { - instruction: Instruction::EightBitLoad( - EightBitLoadInstruction::LoadFromRegisterAddress { src, dst }, - ), - cycles: 8, - }, - remaining_data, - ) - }) - .ok_or(Error::NoData) +) -> ParseOutput { + ( + RunnableInstruction { + instruction: Instruction::EightBitLoad( + EightBitLoadInstruction::LoadFromRegisterAddress { src, dst }, + ), + cycles: 8, + }, + 1, + ) } fn make_load_to_register_address( dst: register::Combined, src: register::SingleEightBit, - data: &[u8], -) -> ParseResult { - data.get(1..) - .map(|remaining_data| { - ( - RunnableInstruction { - instruction: Instruction::EightBitLoad( - EightBitLoadInstruction::LoadToRegisterAddress { src, dst }, - ), - cycles: 8, - }, - remaining_data, - ) - }) - .ok_or(Error::NoData) +) -> ParseOutput { + ( + RunnableInstruction { + instruction: Instruction::EightBitLoad( + EightBitLoadInstruction::LoadToRegisterAddress { src, dst }, + ), + cycles: 8, + }, + 1, + ) } -fn make_load_from_immediate_address(dst: register::SingleEightBit, data: &[u8]) -> ParseResult { - let opcode = parse::get_opcode_from_data(data)?; - let args = data.get(1..=2).ok_or(Error::NotEnoughArgs(opcode))?; +fn make_load_from_immediate_address(dst: register::SingleEightBit, data: &View) -> ParseOutput { + let (_opcode, lower_bytes, upper_bytes) = data.get_tuple(); // The manual states that the LSB of the address is specified first (i.e. little endian) - let src_address = u16::from_le_bytes([args[0], args[1]]); - Ok(( + let src_address = u16::from_le_bytes([lower_bytes, upper_bytes]); + ( RunnableInstruction { instruction: Instruction::EightBitLoad( EightBitLoadInstruction::LoadFromImmediateAddress { src_address, dst }, ), cycles: 16, }, - // This can't fail if get(1..=2) passed - &data[3..], - )) + 3, + ) } -fn make_load_to_immediate_address(src: register::SingleEightBit, data: &[u8]) -> ParseResult { - let opcode = parse::get_opcode_from_data(data)?; - let args = data.get(1..=2).ok_or(Error::NotEnoughArgs(opcode))?; +fn make_load_to_immediate_address(src: register::SingleEightBit, data: &View) -> ParseOutput { + let (_opcode, lower_bytes, upper_bytes) = data.get_tuple(); // The manual states that the LSB of the address is specified first (i.e. little endian) - let dst_address = u16::from_le_bytes([args[0], args[1]]); - Ok(( + let dst_address = u16::from_le_bytes([lower_bytes, upper_bytes]); + ( RunnableInstruction { instruction: Instruction::EightBitLoad( EightBitLoadInstruction::LoadToImmediateAddress { src, dst_address }, ), cycles: 16, }, - // This can't fail if get(1..=2) passed - &data[3..], - )) + 3, + ) } -fn make_load_n_to_hl_address(data: &[u8]) -> ParseResult { - let opcode = parse::get_opcode_from_data(data)?; - let value = data.get(1).ok_or(Error::NotEnoughArgs(opcode))?; - Ok(( +fn make_load_n_to_hl_address(data: &View) -> ParseOutput { + let (_opcode, value) = data.get_tuple(); + ( RunnableInstruction { instruction: Instruction::EightBitLoad(EightBitLoadInstruction::LoadnToHLAddress { - value: *value, + value, }), cycles: 12, }, - // This can't fail if get(1) succeeeded - &data[2..], - )) + 2, + ) } fn make_load_from_memory_relative_to_io_register_start( offset_register: register::SingleEightBit, dst: register::SingleEightBit, - data: &[u8], -) -> ParseResult { - data.get(1..) - .map(|remaining_data| { - ( - RunnableInstruction { - instruction: Instruction::EightBitLoad( - EightBitLoadInstruction::LoadFromMemoryRelativeToIORegisterStart { - offset_register, - dst, - }, - ), - cycles: 8, +) -> ParseOutput { + ( + RunnableInstruction { + instruction: Instruction::EightBitLoad( + EightBitLoadInstruction::LoadFromMemoryRelativeToIORegisterStart { + offset_register, + dst, }, - // guaranteed to succeed given we found the opcode - remaining_data, - ) - }) - .ok_or(Error::NoData) + ), + cycles: 8, + }, + // guaranteed to succeed given we found the opcode + 1, + ) } fn make_load_to_memory_relative_to_io_register_start( offset_register: register::SingleEightBit, src: register::SingleEightBit, - data: &[u8], -) -> ParseResult { - data.get(1..) - .map(|remaining_data| { - ( - RunnableInstruction { - instruction: Instruction::EightBitLoad( - EightBitLoadInstruction::LoadToMemoryRelativeToIORegisterStart { - src, - offset_register, - }, - ), - cycles: 8, +) -> ParseOutput { + ( + RunnableInstruction { + instruction: Instruction::EightBitLoad( + EightBitLoadInstruction::LoadToMemoryRelativeToIORegisterStart { + src, + offset_register, }, - // guaranteed to succeed given we found the opcode - remaining_data, - ) - }) - .ok_or(Error::NoData) + ), + cycles: 8, + }, + // guaranteed to succeed given we found the opcode + 1, + ) } fn make_load_to_memory_relative_to_io_register_start_by_immediate( src: register::SingleEightBit, - data: &[u8], -) -> ParseResult { - let opcode = parse::get_opcode_from_data(data)?; - let value = data.get(1).ok_or(Error::NotEnoughArgs(opcode))?; + data: &View, +) -> ParseOutput { + let (_opcode, offset) = data.get_tuple(); - Ok(( + ( RunnableInstruction { instruction: Instruction::EightBitLoad( EightBitLoadInstruction::LoadToMemoryRelativeToIORegisterStartByImmediate { src, - offset: *value, + offset, }, ), // guaranteed to succeed given we got the value cycles: 12, }, - &data[2..], - )) + 2, + ) } fn make_load_from_memory_relative_to_io_register_start_by_immediate( dst: register::SingleEightBit, - data: &[u8], -) -> ParseResult { - let opcode = parse::get_opcode_from_data(data)?; - let value = data.get(1).ok_or(Error::NotEnoughArgs(opcode))?; + data: &View, +) -> ParseOutput { + let (_opcode, offset) = data.get_tuple(); - Ok(( + ( RunnableInstruction { instruction: Instruction::EightBitLoad( EightBitLoadInstruction::LoadFromMemoryRelativeToIORegisterStartByImmediate { - offset: *value, + offset, dst, }, ), cycles: 12, }, - // guaranteed to succeed given we got the value - &data[2..], - )) + 2, + ) } /// `make_load_to_address_and_do_arithmetic` will make one of the instructions that load to memory and either increment @@ -368,9 +343,8 @@ fn make_load_to_address_and_do_arithmetic< dst: register::Combined, src: register::SingleEightBit, make: F, - data: &[u8], -) -> ParseResult { - make_load_and_do_arithmetic(|| make(dst, src), data) +) -> ParseOutput { + make_load_and_do_arithmetic(|| make(dst, src)) } /// `make_load_to_address_and_do_arithmetic` will make one of the instructions that load from memory and either increment @@ -381,28 +355,20 @@ fn make_load_from_address_and_do_arithmetic< dst: register::SingleEightBit, src: register::Combined, make: F, - data: &[u8], -) -> ParseResult { - make_load_and_do_arithmetic(|| make(dst, src), data) +) -> ParseOutput { + make_load_and_do_arithmetic(|| make(dst, src)) } /// `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(); +fn make_load_and_do_arithmetic EightBitLoadInstruction>(make: F) -> ParseOutput { + let load_instruction = make(); - ( - RunnableInstruction { - instruction: Instruction::EightBitLoad(load_instruction), - cycles: 8, - }, - remaining_data, - ) - }) - .ok_or(Error::NoData) + ( + RunnableInstruction { + instruction: Instruction::EightBitLoad(load_instruction), + cycles: 8, + }, + 1, + ) } diff --git a/src/cpu/parse/load8/transfer.rs b/src/cpu/parse/load8/transfer.rs index a7ad8c6..7517e65 100644 --- a/src/cpu/parse/load8/transfer.rs +++ b/src/cpu/parse/load8/transfer.rs @@ -1,78 +1,80 @@ -use crate::register; use crate::cpu::instructions::load8::EightBitLoadInstruction; use crate::cpu::instructions::Instruction; +use crate::cpu::parse::ParseOutput; use crate::cpu::{ instructions::RunnableInstruction, parse::{self, Error, OpcodeParser, ParseResult}, }; +use crate::memory::View; +use crate::register; pub struct Between8BitRegisterParser; impl OpcodeParser for Between8BitRegisterParser { /// Parses an opcode that transfers an 8bit values between single 8 bit registers. - fn parse_opcode(data: &[u8]) -> ParseResult { + fn parse_opcode(data: &View) -> ParseResult { // I don't normally like doing this but this function was ridiculous with the line length wrapping // caused by not doing it #[allow(clippy::enum_glob_use)] use register::SingleEightBit::*; - let opcode = parse::get_opcode_from_data(data)?; + let opcode = parse::get_opcode_from_data(data); match opcode { - 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), + 0x7f => Ok(make_load_between_register_data(A, A)), + 0x78 => Ok(make_load_between_register_data(A, B)), + 0x79 => Ok(make_load_between_register_data(A, C)), + 0x7A => Ok(make_load_between_register_data(A, D)), + 0x7B => Ok(make_load_between_register_data(A, E)), + 0x7C => Ok(make_load_between_register_data(A, H)), + 0x7D => Ok(make_load_between_register_data(A, L)), - 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), + 0x47 => Ok(make_load_between_register_data(B, A)), + 0x40 => Ok(make_load_between_register_data(B, B)), + 0x41 => Ok(make_load_between_register_data(B, C)), + 0x42 => Ok(make_load_between_register_data(B, D)), + 0x43 => Ok(make_load_between_register_data(B, E)), + 0x44 => Ok(make_load_between_register_data(B, H)), + 0x45 => Ok(make_load_between_register_data(B, L)), - 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), + 0x4F => Ok(make_load_between_register_data(C, A)), + 0x48 => Ok(make_load_between_register_data(C, B)), + 0x49 => Ok(make_load_between_register_data(C, C)), + 0x4A => Ok(make_load_between_register_data(C, D)), + 0x4B => Ok(make_load_between_register_data(C, E)), + 0x4C => Ok(make_load_between_register_data(C, H)), + 0x4D => Ok(make_load_between_register_data(C, L)), - 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), + 0x57 => Ok(make_load_between_register_data(D, A)), + 0x50 => Ok(make_load_between_register_data(D, B)), + 0x51 => Ok(make_load_between_register_data(D, C)), + 0x52 => Ok(make_load_between_register_data(D, D)), + 0x53 => Ok(make_load_between_register_data(D, E)), + 0x54 => Ok(make_load_between_register_data(D, H)), + 0x55 => Ok(make_load_between_register_data(D, L)), - 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), + 0x5F => Ok(make_load_between_register_data(E, A)), + 0x58 => Ok(make_load_between_register_data(E, B)), + 0x59 => Ok(make_load_between_register_data(E, C)), + 0x5A => Ok(make_load_between_register_data(E, D)), + 0x5B => Ok(make_load_between_register_data(E, E)), + 0x5C => Ok(make_load_between_register_data(E, H)), + 0x5D => Ok(make_load_between_register_data(E, L)), - 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), + 0x67 => Ok(make_load_between_register_data(H, A)), + 0x60 => Ok(make_load_between_register_data(H, B)), + 0x61 => Ok(make_load_between_register_data(H, C)), + 0x62 => Ok(make_load_between_register_data(H, D)), + 0x63 => Ok(make_load_between_register_data(H, E)), + 0x64 => Ok(make_load_between_register_data(H, H)), + 0x65 => Ok(make_load_between_register_data(H, L)), - 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), + 0x6F => Ok(make_load_between_register_data(L, A)), + 0x68 => Ok(make_load_between_register_data(L, B)), + 0x69 => Ok(make_load_between_register_data(L, C)), + 0x6A => Ok(make_load_between_register_data(L, D)), + 0x6B => Ok(make_load_between_register_data(L, E)), + 0x6c => Ok(make_load_between_register_data(L, H)), + 0x6D => Ok(make_load_between_register_data(L, L)), _ => Err(Error::UnknownOpcode(opcode)), } @@ -82,19 +84,15 @@ impl OpcodeParser for Between8BitRegisterParser { fn make_load_between_register_data( dst: register::SingleEightBit, src: register::SingleEightBit, - data: &[u8], -) -> ParseResult { - data.get(1..) - .map(|remaining_data| { - ( - RunnableInstruction { - instruction: Instruction::EightBitLoad( - EightBitLoadInstruction::LoadBetweenRegisters { dst, src }, - ), - cycles: 4, - }, - remaining_data, - ) - }) - .ok_or(Error::NoData) +) -> ParseOutput { + ( + RunnableInstruction { + instruction: Instruction::EightBitLoad(EightBitLoadInstruction::LoadBetweenRegisters { + dst, + src, + }), + cycles: 4, + }, + 1, + ) } diff --git a/src/memory.rs b/src/memory.rs index c7f3f76..1c6ceff 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -1,9 +1,12 @@ +pub use self::view::{GetTuple as GetViewTuple, View}; use thiserror::Error; /// The location at which the IO registers start in memory. (see 2.13.1 of the manual) pub const IO_REGISTER_START_ADDRESS: usize = 0xFF00; const MAX_MEMORY_ADDRESS: usize = 0xFFFF; +mod view; + #[derive(Error, Debug)] pub enum Error { #[error("attempted to fetch from invalid address {0:X}")] @@ -34,12 +37,12 @@ impl Memory { .ok_or(Error::GetInvalidAddress(index)) } - pub fn get_from(&self, index: usize) -> Result<&[u8], Error> { + pub fn get_from(&self, index: usize) -> Result { if index > self.data.len() { return Err(Error::GetInvalidAddress(index)); } - Ok(&self.data[index..]) + Ok(View::new(self, index)) } /// `set` will get a single byte value at the given address, and return the new value diff --git a/src/memory/view.rs b/src/memory/view.rs new file mode 100644 index 0000000..ad76998 --- /dev/null +++ b/src/memory/view.rs @@ -0,0 +1,303 @@ +use super::Memory; + +/// Holds a view of memory, starting at some offset +pub struct View<'a> { + underlying_memory: &'a [u8], + start: usize, +} + +impl<'a> View<'a> { + pub(crate) fn new(memory: &'a Memory, start: usize) -> Self { + Self { + underlying_memory: &memory.data, + start, + } + } + + pub(crate) fn new_from_data(memory: &'a [u8], start: usize) -> Self { + Self { + underlying_memory: memory, + start, + } + } + + /// Get the first value in this view + pub fn get(&self) -> u8 { + self.get_index(0) + } + + /// Get the nth value in this view. This will wrap around if index is too large + pub fn get_index(&self, index: usize) -> u8 { + let data_length = self.underlying_memory.len(); + self.underlying_memory[(self.start + index) % data_length] + } + + /// Construct a new view that has been advanced by `by` values. This will wrap around if index is too large + pub fn advance(self, by: usize) -> Self { + let data_length = self.underlying_memory.len(); + let next_idx = (self.start + by) % data_length; + View { + underlying_memory: self.underlying_memory, + start: next_idx, + } + } +} + +pub trait GetTuple { + /// Get the type this is implemented on as a tuple, as dictated by type T + fn get_tuple(&self) -> T; +} + +impl GetTuple for View<'_> { + /// Get the first n elements in this view (where n is the length of the tuple in type T). + /// This is implemented for all tuples up to length 6. + /// + /// # Examples + /// ```ignore + /// # // This can't run because memory isn't public + /// + /// let mut memory = Memory::new(); + /// memory.set(0xF0, 10).expect("failed to set memory value"); + /// memory.set(0xF1, 11).expect("failed to set memory value"); + /// memory.set(0xF2, 12).expect("failed to set memory value"); + /// memory.set(0xF3, 13).expect("failed to set memory value"); + + /// let view = View::new(&memory, 0xF0); + /// assert_eq!(10, view.get_index(0)); + /// assert_eq!(11, view.get_index(1)); + /// assert_eq!(12, view.get_index(2)); + /// assert_eq!(13, view.get_index(3)); + /// ``` + fn get_tuple(&self) -> T { + T::into_tuple(self) + } +} + +/** + * The following macro implementation is "heavily inspired" (read: stolen with many tweaks) by itertools' `tuples` method. + * + * The implementation is + * + * itertools is licensed under the MIT license, which is expanded upon here + * + * Copyright (c) 2015 + * + * Permission is hereby granted, free of charge, to any + * person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the + * Software without restriction, including without + * limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software + * is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice + * shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF + * ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + * TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT + * SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR + * IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +macro_rules! get_for_each_ident{ + ($arr:ident, $n: expr,) => {}; + ($arr:ident, $($i:ident,)*) => { + get_for_each_ident!($arr, 0, $($i,)*); + }; + ($view:ident, $n: expr, $i0:ident, $($i:ident,)*) => { + $i0 = $view.get_index($n); + get_for_each_ident!($view, $n + 1, $($i,)*); + }; +} + +/// Convert an identity and a tokentree to just that tree. Useful to abuse wildcard semantics. +macro_rules! ignore_ident{ + ($id:ident, $($t:tt)*) => {$($t)*}; +} + +// this trait could _PROBABLY_ be converted into From<&View<'_>> with some trickery but the lifetimes +// are making my head hurt and I don't think it's worth it for an implementation detail like this +trait ViewIntoTuple { + fn into_tuple(view: &View<'_>) -> Self; +} + +/* +A implementation implemtnation of what the below macro will implement + +impl TupleGetFrom for (T, T, T) { + fn get_from(arr: &[T]) -> Self { + let (mut a, mut b, mut c) = Self::default(); + a = arr[0]; + b = arr[1]; + c = arr[2]; + + (a, b, c) + } +} +*/ + +macro_rules! impl_tuple_get_from { + ($_: ident, $__: ident,) => {}; + // We recursively implement this for all tuples > 2 + ($_: ident, $($idents: ident,)+) => { + impl_tuple_get_from!($($idents,)*); + + // implement the trait - we abuse the wildcard here with ignore_ident to implement on the appropriate length + // u8 tuple - the idea here is this becomes (ignore_ident!(a, u8), ignore_ident!(b, u8), ...), which expands + // to just (u8, u8...) + impl ViewIntoTuple for ($(ignore_ident!($idents, u8),)*) { + fn into_tuple(view: &View<'_>) -> Self { + // Create a tuple of the correct length; View uses u8s, which has a default + // (though this could be implemented generically with little effort) + #[allow(unused_assignments)] + let ($(mut $idents, )*) = Self::default(); + // Perform a View::get for each identity + get_for_each_ident!(view, $($idents, )*); + ($($idents, )*) + } + } + } +} + +// and kick off the implementation here +impl_tuple_get_from!(ignore, a, b, c, d, e, f,); + +#[cfg(test)] +mod tests { + use crate::memory::MAX_MEMORY_ADDRESS; + + use super::*; + + #[test] + fn test_can_get_single_value() { + let mut memory = Memory::new(); + memory.set(0xF0, 10).expect("failed to set memory value"); + + let view = View::new(&memory, 0xF0); + assert_eq!(10, view.get()); + } + + #[test] + fn test_can_get_single_value_by_index() { + let mut memory = Memory::new(); + memory.set(0xF0, 10).expect("failed to set memory value"); + memory.set(0xF1, 11).expect("failed to set memory value"); + memory.set(0xF2, 12).expect("failed to set memory value"); + memory.set(0xF3, 13).expect("failed to set memory value"); + + let view = View::new(&memory, 0xF0); + assert_eq!(10, view.get_index(0)); + assert_eq!(11, view.get_index(1)); + assert_eq!(12, view.get_index(2)); + assert_eq!(13, view.get_index(3)); + } + + #[test] + fn get_can_advance_view() { + let mut memory = Memory::new(); + memory.set(0xF0, 10).expect("failed to set memory value"); + memory.set(0xF1, 11).expect("failed to set memory value"); + memory.set(0xF2, 12).expect("failed to set memory value"); + memory.set(0xF3, 13).expect("failed to set memory value"); + + let view = View::new(&memory, 0xF0); + let advanced_view = view.advance(1); + assert_eq!(11, advanced_view.get_index(0)); + assert_eq!(12, advanced_view.get_index(1)); + assert_eq!(13, advanced_view.get_index(2)); + } + + #[test] + fn get_can_advance_view_with_wraparound() { + let mut memory = Memory::new(); + memory.set(0x01, 10).expect("failed to set memory value"); + + let view = View::new(&memory, MAX_MEMORY_ADDRESS); + let advanced_view = view.advance(2); + assert_eq!(10, advanced_view.get()); + } + + #[test] + fn test_can_get_two_values() { + let mut memory = Memory::new(); + memory.set(0xF0, 10).expect("failed to set memory value"); + memory.set(0xF1, 11).expect("failed to set memory value"); + memory.set(0xF2, 12).expect("failed to set memory value"); + memory.set(0xF3, 13).expect("failed to set memory value"); + + let view = View::new(&memory, 0xF0); + let (a, b) = view.get_tuple(); + assert_eq!(a, 10); + assert_eq!(b, 11); + } + + #[test] + fn test_can_get_three_values() { + let mut memory = Memory::new(); + memory.set(0xF0, 10).expect("failed to set memory value"); + memory.set(0xF1, 11).expect("failed to set memory value"); + memory.set(0xF2, 12).expect("failed to set memory value"); + memory.set(0xF3, 13).expect("failed to set memory value"); + + let view = View::new(&memory, 0xF0); + let (a, b, c) = view.get_tuple(); + assert_eq!(a, 10); + assert_eq!(b, 11); + assert_eq!(c, 12); + } + + #[test] + fn test_can_get_four_values() { + let mut memory = Memory::new(); + memory.set(0xF0, 10).expect("failed to set memory value"); + memory.set(0xF1, 11).expect("failed to set memory value"); + memory.set(0xF2, 12).expect("failed to set memory value"); + memory.set(0xF3, 13).expect("failed to set memory value"); + + let view = View::new(&memory, 0xF0); + let (a, b, c, d) = view.get_tuple(); + assert_eq!(a, 10); + assert_eq!(b, 11); + assert_eq!(c, 12); + assert_eq!(d, 13); + } + + #[test] + fn test_getting_more_values_than_in_view_wraps_around() { + let mut memory = Memory::new(); + memory.set(0, 100).expect("failed to set memory value"); + memory + .set(MAX_MEMORY_ADDRESS - 4, 10) + .expect("failed to set memory value"); + memory + .set(MAX_MEMORY_ADDRESS - 3, 11) + .expect("failed to set memory value"); + memory + .set(MAX_MEMORY_ADDRESS - 2, 12) + .expect("failed to set memory value"); + memory + .set(MAX_MEMORY_ADDRESS - 1, 13) + .expect("failed to set memory value"); + memory + .set(MAX_MEMORY_ADDRESS, 14) + .expect("failed to set memory value"); + + let view = View::new(&memory, MAX_MEMORY_ADDRESS - 4); + + #[allow(clippy::many_single_char_names)] + let (a, b, c, d, e, f) = view.get_tuple(); + assert_eq!(a, 10); + assert_eq!(b, 11); + assert_eq!(c, 12); + assert_eq!(d, 13); + assert_eq!(e, 14); + assert_eq!(f, 100); + } +}