Implement xor instructions
parent
05333a0bb8
commit
59bafee838
|
@ -17,6 +17,7 @@ pub enum Instruction {
|
|||
EightBitAdd(arith8::EightBitAddInstruction),
|
||||
EightBitSub(arith8::EightBitSubInstruction),
|
||||
EightBitAnd(arith8::EightBitAndInstruction),
|
||||
EightBitXor(arith8::EightBitXorInstruction),
|
||||
}
|
||||
|
||||
/// `RunnableInstruction` is an instruction that can run on the processor, and has any metadata needed to do so
|
||||
|
|
|
@ -31,3 +31,10 @@ pub enum EightBitAndInstruction {
|
|||
AndHLAddressWithA,
|
||||
AndImmediateWithA { n: u8 },
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum EightBitXorInstruction {
|
||||
XorSingleRegisterWithA { src: register::SingleEightBit },
|
||||
XorHLAddressWithA,
|
||||
XorImmediateWithA { n: u8 },
|
||||
}
|
||||
|
|
|
@ -44,6 +44,7 @@ pub fn next_instruction(data: &View) -> ParseResult {
|
|||
arith8::add::EightBitAddParser::parse_opcode,
|
||||
arith8::sub::EightBitSubParser::parse_opcode,
|
||||
arith8::and::EightBitAndParser::parse_opcode,
|
||||
arith8::xor::EightBitXorParser::parse_opcode,
|
||||
];
|
||||
|
||||
for parse_func in parse_funcs {
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
pub mod add;
|
||||
pub mod and;
|
||||
pub mod sub;
|
||||
pub mod xor;
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
use crate::{
|
||||
cpu::{
|
||||
instructions::{arith8::EightBitXorInstruction, Instruction, RunnableInstruction},
|
||||
parse::{self, Error, OpcodeParser, ParseOutput, ParseResult},
|
||||
},
|
||||
memory::{GetViewTuple, View},
|
||||
register,
|
||||
};
|
||||
|
||||
pub struct EightBitXorParser;
|
||||
|
||||
impl OpcodeParser for EightBitXorParser {
|
||||
fn parse_opcode(data: &View) -> ParseResult {
|
||||
let opcode = parse::get_opcode_from_data(data);
|
||||
match opcode {
|
||||
0xA8 => Ok(build_xor_register_with_a_data(register::SingleEightBit::B)),
|
||||
0xA9 => Ok(build_xor_register_with_a_data(register::SingleEightBit::C)),
|
||||
0xAA => Ok(build_xor_register_with_a_data(register::SingleEightBit::D)),
|
||||
0xAB => Ok(build_xor_register_with_a_data(register::SingleEightBit::E)),
|
||||
0xAC => Ok(build_xor_register_with_a_data(register::SingleEightBit::H)),
|
||||
0xAD => Ok(build_xor_register_with_a_data(register::SingleEightBit::L)),
|
||||
0xAF => Ok(build_xor_register_with_a_data(register::SingleEightBit::A)),
|
||||
0xAE => Ok(build_xor_hl_value_with_a_data()),
|
||||
0xEE => Ok(build_xor_immediate_with_a_data(data)),
|
||||
_ => Err(Error::UnknownOpcode(opcode)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn build_xor_register_with_a_data(src: register::SingleEightBit) -> ParseOutput {
|
||||
(
|
||||
RunnableInstruction {
|
||||
instruction: Instruction::EightBitXor(EightBitXorInstruction::XorSingleRegisterWithA {
|
||||
src,
|
||||
}),
|
||||
cycles: 4,
|
||||
},
|
||||
1,
|
||||
)
|
||||
}
|
||||
|
||||
fn build_xor_hl_value_with_a_data() -> ParseOutput {
|
||||
(
|
||||
RunnableInstruction {
|
||||
instruction: Instruction::EightBitXor(EightBitXorInstruction::XorHLAddressWithA),
|
||||
cycles: 8,
|
||||
},
|
||||
1,
|
||||
)
|
||||
}
|
||||
|
||||
fn build_xor_immediate_with_a_data(data: &View) -> ParseOutput {
|
||||
let (_opcode, n) = data.get_tuple();
|
||||
(
|
||||
RunnableInstruction {
|
||||
instruction: Instruction::EightBitXor(EightBitXorInstruction::XorImmediateWithA { n }),
|
||||
cycles: 9,
|
||||
},
|
||||
2,
|
||||
)
|
||||
}
|
|
@ -22,7 +22,7 @@ pub enum Error {
|
|||
/// An invalid address was stored in the given register and attempted to be loaded to/from
|
||||
#[error("invalid value stored in carry flag: {0:X}")]
|
||||
InvalidCarryFlagValue(u8),
|
||||
#[error("an unknown error occured: {0}")]
|
||||
#[error("an unknown error occurred: {0}")]
|
||||
Unknown(Box<dyn std::error::Error>),
|
||||
}
|
||||
|
||||
|
@ -47,8 +47,11 @@ pub fn run_instruction(processor: &mut Processor, instruction: Instruction) -> R
|
|||
Instruction::EightBitSub(sub_instruction) => {
|
||||
arith8::EightBitSubRunner::run_instruction(processor, sub_instruction)
|
||||
}
|
||||
Instruction::EightBitAnd(sub_instruction) => {
|
||||
arith8::EightBitAndRunner::run_instruction(processor, sub_instruction)
|
||||
Instruction::EightBitAnd(and_instruction) => {
|
||||
arith8::EightBitAndRunner::run_instruction(processor, and_instruction)
|
||||
}
|
||||
Instruction::EightBitXor(xor_instruction) => {
|
||||
arith8::EightBitXorRunner::run_instruction(processor, xor_instruction)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,12 +3,14 @@ use crate::{cpu::run::Error, cpu::Processor, memory, register};
|
|||
pub use add::EightBitAddRunner;
|
||||
pub use and::EightBitAndRunner;
|
||||
pub use sub::EightBitSubRunner;
|
||||
pub use xor::EightBitXorRunner;
|
||||
|
||||
use super::arithutil::{self, CarriedNumber};
|
||||
|
||||
mod add;
|
||||
mod and;
|
||||
mod sub;
|
||||
mod xor;
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
enum EightBitArithmeticOperation {
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
use crate::{
|
||||
cpu::{
|
||||
instructions::arith8::EightBitXorInstruction,
|
||||
run::{Error, InstructionRunner},
|
||||
Processor,
|
||||
},
|
||||
register,
|
||||
};
|
||||
|
||||
use super::{gather_operands, EightBitArithmeticOperation};
|
||||
|
||||
pub struct EightBitXorRunner;
|
||||
|
||||
impl From<EightBitXorInstruction> for EightBitArithmeticOperation {
|
||||
fn from(value: EightBitXorInstruction) -> Self {
|
||||
match value {
|
||||
EightBitXorInstruction::XorSingleRegisterWithA { src } => {
|
||||
EightBitArithmeticOperation::SingleRegisterToA { src }
|
||||
}
|
||||
|
||||
EightBitXorInstruction::XorImmediateWithA { n } => {
|
||||
EightBitArithmeticOperation::ImmediateToA { n }
|
||||
}
|
||||
|
||||
EightBitXorInstruction::XorHLAddressWithA => EightBitArithmeticOperation::HLAddressToA,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl EightBitXorRunner {
|
||||
fn do_xor(processor: &mut Processor, lhs: u8, rhs: u8) {
|
||||
let result = lhs ^ rhs;
|
||||
processor.registers.a = result;
|
||||
processor
|
||||
.registers
|
||||
.set_flag_bit(register::Flag::Zero, (result == 0).into());
|
||||
processor
|
||||
.registers
|
||||
.set_flag_bit(register::Flag::Subtract, 0);
|
||||
processor
|
||||
.registers
|
||||
.set_flag_bit(register::Flag::HalfCarry, 0);
|
||||
processor.registers.set_flag_bit(register::Flag::Carry, 0);
|
||||
}
|
||||
}
|
||||
|
||||
impl InstructionRunner<EightBitXorInstruction> for EightBitXorRunner {
|
||||
fn run_instruction(
|
||||
processor: &mut Processor,
|
||||
instruction: EightBitXorInstruction,
|
||||
) -> Result<(), Error> {
|
||||
let (lhs, rhs) = gather_operands(processor, instruction.into())?;
|
||||
Self::do_xor(processor, lhs, rhs);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
|
@ -1350,3 +1350,208 @@ fn test_and_immediate_with_a_flags(a_value: u8, operand: u8, expected_zero_flag:
|
|||
(register::Flag::Carry, 0),
|
||||
);
|
||||
}
|
||||
|
||||
#[test_case(0xA8, 0xFF, register::SingleEightBit::B, 0xCC, 0x33; "register b")]
|
||||
#[test_case(0xA9, 0xFF, register::SingleEightBit::C, 0x33, 0xCC; "register c")]
|
||||
#[test_case(0xAA, 0xFF, register::SingleEightBit::D, 0xCC, 0x33; "register d")]
|
||||
#[test_case(0xAB, 0xFF, register::SingleEightBit::E, 0x33, 0xCC; "register e")]
|
||||
#[test_case(0xAC, 0xFF, register::SingleEightBit::H, 0xCC, 0x33; "register h")]
|
||||
#[test_case(0xAD, 0xFF, register::SingleEightBit::L, 0x33, 0xCC; "register l")]
|
||||
fn test_xor_register_with_a_value(
|
||||
opcode: u8,
|
||||
a_value: u8,
|
||||
operand_register: register::SingleEightBit,
|
||||
operand: u8,
|
||||
expected_value: u8,
|
||||
) {
|
||||
let mut processor = Processor::default();
|
||||
processor
|
||||
.registers
|
||||
.set_single_8bit_register(register::SingleEightBit::A, a_value);
|
||||
processor
|
||||
.registers
|
||||
.set_single_8bit_register(operand_register, operand);
|
||||
|
||||
let data = [opcode, 0x03];
|
||||
let (ins, extra_data) =
|
||||
RunnableInstruction::from_data(&data).expect("could not parse instruction");
|
||||
assert_eq!(extra_data, &[0x03]);
|
||||
processor.run_instruction(&ins);
|
||||
|
||||
assert_eq!(expected_value, processor.registers.a);
|
||||
}
|
||||
|
||||
#[test_case(0xA8, 0xFF, register::SingleEightBit::B, 0xCC, 0; "register b, not zero")]
|
||||
#[test_case(0xA8, 0xCC, register::SingleEightBit::B, 0xCC, 1; "register b, zero")]
|
||||
#[test_case(0xA9, 0xFF, register::SingleEightBit::C, 0x33, 0; "register c, not zero")]
|
||||
#[test_case(0xA9, 0x33, register::SingleEightBit::C, 0x33, 1; "register c, zero")]
|
||||
#[test_case(0xAA, 0xFF, register::SingleEightBit::D, 0xCC, 0; "register d, not zero")]
|
||||
#[test_case(0xAA, 0xCC, register::SingleEightBit::D, 0xCC, 1; "register d, zero")]
|
||||
#[test_case(0xAB, 0xFF, register::SingleEightBit::E, 0x33, 0; "register e, not zero")]
|
||||
#[test_case(0xAB, 0x33, register::SingleEightBit::E, 0x33, 1; "register e, zero")]
|
||||
#[test_case(0xAC, 0xFF, register::SingleEightBit::H, 0xCC, 0; "register h, not zero")]
|
||||
#[test_case(0xAC, 0xCC, register::SingleEightBit::H, 0xCC, 1; "register h, zero")]
|
||||
#[test_case(0xAD, 0xFF, register::SingleEightBit::L, 0x33, 0; "register l, not zero")]
|
||||
#[test_case(0xAD, 0x33, register::SingleEightBit::L, 0x33, 1; "register l, zero")]
|
||||
fn test_xor_register_with_a_flags(
|
||||
opcode: u8,
|
||||
a_value: u8,
|
||||
operand_register: register::SingleEightBit,
|
||||
operand: u8,
|
||||
expected_zero_flag: u8,
|
||||
) {
|
||||
let mut processor = Processor::default();
|
||||
// Set all the register to the opposite we expect to ensure they all get set
|
||||
testutil::set_opposite_of_expected_flags(&mut processor, (expected_zero_flag, 0, 0, 0));
|
||||
|
||||
processor.registers.a = a_value;
|
||||
processor
|
||||
.registers
|
||||
.set_single_8bit_register(operand_register, operand);
|
||||
|
||||
let data = [opcode, 0x03];
|
||||
let (ins, extra_data) =
|
||||
RunnableInstruction::from_data(&data).expect("could not parse instruction");
|
||||
assert_eq!(extra_data, &[0x03]);
|
||||
processor.run_instruction(&ins);
|
||||
|
||||
testutil::assert_flags_eq!(
|
||||
processor,
|
||||
(register::Flag::Zero, expected_zero_flag),
|
||||
(register::Flag::Subtract, 0),
|
||||
(register::Flag::HalfCarry, 0),
|
||||
(register::Flag::Carry, 0),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_xor_register_a_with_itself_value() {
|
||||
let mut processor = Processor::default();
|
||||
processor.registers.a = 0x45;
|
||||
|
||||
let data = [0xAF, 0x01];
|
||||
let (ins, extra_data) =
|
||||
RunnableInstruction::from_data(&data).expect("could not parse instruction");
|
||||
assert_eq!(extra_data, &[0x01]);
|
||||
processor.run_instruction(&ins);
|
||||
|
||||
assert_eq!(0, processor.registers.a);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_xor_register_a_with_itself_flags() {
|
||||
let mut processor = Processor::default();
|
||||
// Set all the register to the opposite we expect to ensure they all get set
|
||||
testutil::set_opposite_of_expected_flags(&mut processor, (1, 0, 0, 0));
|
||||
processor.registers.a = 0x45;
|
||||
|
||||
let data = [0xAF, 0x01];
|
||||
let (ins, extra_data) =
|
||||
RunnableInstruction::from_data(&data).expect("could not parse instruction");
|
||||
assert_eq!(extra_data, &[0x01]);
|
||||
processor.run_instruction(&ins);
|
||||
|
||||
testutil::assert_flags_eq!(
|
||||
processor,
|
||||
(register::Flag::Zero, 1),
|
||||
(register::Flag::Subtract, 0),
|
||||
(register::Flag::HalfCarry, 0),
|
||||
(register::Flag::Carry, 0),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_xor_hl_value_with_a_value() {
|
||||
let mut processor = Processor::default();
|
||||
processor
|
||||
.registers
|
||||
.set_single_8bit_register(register::SingleEightBit::A, 0xEE);
|
||||
processor
|
||||
.registers
|
||||
.set_combined_register(register::Combined::HL, 0xFF00);
|
||||
|
||||
processor
|
||||
.memory
|
||||
.set(0xFF00, 0xBB)
|
||||
.expect("failed to set memory value");
|
||||
|
||||
let data = [0xAE, 0x03];
|
||||
let (ins, extra_data) =
|
||||
RunnableInstruction::from_data(&data).expect("could not parse instruction");
|
||||
assert_eq!(extra_data, &[0x03]);
|
||||
processor.run_instruction(&ins);
|
||||
|
||||
assert_eq!(0x55, processor.registers.a);
|
||||
}
|
||||
|
||||
#[test_case(0xCC, 0x33, 0; "not zero")]
|
||||
#[test_case(0xCC, 0xCC, 1; "zero")]
|
||||
fn test_xor_hl_value_with_a_flags(a_value: u8, operand: u8, expected_zero_flag: u8) {
|
||||
let mut processor = Processor::default();
|
||||
testutil::set_opposite_of_expected_flags(&mut processor, (expected_zero_flag, 0, 0, 0));
|
||||
processor
|
||||
.registers
|
||||
.set_single_8bit_register(register::SingleEightBit::A, a_value);
|
||||
processor
|
||||
.registers
|
||||
.set_combined_register(register::Combined::HL, 0xFF00);
|
||||
|
||||
processor
|
||||
.memory
|
||||
.set(0xFF00, operand)
|
||||
.expect("failed to set memory value");
|
||||
|
||||
let data = [0xAE, 0x03];
|
||||
let (ins, extra_data) =
|
||||
RunnableInstruction::from_data(&data).expect("could not parse instruction");
|
||||
assert_eq!(extra_data, &[0x03]);
|
||||
processor.run_instruction(&ins);
|
||||
|
||||
testutil::assert_flags_eq!(
|
||||
processor,
|
||||
(register::Flag::Zero, expected_zero_flag),
|
||||
(register::Flag::Subtract, 0),
|
||||
(register::Flag::HalfCarry, 0),
|
||||
(register::Flag::Carry, 0),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_xor_immediate_value_with_a_value() {
|
||||
let mut processor = Processor::default();
|
||||
processor
|
||||
.registers
|
||||
.set_single_8bit_register(register::SingleEightBit::A, 0x22);
|
||||
|
||||
let data = [0xEE, 0xEE, 0x03];
|
||||
let (ins, extra_data) =
|
||||
RunnableInstruction::from_data(&data).expect("could not parse instruction");
|
||||
assert_eq!(extra_data, &[0x03]);
|
||||
processor.run_instruction(&ins);
|
||||
|
||||
assert_eq!(0xCC, processor.registers.a);
|
||||
}
|
||||
|
||||
#[test_case(0xCC, 0x33, 0; "not zero")]
|
||||
#[test_case(0xCC, 0xCC, 1; "zero")]
|
||||
fn test_xor_immediate_value_with_a_flags(a_value: u8, operand: u8, expected_zero_flag: u8) {
|
||||
let mut processor = Processor::default();
|
||||
testutil::set_opposite_of_expected_flags(&mut processor, (expected_zero_flag, 0, 0, 0));
|
||||
processor
|
||||
.registers
|
||||
.set_single_8bit_register(register::SingleEightBit::A, a_value);
|
||||
|
||||
let data = [0xEE, operand, 0x03];
|
||||
let (ins, extra_data) =
|
||||
RunnableInstruction::from_data(&data).expect("could not parse instruction");
|
||||
assert_eq!(extra_data, &[0x03]);
|
||||
processor.run_instruction(&ins);
|
||||
|
||||
testutil::assert_flags_eq!(
|
||||
processor,
|
||||
(register::Flag::Zero, expected_zero_flag),
|
||||
(register::Flag::Subtract, 0),
|
||||
(register::Flag::HalfCarry, 0),
|
||||
(register::Flag::Carry, 0),
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue