Add support for ADD SP e8

old-bit-manip
Nick Krichevsky 2023-11-18 18:03:17 -05:00
parent 132b46ecef
commit 6f25c91134
7 changed files with 150 additions and 23 deletions

View File

@ -16,6 +16,7 @@ pub enum Instruction {
EightBitLoad(load8::EightBitLoadInstruction),
SixteenBitLoad(load16::SixteenBitLoadInstruction),
EightBitArithmetic(arith8::EightBitArithmeticInstruction),
StackPointerAdjust(arith8::AdjustStackPointerInstruction),
SixteenBitArithmetic(arith16::SixteenBitArithmeticInstruction),
}

View File

@ -28,3 +28,8 @@ pub struct EightBitArithmeticInstruction {
pub operation: Operation,
pub operand: Operand,
}
#[derive(Debug, Copy, Clone)]
pub struct AdjustStackPointerInstruction {
pub operand: i8,
}

View File

@ -1,6 +1,8 @@
use crate::{
cpu::instructions::{
arith8::{EightBitArithmeticInstruction, Operand, Operation},
arith8::{
AdjustStackPointerInstruction, EightBitArithmeticInstruction, Operand, Operation,
},
Instruction, RunnableInstruction,
},
memory::{GetViewTuple, View},
@ -22,28 +24,51 @@ pub struct Parser;
impl OpcodeParser for Parser {
fn parse_opcode(data: &View) -> super::ParseResult {
let opcode = super::get_opcode_from_data(data);
let operation = operation_for_opcode(opcode)?;
let operand = operand_for_opcode(opcode)?;
parse_eight_bit_arithmetic_instruction(data)
.or_else(|_err| parse_stack_pointer_adjust_instruction(data))
}
}
let (instruction, bytes_read) = match (operation, operand) {
(_operation, OpcodeOperand::SingleRegister(register)) => {
build_instruction_between_register_and_a_data(operation, register)
}
(Operation::Inc | Operation::Dec, OpcodeOperand::HLAddressValue) => {
let (runnable_ins, bytes_read) = build_instruction_between_hl_value_and_a_data(operation);
// This is an exception to the other arithmetic instructions
(RunnableInstruction{cycles: 12, ..runnable_ins}, bytes_read)
}
(_operation, OpcodeOperand::HLAddressValue) => {
build_instruction_between_hl_value_and_a_data(operation)
}
(_operation, OpcodeOperand::Immediate) => {
build_instruction_between_immediate_and_a_data(operation, data)
}
};
fn parse_eight_bit_arithmetic_instruction(data: &View) -> super::ParseResult {
let opcode = super::get_opcode_from_data(data);
Ok((instruction, bytes_read))
let operation = operation_for_opcode(opcode)?;
let operand = operand_for_opcode(opcode)?;
let parse_data = match (operation, operand) {
(_operation, OpcodeOperand::SingleRegister(register)) => {
build_instruction_between_register_and_a_data(operation, register)
}
(Operation::Inc | Operation::Dec, OpcodeOperand::HLAddressValue) => {
let (runnable_ins, bytes_read) =
build_instruction_between_hl_value_and_a_data(operation);
// This is an exception to the other arithmetic instructions
(
RunnableInstruction {
cycles: 12,
..runnable_ins
},
bytes_read,
)
}
(_operation, OpcodeOperand::HLAddressValue) => {
build_instruction_between_hl_value_and_a_data(operation)
}
(_operation, OpcodeOperand::Immediate) => {
build_instruction_between_immediate_and_a_data(operation, data)
}
};
Ok(parse_data)
}
fn parse_stack_pointer_adjust_instruction(data: &View) -> super::ParseResult {
let opcode = super::get_opcode_from_data(data);
if opcode == 0xE8 {
Ok(build_stack_pointer_adjust_data(data))
} else {
Err(super::Error::UnknownOpcode(opcode))
}
}
@ -176,6 +201,21 @@ fn build_instruction_between_immediate_and_a_data(
)
}
fn build_stack_pointer_adjust_data(data: &View) -> ParseOutput {
let (_opcode, n) = data.get_tuple();
let instruction = AdjustStackPointerInstruction {
operand: i8::from_be_bytes([n]),
};
(
RunnableInstruction {
instruction: Instruction::StackPointerAdjust(instruction),
cycles: 16,
},
2,
)
}
#[cfg(test)]
mod tests {
use super::*;

View File

@ -39,6 +39,7 @@ pub fn run_instruction(processor: &mut Processor, instruction: Instruction) -> R
Instruction::EightBitLoad(load_instruction) => load_instruction.run_on(processor),
Instruction::SixteenBitLoad(load_instruction) => load_instruction.run_on(processor),
Instruction::EightBitArithmetic(arith_instruction) => arith_instruction.run_on(processor),
Instruction::StackPointerAdjust(adjust_instruction) => adjust_instruction.run_on(processor),
Instruction::SixteenBitArithmetic(arith_instruction) => arith_instruction.run_on(processor),
}
}

