Implement SWAP r8 instructions
parent
7e44edeb24
commit
f647919ae0
|
@ -23,4 +23,7 @@ pub enum BitManipulationInstruction {
|
|||
operand: Operand,
|
||||
bit: u8,
|
||||
},
|
||||
SwapNibbles {
|
||||
operand: Operand,
|
||||
},
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue