add support for dec instructions

old-bit-manip
Nick Krichevsky 2023-06-17 14:24:21 -04:00
parent a38be4420a
commit 915f33307e
15 changed files with 147 additions and 37 deletions

View File

@ -12,6 +12,7 @@ pub enum Operation {
Or,
Compare,
Inc,
Dec,
}
#[derive(Debug, Copy, Clone)]

View File

@ -48,6 +48,7 @@ pub fn next_instruction(data: &View) -> ParseResult {
arith8::or::Parser::parse_opcode,
arith8::compare::Parser::parse_opcode,
arith8::inc::Parser::parse_opcode,
arith8::dec::Parser::parse_opcode,
];
for parse_func in parse_funcs {

View File

@ -12,6 +12,7 @@ use super::ParseOutput;
pub mod add;
pub mod and;
pub mod compare;
pub mod dec;
pub mod inc;
pub mod or;
pub mod sub;

View File

@ -0,0 +1,37 @@
use crate::{
cpu::{
instructions::arith8::Operation,
parse::{self, Error, OpcodeParser, ParseOutput, ParseResult},
},
memory::View,
register,
};
pub struct Parser;
impl OpcodeParser for Parser {
fn parse_opcode(data: &View) -> ParseResult {
let opcode = parse::get_opcode_from_data(data);
match opcode {
0x05 => Ok(build_dec_single_register_data(register::SingleEightBit::B)),
0x0D => Ok(build_dec_single_register_data(register::SingleEightBit::C)),
0x15 => Ok(build_dec_single_register_data(register::SingleEightBit::D)),
0x1D => Ok(build_dec_single_register_data(register::SingleEightBit::E)),
0x25 => Ok(build_dec_single_register_data(register::SingleEightBit::H)),
0x2D => Ok(build_dec_single_register_data(register::SingleEightBit::L)),
0x3D => Ok(build_dec_single_register_data(register::SingleEightBit::A)),
0x35 => Ok(build_dec_hl_value_data()),
_ => Err(Error::UnknownOpcode(opcode)),
}
}
}
fn build_dec_single_register_data(src: register::SingleEightBit) -> ParseOutput {
super::build_instruction_between_register_and_a_data(Operation::Dec, src)
}
fn build_dec_hl_value_data() -> ParseOutput {
super::build_instruction_between_hl_value_and_a_data(Operation::Dec)
}

View File

@ -48,6 +48,8 @@ impl InstructionRunner<EightBitArithmeticInstruction> for EightBitArithmeticRunn
Operation::Compare => binary::run_compare(processor, instruction.operand),
Operation::Inc => unary::run_inc(processor, instruction.operand),
Operation::Dec => unary::run_dec(processor, instruction.operand),
}
}
}

View File

@ -1,7 +1,7 @@
use crate::{
cpu::{
instructions::arith8::Operand,
run::{arithutil::CarryingAdd, Error},
run::{arithutil::{CarryingAdd, CarryingSub}, Error},
Processor,
},
memory, register,
@ -26,6 +26,23 @@ pub fn run_inc(processor: &mut Processor, operand: Operand) -> Result<(), Error>
})
}
pub fn run_dec(processor: &mut Processor, operand: Operand) -> Result<(), Error> {
let current_carry_flag = processor.registers.get_flag_bit(register::Flag::Carry);
run_operation(processor, operand, |value| {
let (result, half_carry, _full_carry) = value.sub_with_carry(1);
OperationOutput {
value: result,
flags: OperationFlagOutput {
zero_flag: (result == 0).into(),
half_carry_flag: half_carry.into(),
carry_flag: current_carry_flag,
subtract_flag: 1,
},
}
})
}
fn run_operation<F>(processor: &mut Processor, operand: Operand, operation: F) -> Result<(), Error>
where
F: Fn(u8) -> OperationOutput,

View File

@ -15,6 +15,7 @@ struct AdditionOperationFlags {
struct IncrementOperationFlags {
zero: u8,
subtract: u8,
half_carry: u8,
}
@ -2149,7 +2150,22 @@ fn test_comparing_immediate_value_to_a(
#[test_case(0x2C, register::SingleEightBit::L, 0xFF, 0x00; "add one with wrapping, register L")]
#[test_case(0x3C, register::SingleEightBit::A, 0x05, 0x06; "add one, register A")]
#[test_case(0x3C, register::SingleEightBit::A, 0xFF, 0x00; "add one with wrapping, register A")]
fn test_increment_single_register_value(
// decrement
#[test_case(0x05, register::SingleEightBit::B, 0x05, 0x04; "sub one, register B")]
#[test_case(0x05, register::SingleEightBit::B, 0x00, 0xFF; "sub one with wrapping, register B")]
#[test_case(0x0D, register::SingleEightBit::C, 0x05, 0x04; "sub one, register C")]
#[test_case(0x0D, register::SingleEightBit::C, 0x00, 0xFF; "sub one with wrapping, register C")]
#[test_case(0x15, register::SingleEightBit::D, 0x05, 0x04; "sub one, register D")]
#[test_case(0x15, register::SingleEightBit::D, 0x00, 0xFF; "sub one with wrapping, register D")]
#[test_case(0x1D, register::SingleEightBit::E, 0x05, 0x04; "sub one, register E")]
#[test_case(0x1D, register::SingleEightBit::E, 0x00, 0xFF; "sub one with wrapping, register E")]
#[test_case(0x25, register::SingleEightBit::H, 0x05, 0x04; "sub one, register H")]
#[test_case(0x25, register::SingleEightBit::H, 0x00, 0xFF; "sub one with wrapping, register H")]
#[test_case(0x2D, register::SingleEightBit::L, 0x05, 0x04; "sub one, register L")]
#[test_case(0x2D, register::SingleEightBit::L, 0x00, 0xFF; "sub one with wrapping, register L")]
#[test_case(0x3D, register::SingleEightBit::A, 0x05, 0x04; "sub one, register A")]
#[test_case(0x3D, register::SingleEightBit::A, 0x00, 0xFF; "sub one with wrapping, register A")]
fn test_increment_decrement_single_register_value(
opcode: u8,
register: register::SingleEightBit,
initial_value: u8,
@ -2172,28 +2188,57 @@ fn test_increment_single_register_value(
);
}
#[test_case(0x04, register::SingleEightBit::B, 0x05, IncrementOperationFlags { zero: 0, half_carry: 0 }; "increment, register B")]
#[test_case(0x04, register::SingleEightBit::B, 0xFF, IncrementOperationFlags { zero: 1, half_carry: 1 }; "increment with wrapping, register B")]
#[test_case(0x04, register::SingleEightBit::B, 0x0F, IncrementOperationFlags { zero: 0, half_carry: 1 }; "increment with half carry, register B")]
#[test_case(0x0C, register::SingleEightBit::C, 0x05, IncrementOperationFlags { zero: 0, half_carry: 0 }; "increment, register C")]
#[test_case(0x0C, register::SingleEightBit::C, 0xFF, IncrementOperationFlags { zero: 1, half_carry: 1 }; "increment with wrapping, register C")]
#[test_case(0x0C, register::SingleEightBit::C, 0x0F, IncrementOperationFlags { zero: 0, half_carry: 1 }; "increment with half carry, register C")]
#[test_case(0x14, register::SingleEightBit::D, 0x05, IncrementOperationFlags { zero: 0, half_carry: 0 }; "increment, register D")]
#[test_case(0x14, register::SingleEightBit::D, 0xFF, IncrementOperationFlags { zero: 1, half_carry: 1 }; "increment with wrapping, register D")]
#[test_case(0x14, register::SingleEightBit::D, 0x0F, IncrementOperationFlags { zero: 0, half_carry: 1 }; "increment with half carry, register D")]
#[test_case(0x1C, register::SingleEightBit::E, 0x05, IncrementOperationFlags { zero: 0, half_carry: 0 }; "increment, register E")]
#[test_case(0x1C, register::SingleEightBit::E, 0xFF, IncrementOperationFlags { zero: 1, half_carry: 1 }; "increment with wrapping, register E")]
#[test_case(0x1C, register::SingleEightBit::E, 0x0F, IncrementOperationFlags { zero: 0, half_carry: 1 }; "increment with half carry, register E")]
#[test_case(0x24, register::SingleEightBit::H, 0x05, IncrementOperationFlags { zero: 0, half_carry: 0 }; "increment, register H")]
#[test_case(0x24, register::SingleEightBit::H, 0xFF, IncrementOperationFlags { zero: 1, half_carry: 1 }; "increment with wrapping, register H")]
#[test_case(0x24, register::SingleEightBit::H, 0x0F, IncrementOperationFlags { zero: 0, half_carry: 1 }; "increment with half carry, register H")]
#[test_case(0x2C, register::SingleEightBit::L, 0x05, IncrementOperationFlags { zero: 0, half_carry: 0 }; "increment, register L")]
#[test_case(0x2C, register::SingleEightBit::L, 0xFF, IncrementOperationFlags { zero: 1, half_carry: 1 }; "increment with wrapping, register L")]
#[test_case(0x2C, register::SingleEightBit::L, 0x0F, IncrementOperationFlags { zero: 0, half_carry: 1 }; "increment with half carry, register L")]
#[test_case(0x3C, register::SingleEightBit::A, 0x05, IncrementOperationFlags { zero: 0, half_carry: 0 }; "increment, register A")]
#[test_case(0x3C, register::SingleEightBit::A, 0xFF, IncrementOperationFlags { zero: 1, half_carry: 1 }; "increment with wrapping, register A")]
#[test_case(0x3C, register::SingleEightBit::A, 0x0F, IncrementOperationFlags { zero: 0, half_carry: 1 }; "increment with half carry, register A")]
fn test_increment_single_register_flags(
#[test_case(0x04, register::SingleEightBit::B, 0x05, IncrementOperationFlags { zero: 0, half_carry: 0, subtract: 0}; "increment, register B")]
#[test_case(0x04, register::SingleEightBit::B, 0xFF, IncrementOperationFlags { zero: 1, half_carry: 1, subtract: 0}; "increment with wrapping, register B")]
#[test_case(0x04, register::SingleEightBit::B, 0x0F, IncrementOperationFlags { zero: 0, half_carry: 1, subtract: 0 }; "increment with half carry, register B")]
#[test_case(0x0C, register::SingleEightBit::C, 0x05, IncrementOperationFlags { zero: 0, half_carry: 0, subtract: 0 }; "increment, register C")]
#[test_case(0x0C, register::SingleEightBit::C, 0xFF, IncrementOperationFlags { zero: 1, half_carry: 1, subtract: 0 }; "increment with wrapping, register C")]
#[test_case(0x0C, register::SingleEightBit::C, 0x0F, IncrementOperationFlags { zero: 0, half_carry: 1, subtract: 0 }; "increment with half carry, register C")]
#[test_case(0x14, register::SingleEightBit::D, 0x05, IncrementOperationFlags { zero: 0, half_carry: 0, subtract: 0 }; "increment, register D")]
#[test_case(0x14, register::SingleEightBit::D, 0xFF, IncrementOperationFlags { zero: 1, half_carry: 1, subtract: 0 }; "increment with wrapping, register D")]
#[test_case(0x14, register::SingleEightBit::D, 0x0F, IncrementOperationFlags { zero: 0, half_carry: 1, subtract: 0 }; "increment with half carry, register D")]
#[test_case(0x1C, register::SingleEightBit::E, 0x05, IncrementOperationFlags { zero: 0, half_carry: 0, subtract: 0 }; "increment, register E")]
#[test_case(0x1C, register::SingleEightBit::E, 0xFF, IncrementOperationFlags { zero: 1, half_carry: 1, subtract: 0 }; "increment with wrapping, register E")]
#[test_case(0x1C, register::SingleEightBit::E, 0x0F, IncrementOperationFlags { zero: 0, half_carry: 1, subtract: 0 }; "increment with half carry, register E")]
#[test_case(0x24, register::SingleEightBit::H, 0x05, IncrementOperationFlags { zero: 0, half_carry: 0, subtract: 0 }; "increment, register H")]
#[test_case(0x24, register::SingleEightBit::H, 0xFF, IncrementOperationFlags { zero: 1, half_carry: 1, subtract: 0 }; "increment with wrapping, register H")]
#[test_case(0x24, register::SingleEightBit::H, 0x0F, IncrementOperationFlags { zero: 0, half_carry: 1, subtract: 0 }; "increment with half carry, register H")]
#[test_case(0x2C, register::SingleEightBit::L, 0x05, IncrementOperationFlags { zero: 0, half_carry: 0, subtract: 0 }; "increment, register L")]
#[test_case(0x2C, register::SingleEightBit::L, 0xFF, IncrementOperationFlags { zero: 1, half_carry: 1, subtract: 0 }; "increment with wrapping, register L")]
#[test_case(0x2C, register::SingleEightBit::L, 0x0F, IncrementOperationFlags { zero: 0, half_carry: 1, subtract: 0 }; "increment with half carry, register L")]
#[test_case(0x3C, register::SingleEightBit::A, 0x05, IncrementOperationFlags { zero: 0, half_carry: 0, subtract: 0 }; "increment, register A")]
#[test_case(0x3C, register::SingleEightBit::A, 0xFF, IncrementOperationFlags { zero: 1, half_carry: 1, subtract: 0 }; "increment with wrapping, register A")]
#[test_case(0x3C, register::SingleEightBit::A, 0x0F, IncrementOperationFlags { zero: 0, half_carry: 1, subtract: 0 }; "increment with half carry, register A")]
// decrement
#[test_case(0x05, register::SingleEightBit::B, 0x05, IncrementOperationFlags { zero: 0, half_carry: 0, subtract: 1}; "decrement, register B")]
#[test_case(0x05, register::SingleEightBit::B, 0x00, IncrementOperationFlags { zero: 0, half_carry: 1, subtract: 1}; "decrement with wrapping, register B")]
#[test_case(0x05, register::SingleEightBit::B, 0x01, IncrementOperationFlags { zero: 1, half_carry: 0, subtract: 1}; "decrement to zero, register B")]
#[test_case(0x05, register::SingleEightBit::B, 0x10, IncrementOperationFlags { zero: 0, half_carry: 1, subtract: 1}; "decrement with half carry, register B")]
#[test_case(0x0D, register::SingleEightBit::C, 0x05, IncrementOperationFlags { zero: 0, half_carry: 0, subtract: 1}; "decrement, register C")]
#[test_case(0x0D, register::SingleEightBit::C, 0x00, IncrementOperationFlags { zero: 0, half_carry: 1, subtract: 1}; "decrement with wrapping, register C")]
#[test_case(0x0D, register::SingleEightBit::C, 0x01, IncrementOperationFlags { zero: 1, half_carry: 0, subtract: 1}; "decrement to zero, register C")]
#[test_case(0x0D, register::SingleEightBit::C, 0x10, IncrementOperationFlags { zero: 0, half_carry: 1, subtract: 1}; "decrement with half carry, register C")]
#[test_case(0x15, register::SingleEightBit::D, 0x05, IncrementOperationFlags { zero: 0, half_carry: 0, subtract: 1}; "decrement, register D")]
#[test_case(0x15, register::SingleEightBit::D, 0x00, IncrementOperationFlags { zero: 0, half_carry: 1, subtract: 1}; "decrement with wrapping, register D")]
#[test_case(0x15, register::SingleEightBit::D, 0x01, IncrementOperationFlags { zero: 1, half_carry: 0, subtract: 1}; "decrement to zero, register D")]
#[test_case(0x15, register::SingleEightBit::D, 0x10, IncrementOperationFlags { zero: 0, half_carry: 1, subtract: 1}; "decrement with half carry, register D")]
#[test_case(0x1D, register::SingleEightBit::E, 0x05, IncrementOperationFlags { zero: 0, half_carry: 0, subtract: 1}; "decrement, register E")]
#[test_case(0x1D, register::SingleEightBit::E, 0x00, IncrementOperationFlags { zero: 0, half_carry: 1, subtract: 1}; "decrement with wrapping, register E")]
#[test_case(0x1D, register::SingleEightBit::E, 0x01, IncrementOperationFlags { zero: 1, half_carry: 0, subtract: 1}; "decrement to zero, register E")]
#[test_case(0x1D, register::SingleEightBit::E, 0x10, IncrementOperationFlags { zero: 0, half_carry: 1, subtract: 1}; "decrement with half carry, register E")]
#[test_case(0x25, register::SingleEightBit::H, 0x05, IncrementOperationFlags { zero: 0, half_carry: 0, subtract: 1}; "decrement, register H")]
#[test_case(0x25, register::SingleEightBit::H, 0x00, IncrementOperationFlags { zero: 0, half_carry: 1, subtract: 1}; "decrement with wrapping, register H")]
#[test_case(0x25, register::SingleEightBit::H, 0x01, IncrementOperationFlags { zero: 1, half_carry: 0, subtract: 1}; "decrement to zero, register H")]
#[test_case(0x25, register::SingleEightBit::H, 0x10, IncrementOperationFlags { zero: 0, half_carry: 1, subtract: 1}; "decrement with half carry, register H")]
#[test_case(0x2D, register::SingleEightBit::L, 0x05, IncrementOperationFlags { zero: 0, half_carry: 0, subtract: 1}; "decrement, register L")]
#[test_case(0x2D, register::SingleEightBit::L, 0x00, IncrementOperationFlags { zero: 0, half_carry: 1, subtract: 1}; "decrement with wrapping, register L")]
#[test_case(0x2D, register::SingleEightBit::L, 0x01, IncrementOperationFlags { zero: 1, half_carry: 0, subtract: 1}; "decrement to zero, register L")]
#[test_case(0x2D, register::SingleEightBit::L, 0x10, IncrementOperationFlags { zero: 0, half_carry: 1, subtract: 1}; "decrement with half carry, register L")]
#[test_case(0x3D, register::SingleEightBit::A, 0x05, IncrementOperationFlags { zero: 0, half_carry: 0, subtract: 1}; "decrement, register A")]
#[test_case(0x3D, register::SingleEightBit::A, 0x00, IncrementOperationFlags { zero: 0, half_carry: 1, subtract: 1}; "decrement with wrapping, register A")]
#[test_case(0x3D, register::SingleEightBit::A, 0x01, IncrementOperationFlags { zero: 1, half_carry: 0, subtract: 1}; "decrement to zero, register A")]
#[test_case(0x3D, register::SingleEightBit::A, 0x10, IncrementOperationFlags { zero: 0, half_carry: 1, subtract: 1}; "decrement with half carry, register A")]
fn test_increment_decrement_single_register_flags(
opcode: u8,
register: register::SingleEightBit,
initial_value: u8,
@ -2209,7 +2254,7 @@ fn test_increment_single_register_flags(
&mut processor,
(
expected_flags.zero,
0,
expected_flags.subtract,
expected_flags.half_carry,
// Carry flag should remain unchanged - quick lazy hack to just set this to 1.
0,
@ -2225,16 +2270,18 @@ fn test_increment_single_register_flags(
testutil::assert_flags_eq!(
processor,
(register::Flag::Zero, expected_flags.zero),
(register::Flag::Subtract, 0),
(register::Flag::Subtract, expected_flags.subtract),
(register::Flag::HalfCarry, expected_flags.half_carry),
// Carry flag should remain unchanged
(register::Flag::Carry, 1),
);
}
#[test_case(0x05, 0x06; "add one")]
#[test_case(0xFF, 0x00; "add one with wrapping")]
fn test_increment_hl_value(initial_value: u8, expected_value: u8) {
#[test_case(0x34, 0x05, 0x06; "add one")]
#[test_case(0x34, 0xFF, 0x00; "add one with wrapping")]
#[test_case(0x35, 0x05, 0x04; "sub one")]
#[test_case(0x35, 0x000, 0xFF; "sub one with wrapping")]
fn test_increment_decrement_hl_value(opcode: u8, initial_value: u8, expected_value: u8) {
let mut processor = Processor::default();
processor
.registers
@ -2244,7 +2291,7 @@ fn test_increment_hl_value(initial_value: u8, expected_value: u8) {
.set(0xFF23, initial_value)
.expect("failed to set memory value");
let data = [0x34, 0x03];
let data = [opcode, 0x03];
let (ins, extra_data) =
RunnableInstruction::from_data(&data).expect("could not parse instruction");
assert_eq!(extra_data, &[0x03]);
@ -2259,10 +2306,14 @@ fn test_increment_hl_value(initial_value: u8, expected_value: u8) {
);
}
#[test_case(0x05, IncrementOperationFlags { zero: 0, half_carry: 0 }; "increment")]
#[test_case(0xFF, IncrementOperationFlags { zero: 1, half_carry: 1 }; "increment with wrapping")]
#[test_case(0x0F, IncrementOperationFlags { zero: 0, half_carry: 1 }; "increment with half carry")]
fn test_increment_hl_flags(initial_value: u8, expected_flags: IncrementOperationFlags) {
#[test_case(0x34, 0x05, IncrementOperationFlags { zero: 0, half_carry: 0, subtract: 0}; "increment")]
#[test_case(0x34, 0xFF, IncrementOperationFlags { zero: 1, half_carry: 1, subtract: 0}; "increment with wrapping")]
#[test_case(0x34, 0x0F, IncrementOperationFlags { zero: 0, half_carry: 1, subtract: 0}; "increment with half carry")]
#[test_case(0x35, 0x05, IncrementOperationFlags { zero: 0, half_carry: 0, subtract: 1}; "decrement")]
#[test_case(0x35, 0x01, IncrementOperationFlags { zero: 1, half_carry: 0, subtract: 1}; "decrement to zero")]
#[test_case(0x35, 0x00, IncrementOperationFlags { zero: 0, half_carry: 1, subtract: 1}; "decrement with wrapping")]
#[test_case(0x35, 0x10, IncrementOperationFlags { zero: 0, half_carry: 1, subtract: 1}; "decrement with half carry")]
fn test_increment_decrement_hl_flags(opcode: u8, initial_value: u8, expected_flags: IncrementOperationFlags) {
let mut processor = Processor::default();
processor
.registers
@ -2277,14 +2328,14 @@ fn test_increment_hl_flags(initial_value: u8, expected_flags: IncrementOperation
&mut processor,
(
expected_flags.zero,
0,
expected_flags.subtract,
expected_flags.half_carry,
// Carry flag should remain unchanged - quick lazy hack to just set this to 1.
0,
),
);
let data = [0x34, 0x03];
let data = [opcode, 0x03];
let (ins, extra_data) =
RunnableInstruction::from_data(&data).expect("could not parse instruction");
assert_eq!(extra_data, &[0x03]);
@ -2293,7 +2344,7 @@ fn test_increment_hl_flags(initial_value: u8, expected_flags: IncrementOperation
testutil::assert_flags_eq!(
processor,
(register::Flag::Zero, expected_flags.zero),
(register::Flag::Subtract, 0),
(register::Flag::Subtract, expected_flags.subtract),
(register::Flag::HalfCarry, expected_flags.half_carry),
// Carry flag should remain unchanged
(register::Flag::Carry, 1),