Add support for ADD SP e8
parent
132b46ecef
commit
6f25c91134
|
@ -16,6 +16,7 @@ pub enum Instruction {
|
|||
EightBitLoad(load8::EightBitLoadInstruction),
|
||||
SixteenBitLoad(load16::SixteenBitLoadInstruction),
|
||||
EightBitArithmetic(arith8::EightBitArithmeticInstruction),
|
||||
StackPointerAdjust(arith8::AdjustStackPointerInstruction),
|
||||
SixteenBitArithmetic(arith16::SixteenBitArithmeticInstruction),
|
||||
}
|
||||
|
||||
|
|
|
@ -28,3 +28,8 @@ pub struct EightBitArithmeticInstruction {
|
|||
pub operation: Operation,
|
||||
pub operand: Operand,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct AdjustStackPointerInstruction {
|
||||
pub operand: i8,
|
||||
}
|
||||
|
|
|
@ -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::*;
|
||||
|
|
|
@ -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),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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),
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue