Add AND instructions
parent
5a6f048a38
commit
ce855cb06c
|
@ -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
|
||||
|
|
|
@ -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 },
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
pub mod add;
|
||||
pub mod and;
|
||||
pub mod sub;
|
||||
|
|
|
@ -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,
|
||||
)
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -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(())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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),
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue