Compare commits

..

1 Commits

137 changed files with 112 additions and 581 deletions

View File

@ -1,29 +1,16 @@
use crate::register;
#[derive(Debug, Copy, Clone)]
pub enum IndividualBitOperation {
Set,
Clear,
}
#[derive(Debug, Copy, Clone)]
pub enum Operand {
Register(register::SingleEightBit),
HLValue,
}
use crate::cpu::register;
#[derive(Debug, Copy, Clone)]
pub enum BitManipulationInstruction {
ManipulateIndividualBit {
operation: IndividualBitOperation,
operand: Operand,
SetRegisterBit {
register: register::SingleEightBit,
bit: u8,
},
TestIndividualBit {
operand: Operand,
SetHLValueBit {
bit: u8,
},
SwapNibbles {
operand: Operand,
ResetRegisterBit {
register: register::SingleEightBit,
bit: u8,
},
}

View File

@ -1,12 +1,7 @@
use std::ops::RangeInclusive;
use super::{unparsed::Opcode, OpcodeParser, OrParse, ParseResult};
use crate::{
cpu::{
instructions::{
manip::{BitManipulationInstruction, IndividualBitOperation, Operand},
Instruction,
},
instructions::{manip::BitManipulationInstruction, Instruction},
parse,
},
memory::View,
@ -15,104 +10,93 @@ use crate::{
pub struct Parser;
enum OpcodeOperand {
Register(register::SingleEightBit),
HLValue,
}
impl OpcodeParser for Parser {
fn parse_opcode(data: &View) -> ParseResult {
let opcode = parse::get_opcode_from_data(data);
parse_set_opcode(opcode)
.or_parse(parse_clear_opcode)
.or_parse(parse_bit_test_opcode)
.or_parse(parse_swap_opcode)
parse_set_opcode(opcode).or_parse(parse_res_opcode)
}
}
fn parse_set_opcode(opcode: Opcode) -> ParseResult {
parse_individual_bit_operation(opcode, IndividualBitOperation::Set, 0xCB_C0..=0xCB_FF)
}
fn parse_clear_opcode(opcode: Opcode) -> ParseResult {
parse_individual_bit_operation(opcode, IndividualBitOperation::Clear, 0xCB_80..=0xCB_BF)
}
fn parse_bit_test_opcode(opcode: Opcode) -> ParseResult {
let (operand, bit) = decompose_bit_action_opcode(opcode, 0xCB_40..=0xCB_7F)?;
let ins = BitManipulationInstruction::TestIndividualBit { operand, bit };
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) {
if !(0xCB_C0..=0xCB_FF).contains(&sixteen_bit_opcode) {
return Err(parse::Error::UnknownOpcode(opcode));
}
let bit = bit_for_set_opcode(sixteen_bit_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,
opcode_range: RangeInclusive<u16>,
) -> ParseResult {
let (operand, bit) = decompose_bit_action_opcode(opcode, opcode_range)?;
let ins = BitManipulationInstruction::ManipulateIndividualBit {
operation,
operand,
bit,
let ins = match operand {
OpcodeOperand::HLValue => BitManipulationInstruction::SetHLValueBit { bit },
OpcodeOperand::Register(register) => {
BitManipulationInstruction::SetRegisterBit { register, bit }
}
};
Ok((Instruction::BitManipulation(ins), 2))
}
fn decompose_bit_action_opcode(
opcode: Opcode,
opcode_range: RangeInclusive<u16>,
) -> Result<(Operand, u8), parse::Error> {
fn parse_res_opcode(opcode: Opcode) -> ParseResult {
let Opcode::SixteenBit(sixteen_bit_opcode) = opcode else {
return Err(parse::Error::UnknownOpcode(opcode));
};
if !opcode_range.contains(&sixteen_bit_opcode) {
if !(0xCB_80..=0xCB_BF).contains(&sixteen_bit_opcode) {
return Err(parse::Error::UnknownOpcode(opcode));
}
let bit = bit_for_reset_opcode(sixteen_bit_opcode)?;
let operand = operand_for_opcode(sixteen_bit_opcode)?;
let bit = bit_for_opcode(sixteen_bit_opcode, opcode_range)?;
let ins = match operand {
OpcodeOperand::HLValue => todo!(),
OpcodeOperand::Register(register) => {
BitManipulationInstruction::ResetRegisterBit { register, bit }
}
};
Ok((operand, bit))
Ok((Instruction::BitManipulation(ins), 2))
}
fn operand_for_opcode(opcode: u16) -> Result<Operand, parse::Error> {
fn operand_for_opcode(opcode: u16) -> Result<OpcodeOperand, parse::Error> {
let register_nibble = opcode & 0x000F;
match register_nibble {
0x0 | 0x8 => Ok(Operand::Register(register::SingleEightBit::B)),
0x1 | 0x9 => Ok(Operand::Register(register::SingleEightBit::C)),
0x2 | 0xA => Ok(Operand::Register(register::SingleEightBit::D)),
0x3 | 0xB => Ok(Operand::Register(register::SingleEightBit::E)),
0x4 | 0xC => Ok(Operand::Register(register::SingleEightBit::H)),
0x5 | 0xD => Ok(Operand::Register(register::SingleEightBit::L)),
0x7 | 0xF => Ok(Operand::Register(register::SingleEightBit::A)),
0x6 | 0xE => Ok(Operand::HLValue),
0x0 | 0x8 => Ok(OpcodeOperand::Register(register::SingleEightBit::B)),
0x1 | 0x9 => Ok(OpcodeOperand::Register(register::SingleEightBit::C)),
0x2 | 0xA => Ok(OpcodeOperand::Register(register::SingleEightBit::D)),
0x3 | 0xB => Ok(OpcodeOperand::Register(register::SingleEightBit::E)),
0x4 | 0xC => Ok(OpcodeOperand::Register(register::SingleEightBit::H)),
0x5 | 0xD => Ok(OpcodeOperand::Register(register::SingleEightBit::L)),
0x7 | 0xF => Ok(OpcodeOperand::Register(register::SingleEightBit::A)),
0x6 | 0xE => Ok(OpcodeOperand::HLValue),
_ => Err(super::Error::UnknownOpcode(opcode.into())),
}
}
fn bit_for_opcode(
opcode: u16,
opcode_variant_range: RangeInclusive<u16>,
) -> Result<u8, parse::Error> {
if !opcode_variant_range.contains(&opcode) {
fn bit_for_set_opcode(opcode: u16) -> Result<u8, parse::Error> {
if !(0xCB_C0..=0xCB_FF).contains(&opcode) {
return Err(parse::Error::UnknownOpcode(opcode.into()));
}
let bit = (opcode - opcode_variant_range.start()) / 8;
let bit = (opcode - 0xCB_C0) / 8;
// Given the range we've constrained to, this has to be able to fit in 8 bits
Ok(bit.try_into().unwrap())
}
fn bit_for_reset_opcode(opcode: u16) -> Result<u8, parse::Error> {
if !(0xCB_80..=0xCB_BF).contains(&opcode) {
return Err(parse::Error::UnknownOpcode(opcode.into()));
}
let bit = (opcode - 0xCB_80) / 8;
// Given the range we've constrained to, this has to be able to fit in 8 bits
Ok(bit.try_into().unwrap())

View File

@ -1,10 +1,6 @@
use crate::{
cpu::{
instructions::manip::{BitManipulationInstruction, IndividualBitOperation, Operand},
run, Processor,
},
memory,
register::{self, SingleEightBit},
cpu::{instructions::manip::BitManipulationInstruction, run, Processor},
memory, register,
};
use super::{Cycles, Run};
@ -12,67 +8,71 @@ use super::{Cycles, Run};
impl Run for BitManipulationInstruction {
fn run_on(&self, processor: &mut Processor) -> Result<Cycles, run::Error> {
match *self {
BitManipulationInstruction::ManipulateIndividualBit {
operand: Operand::Register(register),
operation,
bit,
} => {
transform_register_value(processor, register, |value| {
operate_on_bit(operation, value, bit)
});
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);
Ok(Cycles(8))
}
BitManipulationInstruction::ManipulateIndividualBit {
operand: Operand::HLValue,
operation,
bit,
} => {
transform_hl_value(processor, |value| operate_on_bit(operation, value, bit))?;
BitManipulationInstruction::SetHLValueBit { bit } => {
// TODO: wow this code kinda sucks, I kinda wanna make a better to deal with the HL values
let hl_addr = processor
.registers
.get_combined_register(register::Combined::HL);
#[allow(clippy::match_wildcard_for_single_variants)]
let hl_value = processor
.memory
.get(hl_addr.into())
.map_err(|err| match err {
memory::Error::GetInvalidAddress(addr) => {
run::Error::InvalidRegisterAddress(
register::SixteenBit::Combined(register::Combined::HL),
addr,
)
}
err => run::Error::Unknown(Box::new(err)),
})?;
let upd_value = set_nth_bit(hl_value, bit);
#[allow(clippy::match_wildcard_for_single_variants)]
processor
.memory
.set(hl_addr.into(), upd_value)
.map_err(|err| match err {
memory::Error::SetInvalidAddress(addr, _value) => {
run::Error::InvalidRegisterAddress(
register::SixteenBit::Combined(register::Combined::HL),
addr,
)
}
err => run::Error::Unknown(Box::new(err)),
})?;
Ok(Cycles(16))
}
BitManipulationInstruction::TestIndividualBit {
operand: Operand::Register(register),
bit,
} => {
test_bit_and_set_flags(processor, Operand::Register(register), bit)?;
BitManipulationInstruction::ResetRegisterBit { register, bit } => {
let register_value = processor.registers.get_single_8bit_register(register);
let upd_value = clear_nth_bit(register_value, bit);
processor
.registers
.set_single_8bit_register(register, upd_value);
Ok(Cycles(8))
}
BitManipulationInstruction::TestIndividualBit {
operand: 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!(),
}
}
}
fn operate_on_bit(operation: IndividualBitOperation, value: u8, bit: u8) -> u8 {
match operation {
IndividualBitOperation::Set => set_nth_bit(value, bit),
IndividualBitOperation::Clear => clear_nth_bit(value, bit),
}
}
fn set_nth_bit(value: u8, bit: u8) -> u8 {
assert!(bit < 8, "Cannot set a bit greater than 7");
@ -80,143 +80,7 @@ fn set_nth_bit(value: u8, bit: u8) -> u8 {
}
fn clear_nth_bit(value: u8, bit: u8) -> u8 {
assert!(bit < 8, "Cannot clear a bit greater than 7");
assert!(bit < 8, "Cannot set a bit greater than 7");
let clear_mask = !(0x1 << bit);
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,
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,
) -> Result<(), run::Error> {
let hl_addr = processor
.registers
.get_combined_register(register::Combined::HL);
#[allow(clippy::match_wildcard_for_single_variants)]
let hl_value = processor
.memory
.get(hl_addr.into())
.map_err(|err| match err {
memory::Error::GetInvalidAddress(addr) => run::Error::InvalidRegisterAddress(
register::SixteenBit::Combined(register::Combined::HL),
addr,
),
err => run::Error::Unknown(Box::new(err)),
})?;
let upd_value = transform(hl_value);
#[allow(clippy::match_wildcard_for_single_variants)]
processor
.memory
.set(hl_addr.into(), upd_value)
.map_err(|err| match err {
memory::Error::SetInvalidAddress(addr, _value) => run::Error::InvalidRegisterAddress(
register::SixteenBit::Combined(register::Combined::HL),
addr,
),
err => run::Error::Unknown(Box::new(err)),
})?;
Ok(())
}
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,
) -> Result<(), run::Error> {
assert!(bit < 8, "Cannot test a bit greater than 7");
let operand_val = get_operand_value(processor, operand)?;
let compare_mask = 0x1 << bit;
let zero_flag = (compare_mask & operand_val) == 0;
processor
.registers
.set_flag_bit(register::Flag::Zero, zero_flag.into());
processor
.registers
.set_flag_bit(register::Flag::Subtract, 0);
processor
.registers
.set_flag_bit(register::Flag::HalfCarry, 1);
Ok(())
}
fn get_operand_value(processor: &mut Processor, operand: Operand) -> Result<u8, run::Error> {
let val = match operand {
Operand::Register(register) => processor.registers.get_single_8bit_register(register),
Operand::HLValue => {
let hl_addr = processor
.registers
.get_combined_register(register::Combined::HL);
#[allow(clippy::match_wildcard_for_single_variants)]
processor
.memory
.get(hl_addr.into())
.map_err(|err| match err {
memory::Error::GetInvalidAddress(addr) => run::Error::InvalidRegisterAddress(
register::SixteenBit::Combined(register::Combined::HL),
addr,
),
err => run::Error::Unknown(Box::new(err)),
})?
}
};
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));
}
value & !(0x1 << bit)
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Some files were not shown because too many files have changed in this diff Show More