Add RES r8 instruction
parent
6447db5df7
commit
b71a4991b1
|
@ -9,4 +9,8 @@ pub enum BitManipulationInstruction {
|
|||
SetHLValueBit {
|
||||
bit: u8,
|
||||
},
|
||||
ClearRegisterBit {
|
||||
register: register::SingleEightBit,
|
||||
bit: u8,
|
||||
},
|
||||
}
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
use super::{unparsed::Opcode, OpcodeParser, ParseResult};
|
||||
use std::ops::RangeInclusive;
|
||||
|
||||
use super::{unparsed::Opcode, OpcodeParser, OrParse, ParseResult};
|
||||
use crate::{
|
||||
cpu::{
|
||||
instructions::{manip::BitManipulationInstruction, Instruction},
|
||||
|
@ -19,7 +21,7 @@ impl OpcodeParser for Parser {
|
|||
fn parse_opcode(data: &View) -> ParseResult {
|
||||
let opcode = parse::get_opcode_from_data(data);
|
||||
|
||||
parse_set_opcode(opcode)
|
||||
parse_set_opcode(opcode).or_parse(parse_clear_opcode)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -32,7 +34,7 @@ fn parse_set_opcode(opcode: Opcode) -> ParseResult {
|
|||
return Err(parse::Error::UnknownOpcode(opcode));
|
||||
}
|
||||
|
||||
let bit = bit_for_set_opcode(sixteen_bit_opcode)?;
|
||||
let bit = bit_for_opcode(sixteen_bit_opcode, 0xCB_C0..=0xCB_FF)?;
|
||||
let operand = operand_for_opcode(sixteen_bit_opcode)?;
|
||||
let ins = match operand {
|
||||
OpcodeOperand::HLValue => BitManipulationInstruction::SetHLValueBit { bit },
|
||||
|
@ -44,6 +46,27 @@ fn parse_set_opcode(opcode: Opcode) -> ParseResult {
|
|||
Ok((Instruction::BitManipulation(ins), 2))
|
||||
}
|
||||
|
||||
fn parse_clear_opcode(opcode: Opcode) -> ParseResult {
|
||||
let Opcode::SixteenBit(sixteen_bit_opcode) = opcode else {
|
||||
return Err(parse::Error::UnknownOpcode(opcode));
|
||||
};
|
||||
|
||||
if !(0xCB_80..=0xCB_BF).contains(&sixteen_bit_opcode) {
|
||||
return Err(parse::Error::UnknownOpcode(opcode));
|
||||
}
|
||||
|
||||
let bit = bit_for_opcode(sixteen_bit_opcode, 0xCB_80..=0xCB_BF)?;
|
||||
let operand = operand_for_opcode(sixteen_bit_opcode)?;
|
||||
let ins = match operand {
|
||||
OpcodeOperand::HLValue => todo!(),
|
||||
OpcodeOperand::Register(register) => {
|
||||
BitManipulationInstruction::ClearRegisterBit { register, bit }
|
||||
}
|
||||
};
|
||||
|
||||
Ok((Instruction::BitManipulation(ins), 2))
|
||||
}
|
||||
|
||||
fn operand_for_opcode(opcode: u16) -> Result<OpcodeOperand, parse::Error> {
|
||||
let register_nibble = opcode & 0x000F;
|
||||
match register_nibble {
|
||||
|
@ -59,12 +82,15 @@ fn operand_for_opcode(opcode: u16) -> Result<OpcodeOperand, parse::Error> {
|
|||
}
|
||||
}
|
||||
|
||||
fn bit_for_set_opcode(opcode: u16) -> Result<u8, parse::Error> {
|
||||
if !(0xCB_C0..=0xCB_FF).contains(&opcode) {
|
||||
fn bit_for_opcode(
|
||||
opcode: u16,
|
||||
opcode_variant_range: RangeInclusive<u16>,
|
||||
) -> Result<u8, parse::Error> {
|
||||
if !opcode_variant_range.contains(&opcode) {
|
||||
return Err(parse::Error::UnknownOpcode(opcode.into()));
|
||||
}
|
||||
|
||||
let bit = (opcode - 0xCB_C0) / 8;
|
||||
let bit = (opcode - opcode_variant_range.start()) / 8;
|
||||
|
||||
// Given the range we've constrained to, this has to be able to fit in 8 bits
|
||||
Ok(bit.try_into().unwrap())
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use crate::{
|
||||
cpu::{instructions::manip::BitManipulationInstruction, run, Processor},
|
||||
memory, register,
|
||||
memory,
|
||||
register::{self, SingleEightBit},
|
||||
};
|
||||
|
||||
use super::{Cycles, Run};
|
||||
|
@ -9,12 +10,7 @@ impl Run for BitManipulationInstruction {
|
|||
fn run_on(&self, processor: &mut Processor) -> Result<Cycles, run::Error> {
|
||||
match *self {
|
||||
BitManipulationInstruction::SetRegisterBit { register, bit } => {
|
||||
let register_value = processor.registers.get_single_8bit_register(register);
|
||||
let upd_value = set_nth_bit(register_value, bit);
|
||||
|
||||
processor
|
||||
.registers
|
||||
.set_single_8bit_register(register, upd_value);
|
||||
transform_register_value(processor, register, |value| set_nth_bit(value, bit));
|
||||
|
||||
Ok(Cycles(8))
|
||||
}
|
||||
|
@ -24,6 +20,12 @@ impl Run for BitManipulationInstruction {
|
|||
|
||||
Ok(Cycles(16))
|
||||
}
|
||||
|
||||
BitManipulationInstruction::ClearRegisterBit { register, bit } => {
|
||||
transform_register_value(processor, register, |value| clear_nth_bit(value, bit));
|
||||
|
||||
Ok(Cycles(8))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -34,6 +36,26 @@ fn set_nth_bit(value: u8, bit: u8) -> u8 {
|
|||
value | (0x1 << bit)
|
||||
}
|
||||
|
||||
fn clear_nth_bit(value: u8, bit: u8) -> u8 {
|
||||
assert!(bit < 8, "Cannot clear a bit greater than 7");
|
||||
|
||||
let clear_mask = !(0x1 << bit);
|
||||
value & clear_mask
|
||||
}
|
||||
|
||||
fn transform_register_value<F: Fn(u8) -> u8>(
|
||||
processor: &mut Processor,
|
||||
register: SingleEightBit,
|
||||
transform: F,
|
||||
) {
|
||||
let register_value = processor.registers.get_single_8bit_register(register);
|
||||
let upd_value = transform(register_value);
|
||||
|
||||
processor
|
||||
.registers
|
||||
.set_single_8bit_register(register, upd_value);
|
||||
}
|
||||
|
||||
fn transform_hl_value<F: Fn(u8) -> u8>(
|
||||
processor: &mut Processor,
|
||||
transform: F,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use ferris_boi::{
|
||||
cpu::{instructions::Instruction, Processor},
|
||||
register,
|
||||
register::{self, SingleEightBit},
|
||||
};
|
||||
use test_case::test_case;
|
||||
|
||||
|
@ -83,7 +83,6 @@ fn test_set_register_bit(opcode_variant: u8, register: register::SingleEightBit,
|
|||
);
|
||||
}
|
||||
|
||||
// Register A
|
||||
#[test_case(0xC6, 0b00000001)]
|
||||
#[test_case(0xCE, 0b00000010)]
|
||||
#[test_case(0xD6, 0b00000100)]
|
||||
|
@ -119,3 +118,83 @@ fn test_set_hl_value_bit(opcode_variant: u8, expected: u8) {
|
|||
.expect("failed to get memory value")
|
||||
);
|
||||
}
|
||||
|
||||
// Register B
|
||||
#[test_case(0x80, register::SingleEightBit::B, 0b11111110)]
|
||||
#[test_case(0x88, register::SingleEightBit::B, 0b11111101)]
|
||||
#[test_case(0x90, register::SingleEightBit::B, 0b11111011)]
|
||||
#[test_case(0x98, register::SingleEightBit::B, 0b11110111)]
|
||||
#[test_case(0xA0, register::SingleEightBit::B, 0b11101111)]
|
||||
#[test_case(0xA8, register::SingleEightBit::B, 0b11011111)]
|
||||
#[test_case(0xB0, register::SingleEightBit::B, 0b10111111)]
|
||||
#[test_case(0xB8, register::SingleEightBit::B, 0b01111111)]
|
||||
// Register C
|
||||
#[test_case(0x81, register::SingleEightBit::C, 0b11111110)]
|
||||
#[test_case(0x89, register::SingleEightBit::C, 0b11111101)]
|
||||
#[test_case(0x91, register::SingleEightBit::C, 0b11111011)]
|
||||
#[test_case(0x99, register::SingleEightBit::C, 0b11110111)]
|
||||
#[test_case(0xA1, register::SingleEightBit::C, 0b11101111)]
|
||||
#[test_case(0xA9, register::SingleEightBit::C, 0b11011111)]
|
||||
#[test_case(0xB1, register::SingleEightBit::C, 0b10111111)]
|
||||
#[test_case(0xB9, register::SingleEightBit::C, 0b01111111)]
|
||||
// Register D
|
||||
#[test_case(0x82, register::SingleEightBit::D, 0b11111110)]
|
||||
#[test_case(0x8A, register::SingleEightBit::D, 0b11111101)]
|
||||
#[test_case(0x92, register::SingleEightBit::D, 0b11111011)]
|
||||
#[test_case(0x9A, register::SingleEightBit::D, 0b11110111)]
|
||||
#[test_case(0xA2, register::SingleEightBit::D, 0b11101111)]
|
||||
#[test_case(0xAA, register::SingleEightBit::D, 0b11011111)]
|
||||
#[test_case(0xB2, register::SingleEightBit::D, 0b10111111)]
|
||||
#[test_case(0xBA, register::SingleEightBit::D, 0b01111111)]
|
||||
// Register E
|
||||
#[test_case(0x83, register::SingleEightBit::E, 0b11111110)]
|
||||
#[test_case(0x8B, register::SingleEightBit::E, 0b11111101)]
|
||||
#[test_case(0x93, register::SingleEightBit::E, 0b11111011)]
|
||||
#[test_case(0x9B, register::SingleEightBit::E, 0b11110111)]
|
||||
#[test_case(0xA3, register::SingleEightBit::E, 0b11101111)]
|
||||
#[test_case(0xAB, register::SingleEightBit::E, 0b11011111)]
|
||||
#[test_case(0xB3, register::SingleEightBit::E, 0b10111111)]
|
||||
#[test_case(0xBB, register::SingleEightBit::E, 0b01111111)]
|
||||
// Register H
|
||||
#[test_case(0x84, register::SingleEightBit::H, 0b11111110)]
|
||||
#[test_case(0x8C, register::SingleEightBit::H, 0b11111101)]
|
||||
#[test_case(0x94, register::SingleEightBit::H, 0b11111011)]
|
||||
#[test_case(0x9C, register::SingleEightBit::H, 0b11110111)]
|
||||
#[test_case(0xA4, register::SingleEightBit::H, 0b11101111)]
|
||||
#[test_case(0xAC, register::SingleEightBit::H, 0b11011111)]
|
||||
#[test_case(0xB4, register::SingleEightBit::H, 0b10111111)]
|
||||
#[test_case(0xBC, register::SingleEightBit::H, 0b01111111)]
|
||||
// Register L
|
||||
#[test_case(0x85, register::SingleEightBit::L, 0b11111110)]
|
||||
#[test_case(0x8D, register::SingleEightBit::L, 0b11111101)]
|
||||
#[test_case(0x95, register::SingleEightBit::L, 0b11111011)]
|
||||
#[test_case(0x9D, register::SingleEightBit::L, 0b11110111)]
|
||||
#[test_case(0xA5, register::SingleEightBit::L, 0b11101111)]
|
||||
#[test_case(0xAD, register::SingleEightBit::L, 0b11011111)]
|
||||
#[test_case(0xB5, register::SingleEightBit::L, 0b10111111)]
|
||||
#[test_case(0xBD, register::SingleEightBit::L, 0b01111111)]
|
||||
// Register A
|
||||
#[test_case(0x87, register::SingleEightBit::A, 0b11111110)]
|
||||
#[test_case(0x8F, register::SingleEightBit::A, 0b11111101)]
|
||||
#[test_case(0x97, register::SingleEightBit::A, 0b11111011)]
|
||||
#[test_case(0x9F, register::SingleEightBit::A, 0b11110111)]
|
||||
#[test_case(0xA7, register::SingleEightBit::A, 0b11101111)]
|
||||
#[test_case(0xAF, register::SingleEightBit::A, 0b11011111)]
|
||||
#[test_case(0xB7, register::SingleEightBit::A, 0b10111111)]
|
||||
#[test_case(0xBF, register::SingleEightBit::A, 0b01111111)]
|
||||
fn test_clear_register_bit(opcode_variant: u8, register: SingleEightBit, expected: u8) {
|
||||
let mut processor = Processor::default();
|
||||
|
||||
processor.registers.set_single_8bit_register(register, 0xFF);
|
||||
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!(
|
||||
expected,
|
||||
processor.registers.get_single_8bit_register(register)
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue