Implement sub/subc immediate instructions
parent
cf71063e59
commit
01a68e1415
|
@ -19,6 +19,8 @@ pub enum EightBitAddInstruction {
|
||||||
pub enum EightBitSubInstruction {
|
pub enum EightBitSubInstruction {
|
||||||
SubSingleRegisterFromA { src: register::SingleEightBit },
|
SubSingleRegisterFromA { src: register::SingleEightBit },
|
||||||
SubHLAddressFromA,
|
SubHLAddressFromA,
|
||||||
|
SubImmediateFromA { n: u8 },
|
||||||
SubSingleRegisterFromAWithCarry { src: register::SingleEightBit },
|
SubSingleRegisterFromAWithCarry { src: register::SingleEightBit },
|
||||||
SubHLAddressFromAWithCarry,
|
SubHLAddressFromAWithCarry,
|
||||||
|
SubImmediateFromAWithCarry { n: u8 },
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ use crate::{
|
||||||
instructions::{arith8::EightBitSubInstruction, Instruction, RunnableInstruction},
|
instructions::{arith8::EightBitSubInstruction, Instruction, RunnableInstruction},
|
||||||
parse::{self, Error, OpcodeParser, ParseOutput, ParseResult},
|
parse::{self, Error, OpcodeParser, ParseOutput, ParseResult},
|
||||||
},
|
},
|
||||||
memory::View,
|
memory::{GetViewTuple, View},
|
||||||
register,
|
register,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -43,6 +43,8 @@ impl OpcodeParser for EightBitSubParser {
|
||||||
)),
|
)),
|
||||||
0x96 => Ok(build_sub_hl_value_from_a_data()),
|
0x96 => Ok(build_sub_hl_value_from_a_data()),
|
||||||
0x9E => Ok(build_sub_hl_value_from_a_with_carry_data()),
|
0x9E => Ok(build_sub_hl_value_from_a_with_carry_data()),
|
||||||
|
0xD6 => Ok(build_sub_immediate_from_a_data(data)),
|
||||||
|
0xDE => Ok(build_sub_immediate_from_a_with_carry_data(data)),
|
||||||
_ => Err(Error::UnknownOpcode(opcode)),
|
_ => Err(Error::UnknownOpcode(opcode)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -70,6 +72,18 @@ fn build_sub_hl_value_from_a_data() -> ParseOutput {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn build_sub_immediate_from_a_data(data: &View) -> ParseOutput {
|
||||||
|
let (_opcode, n) = data.get_tuple();
|
||||||
|
|
||||||
|
(
|
||||||
|
RunnableInstruction {
|
||||||
|
instruction: Instruction::EightBitSub(EightBitSubInstruction::SubImmediateFromA { n }),
|
||||||
|
cycles: 8,
|
||||||
|
},
|
||||||
|
2,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
fn build_sub_register_from_a_with_carry_data(
|
fn build_sub_register_from_a_with_carry_data(
|
||||||
src_register: register::SingleEightBit,
|
src_register: register::SingleEightBit,
|
||||||
) -> ParseOutput {
|
) -> ParseOutput {
|
||||||
|
@ -95,3 +109,17 @@ fn build_sub_hl_value_from_a_with_carry_data() -> ParseOutput {
|
||||||
1,
|
1,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn build_sub_immediate_from_a_with_carry_data(data: &View) -> ParseOutput {
|
||||||
|
let (_opcode, n) = data.get_tuple();
|
||||||
|
|
||||||
|
(
|
||||||
|
RunnableInstruction {
|
||||||
|
instruction: Instruction::EightBitSub(
|
||||||
|
EightBitSubInstruction::SubImmediateFromAWithCarry { n },
|
||||||
|
),
|
||||||
|
cycles: 8,
|
||||||
|
},
|
||||||
|
2,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
|
@ -27,6 +27,15 @@ impl InstructionRunner<EightBitSubInstruction> for EightBitSubRunner {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
EightBitSubInstruction::SubImmediateFromA { n } => {
|
||||||
|
let a_value = processor.registers.a;
|
||||||
|
|
||||||
|
let result = a_value.sub_with_carry(n);
|
||||||
|
store_subtraction_result(processor, result);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
EightBitSubInstruction::SubSingleRegisterFromAWithCarry { src } => {
|
EightBitSubInstruction::SubSingleRegisterFromAWithCarry { src } => {
|
||||||
let a_value = processor.registers.a;
|
let a_value = processor.registers.a;
|
||||||
let src_register_value = processor.registers.get_single_8bit_register(src);
|
let src_register_value = processor.registers.get_single_8bit_register(src);
|
||||||
|
@ -104,6 +113,22 @@ impl InstructionRunner<EightBitSubInstruction> for EightBitSubRunner {
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
EightBitSubInstruction::SubImmediateFromAWithCarry { n } => {
|
||||||
|
let a_value = processor.registers.a;
|
||||||
|
let carry_bit = processor.registers.get_flag_bit(register::Flag::Carry);
|
||||||
|
let carried_operand =
|
||||||
|
CarriedNumber::new(n, carry_bit).map_err(|err| match err {
|
||||||
|
arithutil::Error::InvalidCarryBit(value) => {
|
||||||
|
Error::InvalidCarryFlagValue(value)
|
||||||
|
}
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let result = a_value.sub_with_carry(carried_operand);
|
||||||
|
store_subtraction_result(processor, result);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -827,6 +827,131 @@ fn test_sub_register_from_carry_from_a_flags(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_subtract_immediate_from_a_value() {
|
||||||
|
let mut processor = Processor::default();
|
||||||
|
processor.registers.a = 0xFF;
|
||||||
|
let data = [0xD6, 0x0F, 0x02];
|
||||||
|
|
||||||
|
let (ins, extra_data) =
|
||||||
|
RunnableInstruction::from_data(&data).expect("could not parse instruction");
|
||||||
|
assert_eq!(extra_data, &[0x02]);
|
||||||
|
|
||||||
|
processor.run_instruction(&ins);
|
||||||
|
assert_eq!(processor.registers.a, 0xF0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test_case(0xFF, 0x0F, AdditionOperationFlags { zero: 0, half_carry: 0, carry: 0 }; "no flags")]
|
||||||
|
#[test_case(0xF0, 0x0F, AdditionOperationFlags { zero: 0, half_carry: 1, carry: 0 }; "half carry")]
|
||||||
|
#[test_case(0x0F, 0xF0, AdditionOperationFlags { zero: 0, half_carry: 0, carry: 1 }; "full carry")]
|
||||||
|
#[test_case(0x01, 0x01, AdditionOperationFlags { zero: 1, half_carry: 0, carry: 0 }; "zero")]
|
||||||
|
fn test_sub_immediate_from_a_flags(
|
||||||
|
initial_value: u8,
|
||||||
|
operand: u8,
|
||||||
|
expected_flags: AdditionOperationFlags,
|
||||||
|
) {
|
||||||
|
let mut processor = Processor::default();
|
||||||
|
processor.registers.a = initial_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_flags.zero,
|
||||||
|
0,
|
||||||
|
expected_flags.half_carry,
|
||||||
|
expected_flags.carry,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
let data = [0xD6, 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_flags.zero),
|
||||||
|
(register::Flag::Subtract, 1),
|
||||||
|
(register::Flag::HalfCarry, expected_flags.half_carry),
|
||||||
|
(register::Flag::Carry, expected_flags.carry),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test_case(0xFF, 0x0F, 0, 0xF0; "no carry")]
|
||||||
|
#[test_case(0xFF, 0x0F, 1, 0xEF; "carry")]
|
||||||
|
fn test_sub_immediate_with_carry_from_a_value(
|
||||||
|
a_value: u8,
|
||||||
|
operand: u8,
|
||||||
|
carry_bit: u8,
|
||||||
|
expected: u8,
|
||||||
|
) {
|
||||||
|
let mut processor = Processor::default();
|
||||||
|
processor.registers.a = a_value;
|
||||||
|
processor
|
||||||
|
.registers
|
||||||
|
.set_flag_bit(register::Flag::Carry, carry_bit);
|
||||||
|
|
||||||
|
let data = [0xDE, operand, 0x02];
|
||||||
|
|
||||||
|
let (ins, extra_data) =
|
||||||
|
RunnableInstruction::from_data(&data).expect("could not parse instruction");
|
||||||
|
assert_eq!(extra_data, &[0x02]);
|
||||||
|
|
||||||
|
processor.run_instruction(&ins);
|
||||||
|
assert_eq!(processor.registers.a, expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test_case(0xFE, 0, 0x1, AdditionOperationFlags{zero: 0, half_carry: 0, carry: 0}; "no carry bit set, results in no flags")]
|
||||||
|
#[test_case(0xFE, 1, 0x1, AdditionOperationFlags{zero: 0, half_carry: 0, carry: 0}; "carry bit set, results in no flags")]
|
||||||
|
#[test_case(0x00, 0, 0x0, AdditionOperationFlags{zero: 1, half_carry: 0, carry: 0}; "all zero operands give zero flag")]
|
||||||
|
#[test_case(0x01, 1, 0x0, AdditionOperationFlags{zero: 1, half_carry: 0, carry: 0}; "subtracting from 1 gives zero flag with carry bit")]
|
||||||
|
#[test_case(0x02, 0, 0x2, AdditionOperationFlags{zero: 1, half_carry: 0, carry: 0}; "subtracting from 2 gives zero flag with no")]
|
||||||
|
#[test_case(0xF0, 0, 0x0F, AdditionOperationFlags{zero: 0, half_carry: 1, carry: 0}; "no carry bit set, half carry bit")]
|
||||||
|
#[test_case(0x0F, 0, 0xF0, AdditionOperationFlags{zero: 0, half_carry: 0, carry: 1}; "no carry bit set, full carry bit")]
|
||||||
|
#[test_case(0xF0, 1, 0x0E, AdditionOperationFlags{zero: 0, half_carry: 1, carry: 0}; "carry bit set, half carry bit")]
|
||||||
|
#[test_case(0x0F, 1, 0xEE, AdditionOperationFlags{zero: 0, half_carry: 0, carry: 1}; "carry bit set, full carry bit")]
|
||||||
|
fn test_sub_immediate_from_carry_from_a_flags(
|
||||||
|
initial_value: u8,
|
||||||
|
carry_bit: u8,
|
||||||
|
operand: u8,
|
||||||
|
expected_flags: AdditionOperationFlags,
|
||||||
|
) {
|
||||||
|
let mut processor = Processor::default();
|
||||||
|
processor.registers.a = initial_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_flags.zero,
|
||||||
|
0,
|
||||||
|
expected_flags.half_carry,
|
||||||
|
expected_flags.carry,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
// ...except for the carry bit, which we must set for the test
|
||||||
|
processor
|
||||||
|
.registers
|
||||||
|
.set_flag_bit(register::Flag::Carry, carry_bit);
|
||||||
|
|
||||||
|
let data = [0xDE, 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_flags.zero),
|
||||||
|
(register::Flag::Subtract, 1),
|
||||||
|
(register::Flag::HalfCarry, expected_flags.half_carry),
|
||||||
|
(register::Flag::Carry, expected_flags.carry),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test_case(0, 0; "subtract from itself")]
|
#[test_case(0, 0; "subtract from itself")]
|
||||||
#[test_case(1, 0xFF; "subtract from itself with carry bit")]
|
#[test_case(1, 0xFF; "subtract from itself with carry bit")]
|
||||||
fn test_sub_a_register_from_itself_with_carry_value(carry_bit: u8, expected: u8) {
|
fn test_sub_a_register_from_itself_with_carry_value(carry_bit: u8, expected: u8) {
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue