Restructure instruction enums to be categorized based on the type of instruction
This helps break up some of the long match statement in Processor::run, but construction does become a bit annoying.jsmoo
parent
16bc8833d5
commit
f1e72b45de
66
src/run.rs
66
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(
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
}
|
|
@ -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,
|
||||
},
|
||||
}
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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..],
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
};
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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<F: Fn() -> 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<F: Fn() -> 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,
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Reference in New Issue