Refactor eight bit arithmetic to extract operand collection
This commit is contained in:
parent
01a68e1415
commit
4fb9144c6d
|
@ -1,12 +1,90 @@
|
||||||
use crate::cpu::Processor;
|
use crate::{cpu::run::Error, cpu::Processor, memory, register};
|
||||||
use crate::register;
|
|
||||||
|
|
||||||
pub use add::EightBitAddRunner;
|
pub use add::EightBitAddRunner;
|
||||||
pub use sub::EightBitSubRunner;
|
pub use sub::EightBitSubRunner;
|
||||||
|
|
||||||
|
use super::arithutil::{self, CarriedNumber};
|
||||||
|
|
||||||
mod add;
|
mod add;
|
||||||
mod sub;
|
mod sub;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
enum EightBitArithmeticOperation {
|
||||||
|
SingleRegisterToA { src: register::SingleEightBit },
|
||||||
|
ImmediateToA { n: u8 },
|
||||||
|
HLAddressToA,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn gather_operands(
|
||||||
|
processor: &mut Processor,
|
||||||
|
operation: EightBitArithmeticOperation,
|
||||||
|
) -> Result<(u8, u8), Error> {
|
||||||
|
match operation {
|
||||||
|
EightBitArithmeticOperation::SingleRegisterToA { src } => {
|
||||||
|
let a_value = processor
|
||||||
|
.registers
|
||||||
|
.get_single_8bit_register(register::SingleEightBit::A);
|
||||||
|
let src_register_value = processor.registers.get_single_8bit_register(src);
|
||||||
|
|
||||||
|
Ok((a_value, src_register_value))
|
||||||
|
}
|
||||||
|
|
||||||
|
EightBitArithmeticOperation::ImmediateToA { n } => {
|
||||||
|
let a_value = processor
|
||||||
|
.registers
|
||||||
|
.get_single_8bit_register(register::SingleEightBit::A);
|
||||||
|
|
||||||
|
Ok((a_value, n))
|
||||||
|
}
|
||||||
|
|
||||||
|
EightBitArithmeticOperation::HLAddressToA => {
|
||||||
|
let a_value = processor
|
||||||
|
.registers
|
||||||
|
.get_single_8bit_register(register::SingleEightBit::A);
|
||||||
|
|
||||||
|
let src_addr = 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 hl_addr_value = processor
|
||||||
|
.memory
|
||||||
|
.get(src_addr.into())
|
||||||
|
.map_err(|err| match err {
|
||||||
|
memory::Error::GetInvalidAddress(bad_addr) => Error::InvalidRegisterAddress(
|
||||||
|
register::SixteenBit::Combined(register::Combined::HL),
|
||||||
|
bad_addr,
|
||||||
|
),
|
||||||
|
err => Error::Unknown(Box::new(err)),
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Ok((a_value, hl_addr_value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn gather_operands_with_carry(
|
||||||
|
processor: &mut Processor,
|
||||||
|
operation: EightBitArithmeticOperation,
|
||||||
|
) -> Result<(u8, CarriedNumber<u8>), Error> {
|
||||||
|
let (lhs, no_carry_rhs) = gather_operands(processor, operation)?;
|
||||||
|
let rhs = attach_carry_bit_to_operand(processor, no_carry_rhs)?;
|
||||||
|
|
||||||
|
Ok((lhs, rhs))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn attach_carry_bit_to_operand(
|
||||||
|
processor: &mut Processor,
|
||||||
|
operand: u8,
|
||||||
|
) -> Result<CarriedNumber<u8>, Error> {
|
||||||
|
let carry_bit = processor.registers.get_flag_bit(register::Flag::Carry);
|
||||||
|
|
||||||
|
CarriedNumber::new(operand, carry_bit).map_err(|err| match err {
|
||||||
|
arithutil::Error::InvalidCarryBit(value) => Error::InvalidCarryFlagValue(value),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
fn set_subtraction_flags(processor: &mut Processor, total: u8, half_carry: bool, carry: bool) {
|
fn set_subtraction_flags(processor: &mut Processor, total: u8, half_carry: bool, carry: bool) {
|
||||||
set_addition_flags(processor, total, half_carry, carry);
|
set_addition_flags(processor, total, half_carry, carry);
|
||||||
processor
|
processor
|
||||||
|
@ -31,3 +109,200 @@ fn set_addition_flags(processor: &mut Processor, total: u8, half_carry: bool, ca
|
||||||
.registers
|
.registers
|
||||||
.set_flag_bit(register::Flag::Carry, carry.into());
|
.set_flag_bit(register::Flag::Carry, carry.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use test_case::test_case;
|
||||||
|
|
||||||
|
#[test_case(register::SingleEightBit::B)]
|
||||||
|
#[test_case(register::SingleEightBit::C)]
|
||||||
|
#[test_case(register::SingleEightBit::D)]
|
||||||
|
#[test_case(register::SingleEightBit::E)]
|
||||||
|
#[test_case(register::SingleEightBit::H)]
|
||||||
|
#[test_case(register::SingleEightBit::L)]
|
||||||
|
fn test_gather_operands_from_single_register_to_a(src_register: register::SingleEightBit) {
|
||||||
|
let mut processor = Processor::default();
|
||||||
|
processor
|
||||||
|
.registers
|
||||||
|
.set_single_8bit_register(register::SingleEightBit::A, 0xAA);
|
||||||
|
processor
|
||||||
|
.registers
|
||||||
|
.set_single_8bit_register(src_register, 0xBB);
|
||||||
|
|
||||||
|
let (lhs, rhs) = gather_operands(
|
||||||
|
&mut processor,
|
||||||
|
EightBitArithmeticOperation::SingleRegisterToA { src: src_register },
|
||||||
|
)
|
||||||
|
.expect("failed to gather operands");
|
||||||
|
|
||||||
|
assert_eq!((0xAA, 0xBB), (lhs, rhs));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_gather_operands_from_single_register_to_a_with_itself() {
|
||||||
|
let mut processor = Processor::default();
|
||||||
|
processor
|
||||||
|
.registers
|
||||||
|
.set_single_8bit_register(register::SingleEightBit::A, 0xAA);
|
||||||
|
|
||||||
|
let (lhs, rhs) = gather_operands(
|
||||||
|
&mut processor,
|
||||||
|
EightBitArithmeticOperation::SingleRegisterToA {
|
||||||
|
src: register::SingleEightBit::A,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.expect("failed to gather operands");
|
||||||
|
|
||||||
|
assert_eq!((0xAA, 0xAA), (lhs, rhs));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_gather_operands_from_immediate_to_a() {
|
||||||
|
let mut processor = Processor::default();
|
||||||
|
processor
|
||||||
|
.registers
|
||||||
|
.set_single_8bit_register(register::SingleEightBit::A, 0xAA);
|
||||||
|
let (lhs, rhs) = gather_operands(
|
||||||
|
&mut processor,
|
||||||
|
EightBitArithmeticOperation::ImmediateToA { n: 0x45 },
|
||||||
|
)
|
||||||
|
.expect("failed to gather operands");
|
||||||
|
|
||||||
|
assert_eq!((0xAA, 0x45), (lhs, rhs));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_gather_operands_from_hl_value_a() {
|
||||||
|
let mut processor = Processor::default();
|
||||||
|
processor
|
||||||
|
.registers
|
||||||
|
.set_single_8bit_register(register::SingleEightBit::A, 0xAA);
|
||||||
|
processor
|
||||||
|
.registers
|
||||||
|
.set_combined_register(register::Combined::HL, 0xFFAA);
|
||||||
|
processor
|
||||||
|
.memory
|
||||||
|
.set(0xFFAA, 0xBB)
|
||||||
|
.expect("failed to set memory value");
|
||||||
|
|
||||||
|
let (lhs, rhs) = gather_operands(&mut processor, EightBitArithmeticOperation::HLAddressToA)
|
||||||
|
.expect("failed to gather operands");
|
||||||
|
|
||||||
|
assert_eq!((0xAA, 0xBB), (lhs, rhs));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test_case(register::SingleEightBit::B, 0; "register b, no carry bit")]
|
||||||
|
#[test_case(register::SingleEightBit::B, 1; "register b, carry bit")]
|
||||||
|
#[test_case(register::SingleEightBit::C, 0; "register c, no carry bit")]
|
||||||
|
#[test_case(register::SingleEightBit::C, 1; "register c, carry bit")]
|
||||||
|
#[test_case(register::SingleEightBit::D, 0; "register d, no carry bit")]
|
||||||
|
#[test_case(register::SingleEightBit::D, 1; "register d, carry bit")]
|
||||||
|
#[test_case(register::SingleEightBit::E, 0; "register e, carry bit")]
|
||||||
|
#[test_case(register::SingleEightBit::E, 1; "register e, no carry bit")]
|
||||||
|
#[test_case(register::SingleEightBit::H, 0; "register h, carry bit")]
|
||||||
|
#[test_case(register::SingleEightBit::H, 1; "register h, no carry bit")]
|
||||||
|
#[test_case(register::SingleEightBit::L, 0; "register l, carry bit")]
|
||||||
|
#[test_case(register::SingleEightBit::L, 1; "register l, no carry bit")]
|
||||||
|
fn test_gather_operands_with_carry_from_single_register_to_a(
|
||||||
|
src_register: register::SingleEightBit,
|
||||||
|
carry_bit: u8,
|
||||||
|
) {
|
||||||
|
let mut processor = Processor::default();
|
||||||
|
processor
|
||||||
|
.registers
|
||||||
|
.set_flag_bit(register::Flag::Carry, carry_bit);
|
||||||
|
processor
|
||||||
|
.registers
|
||||||
|
.set_single_8bit_register(register::SingleEightBit::A, 0xAA);
|
||||||
|
processor
|
||||||
|
.registers
|
||||||
|
.set_single_8bit_register(src_register, 0xBB);
|
||||||
|
|
||||||
|
let (lhs, rhs) = gather_operands_with_carry(
|
||||||
|
&mut processor,
|
||||||
|
EightBitArithmeticOperation::SingleRegisterToA { src: src_register },
|
||||||
|
)
|
||||||
|
.expect("failed to gather operands");
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
(0xAA, CarriedNumber::new(0xBB, carry_bit).unwrap()),
|
||||||
|
(lhs, rhs)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test_case(0; "no carry bit")]
|
||||||
|
#[test_case(1; "carry bit")]
|
||||||
|
fn test_gather_operands_with_carry_from_single_register_to_a_with_itself(carry_bit: u8) {
|
||||||
|
let mut processor = Processor::default();
|
||||||
|
processor
|
||||||
|
.registers
|
||||||
|
.set_flag_bit(register::Flag::Carry, carry_bit);
|
||||||
|
processor
|
||||||
|
.registers
|
||||||
|
.set_single_8bit_register(register::SingleEightBit::A, 0xAA);
|
||||||
|
|
||||||
|
let (lhs, rhs) = gather_operands_with_carry(
|
||||||
|
&mut processor,
|
||||||
|
EightBitArithmeticOperation::SingleRegisterToA {
|
||||||
|
src: register::SingleEightBit::A,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.expect("failed to gather operands");
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
(0xAA, CarriedNumber::new(0xAA, carry_bit).unwrap()),
|
||||||
|
(lhs, rhs)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test_case(0; "no carry bit")]
|
||||||
|
#[test_case(1; "carry bit")]
|
||||||
|
fn test_gather_operands_with_carry_from_immediate_to_a(carry_bit: u8) {
|
||||||
|
let mut processor = Processor::default();
|
||||||
|
processor
|
||||||
|
.registers
|
||||||
|
.set_flag_bit(register::Flag::Carry, carry_bit);
|
||||||
|
processor
|
||||||
|
.registers
|
||||||
|
.set_single_8bit_register(register::SingleEightBit::A, 0xAA);
|
||||||
|
let (lhs, rhs) = gather_operands_with_carry(
|
||||||
|
&mut processor,
|
||||||
|
EightBitArithmeticOperation::ImmediateToA { n: 0x45 },
|
||||||
|
)
|
||||||
|
.expect("failed to gather operands");
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
(0xAA, CarriedNumber::new(0x45, carry_bit).unwrap()),
|
||||||
|
(lhs, rhs)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test_case(0; "no carry bit")]
|
||||||
|
#[test_case(1; "carry bit")]
|
||||||
|
fn test_gather_with_carry_operands_from_hl_value_a(carry_bit: u8) {
|
||||||
|
let mut processor = Processor::default();
|
||||||
|
processor
|
||||||
|
.registers
|
||||||
|
.set_flag_bit(register::Flag::Carry, carry_bit);
|
||||||
|
processor
|
||||||
|
.registers
|
||||||
|
.set_single_8bit_register(register::SingleEightBit::A, 0xAA);
|
||||||
|
processor
|
||||||
|
.registers
|
||||||
|
.set_combined_register(register::Combined::HL, 0xFFAA);
|
||||||
|
processor
|
||||||
|
.memory
|
||||||
|
.set(0xFFAA, 0xBB)
|
||||||
|
.expect("failed to set memory value");
|
||||||
|
|
||||||
|
let (lhs, rhs) =
|
||||||
|
gather_operands_with_carry(&mut processor, EightBitArithmeticOperation::HLAddressToA)
|
||||||
|
.expect("failed to gather operands");
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
(0xAA, CarriedNumber::new(0xBB, carry_bit).unwrap()),
|
||||||
|
(lhs, rhs)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,17 +1,37 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
cpu::{
|
cpu::{
|
||||||
instructions::arith8::EightBitAddInstruction,
|
instructions::arith8::EightBitAddInstruction,
|
||||||
run::{
|
run::{arithutil::CarryingAdd, Error, InstructionRunner},
|
||||||
arithutil::{self, CarriedNumber, CarryingAdd},
|
|
||||||
Error, InstructionRunner,
|
|
||||||
},
|
|
||||||
Processor,
|
Processor,
|
||||||
},
|
},
|
||||||
memory, register,
|
register,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use super::EightBitArithmeticOperation;
|
||||||
|
|
||||||
pub struct EightBitAddRunner;
|
pub struct EightBitAddRunner;
|
||||||
|
|
||||||
|
impl EightBitAddRunner {
|
||||||
|
fn do_add<T>(processor: &mut Processor, lhs: u8, rhs: T)
|
||||||
|
where
|
||||||
|
u8: CarryingAdd<T, Output = u8>,
|
||||||
|
{
|
||||||
|
let result = lhs.add_with_carry(rhs);
|
||||||
|
Self::store_addition_result(processor, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn store_addition_result(
|
||||||
|
processor: &mut Processor,
|
||||||
|
(total, half_carry, carry): (u8, bool, bool),
|
||||||
|
) {
|
||||||
|
processor
|
||||||
|
.registers
|
||||||
|
.set_single_8bit_register(register::SingleEightBit::A, total);
|
||||||
|
|
||||||
|
super::set_addition_flags(processor, total, half_carry, carry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl InstructionRunner<EightBitAddInstruction> for EightBitAddRunner {
|
impl InstructionRunner<EightBitAddInstruction> for EightBitAddRunner {
|
||||||
fn run_instruction(
|
fn run_instruction(
|
||||||
processor: &mut Processor,
|
processor: &mut Processor,
|
||||||
|
@ -19,134 +39,68 @@ impl InstructionRunner<EightBitAddInstruction> for EightBitAddRunner {
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
match *instruction {
|
match *instruction {
|
||||||
EightBitAddInstruction::AddSingleRegisterToA { src } => {
|
EightBitAddInstruction::AddSingleRegisterToA { src } => {
|
||||||
let a_value = processor
|
let (lhs, rhs) = super::gather_operands(
|
||||||
.registers
|
processor,
|
||||||
.get_single_8bit_register(register::SingleEightBit::A);
|
EightBitArithmeticOperation::SingleRegisterToA { src },
|
||||||
let src_value = processor.registers.get_single_8bit_register(src);
|
)?;
|
||||||
|
|
||||||
let result = a_value.add_with_carry(src_value);
|
Self::do_add(processor, lhs, rhs);
|
||||||
store_addition_result(processor, result);
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
EightBitAddInstruction::AddImmediateToA { n } => {
|
EightBitAddInstruction::AddImmediateToA { n } => {
|
||||||
let result = processor
|
let (lhs, rhs) = super::gather_operands(
|
||||||
.registers
|
processor,
|
||||||
.get_single_8bit_register(register::SingleEightBit::A)
|
EightBitArithmeticOperation::ImmediateToA { n },
|
||||||
.add_with_carry(n);
|
)?;
|
||||||
|
|
||||||
store_addition_result(processor, result);
|
Self::do_add(processor, lhs, rhs);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
EightBitAddInstruction::AddHLAddressToA => {
|
EightBitAddInstruction::AddHLAddressToA => {
|
||||||
let src_addr = processor
|
let (lhs, rhs) =
|
||||||
.registers
|
super::gather_operands(processor, EightBitArithmeticOperation::HLAddressToA)?;
|
||||||
.get_combined_register(register::Combined::HL);
|
|
||||||
|
|
||||||
// While this is true, we really do want a wildcard match in map_err
|
Self::do_add(processor, lhs, rhs);
|
||||||
#[allow(clippy::match_wildcard_for_single_variants)]
|
|
||||||
let hl_addr_value =
|
|
||||||
processor
|
|
||||||
.memory
|
|
||||||
.get(src_addr.into())
|
|
||||||
.map_err(|err| match err {
|
|
||||||
memory::Error::GetInvalidAddress(bad_addr) => {
|
|
||||||
Error::InvalidRegisterAddress(
|
|
||||||
register::SixteenBit::Combined(register::Combined::HL),
|
|
||||||
bad_addr,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
err => Error::Unknown(Box::new(err)),
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let result = processor
|
|
||||||
.registers
|
|
||||||
.get_single_8bit_register(register::SingleEightBit::A)
|
|
||||||
.add_with_carry(hl_addr_value);
|
|
||||||
|
|
||||||
store_addition_result(processor, result);
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
EightBitAddInstruction::AddSingleRegisterToAWithCarry { src } => {
|
EightBitAddInstruction::AddSingleRegisterToAWithCarry { src } => {
|
||||||
let src_register_value = processor.registers.get_single_8bit_register(src);
|
let (lhs, rhs) = super::gather_operands_with_carry(
|
||||||
|
processor,
|
||||||
|
EightBitArithmeticOperation::SingleRegisterToA { src },
|
||||||
|
)?;
|
||||||
|
|
||||||
let carried_operand = CarriedNumber::new(
|
Self::do_add(processor, lhs, rhs);
|
||||||
src_register_value,
|
|
||||||
processor.registers.get_flag_bit(register::Flag::Carry),
|
|
||||||
)
|
|
||||||
.map_err(|err| match err {
|
|
||||||
arithutil::Error::InvalidCarryBit(value) => Error::InvalidCarryFlagValue(value),
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let result = processor.registers.a.add_with_carry(carried_operand);
|
|
||||||
store_addition_result(processor, result);
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
EightBitAddInstruction::AddImmediateToAWithCarry { n } => {
|
EightBitAddInstruction::AddImmediateToAWithCarry { n } => {
|
||||||
let a_value = processor.registers.a;
|
let (lhs, rhs) = super::gather_operands_with_carry(
|
||||||
let carried_operand =
|
processor,
|
||||||
CarriedNumber::new(n, processor.registers.get_flag_bit(register::Flag::Carry))
|
EightBitArithmeticOperation::ImmediateToA { n },
|
||||||
.map_err(|err| match err {
|
)?;
|
||||||
arithutil::Error::InvalidCarryBit(value) => {
|
|
||||||
Error::InvalidCarryFlagValue(value)
|
|
||||||
}
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let result = a_value.add_with_carry(carried_operand);
|
Self::do_add(processor, lhs, rhs);
|
||||||
store_addition_result(processor, result);
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
EightBitAddInstruction::AddHLAddressToAWithCarry => {
|
EightBitAddInstruction::AddHLAddressToAWithCarry => {
|
||||||
let a_value = processor.registers.a;
|
let (lhs, rhs) = super::gather_operands_with_carry(
|
||||||
let src_address = processor
|
processor,
|
||||||
.registers
|
EightBitArithmeticOperation::HLAddressToA,
|
||||||
.get_combined_register(register::Combined::HL);
|
)?;
|
||||||
|
|
||||||
// While this is true, we really do want a wildcard match in map_err
|
Self::do_add(processor, lhs, rhs);
|
||||||
#[allow(clippy::match_wildcard_for_single_variants)]
|
|
||||||
let stored_value =
|
|
||||||
processor
|
|
||||||
.memory
|
|
||||||
.get(src_address.into())
|
|
||||||
.map_err(|err| match err {
|
|
||||||
memory::Error::GetInvalidAddress(bad_addr) => {
|
|
||||||
Error::InvalidRegisterAddress(
|
|
||||||
register::SixteenBit::Combined(register::Combined::HL),
|
|
||||||
bad_addr,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
err => Error::Unknown(Box::new(err)),
|
|
||||||
})?;
|
|
||||||
let carried_operand = CarriedNumber::new(
|
|
||||||
stored_value,
|
|
||||||
processor.registers.get_flag_bit(register::Flag::Carry),
|
|
||||||
)
|
|
||||||
.map_err(|err| match err {
|
|
||||||
arithutil::Error::InvalidCarryBit(value) => Error::InvalidCarryFlagValue(value),
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let result = a_value.add_with_carry(carried_operand);
|
|
||||||
store_addition_result(processor, result);
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn store_addition_result(processor: &mut Processor, (total, half_carry, carry): (u8, bool, bool)) {
|
|
||||||
processor
|
|
||||||
.registers
|
|
||||||
.set_single_8bit_register(register::SingleEightBit::A, total);
|
|
||||||
|
|
||||||
super::set_addition_flags(processor, total, half_carry, carry);
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,17 +1,37 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
cpu::{
|
cpu::{
|
||||||
instructions::arith8::EightBitSubInstruction,
|
instructions::arith8::EightBitSubInstruction,
|
||||||
run::{
|
run::{arithutil::CarryingSub, Error, InstructionRunner},
|
||||||
arithutil::{self, CarriedNumber, CarryingSub},
|
|
||||||
Error, InstructionRunner,
|
|
||||||
},
|
|
||||||
Processor,
|
Processor,
|
||||||
},
|
},
|
||||||
memory, register,
|
register,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use super::EightBitArithmeticOperation;
|
||||||
|
|
||||||
pub struct EightBitSubRunner;
|
pub struct EightBitSubRunner;
|
||||||
|
|
||||||
|
impl EightBitSubRunner {
|
||||||
|
fn do_sub<T>(processor: &mut Processor, lhs: u8, rhs: T)
|
||||||
|
where
|
||||||
|
u8: CarryingSub<T, Output = u8>,
|
||||||
|
{
|
||||||
|
let result = lhs.sub_with_carry(rhs);
|
||||||
|
Self::store_subtraction_result(processor, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn store_subtraction_result(
|
||||||
|
processor: &mut Processor,
|
||||||
|
(total, half_carry, carry): (u8, bool, bool),
|
||||||
|
) {
|
||||||
|
processor
|
||||||
|
.registers
|
||||||
|
.set_single_8bit_register(register::SingleEightBit::A, total);
|
||||||
|
|
||||||
|
super::set_subtraction_flags(processor, total, half_carry, carry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl InstructionRunner<EightBitSubInstruction> for EightBitSubRunner {
|
impl InstructionRunner<EightBitSubInstruction> for EightBitSubRunner {
|
||||||
fn run_instruction(
|
fn run_instruction(
|
||||||
processor: &mut Processor,
|
processor: &mut Processor,
|
||||||
|
@ -19,127 +39,68 @@ impl InstructionRunner<EightBitSubInstruction> for EightBitSubRunner {
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
match *instruction {
|
match *instruction {
|
||||||
EightBitSubInstruction::SubSingleRegisterFromA { src } => {
|
EightBitSubInstruction::SubSingleRegisterFromA { src } => {
|
||||||
let a_value = processor.registers.a;
|
let (lhs, rhs) = super::gather_operands(
|
||||||
let src_register_value = processor.registers.get_single_8bit_register(src);
|
processor,
|
||||||
let result = a_value.sub_with_carry(src_register_value);
|
EightBitArithmeticOperation::SingleRegisterToA { src },
|
||||||
store_subtraction_result(processor, result);
|
)?;
|
||||||
|
|
||||||
|
Self::do_sub(processor, lhs, rhs);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
EightBitSubInstruction::SubImmediateFromA { n } => {
|
EightBitSubInstruction::SubImmediateFromA { n } => {
|
||||||
let a_value = processor.registers.a;
|
let (lhs, rhs) = super::gather_operands(
|
||||||
|
processor,
|
||||||
|
EightBitArithmeticOperation::ImmediateToA { n },
|
||||||
|
)?;
|
||||||
|
|
||||||
let result = a_value.sub_with_carry(n);
|
Self::do_sub(processor, lhs, rhs);
|
||||||
store_subtraction_result(processor, result);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
EightBitSubInstruction::SubSingleRegisterFromAWithCarry { src } => {
|
|
||||||
let a_value = processor.registers.a;
|
|
||||||
let src_register_value = processor.registers.get_single_8bit_register(src);
|
|
||||||
let carry_bit = processor.registers.get_flag_bit(register::Flag::Carry);
|
|
||||||
let carried_operand =
|
|
||||||
CarriedNumber::new(src_register_value, 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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
EightBitSubInstruction::SubHLAddressFromA => {
|
EightBitSubInstruction::SubHLAddressFromA => {
|
||||||
let a_value = processor.registers.a;
|
let (lhs, rhs) =
|
||||||
let src_addr = processor
|
super::gather_operands(processor, EightBitArithmeticOperation::HLAddressToA)?;
|
||||||
.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 hl_addr_value =
|
|
||||||
processor
|
|
||||||
.memory
|
|
||||||
.get(src_addr.into())
|
|
||||||
.map_err(|err| match err {
|
|
||||||
memory::Error::GetInvalidAddress(bad_addr) => {
|
|
||||||
Error::InvalidRegisterAddress(
|
|
||||||
register::SixteenBit::Combined(register::Combined::HL),
|
|
||||||
bad_addr,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
err => Error::Unknown(Box::new(err)),
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let result = a_value.sub_with_carry(hl_addr_value);
|
Self::do_sub(processor, lhs, rhs);
|
||||||
store_subtraction_result(processor, result);
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
EightBitSubInstruction::SubHLAddressFromAWithCarry => {
|
|
||||||
let a_value = processor.registers.a;
|
|
||||||
let carry_bit = processor.registers.get_flag_bit(register::Flag::Carry);
|
|
||||||
let src_addr = 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 hl_addr_value =
|
|
||||||
processor
|
|
||||||
.memory
|
|
||||||
.get(src_addr.into())
|
|
||||||
.map_err(|err| match err {
|
|
||||||
memory::Error::GetInvalidAddress(bad_addr) => {
|
|
||||||
Error::InvalidRegisterAddress(
|
|
||||||
register::SixteenBit::Combined(register::Combined::HL),
|
|
||||||
bad_addr,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
err => Error::Unknown(Box::new(err)),
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let carried_operand =
|
EightBitSubInstruction::SubSingleRegisterFromAWithCarry { src } => {
|
||||||
CarriedNumber::new(hl_addr_value, carry_bit).map_err(|err| match err {
|
let (lhs, rhs) = super::gather_operands_with_carry(
|
||||||
arithutil::Error::InvalidCarryBit(value) => {
|
processor,
|
||||||
Error::InvalidCarryFlagValue(value)
|
EightBitArithmeticOperation::SingleRegisterToA { src },
|
||||||
}
|
)?;
|
||||||
})?;
|
|
||||||
|
|
||||||
let result = a_value.sub_with_carry(carried_operand);
|
Self::do_sub(processor, lhs, rhs);
|
||||||
store_subtraction_result(processor, result);
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
EightBitSubInstruction::SubImmediateFromAWithCarry { n } => {
|
EightBitSubInstruction::SubImmediateFromAWithCarry { n } => {
|
||||||
let a_value = processor.registers.a;
|
let (lhs, rhs) = super::gather_operands_with_carry(
|
||||||
let carry_bit = processor.registers.get_flag_bit(register::Flag::Carry);
|
processor,
|
||||||
let carried_operand =
|
EightBitArithmeticOperation::ImmediateToA { n },
|
||||||
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);
|
Self::do_sub(processor, lhs, rhs);
|
||||||
store_subtraction_result(processor, result);
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
EightBitSubInstruction::SubHLAddressFromAWithCarry => {
|
||||||
|
let (lhs, rhs) = super::gather_operands_with_carry(
|
||||||
|
processor,
|
||||||
|
EightBitArithmeticOperation::HLAddressToA,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Self::do_sub(processor, lhs, rhs);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn store_subtraction_result(
|
|
||||||
processor: &mut Processor,
|
|
||||||
(total, half_carry, carry): (u8, bool, bool),
|
|
||||||
) {
|
|
||||||
processor
|
|
||||||
.registers
|
|
||||||
.set_single_8bit_register(register::SingleEightBit::A, total);
|
|
||||||
|
|
||||||
super::set_subtraction_flags(processor, total, half_carry, carry);
|
|
||||||
}
|
|
||||||
|
|
|
@ -50,25 +50,29 @@ where
|
||||||
|
|
||||||
/// `CarryingAdd` describes a type that can perform addition to produce the carry flags needed for the operation
|
/// `CarryingAdd` describes a type that can perform addition to produce the carry flags needed for the operation
|
||||||
/// of the gameboy
|
/// of the gameboy
|
||||||
pub trait CarryingAdd<R, O> {
|
pub trait CarryingAdd<R = Self> {
|
||||||
|
type Output;
|
||||||
|
|
||||||
/// `add_with_carry` will perform an add operation, and then return the result, whether or not a half carry was
|
/// `add_with_carry` will perform an add 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
|
/// performed, and whether or not a full carry was performed. This can be thought of analgous to
|
||||||
/// [`u8::overflowing_add`] (or `overflowing_add` on any of the other numeric types), but with the addition of
|
/// [`u8::overflowing_add`] (or `overflowing_add` on any of the other numeric types), but with the addition of
|
||||||
/// the half carry flag
|
/// the half carry flag
|
||||||
fn add_with_carry(self, rhs: R) -> (O, bool, bool);
|
fn add_with_carry(self, rhs: R) -> (Self::Output, bool, bool);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `CarryingSub` describes a type that can perform addition to produce the carry flags needed for the operation
|
/// `CarryingSub` describes a type that can perform addition to produce the carry flags needed for the operation
|
||||||
/// of the gameboy
|
/// of the gameboy
|
||||||
pub trait CarryingSub<R, O> {
|
pub trait CarryingSub<R = Self> {
|
||||||
|
type Output;
|
||||||
/// `sub_with_carry` will perform a sub operation, and then return the result, whether or not a half carry was
|
/// `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
|
/// 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
|
/// [`u8::overflowing_sub`] (or `overflowing_sub` on any of the other numeric types), but with the addition of
|
||||||
/// the half carry flag
|
/// the half carry flag
|
||||||
fn sub_with_carry(self, rhs: R) -> (O, bool, bool);
|
fn sub_with_carry(self, rhs: R) -> (Self::Output, bool, bool);
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CarryingAdd<u8, u8> for u8 {
|
impl CarryingAdd for u8 {
|
||||||
|
type Output = u8;
|
||||||
fn add_with_carry(self, rhs: u8) -> (u8, bool, bool) {
|
fn add_with_carry(self, rhs: u8) -> (u8, bool, bool) {
|
||||||
let (total, carry) = self.overflowing_add(rhs);
|
let (total, carry) = self.overflowing_add(rhs);
|
||||||
|
|
||||||
|
@ -76,7 +80,9 @@ impl CarryingAdd<u8, u8> for u8 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CarryingAdd<u8, u16> for u16 {
|
impl CarryingAdd<u8> for u16 {
|
||||||
|
type Output = u16;
|
||||||
|
|
||||||
fn add_with_carry(self, rhs: u8) -> (u16, bool, bool) {
|
fn add_with_carry(self, rhs: u8) -> (u16, bool, bool) {
|
||||||
let (total, _16_bit_carry) = self.overflowing_add(rhs.into());
|
let (total, _16_bit_carry) = self.overflowing_add(rhs.into());
|
||||||
let half_carry = did_8bit_add_half_carry(self, rhs);
|
let half_carry = did_8bit_add_half_carry(self, rhs);
|
||||||
|
@ -86,7 +92,9 @@ impl CarryingAdd<u8, u16> for u16 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CarryingAdd<i8, u16> for u16 {
|
impl CarryingAdd<i8> for u16 {
|
||||||
|
type Output = u16;
|
||||||
|
|
||||||
fn add_with_carry(self, rhs: i8) -> (u16, bool, bool) {
|
fn add_with_carry(self, rhs: i8) -> (u16, bool, bool) {
|
||||||
let [upper_8_bits, lower_8_bits] = self.to_be_bytes();
|
let [upper_8_bits, lower_8_bits] = self.to_be_bytes();
|
||||||
let operand = twos_comp_abs(rhs);
|
let operand = twos_comp_abs(rhs);
|
||||||
|
@ -111,7 +119,9 @@ impl CarryingAdd<i8, u16> for u16 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CarryingAdd<CarriedNumber<u8>, u8> for u8 {
|
impl CarryingAdd<CarriedNumber<u8>> for u8 {
|
||||||
|
type Output = u8;
|
||||||
|
|
||||||
fn add_with_carry(self, rhs: CarriedNumber<u8>) -> (u8, bool, bool) {
|
fn add_with_carry(self, rhs: CarriedNumber<u8>) -> (u8, bool, bool) {
|
||||||
let total = rhs.value() + u16::from(self);
|
let total = rhs.value() + u16::from(self);
|
||||||
// Given this is adding two u8s, the largest value we can produce is 0x01FF (both operands 0xFF, plus a carry bit)
|
// Given this is adding two u8s, the largest value we can produce is 0x01FF (both operands 0xFF, plus a carry bit)
|
||||||
|
@ -126,7 +136,9 @@ impl CarryingAdd<CarriedNumber<u8>, u8> for u8 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CarryingSub<u8, u8> for u8 {
|
impl CarryingSub<u8> for u8 {
|
||||||
|
type Output = u8;
|
||||||
|
|
||||||
fn sub_with_carry(self, rhs: u8) -> (u8, bool, bool) {
|
fn sub_with_carry(self, rhs: u8) -> (u8, bool, bool) {
|
||||||
let (total, carry) = self.overflowing_sub(rhs);
|
let (total, carry) = self.overflowing_sub(rhs);
|
||||||
let half_carry = did_8bit_sub_half_carry(self, rhs);
|
let half_carry = did_8bit_sub_half_carry(self, rhs);
|
||||||
|
@ -135,7 +147,9 @@ impl CarryingSub<u8, u8> for u8 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CarryingSub<CarriedNumber<u8>, u8> for u8 {
|
impl CarryingSub<CarriedNumber<u8>> for u8 {
|
||||||
|
type Output = u8;
|
||||||
|
|
||||||
fn sub_with_carry(self, rhs: CarriedNumber<u8>) -> (u8, bool, bool) {
|
fn sub_with_carry(self, rhs: CarriedNumber<u8>) -> (u8, bool, bool) {
|
||||||
let total = self.wrapping_sub(rhs.num).wrapping_sub(rhs.carry_bit);
|
let total = self.wrapping_sub(rhs.num).wrapping_sub(rhs.carry_bit);
|
||||||
let half_carry = did_8bit_sub_half_carry_including_carry_bit(self, rhs.num, rhs.carry_bit);
|
let half_carry = did_8bit_sub_half_carry_including_carry_bit(self, rhs.num, rhs.carry_bit);
|
||||||
|
|
Loading…
Reference in a new issue