Add support for OR instructions

old-bit-manip
Nick Krichevsky 2023-05-13 14:41:09 -04:00
parent 97d6df639f
commit e841931d55
15 changed files with 284 additions and 10 deletions

View File

@ -9,6 +9,7 @@ pub enum Operation {
SubWithCarry,
And,
Xor,
Or,
}
#[derive(Debug, Copy, Clone)]

View File

@ -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 {

View File

@ -11,6 +11,7 @@ use super::ParseOutput;
pub mod add;
pub mod and;
pub mod or;
pub mod sub;
pub mod xor;

View File

@ -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)
}

View File

@ -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(())

16
src/cpu/run/arith8/or.rs Normal file
View File

@ -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);
}

View File

@ -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),
);
}