Add support for ADD SP e8
parent
132b46ecef
commit
6f25c91134
|
@ -16,6 +16,7 @@ pub enum Instruction {
|
||||||
EightBitLoad(load8::EightBitLoadInstruction),
|
EightBitLoad(load8::EightBitLoadInstruction),
|
||||||
SixteenBitLoad(load16::SixteenBitLoadInstruction),
|
SixteenBitLoad(load16::SixteenBitLoadInstruction),
|
||||||
EightBitArithmetic(arith8::EightBitArithmeticInstruction),
|
EightBitArithmetic(arith8::EightBitArithmeticInstruction),
|
||||||
|
StackPointerAdjust(arith8::AdjustStackPointerInstruction),
|
||||||
SixteenBitArithmetic(arith16::SixteenBitArithmeticInstruction),
|
SixteenBitArithmetic(arith16::SixteenBitArithmeticInstruction),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -28,3 +28,8 @@ pub struct EightBitArithmeticInstruction {
|
||||||
pub operation: Operation,
|
pub operation: Operation,
|
||||||
pub operand: Operand,
|
pub operand: Operand,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub struct AdjustStackPointerInstruction {
|
||||||
|
pub operand: i8,
|
||||||
|
}
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
cpu::instructions::{
|
cpu::instructions::{
|
||||||
arith8::{EightBitArithmeticInstruction, Operand, Operation},
|
arith8::{
|
||||||
|
AdjustStackPointerInstruction, EightBitArithmeticInstruction, Operand, Operation,
|
||||||
|
},
|
||||||
Instruction, RunnableInstruction,
|
Instruction, RunnableInstruction,
|
||||||
},
|
},
|
||||||
memory::{GetViewTuple, View},
|
memory::{GetViewTuple, View},
|
||||||
|
@ -22,28 +24,51 @@ pub struct Parser;
|
||||||
|
|
||||||
impl OpcodeParser for Parser {
|
impl OpcodeParser for Parser {
|
||||||
fn parse_opcode(data: &View) -> super::ParseResult {
|
fn parse_opcode(data: &View) -> super::ParseResult {
|
||||||
let opcode = super::get_opcode_from_data(data);
|
parse_eight_bit_arithmetic_instruction(data)
|
||||||
let operation = operation_for_opcode(opcode)?;
|
.or_else(|_err| parse_stack_pointer_adjust_instruction(data))
|
||||||
let operand = operand_for_opcode(opcode)?;
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let (instruction, bytes_read) = match (operation, operand) {
|
fn parse_eight_bit_arithmetic_instruction(data: &View) -> super::ParseResult {
|
||||||
(_operation, OpcodeOperand::SingleRegister(register)) => {
|
let opcode = super::get_opcode_from_data(data);
|
||||||
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((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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
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::EightBitLoad(load_instruction) => load_instruction.run_on(processor),
|
||||||
Instruction::SixteenBitLoad(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::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),
|
Instruction::SixteenBitArithmetic(arith_instruction) => arith_instruction.run_on(processor),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,13 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
cpu::{instructions::arith8::EightBitArithmeticInstruction, run::Error},
|
cpu::{instructions::arith8::EightBitArithmeticInstruction, run::Error},
|
||||||
cpu::{instructions::arith8::Operation, Processor},
|
cpu::{
|
||||||
|
instructions::arith8::{AdjustStackPointerInstruction, Operation},
|
||||||
|
Processor,
|
||||||
|
},
|
||||||
register,
|
register,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::Run;
|
use super::{arithutil::CarryingAdd, Run};
|
||||||
|
|
||||||
mod binary;
|
mod binary;
|
||||||
mod unary;
|
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) {
|
fn store_flags(processor: &mut Processor, flags: OperationFlagOutput) {
|
||||||
processor
|
processor
|
||||||
.registers
|
.registers
|
||||||
|
|
|
@ -2260,3 +2260,55 @@ fn test_increment_decrement_hl_flags(
|
||||||
(register::Flag::Carry, 1),
|
(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