Add support for OR instructions
parent
97d6df639f
commit
e841931d55
|
@ -9,6 +9,7 @@ pub enum Operation {
|
|||
SubWithCarry,
|
||||
And,
|
||||
Xor,
|
||||
Or,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
|
|
|
@ -45,6 +45,7 @@ pub fn next_instruction(data: &View) -> ParseResult {
|
|||
arith8::sub::EightBitSubParser::parse_opcode,
|
||||
arith8::and::EightBitAndParser::parse_opcode,
|
||||
arith8::xor::EightBitXorParser::parse_opcode,
|
||||
arith8::or::EightBitOrParser::parse_opcode,
|
||||
];
|
||||
|
||||
for parse_func in parse_funcs {
|
||||
|
|
|
@ -11,6 +11,7 @@ use super::ParseOutput;
|
|||
|
||||
pub mod add;
|
||||
pub mod and;
|
||||
pub mod or;
|
||||
pub mod sub;
|
||||
pub mod xor;
|
||||
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
use crate::{
|
||||
cpu::{
|
||||
instructions::arith8::Operation,
|
||||
parse::{self, Error, OpcodeParser, ParseOutput, ParseResult},
|
||||
},
|
||||
memory::View,
|
||||
register,
|
||||
};
|
||||
|
||||
pub struct EightBitOrParser;
|
||||
|
||||
impl OpcodeParser for EightBitOrParser {
|
||||
fn parse_opcode(data: &View) -> ParseResult {
|
||||
let opcode = parse::get_opcode_from_data(data);
|
||||
match opcode {
|
||||
0xB0 => Ok(build_or_register_with_a_data(register::SingleEightBit::B)),
|
||||
0xB1 => Ok(build_or_register_with_a_data(register::SingleEightBit::C)),
|
||||
0xB2 => Ok(build_or_register_with_a_data(register::SingleEightBit::D)),
|
||||
0xB3 => Ok(build_or_register_with_a_data(register::SingleEightBit::E)),
|
||||
0xB4 => Ok(build_or_register_with_a_data(register::SingleEightBit::H)),
|
||||
0xB5 => Ok(build_or_register_with_a_data(register::SingleEightBit::L)),
|
||||
0xB7 => Ok(build_or_register_with_a_data(register::SingleEightBit::A)),
|
||||
|
||||
0xB6 => Ok(build_or_hl_value_with_a_data()),
|
||||
0xF6 => Ok(build_or_immediate_with_a_data(data)),
|
||||
|
||||
_ => Err(Error::UnknownOpcode(opcode)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn build_or_register_with_a_data(src: register::SingleEightBit) -> ParseOutput {
|
||||
super::build_instruction_between_register_and_a_data(Operation::Or, src)
|
||||
}
|
||||
|
||||
fn build_or_hl_value_with_a_data() -> ParseOutput {
|
||||
super::build_instruction_between_hl_value_and_a_data(Operation::Or)
|
||||
}
|
||||
|
||||
fn build_or_immediate_with_a_data(data: &View) -> ParseOutput {
|
||||
super::build_instruction_between_immediate_and_a_data(Operation::Or, data)
|
||||
}
|
|
@ -14,6 +14,7 @@ use super::{
|
|||
|
||||
mod add;
|
||||
mod and;
|
||||
mod or;
|
||||
mod sub;
|
||||
mod xor;
|
||||
|
||||
|
@ -25,16 +26,6 @@ impl InstructionRunner<EightBitArithmeticInstruction> for EightBitArithmeticRunn
|
|||
instruction: EightBitArithmeticInstruction,
|
||||
) -> Result<(), Error> {
|
||||
match instruction.operation {
|
||||
Operation::And => {
|
||||
let (lhs, rhs) = gather_operands(processor, instruction.operand)?;
|
||||
and::run(processor, lhs, rhs);
|
||||
}
|
||||
|
||||
Operation::Xor => {
|
||||
let (lhs, rhs) = gather_operands(processor, instruction.operand)?;
|
||||
xor::run(processor, lhs, rhs);
|
||||
}
|
||||
|
||||
Operation::Add => {
|
||||
let (lhs, rhs) = gather_operands(processor, instruction.operand)?;
|
||||
add::run(processor, lhs, rhs);
|
||||
|
@ -54,6 +45,21 @@ impl InstructionRunner<EightBitArithmeticInstruction> for EightBitArithmeticRunn
|
|||
let (lhs, rhs) = gather_operands_with_carry(processor, instruction.operand)?;
|
||||
sub::run(processor, lhs, rhs);
|
||||
}
|
||||
|
||||
Operation::And => {
|
||||
let (lhs, rhs) = gather_operands(processor, instruction.operand)?;
|
||||
and::run(processor, lhs, rhs);
|
||||
}
|
||||
|
||||
Operation::Xor => {
|
||||
let (lhs, rhs) = gather_operands(processor, instruction.operand)?;
|
||||
xor::run(processor, lhs, rhs);
|
||||
}
|
||||
|
||||
Operation::Or => {
|
||||
let (lhs, rhs) = gather_operands(processor, instruction.operand)?;
|
||||
or::run(processor, lhs, rhs);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
use crate::{cpu::Processor, register};
|
||||
|
||||
pub fn run(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);
|
||||
}
|
|
@ -1555,3 +1555,210 @@ fn test_xor_immediate_value_with_a_flags(a_value: u8, operand: u8, expected_zero
|
|||
(register::Flag::Carry, 0),
|
||||
);
|
||||
}
|
||||
|
||||
#[test_case(0xB0, 0xCC, register::SingleEightBit::B, 0x11, 0xDD; "register b")]
|
||||
#[test_case(0xB1, 0xCC, register::SingleEightBit::C, 0x11, 0xDD; "register c")]
|
||||
#[test_case(0xB2, 0xCC, register::SingleEightBit::D, 0x11, 0xDD; "register d")]
|
||||
#[test_case(0xB3, 0xCC, register::SingleEightBit::E, 0x11, 0xDD; "register e")]
|
||||
#[test_case(0xB4, 0xCC, register::SingleEightBit::H, 0x11, 0xDD; "register h")]
|
||||
#[test_case(0xB5, 0xCC, register::SingleEightBit::L, 0x11, 0xDD; "register l")]
|
||||
fn test_or_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(0xB0, 0xCC, register::SingleEightBit::B, 0x22, 0; "register b, not zero")]
|
||||
#[test_case(0xB0, 0x00, register::SingleEightBit::B, 0x00, 1; "register b, zero")]
|
||||
#[test_case(0xB1, 0xCC, register::SingleEightBit::C, 0x22, 0; "register c, not zero")]
|
||||
#[test_case(0xB1, 0x00, register::SingleEightBit::C, 0x00, 1; "register c, zero")]
|
||||
#[test_case(0xB2, 0xCC, register::SingleEightBit::D, 0x22, 0; "register d, not zero")]
|
||||
#[test_case(0xB2, 0x00, register::SingleEightBit::D, 0x00, 1; "register d, zero")]
|
||||
#[test_case(0xB3, 0xCC, register::SingleEightBit::E, 0x22, 0; "register e, not zero")]
|
||||
#[test_case(0xB3, 0x00, register::SingleEightBit::E, 0x00, 1; "register e, zero")]
|
||||
#[test_case(0xB4, 0xCC, register::SingleEightBit::H, 0x22, 0; "register h, not zero")]
|
||||
#[test_case(0xB4, 0x00, register::SingleEightBit::H, 0x00, 1; "register h, zero")]
|
||||
#[test_case(0xB5, 0xCC, register::SingleEightBit::L, 0x22, 0; "register l, not zero")]
|
||||
#[test_case(0xB5, 0x00, register::SingleEightBit::L, 0x00, 1; "register l, zero")]
|
||||
fn test_or_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_or_register_a_with_itself_value() {
|
||||
let mut processor = Processor::default();
|
||||
processor.registers.a = 0x45;
|
||||
|
||||
let data = [0xB7, 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!(0x45, processor.registers.a);
|
||||
}
|
||||
|
||||
#[test_case(0xCC, 0)]
|
||||
#[test_case(0x00, 1)]
|
||||
fn test_or_register_a_with_itself_flags(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 = operand;
|
||||
|
||||
let data = [0xB7, 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, expected_zero_flag),
|
||||
(register::Flag::Subtract, 0),
|
||||
(register::Flag::HalfCarry, 0),
|
||||
(register::Flag::Carry, 0),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_or_hl_value_with_a_value() {
|
||||
let mut processor = Processor::default();
|
||||
processor
|
||||
.registers
|
||||
.set_single_8bit_register(register::SingleEightBit::A, 0x11);
|
||||
processor
|
||||
.registers
|
||||
.set_combined_register(register::Combined::HL, 0xFF00);
|
||||
|
||||
processor
|
||||
.memory
|
||||
.set(0xFF00, 0xAA)
|
||||
.expect("failed to set memory value");
|
||||
|
||||
let data = [0xB6, 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!(0xBB, processor.registers.a);
|
||||
}
|
||||
|
||||
#[test_case(0xBB, 0x11, 0)]
|
||||
#[test_case(0x00, 0x00, 1)]
|
||||
fn test_or_hl_value_with_a_flags(a_value: u8, hl_value: 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
|
||||
.set_single_8bit_register(register::SingleEightBit::A, a_value);
|
||||
processor
|
||||
.registers
|
||||
.set_combined_register(register::Combined::HL, 0xFF00);
|
||||
|
||||
processor
|
||||
.memory
|
||||
.set(0xFF00, hl_value)
|
||||
.expect("failed to set memory value");
|
||||
|
||||
let data = [0xB7, 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, expected_zero_flag),
|
||||
(register::Flag::Subtract, 0),
|
||||
(register::Flag::HalfCarry, 0),
|
||||
(register::Flag::Carry, 0),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_or_immediate_value_with_a_value() {
|
||||
let mut processor = Processor::default();
|
||||
processor
|
||||
.registers
|
||||
.set_single_8bit_register(register::SingleEightBit::A, 0x33);
|
||||
|
||||
let data = [0xF6, 0xAA, 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!(0xBB, processor.registers.a);
|
||||
}
|
||||
|
||||
#[test_case(0xFF, 0x00, 0; "not zero")]
|
||||
#[test_case(0x00, 0x00, 1; "zero")]
|
||||
fn test_or_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 = [0xF6, 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