Add AND instructions

old-bit-manip
Nick Krichevsky 2023-05-08 07:19:22 -04:00
parent 5a6f048a38
commit ce855cb06c
18 changed files with 341 additions and 0 deletions

View File

@ -16,6 +16,7 @@ pub enum Instruction {
SixteenBitLoad(load16::SixteenBitLoadInstruction),
EightBitAdd(arith8::EightBitAddInstruction),
EightBitSub(arith8::EightBitSubInstruction),
EightBitAnd(arith8::EightBitAndInstruction),
}
/// `RunnableInstruction` is an instruction that can run on the processor, and has any metadata needed to do so

View File

@ -24,3 +24,10 @@ pub enum EightBitSubInstruction {
SubHLAddressFromAWithCarry,
SubImmediateFromAWithCarry { n: u8 },
}
#[derive(Debug, Clone, Copy)]
pub enum EightBitAndInstruction {
AndSingleRegisterWithA { src: register::SingleEightBit },
AndHLAddressWithA,
AndImmediateWithA { n: u8 },
}

View File

@ -43,6 +43,7 @@ pub fn next_instruction(data: &View) -> ParseResult {
load16::stack::StackLoadParser::parse_opcode,
arith8::add::EightBitAddParser::parse_opcode,
arith8::sub::EightBitSubParser::parse_opcode,
arith8::and::EightBitAndParser::parse_opcode,
];
for parse_func in parse_funcs {

View File

@ -1,2 +1,3 @@
pub mod add;
pub mod and;
pub mod sub;

View File

@ -0,0 +1,62 @@
use crate::{
cpu::{
instructions::{arith8::EightBitAndInstruction, Instruction, RunnableInstruction},
parse::{self, Error, OpcodeParser, ParseOutput, ParseResult},
},
memory::{GetViewTuple, View},
register,
};
pub struct EightBitAndParser;
impl OpcodeParser for EightBitAndParser {
fn parse_opcode(data: &View) -> ParseResult {
let opcode = parse::get_opcode_from_data(data);
match opcode {
0xA0 => Ok(build_and_register_with_a_data(register::SingleEightBit::B)),
0xA1 => Ok(build_and_register_with_a_data(register::SingleEightBit::C)),
0xA2 => Ok(build_and_register_with_a_data(register::SingleEightBit::D)),
0xA3 => Ok(build_and_register_with_a_data(register::SingleEightBit::E)),
0xA4 => Ok(build_and_register_with_a_data(register::SingleEightBit::H)),
0xA5 => Ok(build_and_register_with_a_data(register::SingleEightBit::L)),
0xA7 => Ok(build_and_register_with_a_data(register::SingleEightBit::A)),
0xA6 => Ok(build_and_hl_value_with_a_data()),
0xE6 => Ok(build_and_immediate_with_a_data(data)),
_ => Err(Error::UnknownOpcode(opcode)),
}
}
}
fn build_and_register_with_a_data(src: register::SingleEightBit) -> ParseOutput {
(
RunnableInstruction {
instruction: Instruction::EightBitAnd(EightBitAndInstruction::AndSingleRegisterWithA {
src,
}),
cycles: 4,
},
1,
)
}
fn build_and_hl_value_with_a_data() -> ParseOutput {
(
RunnableInstruction {
instruction: Instruction::EightBitAnd(EightBitAndInstruction::AndHLAddressWithA),
cycles: 8,
},
1,
)
}
fn build_and_immediate_with_a_data(data: &View) -> ParseOutput {
let (_opcode, n) = data.get_tuple();
(
RunnableInstruction {
instruction: Instruction::EightBitAnd(EightBitAndInstruction::AndImmediateWithA { n }),
cycles: 8,
},
2,
)
}

View File

@ -47,5 +47,8 @@ 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)
}
}
}

View File

@ -1,11 +1,13 @@
use crate::{cpu::run::Error, cpu::Processor, memory, register};
pub use add::EightBitAddRunner;
pub use and::EightBitAndRunner;
pub use sub::EightBitSubRunner;
use super::arithutil::{self, CarriedNumber};
mod add;
mod and;
mod sub;
#[derive(Debug, Clone, Copy)]

70
src/cpu/run/arith8/and.rs Normal file
View File

@ -0,0 +1,70 @@
use crate::{
cpu::{
instructions::arith8::EightBitAndInstruction,
run::{Error, InstructionRunner},
Processor,
},
register,
};
pub struct EightBitAndRunner;
impl EightBitAndRunner {
fn do_and(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, 1);
processor.registers.set_flag_bit(register::Flag::Carry, 0);
}
}
impl InstructionRunner<EightBitAndInstruction> for EightBitAndRunner {
fn run_instruction(
processor: &mut Processor,
instruction: EightBitAndInstruction,
) -> Result<(), Error> {
match instruction {
EightBitAndInstruction::AndSingleRegisterWithA { src } => {
let (lhs, rhs) = super::gather_operands(
processor,
super::EightBitArithmeticOperation::SingleRegisterToA { src },
)?;
Self::do_and(processor, lhs, rhs);
Ok(())
}
EightBitAndInstruction::AndHLAddressWithA => {
let (lhs, rhs) = super::gather_operands(
processor,
super::EightBitArithmeticOperation::HLAddressToA,
)?;
Self::do_and(processor, lhs, rhs);
Ok(())
}
EightBitAndInstruction::AndImmediateWithA { n } => {
let (lhs, rhs) = super::gather_operands(
processor,
super::EightBitArithmeticOperation::ImmediateToA { n },
)?;
Self::do_and(processor, lhs, rhs);
Ok(())
}
}
}
}

View File

@ -1156,3 +1156,197 @@ fn test_sub_hl_addr_from_a_with_carry_flags(
(register::Flag::Carry, expected_flags.carry),
);
}
#[test_case(0xA0, 0xFB, register::SingleEightBit::B, 0x0F, 0x0B; "AND with B")]
#[test_case(0xA1, 0xFC, register::SingleEightBit::C, 0x0F, 0x0C; "AND with C")]
#[test_case(0xA2, 0xFD, register::SingleEightBit::D, 0x0F, 0x0D; "AND with D")]
#[test_case(0xA3, 0xFE, register::SingleEightBit::E, 0x0F, 0x0E; "AND with E")]
#[test_case(0xA4, 0xF0, register::SingleEightBit::H, 0x0F, 0x00; "AND with H")]
#[test_case(0xA5, 0xF1, register::SingleEightBit::L, 0x0F, 0x01; "AND with L")]
fn test_and_single_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.a = a_value;
processor
.registers
.set_single_8bit_register(operand_register, operand);
let data = [opcode, 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!(expected_value, processor.registers.a);
}
#[test_case(0xA0, 0xFA, register::SingleEightBit::B, 0x0F, 0; "AND with B, not zero")]
#[test_case(0xA0, 0xFA, register::SingleEightBit::B, 0x00, 1; "AND with B, zero")]
#[test_case(0xA1, 0xFA, register::SingleEightBit::C, 0x0F, 0; "AND with C, not zero")]
#[test_case(0xA1, 0xFA, register::SingleEightBit::C, 0x00, 1; "AND with C, zero")]
#[test_case(0xA2, 0xFA, register::SingleEightBit::D, 0x0F, 0; "AND with D, not zero")]
#[test_case(0xA2, 0xFA, register::SingleEightBit::D, 0x00, 1; "AND with D, zero")]
#[test_case(0xA3, 0xFA, register::SingleEightBit::E, 0x0F, 0; "AND with E, not zero")]
#[test_case(0xA3, 0xFA, register::SingleEightBit::E, 0x00, 1; "AND with E, zero")]
fn test_and_single_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, 1, 0));
processor.registers.a = a_value;
processor
.registers
.set_single_8bit_register(operand_register, operand);
let data = [opcode, 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, 1),
(register::Flag::Carry, 0),
);
}
#[test]
fn test_and_a_with_itself_value() {
let mut processor = Processor::default();
processor.registers.a = 0xAB;
let data = [0xA7, 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!(0xAB, processor.registers.a);
}
#[test]
fn test_and_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, (0, 0, 1, 0));
processor.registers.a = 0xAB;
let data = [0xA7, 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, 0),
(register::Flag::Subtract, 0),
(register::Flag::HalfCarry, 1),
(register::Flag::Carry, 0),
);
}
#[test]
fn test_and_hl_value_with_a_value() {
let mut processor = Processor::default();
processor.registers.a = 0xAB;
processor
.registers
.set_combined_register(register::Combined::HL, 0x00FF);
processor
.memory
.set(0x00FF, 0xF0)
.expect("failed to set value");
let data = [0xA6, 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!(0xA0, processor.registers.a);
}
#[test_case(0xFA, 0x0F, 0; "not zero")]
#[test_case(0xFA, 0x00, 1; "zero")]
fn test_and_hl_value_with_a_flags(a_value: u8, operand: u8, expected_zero_flag: u8) {
let mut processor = Processor::default();
processor.registers.a = a_value;
processor
.registers
.set_combined_register(register::Combined::HL, 0x00FF);
processor
.memory
.set(0x00FF, operand)
.expect("failed to set value");
// 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, 1, 0));
let data = [0xA6, 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, 1),
(register::Flag::Carry, 0),
);
}
#[test]
fn test_and_immediate_with_a_value() {
let mut processor = Processor::default();
processor.registers.a = 0xAB;
let data = [0xE6, 0x0F, 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!(0x0B, processor.registers.a);
}
#[test_case(0xFA, 0x0F, 0; "not zero")]
#[test_case(0xFA, 0x00, 1; "zero")]
fn test_and_immediate_with_a_flags(a_value: u8, operand: u8, expected_zero_flag: u8) {
let mut processor = Processor::default();
processor.registers.a = a_value;
// 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, 1, 0));
let data = [0xE6, operand, 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, 1),
(register::Flag::Carry, 0),
);
}