Implement basic SUB operation
parent
c05f8c57e9
commit
b0554a098e
|
@ -11,4 +11,6 @@ pub enum EightBitArithmeticInstruction {
|
|||
AddSingleRegisterToAWithCarry { src: register::SingleEightBit },
|
||||
AddImmediateToAWithCarry { n: u8 },
|
||||
AddHLAddressToAWithCarry,
|
||||
|
||||
SubSingleRegisterFromA { src: register::SingleEightBit },
|
||||
}
|
||||
|
|
|
@ -42,6 +42,7 @@ pub fn next_instruction(data: &View) -> ParseResult {
|
|||
load16::transfer::Between16BitRegisterParser::parse_opcode,
|
||||
load16::stack::StackLoadParser::parse_opcode,
|
||||
arith8::add::EightBitAddParser::parse_opcode,
|
||||
arith8::sub::EightBitSubParser::parse_opcode,
|
||||
];
|
||||
|
||||
for parse_func in parse_funcs {
|
||||
|
|
|
@ -1 +1,2 @@
|
|||
pub mod add;
|
||||
pub mod sub;
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
use crate::{
|
||||
cpu::{
|
||||
instructions::{arith8::EightBitArithmeticInstruction, Instruction, RunnableInstruction},
|
||||
parse::{self, Error, OpcodeParser, ParseOutput, ParseResult},
|
||||
},
|
||||
memory::View,
|
||||
register,
|
||||
};
|
||||
|
||||
pub struct EightBitSubParser;
|
||||
|
||||
impl OpcodeParser for EightBitSubParser {
|
||||
fn parse_opcode(data: &View) -> ParseResult {
|
||||
let opcode = parse::get_opcode_from_data(data);
|
||||
match opcode {
|
||||
0x90 => Ok(build_sub_register_from_a_data(register::SingleEightBit::B)),
|
||||
0x91 => Ok(build_sub_register_from_a_data(register::SingleEightBit::C)),
|
||||
0x92 => Ok(build_sub_register_from_a_data(register::SingleEightBit::D)),
|
||||
0x93 => Ok(build_sub_register_from_a_data(register::SingleEightBit::E)),
|
||||
0x94 => Ok(build_sub_register_from_a_data(register::SingleEightBit::H)),
|
||||
0x95 => Ok(build_sub_register_from_a_data(register::SingleEightBit::L)),
|
||||
0x97 => Ok(build_sub_register_from_a_data(register::SingleEightBit::A)),
|
||||
_ => Err(Error::UnknownOpcode(opcode)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn build_sub_register_from_a_data(src_register: register::SingleEightBit) -> ParseOutput {
|
||||
(
|
||||
RunnableInstruction {
|
||||
instruction: Instruction::EightBitArithmetic(
|
||||
EightBitArithmeticInstruction::SubSingleRegisterFromA { src: src_register },
|
||||
),
|
||||
cycles: 4,
|
||||
},
|
||||
1,
|
||||
)
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
use crate::cpu::{instructions::arith8::EightBitArithmeticInstruction, run::Error, Processor};
|
||||
use crate::{memory, register};
|
||||
|
||||
use super::arithutil;
|
||||
use super::arithutil::{self, CarryingSub};
|
||||
use super::{arithutil::CarryingAdd, InstructionRunner};
|
||||
|
||||
pub(super) struct EightBitArithmeticRunner;
|
||||
|
@ -120,11 +120,16 @@ impl InstructionRunner<EightBitArithmeticInstruction> for EightBitArithmeticRunn
|
|||
|
||||
EightBitArithmeticInstruction::AddHLAddressToAWithCarry => {
|
||||
let a_value = processor.registers.a;
|
||||
let src_address = processor.registers.get_combined_register(register::Combined::HL);
|
||||
let src_address = processor
|
||||
.registers
|
||||
.get_combined_register(register::Combined::HL);
|
||||
|
||||
// While this is true, we really do want a wildcard match in map_err
|
||||
#[allow(clippy::match_wildcard_for_single_variants)]
|
||||
let stored_value = processor.memory.get(src_address.into())
|
||||
let stored_value =
|
||||
processor
|
||||
.memory
|
||||
.get(src_address.into())
|
||||
.map_err(|err| match err {
|
||||
memory::Error::GetInvalidAddress(bad_addr) => {
|
||||
Error::InvalidRegisterAddress(
|
||||
|
@ -136,7 +141,7 @@ impl InstructionRunner<EightBitArithmeticInstruction> for EightBitArithmeticRunn
|
|||
})?;
|
||||
let carried_operand = arithutil::CarriedNumber::new(
|
||||
stored_value,
|
||||
processor.registers.get_flag_bit(register::Flag::Carry)
|
||||
processor.registers.get_flag_bit(register::Flag::Carry),
|
||||
)
|
||||
.map_err(|err| match err {
|
||||
arithutil::Error::InvalidCarryBit(value) => Error::InvalidCarryFlagValue(value),
|
||||
|
@ -146,6 +151,15 @@ impl InstructionRunner<EightBitArithmeticInstruction> for EightBitArithmeticRunn
|
|||
processor.registers.a = result;
|
||||
set_addition_flags(processor, result, half_carry, carry);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
EightBitArithmeticInstruction::SubSingleRegisterFromA { src } => {
|
||||
let a_value = processor.registers.a;
|
||||
let src_register_value = processor.registers.get_single_8bit_register(src);
|
||||
let (result, half_carry, full_carry) = a_value.sub_with_carry(src_register_value);
|
||||
processor.registers.a = result;
|
||||
set_subtraction_flags(processor, result, half_carry, full_carry);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -169,3 +183,10 @@ fn set_addition_flags(processor: &mut Processor, total: u8, half_carry: bool, ca
|
|||
.registers
|
||||
.set_flag_bit(register::Flag::Carry, carry.into());
|
||||
}
|
||||
|
||||
fn set_subtraction_flags(processor: &mut Processor, total: u8, half_carry: bool, carry: bool) {
|
||||
set_addition_flags(processor, total, half_carry, carry);
|
||||
processor
|
||||
.registers
|
||||
.set_flag_bit(register::Flag::Subtract, 1);
|
||||
}
|
||||
|
|
|
@ -63,18 +63,28 @@ pub trait CarryingAdd<R, O> {
|
|||
fn add_with_carry(self, rhs: R) -> (O, bool, bool);
|
||||
}
|
||||
|
||||
/// `CarryingSub` describes a type that can perform addition to produce the carry flags needed for the operation
|
||||
/// of the gameboy
|
||||
pub trait CarryingSub<R, O> {
|
||||
/// `sub_with_carry` will perform a sub operation, and then return the result, whether or not a half carry was
|
||||
/// performed, and whether or not a full carry was performed. This can be thought of analgous to
|
||||
/// [`u8::overflowing_sub`] (or `overflowing_sub` on any of the other numeric types), but with the addition of
|
||||
/// the half carry flag
|
||||
fn sub_with_carry(self, rhs: R) -> (O, bool, bool);
|
||||
}
|
||||
|
||||
impl CarryingAdd<u8, u8> for u8 {
|
||||
fn add_with_carry(self, rhs: u8) -> (u8, bool, bool) {
|
||||
let (total, carry) = self.overflowing_add(rhs);
|
||||
|
||||
(total, did_8bit_half_carry(self, rhs), carry)
|
||||
(total, did_8bit_add_half_carry(self, rhs), carry)
|
||||
}
|
||||
}
|
||||
|
||||
impl CarryingAdd<u8, u16> for u16 {
|
||||
fn add_with_carry(self, rhs: u8) -> (u16, bool, bool) {
|
||||
let (total, _16_bit_carry) = self.overflowing_add(rhs.into());
|
||||
let half_carry = did_8bit_half_carry(self, rhs);
|
||||
let half_carry = did_8bit_add_half_carry(self, rhs);
|
||||
let full_carry = did_8bit_full_carry(self, rhs);
|
||||
|
||||
(total, half_carry, full_carry)
|
||||
|
@ -92,14 +102,15 @@ impl CarryingAdd<i8, u16> for u16 {
|
|||
// with some tweaks to handle overflow
|
||||
let carry_adjustment = if !carry && rhs < 0 {
|
||||
// Subtract from the 8th bit
|
||||
!(1_u16 << 8)+1
|
||||
!(1_u16 << 8) + 1
|
||||
} else if carry && rhs > 0 {
|
||||
// Add to the 8th bit
|
||||
1_u16 << 8
|
||||
} else {
|
||||
0
|
||||
};
|
||||
let (total, _final_carry) = u16::from_be_bytes([upper_8_bits, lower_8_bit_total]).overflowing_add(carry_adjustment);
|
||||
let (total, _final_carry) =
|
||||
u16::from_be_bytes([upper_8_bits, lower_8_bit_total]).overflowing_add(carry_adjustment);
|
||||
|
||||
(total, half_carry, carry)
|
||||
}
|
||||
|
@ -114,20 +125,29 @@ impl CarryingAdd<CarriedNumber<u8, u16>, u8> for u8 {
|
|||
|
||||
(
|
||||
smallsized_total,
|
||||
did_8bit_half_carry_including_carry_bit(self, rhs.num, rhs.carry_bit),
|
||||
did_8bit_add_half_carry_including_carry_bit(self, rhs.num, rhs.carry_bit),
|
||||
did_8bit_full_carry_including_carry_bit(self, rhs.num, rhs.carry_bit),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl CarryingSub<u8, u8> for u8 {
|
||||
fn sub_with_carry(self, rhs: u8) -> (u8, bool, bool) {
|
||||
let (total, carry) = self.overflowing_sub(rhs);
|
||||
let half_carry = did_8_bit_sub_half_carry(self, rhs);
|
||||
|
||||
(total, half_carry, carry)
|
||||
}
|
||||
}
|
||||
|
||||
/// `did_8_bit_half_carry` checks whether or not the given addition would have done an 8 bit half carry.
|
||||
// NOTE: These generics are not as generic as they could be. The Into<u16> is just a shortcut because we
|
||||
// only use up to u16s
|
||||
fn did_8bit_half_carry<L: Into<u16>, R: Into<u16>>(lhs: L, rhs: R) -> bool {
|
||||
did_8bit_half_carry_including_carry_bit(lhs, rhs, 0)
|
||||
fn did_8bit_add_half_carry<L: Into<u16>, R: Into<u16>>(lhs: L, rhs: R) -> bool {
|
||||
did_8bit_add_half_carry_including_carry_bit(lhs, rhs, 0)
|
||||
}
|
||||
|
||||
fn did_8bit_half_carry_including_carry_bit<L: Into<u16>, R: Into<u16>>(
|
||||
fn did_8bit_add_half_carry_including_carry_bit<L: Into<u16>, R: Into<u16>>(
|
||||
lhs: L,
|
||||
rhs: R,
|
||||
carry_bit: u8,
|
||||
|
@ -136,7 +156,15 @@ fn did_8bit_half_carry_including_carry_bit<L: Into<u16>, R: Into<u16>>(
|
|||
assert!(carry_bit <= 1, "carry bit must be zero or one");
|
||||
|
||||
// https://stackoverflow.com/a/7261149/1103734
|
||||
(lhs.into() & 0xf) + (rhs.into() & 0xf) + u16::from(carry_bit) > 0xf
|
||||
let four_bit_result = (lhs.into() & 0xf)
|
||||
.wrapping_add(rhs.into() & 0xf)
|
||||
.wrapping_add(u16::from(carry_bit));
|
||||
|
||||
four_bit_result > 0xf
|
||||
}
|
||||
|
||||
fn did_8_bit_sub_half_carry(lhs: u8, rhs: u8) -> bool {
|
||||
(lhs & 0xf).wrapping_sub(rhs & 0xf) > 0xf
|
||||
}
|
||||
|
||||
/// `did_8_bit_full_carry` checks whether or not the given addition would have done an 8 bit carry.
|
||||
|
@ -253,4 +281,14 @@ mod tests {
|
|||
) {
|
||||
assert_eq!(expected, value.add_with_carry(carried_number));
|
||||
}
|
||||
|
||||
#[test_case(0xFF, 0x0F, (0xF0, false, false); "no borrow")]
|
||||
#[test_case(0xF0, 0x0F, (0xE1, true, false); "half borrow")]
|
||||
#[test_case(0x0F, 0xF0, (0x1F, false, true); "full borrow")]
|
||||
#[test_case(0x0E, 0xFF, (0x0F, true, true); "both borrows")]
|
||||
#[test_case(0x00, 0x0F, (0xF1, true, true); "wrapping")]
|
||||
#[test_case(0x00, 0xFF, (0x01, true, true); "wrapping a lot")]
|
||||
fn test_sub_u8_from_u8(lhs: u8, rhs: u8, expected: (u8, bool, bool)) {
|
||||
assert_eq!(expected, lhs.sub_with_carry(rhs));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -383,7 +383,11 @@ fn test_add_a_to_itself_with_carry_value(initial_value: u8, carry_bit: u8, expec
|
|||
#[test_case(0xF0, 0, AdditionOperationFlags{zero: 0, carry: 1, half_carry: 0}; "carry bit")]
|
||||
#[test_case(0x0F, 0, AdditionOperationFlags{zero: 0, carry: 0, half_carry: 1}; "half carry bit")]
|
||||
#[test_case(0x0E, 1, AdditionOperationFlags{zero: 0, carry: 0, half_carry: 1}; "half carry bit with input carry bit")]
|
||||
fn test_add_a_to_itself_with_carry_flags(initial_value: u8, carry_bit: u8, expected_flags: AdditionOperationFlags) {
|
||||
fn test_add_a_to_itself_with_carry_flags(
|
||||
initial_value: u8,
|
||||
carry_bit: u8,
|
||||
expected_flags: AdditionOperationFlags,
|
||||
) {
|
||||
let mut processor = Processor::default();
|
||||
processor.registers.a = initial_value;
|
||||
processor
|
||||
|
@ -472,7 +476,9 @@ fn test_add_hl_addr_to_a_with_carry_value(carry_flag: u8, expected: u8) {
|
|||
.registers
|
||||
.set_combined_register(register::Combined::HL, 0xFFFE);
|
||||
processor.registers.a = 0x0E;
|
||||
processor.registers.set_flag_bit(register::Flag::Carry, carry_flag);
|
||||
processor
|
||||
.registers
|
||||
.set_flag_bit(register::Flag::Carry, carry_flag);
|
||||
|
||||
let data = [0x8E, 0x02];
|
||||
|
||||
|
@ -506,7 +512,9 @@ fn test_add_hl_addr_to_a_with_carry_flags(
|
|||
.set_combined_register(register::Combined::HL, 0xFFFE);
|
||||
|
||||
processor.registers.a = initial_value;
|
||||
processor.registers.set_flag_bit(register::Flag::Carry, carry_bit);
|
||||
processor
|
||||
.registers
|
||||
.set_flag_bit(register::Flag::Carry, carry_bit);
|
||||
|
||||
let data = [0x8E, 0x02];
|
||||
|
||||
|
@ -524,3 +532,106 @@ fn test_add_hl_addr_to_a_with_carry_flags(
|
|||
(register::Flag::Carry, expected_flags.carry),
|
||||
);
|
||||
}
|
||||
|
||||
#[test_case(0x90, 0xFF, 0x0F, register::SingleEightBit::B, 0xF0; "subtract B")]
|
||||
#[test_case(0x91, 0xFF, 0x0F, register::SingleEightBit::C, 0xF0; "subtract C")]
|
||||
#[test_case(0x92, 0xFF, 0x0F, register::SingleEightBit::D, 0xF0; "subtract D")]
|
||||
#[test_case(0x93, 0xFF, 0x0F, register::SingleEightBit::E, 0xF0; "subtract E")]
|
||||
#[test_case(0x94, 0xFF, 0x0F, register::SingleEightBit::H, 0xF0; "subtract H")]
|
||||
#[test_case(0x95, 0xFF, 0x0F, register::SingleEightBit::L, 0xF0; "subtract L")]
|
||||
fn test_sub_register_from_a_value(
|
||||
opcode: u8,
|
||||
a_value: u8,
|
||||
operand: u8,
|
||||
operand_register: register::SingleEightBit,
|
||||
expected: u8,
|
||||
) {
|
||||
let mut processor = Processor::default();
|
||||
processor.registers.a = a_value;
|
||||
processor
|
||||
.registers
|
||||
.set_single_8bit_register(operand_register, operand);
|
||||
|
||||
let data = [opcode, 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(0x90, 0xFF, 0x0F, register::SingleEightBit::B, AdditionOperationFlags { zero: 0, half_carry: 0, carry: 0 }; "no flags from B")]
|
||||
#[test_case(0x90, 0xF0, 0x0F, register::SingleEightBit::B, AdditionOperationFlags { zero: 0, half_carry: 1, carry: 0 }; "half carry from B")]
|
||||
#[test_case(0x90, 0x0F, 0xF0, register::SingleEightBit::B, AdditionOperationFlags { zero: 0, half_carry: 0, carry: 1 }; "full carry from B")]
|
||||
#[test_case(0x90, 0x01, 0x01, register::SingleEightBit::B, AdditionOperationFlags { zero: 1, half_carry: 0, carry: 0 }; "zero from B")]
|
||||
#[test_case(0x91, 0xFF, 0x0F, register::SingleEightBit::C, AdditionOperationFlags { zero: 0, half_carry: 0, carry: 0 }; "no flags from C")]
|
||||
#[test_case(0x91, 0xF0, 0x0F, register::SingleEightBit::C, AdditionOperationFlags { zero: 0, half_carry: 1, carry: 0 }; "half carry from C")]
|
||||
#[test_case(0x91, 0x0F, 0xF0, register::SingleEightBit::C, AdditionOperationFlags { zero: 0, half_carry: 0, carry: 1 }; "full carry from C")]
|
||||
#[test_case(0x91, 0x01, 0x01, register::SingleEightBit::C, AdditionOperationFlags { zero: 1, half_carry: 0, carry: 0 }; "zero from C")]
|
||||
#[test_case(0x92, 0xFF, 0x0F, register::SingleEightBit::D, AdditionOperationFlags { zero: 0, half_carry: 0, carry: 0 }; "no flags from D")]
|
||||
#[test_case(0x92, 0xF0, 0x0F, register::SingleEightBit::D, AdditionOperationFlags { zero: 0, half_carry: 1, carry: 0 }; "half carry from D")]
|
||||
#[test_case(0x92, 0x0F, 0xF0, register::SingleEightBit::D, AdditionOperationFlags { zero: 0, half_carry: 0, carry: 1 }; "full carry from D")]
|
||||
#[test_case(0x92, 0x01, 0x01, register::SingleEightBit::D, AdditionOperationFlags { zero: 1, half_carry: 0, carry: 0 }; "zero from D")]
|
||||
#[test_case(0x93, 0xFF, 0x0F, register::SingleEightBit::E, AdditionOperationFlags { zero: 0, half_carry: 0, carry: 0 }; "no flags from E")]
|
||||
#[test_case(0x93, 0xF0, 0x0F, register::SingleEightBit::E, AdditionOperationFlags { zero: 0, half_carry: 1, carry: 0 }; "half carry from E")]
|
||||
#[test_case(0x93, 0x0F, 0xF0, register::SingleEightBit::E, AdditionOperationFlags { zero: 0, half_carry: 0, carry: 1 }; "full carry from E")]
|
||||
#[test_case(0x93, 0x01, 0x01, register::SingleEightBit::E, AdditionOperationFlags { zero: 1, half_carry: 0, carry: 0 }; "zero from E")]
|
||||
#[test_case(0x94, 0xFF, 0x0F, register::SingleEightBit::H, AdditionOperationFlags { zero: 0, half_carry: 0, carry: 0 }; "no flags from H")]
|
||||
#[test_case(0x94, 0xF0, 0x0F, register::SingleEightBit::H, AdditionOperationFlags { zero: 0, half_carry: 1, carry: 0 }; "half carry from H")]
|
||||
#[test_case(0x94, 0x0F, 0xF0, register::SingleEightBit::H, AdditionOperationFlags { zero: 0, half_carry: 0, carry: 1 }; "full carry from H")]
|
||||
#[test_case(0x94, 0x01, 0x01, register::SingleEightBit::H, AdditionOperationFlags { zero: 1, half_carry: 0, carry: 0 }; "zero from H")]
|
||||
#[test_case(0x95, 0xFF, 0x0F, register::SingleEightBit::L, AdditionOperationFlags { zero: 0, half_carry: 0, carry: 0 }; "no flags from L")]
|
||||
#[test_case(0x95, 0xF0, 0x0F, register::SingleEightBit::L, AdditionOperationFlags { zero: 0, half_carry: 1, carry: 0 }; "half carry from L")]
|
||||
#[test_case(0x95, 0x0F, 0xF0, register::SingleEightBit::L, AdditionOperationFlags { zero: 0, half_carry: 0, carry: 1 }; "full carry from L")]
|
||||
#[test_case(0x95, 0x01, 0x01, register::SingleEightBit::L, AdditionOperationFlags { zero: 1, half_carry: 0, carry: 0 }; "zero from L")]
|
||||
fn test_sub_register_from_a_flags(
|
||||
opcode: u8,
|
||||
a_value: u8,
|
||||
operand: u8,
|
||||
operand_register: register::SingleEightBit,
|
||||
expected_flags: AdditionOperationFlags,
|
||||
) {
|
||||
let mut processor = Processor::default();
|
||||
processor.registers.a = a_value;
|
||||
processor
|
||||
.registers
|
||||
.set_single_8bit_register(operand_register, operand);
|
||||
|
||||
let data = [opcode, 0x02];
|
||||
let (ins, extra_data) =
|
||||
RunnableInstruction::from_data(&data).expect("could not parse instruction");
|
||||
assert_eq!(extra_data, &[0x02]);
|
||||
|
||||
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]
|
||||
fn test_sub_a_from_itself() {
|
||||
let mut processor = Processor::default();
|
||||
processor.registers.a = 0xFF;
|
||||
|
||||
let data = [0x97, 0x02];
|
||||
let (ins, extra_data) =
|
||||
RunnableInstruction::from_data(&data).expect("could not parse instruction");
|
||||
assert_eq!(extra_data, &[0x02]);
|
||||
|
||||
processor.run_instruction(&ins);
|
||||
|
||||
testutil::assert_flags_eq!(
|
||||
processor,
|
||||
(register::Flag::Zero, 1),
|
||||
(register::Flag::Subtract, 1),
|
||||
(register::Flag::HalfCarry, 0),
|
||||
(register::Flag::Carry, 0),
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue