Implement SWAP r8 instructions
parent
7e44edeb24
commit
f647919ae0
|
@ -23,4 +23,7 @@ pub enum BitManipulationInstruction {
|
||||||
operand: Operand,
|
operand: Operand,
|
||||||
bit: u8,
|
bit: u8,
|
||||||
},
|
},
|
||||||
|
SwapNibbles {
|
||||||
|
operand: Operand,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,7 @@ impl OpcodeParser for Parser {
|
||||||
parse_set_opcode(opcode)
|
parse_set_opcode(opcode)
|
||||||
.or_parse(parse_clear_opcode)
|
.or_parse(parse_clear_opcode)
|
||||||
.or_parse(parse_bit_test_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))
|
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(
|
fn parse_individual_bit_operation(
|
||||||
opcode: Opcode,
|
opcode: Opcode,
|
||||||
operation: IndividualBitOperation,
|
operation: IndividualBitOperation,
|
||||||
|
|
|
@ -38,7 +38,7 @@ impl Run for BitManipulationInstruction {
|
||||||
operand: Operand::Register(register),
|
operand: Operand::Register(register),
|
||||||
bit,
|
bit,
|
||||||
} => {
|
} => {
|
||||||
set_test_flag_bits(processor, Operand::Register(register), bit)?;
|
test_bit_and_set_flags(processor, Operand::Register(register), bit)?;
|
||||||
|
|
||||||
Ok(Cycles(8))
|
Ok(Cycles(8))
|
||||||
}
|
}
|
||||||
|
@ -47,10 +47,21 @@ impl Run for BitManipulationInstruction {
|
||||||
operand: Operand::HLValue,
|
operand: Operand::HLValue,
|
||||||
bit,
|
bit,
|
||||||
} => {
|
} => {
|
||||||
set_test_flag_bits(processor, Operand::HLValue, bit)?;
|
test_bit_and_set_flags(processor, Operand::HLValue, bit)?;
|
||||||
|
|
||||||
Ok(Cycles(12))
|
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
|
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>(
|
fn transform_register_value<F: Fn(u8) -> u8>(
|
||||||
processor: &mut Processor,
|
processor: &mut Processor,
|
||||||
register: SingleEightBit,
|
register: SingleEightBit,
|
||||||
|
@ -125,7 +143,22 @@ fn transform_hl_value<F: Fn(u8) -> u8>(
|
||||||
Ok(())
|
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,
|
processor: &mut Processor,
|
||||||
operand: Operand,
|
operand: Operand,
|
||||||
bit: u8,
|
bit: u8,
|
||||||
|
@ -174,3 +207,16 @@ fn get_operand_value(processor: &mut Processor, operand: Operand) -> Result<u8,
|
||||||
|
|
||||||
Ok(val)
|
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)
|
(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