Implement SWAP r8 instructions

master
Nick Krichevsky 2023-12-06 20:28:59 -05:00
parent 7e44edeb24
commit f647919ae0
11 changed files with 128 additions and 3 deletions

View File

@ -23,4 +23,7 @@ pub enum BitManipulationInstruction {
operand: Operand,
bit: u8,
},
SwapNibbles {
operand: Operand,
},
}

View File

@ -22,6 +22,7 @@ impl OpcodeParser for Parser {
parse_set_opcode(opcode)
.or_parse(parse_clear_opcode)
.or_parse(parse_bit_test_opcode)
.or_parse(parse_swap_opcode)
}
}
@ -40,6 +41,21 @@ fn parse_bit_test_opcode(opcode: Opcode) -> ParseResult {
Ok((Instruction::BitManipulation(ins), 2))
}
fn parse_swap_opcode(opcode: Opcode) -> ParseResult {
let Opcode::SixteenBit(sixteen_bit_opcode) = opcode else {
return Err(parse::Error::UnknownOpcode(opcode));
};
if !(0xCB_30..=0xCB_37).contains(&sixteen_bit_opcode) {
return Err(parse::Error::UnknownOpcode(opcode));
}
let operand = operand_for_opcode(sixteen_bit_opcode)?;
let ins = BitManipulationInstruction::SwapNibbles { operand };
Ok((Instruction::BitManipulation(ins), 2))
}
fn parse_individual_bit_operation(
opcode: Opcode,
operation: IndividualBitOperation,

View File

@ -38,7 +38,7 @@ impl Run for BitManipulationInstruction {
operand: Operand::Register(register),
bit,
} => {
set_test_flag_bits(processor, Operand::Register(register), bit)?;
test_bit_and_set_flags(processor, Operand::Register(register), bit)?;
Ok(Cycles(8))
}
@ -47,10 +47,21 @@ impl Run for BitManipulationInstruction {
operand: Operand::HLValue,
bit,
} => {
set_test_flag_bits(processor, Operand::HLValue, bit)?;
test_bit_and_set_flags(processor, Operand::HLValue, bit)?;
Ok(Cycles(12))
}
BitManipulationInstruction::SwapNibbles {
operand: Operand::Register(register),
} => {
transform_register_value(processor, register, swap_nibbles);
set_zero_flag_after_swap(processor, register);
Ok(Cycles(8))
}
_ => todo!(),
}
}
}
@ -75,6 +86,13 @@ fn clear_nth_bit(value: u8, bit: u8) -> u8 {
value & clear_mask
}
fn swap_nibbles(value: u8) -> u8 {
let top = value & 0xF0;
let bottom = value & 0x0F;
(top >> 4) | (bottom << 4)
}
fn transform_register_value<F: Fn(u8) -> u8>(
processor: &mut Processor,
register: SingleEightBit,
@ -125,7 +143,22 @@ fn transform_hl_value<F: Fn(u8) -> u8>(
Ok(())
}
fn set_test_flag_bits(
fn set_zero_flag_after_swap(processor: &mut Processor, register: register::SingleEightBit) {
let value = processor.registers.get_single_8bit_register(register);
// Clear all flags
processor
.registers
.set_raw_flag_bits(0x00)
.expect("lower four bits of register aren't set");
if value == 0 {
processor.registers.set_flag_bit(register::Flag::Zero, 1);
} else {
processor.registers.set_flag_bit(register::Flag::Zero, 0);
}
}
fn test_bit_and_set_flags(
processor: &mut Processor,
operand: Operand,
bit: u8,
@ -174,3 +207,16 @@ fn get_operand_value(processor: &mut Processor, operand: Operand) -> Result<u8,
Ok(val)
}
#[cfg(test)]
mod tests {
use super::*;
use test_case::test_case;
#[test_case(0xA5, 0x5A)]
#[test_case(0x5A, 0xA5)]
#[test_case(0xFF, 0xFF)]
fn test_swap_nibbles(n: u8, expected: u8) {
assert_eq!(expected, swap_nibbles(n));
}
}

View File

@ -396,3 +396,63 @@ fn test_test_hl_value_bit_sets_zero_flag_to_opposite_of_bit(
(Flag::HalfCarry, 1)
);
}
#[test_case(0x30, SingleEightBit::B)]
#[test_case(0x31, SingleEightBit::C)]
#[test_case(0x32, SingleEightBit::D)]
#[test_case(0x33, SingleEightBit::E)]
#[test_case(0x34, SingleEightBit::H)]
#[test_case(0x35, SingleEightBit::L)]
#[test_case(0x37, SingleEightBit::A)]
fn test_swap_register_nibbles_swaps_register_value(opcode_variant: u8, register: SingleEightBit) {
let mut processor = Processor::default();
processor.registers.set_single_8bit_register(register, 0xA5);
let data = [0xCB, opcode_variant, 0x02];
let (ins, extra_data) = Instruction::from_data(&data).expect("could not parse instruction");
assert_eq!(extra_data, &[0x02]);
processor.run_instruction(ins);
assert_eq!(0x5A, processor.registers.get_single_8bit_register(register));
}
#[test_matrix(
[(0x00, 1), (0xF0, 0)],
[
(0x30, SingleEightBit::B),
(0x31, SingleEightBit::C),
(0x32, SingleEightBit::D),
(0x33, SingleEightBit::E),
(0x34, SingleEightBit::H),
(0x35, SingleEightBit::L),
(0x37, SingleEightBit::A)
]
)]
fn test_swap_register_nibbles_sets_zero_flag_when_value_is_zero(
(value, expected_zero_flag): (u8, u8),
(opcode_variant, register): (u8, SingleEightBit),
) {
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, 0, 0));
processor
.registers
.set_single_8bit_register(register, value);
let data = [0xCB, opcode_variant, 0x02];
let (ins, extra_data) = Instruction::from_data(&data).expect("could not parse instruction");
assert_eq!(extra_data, &[0x02]);
processor.run_instruction(ins);
testutil::assert_flags_eq!(
processor,
(Flag::Zero, expected_zero_flag),
(Flag::Subtract, 0),
(Flag::HalfCarry, 0),
(Flag::Carry, 0)
);
}