View File

@ -1,10 +1,13 @@
use crate::{
cpu::{instructions::arith8::EightBitArithmeticInstruction, run::Error},
cpu::{instructions::arith8::Operation, Processor},
cpu::{
instructions::arith8::{AdjustStackPointerInstruction, Operation},
Processor,
},
register,
};
use super::Run;
use super::{arithutil::CarryingAdd, Run};
mod binary;
mod unary;
@ -48,6 +51,31 @@ impl Run for EightBitArithmeticInstruction {
}
}
impl Run for AdjustStackPointerInstruction {
fn run_on(&self, processor: &mut Processor) -> Result<(), Error> {
let stack_pointer = processor
.registers
.get_single_16bit_register(register::SingleSixteenBit::StackPointer);
let (result, half_carry, full_carry) = stack_pointer.add_with_carry(self.operand);
processor
.registers
.set_single_16bit_register(register::SingleSixteenBit::StackPointer, result);
store_flags(
processor,
OperationFlagOutput {
zero_flag: 0,
subtract_flag: 0,
half_carry_flag: half_carry.into(),
carry_flag: full_carry.into(),
},
);
Ok(())
}
}
fn store_flags(processor: &mut Processor, flags: OperationFlagOutput) {
processor
.registers

View File

@ -2260,3 +2260,55 @@ fn test_increment_decrement_hl_flags(
(register::Flag::Carry, 1),
);
}
#[test_case(0xFF00, 0x22, 0xFF22)]
#[test_case(0xFF00, -0x22, 0xFEDE)]
#[test_case(0x0000, -0x22, 0xFFDE)]
#[test_case(0xFFFF, 0x22, 0x0021)]
fn test_adjust_stack_pointer_value(initial_sp: u16, adjustment: i8, expected_sp: u16) {
let mut processor = Processor::default();
processor
.registers
.set_single_16bit_register(register::SingleSixteenBit::StackPointer, initial_sp);
let twos_comp_adjustment = adjustment.to_be_bytes()[0];
let data = [0xE8, twos_comp_adjustment, 0x03];
let (ins, extra_data) =
RunnableInstruction::from_data(&data).expect("could not parse instruction");
assert_eq!(extra_data, &[0x03]);
processor.run_instruction(&ins);
assert_eq!(expected_sp, processor.registers.stack_pointer);
}
#[test_case(0xFF00, 0x22, false, false)]
#[test_case(0x04F0, -0x22, false, true; "subtraction full carry")]
#[test_case(0x0022, -0x22, true, true; "even as zero, zero flags are unset")]
fn test_adjust_stack_pointer(initial_sp: u16, adjustment: i8, half_carry: bool, carry: bool) {
let mut processor = Processor::default();
processor
.registers
.set_single_16bit_register(register::SingleSixteenBit::StackPointer, initial_sp);
// Set all the register to the opposite we expect to ensure they all get set
testutil::set_opposite_of_expected_flags(
&mut processor,
(0, 0, half_carry.into(), carry.into()),
);
let twos_comp_adjustment = adjustment.to_be_bytes()[0];
let data = [0xE8, twos_comp_adjustment, 0x03];
let (ins, extra_data) =
RunnableInstruction::from_data(&data).expect("could not parse instruction");
assert_eq!(extra_data, &[0x03]);
processor.run_instruction(&ins);
testutil::assert_flags_eq!(
processor,
(register::Flag::HalfCarry, u8::from(half_carry)),
(register::Flag::Carry, u8::from(carry)),
// Both are always zero
(register::Flag::Subtract, 0),
(register::Flag::Zero, 0),
);
}