Add RES r8 instruction

master
Nick Krichevsky 2023-12-06 16:14:07 -05:00
parent 6447db5df7
commit b71a4991b1
56 changed files with 146 additions and 15 deletions

View File

@ -9,4 +9,8 @@ pub enum BitManipulationInstruction {
SetHLValueBit { SetHLValueBit {
bit: u8, bit: u8,
}, },
ClearRegisterBit {
register: register::SingleEightBit,
bit: u8,
},
} }

View File

@ -1,4 +1,6 @@
use super::{unparsed::Opcode, OpcodeParser, ParseResult}; use std::ops::RangeInclusive;
use super::{unparsed::Opcode, OpcodeParser, OrParse, ParseResult};
use crate::{ use crate::{
cpu::{ cpu::{
instructions::{manip::BitManipulationInstruction, Instruction}, instructions::{manip::BitManipulationInstruction, Instruction},
@ -19,7 +21,7 @@ impl OpcodeParser for Parser {
fn parse_opcode(data: &View) -> ParseResult { fn parse_opcode(data: &View) -> ParseResult {
let opcode = parse::get_opcode_from_data(data); 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)); 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 operand = operand_for_opcode(sixteen_bit_opcode)?;
let ins = match operand { let ins = match operand {
OpcodeOperand::HLValue => BitManipulationInstruction::SetHLValueBit { bit }, OpcodeOperand::HLValue => BitManipulationInstruction::SetHLValueBit { bit },
@ -44,6 +46,27 @@ fn parse_set_opcode(opcode: Opcode) -> ParseResult {
Ok((Instruction::BitManipulation(ins), 2)) 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> { fn operand_for_opcode(opcode: u16) -> Result<OpcodeOperand, parse::Error> {
let register_nibble = opcode & 0x000F; let register_nibble = opcode & 0x000F;
match register_nibble { 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> { fn bit_for_opcode(
if !(0xCB_C0..=0xCB_FF).contains(&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())); 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 // Given the range we've constrained to, this has to be able to fit in 8 bits
Ok(bit.try_into().unwrap()) Ok(bit.try_into().unwrap())

View File

@ -1,6 +1,7 @@
use crate::{ use crate::{
cpu::{instructions::manip::BitManipulationInstruction, run, Processor}, cpu::{instructions::manip::BitManipulationInstruction, run, Processor},
memory, register, memory,
register::{self, SingleEightBit},
}; };
use super::{Cycles, Run}; use super::{Cycles, Run};
@ -9,12 +10,7 @@ impl Run for BitManipulationInstruction {
fn run_on(&self, processor: &mut Processor) -> Result<Cycles, run::Error> { fn run_on(&self, processor: &mut Processor) -> Result<Cycles, run::Error> {
match *self { match *self {
BitManipulationInstruction::SetRegisterBit { register, bit } => { BitManipulationInstruction::SetRegisterBit { register, bit } => {
let register_value = processor.registers.get_single_8bit_register(register); transform_register_value(processor, register, |value| set_nth_bit(value, bit));
let upd_value = set_nth_bit(register_value, bit);
processor
.registers
.set_single_8bit_register(register, upd_value);
Ok(Cycles(8)) Ok(Cycles(8))
} }
@ -24,6 +20,12 @@ impl Run for BitManipulationInstruction {
Ok(Cycles(16)) 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) 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>( fn transform_hl_value<F: Fn(u8) -> u8>(
processor: &mut Processor, processor: &mut Processor,
transform: F, transform: F,

View File

@ -1,6 +1,6 @@
use ferris_boi::{ use ferris_boi::{
cpu::{instructions::Instruction, Processor}, cpu::{instructions::Instruction, Processor},
register, register::{self, SingleEightBit},
}; };
use test_case::test_case; 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(0xC6, 0b00000001)]
#[test_case(0xCE, 0b00000010)] #[test_case(0xCE, 0b00000010)]
#[test_case(0xD6, 0b00000100)] #[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") .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)
);
}