Add support for carry bit set instructions
parent
78458d6d31
commit
1bbd14e5d2
|
@ -8,6 +8,7 @@ pub mod arith16;
|
||||||
pub mod arith8;
|
pub mod arith8;
|
||||||
pub mod load16;
|
pub mod load16;
|
||||||
pub mod load8;
|
pub mod load8;
|
||||||
|
pub mod misc;
|
||||||
|
|
||||||
// these are indexed with the instruction numbers in this manual
|
// these are indexed with the instruction numbers in this manual
|
||||||
// http://marc.rawer.de/Gameboy/Docs/GBCPUman.pdf
|
// http://marc.rawer.de/Gameboy/Docs/GBCPUman.pdf
|
||||||
|
@ -18,6 +19,7 @@ pub enum Instruction {
|
||||||
EightBitArithmetic(arith8::EightBitArithmeticInstruction),
|
EightBitArithmetic(arith8::EightBitArithmeticInstruction),
|
||||||
StackPointerAdjust(arith8::AdjustStackPointerInstruction),
|
StackPointerAdjust(arith8::AdjustStackPointerInstruction),
|
||||||
SixteenBitArithmetic(arith16::SixteenBitArithmeticInstruction),
|
SixteenBitArithmetic(arith16::SixteenBitArithmeticInstruction),
|
||||||
|
Misc(misc::MiscInstruction),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `RunnableInstruction` is an instruction that can run on the processor, and has any metadata needed to do so
|
/// `RunnableInstruction` is an instruction that can run on the processor, and has any metadata needed to do so
|
||||||
|
|
|
@ -28,6 +28,6 @@ pub enum SixteenBitLoadInstruction {
|
||||||
},
|
},
|
||||||
|
|
||||||
LoadStackPointerToImmediateAddress {
|
LoadStackPointerToImmediateAddress {
|
||||||
dst_address: u16
|
dst_address: u16,
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
//! The `misc` module holds instructions that I couldn't think of a better category for
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub enum MiscInstruction {
|
||||||
|
SetCarryFlag,
|
||||||
|
ComplementCarryFlag,
|
||||||
|
}
|
|
@ -9,6 +9,7 @@ mod arith16;
|
||||||
mod arith8;
|
mod arith8;
|
||||||
mod load16;
|
mod load16;
|
||||||
mod load8;
|
mod load8;
|
||||||
|
mod misc;
|
||||||
|
|
||||||
#[derive(Error, Debug, Clone)]
|
#[derive(Error, Debug, Clone)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
|
@ -44,6 +45,7 @@ pub fn next_instruction(data: &View) -> ParseResult {
|
||||||
load16::stack::Parser::parse_opcode,
|
load16::stack::Parser::parse_opcode,
|
||||||
arith8::Parser::parse_opcode,
|
arith8::Parser::parse_opcode,
|
||||||
arith16::Parser::parse_opcode,
|
arith16::Parser::parse_opcode,
|
||||||
|
misc::Parser::parse_opcode,
|
||||||
];
|
];
|
||||||
|
|
||||||
for parse_func in parse_funcs {
|
for parse_func in parse_funcs {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::cpu::instructions::load16::SixteenBitLoadInstruction;
|
use crate::cpu::instructions::load16::SixteenBitLoadInstruction;
|
||||||
use crate::cpu::instructions::{Instruction, RunnableInstruction};
|
use crate::cpu::instructions::{Instruction, RunnableInstruction};
|
||||||
use crate::cpu::parse::{self, Error, OpcodeParser, ParseOutput, ParseResult};
|
use crate::cpu::parse::{self, Error, OpcodeParser, ParseOutput, ParseResult};
|
||||||
use crate::memory::{View, GetViewTuple};
|
use crate::memory::{GetViewTuple, View};
|
||||||
use crate::register;
|
use crate::register;
|
||||||
|
|
||||||
#[allow(clippy::module_name_repetitions)]
|
#[allow(clippy::module_name_repetitions)]
|
||||||
|
@ -17,9 +17,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);
|
||||||
match opcode {
|
match opcode {
|
||||||
0x08 => Ok(make_load_sp_to_address_data(
|
0x08 => Ok(make_load_sp_to_address_data(data)),
|
||||||
data,
|
|
||||||
)),
|
|
||||||
0xF5 => Ok(make_stack_operation_data(
|
0xF5 => Ok(make_stack_operation_data(
|
||||||
Operation::Push,
|
Operation::Push,
|
||||||
register::Combined::AF,
|
register::Combined::AF,
|
||||||
|
@ -81,10 +79,10 @@ fn make_load_sp_to_address_data(data: &View) -> ParseOutput {
|
||||||
(
|
(
|
||||||
RunnableInstruction {
|
RunnableInstruction {
|
||||||
instruction: Instruction::SixteenBitLoad(
|
instruction: Instruction::SixteenBitLoad(
|
||||||
SixteenBitLoadInstruction::LoadStackPointerToImmediateAddress { dst_address }
|
SixteenBitLoadInstruction::LoadStackPointerToImmediateAddress { dst_address },
|
||||||
),
|
),
|
||||||
cycles: 20,
|
cycles: 20,
|
||||||
},
|
},
|
||||||
3
|
3,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
use super::{OpcodeParser, ParseOutput, ParseResult};
|
||||||
|
use crate::{
|
||||||
|
cpu::instructions::{misc::MiscInstruction, Instruction, RunnableInstruction},
|
||||||
|
memory::View,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct Parser;
|
||||||
|
|
||||||
|
impl OpcodeParser for Parser {
|
||||||
|
fn parse_opcode(data: &View) -> ParseResult {
|
||||||
|
let opcode = super::get_opcode_from_data(data);
|
||||||
|
match opcode {
|
||||||
|
0x37 => Ok(build_set_carry_flag_data()),
|
||||||
|
0x3F => Ok(build_complement_carry_flag_data()),
|
||||||
|
_ => Err(super::Error::UnknownOpcode(opcode)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_set_carry_flag_data() -> ParseOutput {
|
||||||
|
(
|
||||||
|
RunnableInstruction {
|
||||||
|
instruction: Instruction::Misc(MiscInstruction::SetCarryFlag),
|
||||||
|
cycles: 4,
|
||||||
|
},
|
||||||
|
1,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_complement_carry_flag_data() -> ParseOutput {
|
||||||
|
(
|
||||||
|
RunnableInstruction {
|
||||||
|
instruction: Instruction::Misc(MiscInstruction::ComplementCarryFlag),
|
||||||
|
cycles: 4,
|
||||||
|
},
|
||||||
|
1,
|
||||||
|
)
|
||||||
|
}
|
|
@ -9,6 +9,7 @@ mod arith8;
|
||||||
mod arithutil;
|
mod arithutil;
|
||||||
mod load16;
|
mod load16;
|
||||||
mod load8;
|
mod load8;
|
||||||
|
mod misc;
|
||||||
|
|
||||||
/// `Error` represents a run error. Typically these will be fatal and panicking on their emission
|
/// `Error` represents a run error. Typically these will be fatal and panicking on their emission
|
||||||
/// is likely the best course of action
|
/// is likely the best course of action
|
||||||
|
@ -41,5 +42,6 @@ pub fn run_instruction(processor: &mut Processor, instruction: Instruction) -> R
|
||||||
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::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),
|
||||||
|
Instruction::Misc(instruction) => instruction.run_on(processor),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -113,19 +113,17 @@ impl Run for SixteenBitLoadInstruction {
|
||||||
);
|
);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
},
|
}
|
||||||
SixteenBitLoadInstruction::LoadStackPointerToImmediateAddress { dst_address } => {
|
SixteenBitLoadInstruction::LoadStackPointerToImmediateAddress { dst_address } => {
|
||||||
let current_sp = processor
|
let current_sp = processor
|
||||||
.registers
|
.registers
|
||||||
.get_single_16bit_register(register::SingleSixteenBit::StackPointer);
|
.get_single_16bit_register(register::SingleSixteenBit::StackPointer);
|
||||||
|
|
||||||
let [top_half, bottom_half] = current_sp.to_be_bytes();
|
let [top_half, bottom_half] = current_sp.to_be_bytes();
|
||||||
let memory_res = processor.memory.set_both(
|
let memory_res = processor.memory.set_both((
|
||||||
(
|
(usize::from(dst_address), bottom_half),
|
||||||
(usize::from(dst_address), bottom_half),
|
(usize::from(dst_address + 1), top_half),
|
||||||
(usize::from(dst_address + 1), top_half),
|
));
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
match memory_res {
|
match memory_res {
|
||||||
Ok(_) => Ok(()),
|
Ok(_) => Ok(()),
|
||||||
|
|
|
@ -130,7 +130,7 @@ impl Run for EightBitLoadInstruction {
|
||||||
} => {
|
} => {
|
||||||
let src_address = memory::IO_REGISTER_START_ADDRESS + usize::from(offset);
|
let src_address = memory::IO_REGISTER_START_ADDRESS + usize::from(offset);
|
||||||
processor.load_from_address_to_register(dst, src_address)
|
processor.load_from_address_to_register(dst, src_address)
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
use crate::cpu::{instructions::misc::MiscInstruction, register, run::Error, Processor};
|
||||||
|
|
||||||
|
use super::Run;
|
||||||
|
|
||||||
|
impl Run for MiscInstruction {
|
||||||
|
fn run_on(&self, processor: &mut Processor) -> Result<(), Error> {
|
||||||
|
match *self {
|
||||||
|
MiscInstruction::SetCarryFlag => {
|
||||||
|
set_flags_in_carry_bit_instruction(processor, 1);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
MiscInstruction::ComplementCarryFlag => {
|
||||||
|
let current_carry_flag = processor.registers.get_flag_bit(register::Flag::Carry);
|
||||||
|
let flipped = (current_carry_flag == 0).into();
|
||||||
|
|
||||||
|
set_flags_in_carry_bit_instruction(processor, flipped);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_flags_in_carry_bit_instruction(processor: &mut Processor, carry_flag: u8) {
|
||||||
|
processor
|
||||||
|
.registers
|
||||||
|
.set_flag_bit(register::Flag::Carry, carry_flag);
|
||||||
|
|
||||||
|
processor.registers.set_flag_bit(register::Flag::HalfCarry, 0);
|
||||||
|
processor.registers.set_flag_bit(register::Flag::Subtract, 0);
|
||||||
|
}
|
|
@ -3,4 +3,5 @@ mod arith8;
|
||||||
mod jsmoo;
|
mod jsmoo;
|
||||||
mod load16;
|
mod load16;
|
||||||
mod load8;
|
mod load8;
|
||||||
|
mod misc;
|
||||||
mod testutil;
|
mod testutil;
|
||||||
|
|
|
@ -0,0 +1,68 @@
|
||||||
|
use ferris_boi::{
|
||||||
|
cpu::{instructions::RunnableInstruction, Processor},
|
||||||
|
register,
|
||||||
|
};
|
||||||
|
use test_case::test_case;
|
||||||
|
use crate::testutil;
|
||||||
|
|
||||||
|
#[test_case(1)]
|
||||||
|
#[test_case(0)]
|
||||||
|
fn test_set_carry_flag_always_sets_to_1(starting_value: u8) {
|
||||||
|
let mut processor = Processor::default();
|
||||||
|
processor
|
||||||
|
.registers
|
||||||
|
.set_flag_bit(register::Flag::Carry, starting_value);
|
||||||
|
|
||||||
|
let data = [0x37, 0x03];
|
||||||
|
let (ins, extra_data) =
|
||||||
|
RunnableInstruction::from_data(&data).expect("could not parse instruction");
|
||||||
|
|
||||||
|
assert_eq!(extra_data, &[0x03]);
|
||||||
|
processor.run_instruction(&ins);
|
||||||
|
|
||||||
|
let new_carry_bit = processor.registers.get_flag_bit(register::Flag::Carry);
|
||||||
|
assert_eq!(1, new_carry_bit);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test_case(1, 0)]
|
||||||
|
#[test_case(0, 1)]
|
||||||
|
fn test_complement_carry_bit(starting_value: u8, expected_value: u8) {
|
||||||
|
let mut processor = Processor::default();
|
||||||
|
processor
|
||||||
|
.registers
|
||||||
|
.set_flag_bit(register::Flag::Carry, starting_value);
|
||||||
|
|
||||||
|
let data = [0x3F, 0x03];
|
||||||
|
let (ins, extra_data) =
|
||||||
|
RunnableInstruction::from_data(&data).expect("could not parse instruction");
|
||||||
|
|
||||||
|
assert_eq!(extra_data, &[0x03]);
|
||||||
|
processor.run_instruction(&ins);
|
||||||
|
|
||||||
|
let new_carry_bit = processor.registers.get_flag_bit(register::Flag::Carry);
|
||||||
|
assert_eq!(expected_value, new_carry_bit);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test_case(0x37)]
|
||||||
|
#[test_case(0x3F)]
|
||||||
|
fn test_all_carry_bit_instructions_adjust_flags(opcode: u8) {
|
||||||
|
let mut processor = Processor::default();
|
||||||
|
testutil::set_opposite_of_expected_flags(&mut processor, (0, 0, 0, 1));
|
||||||
|
|
||||||
|
let data = [opcode, 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,
|
||||||
|
// Always zero
|
||||||
|
(register::Flag::HalfCarry, 0),
|
||||||
|
(register::Flag::Carry, 1),
|
||||||
|
(register::Flag::Subtract, 0),
|
||||||
|
// Value is preserved
|
||||||
|
(register::Flag::Zero, 1),
|
||||||
|
);
|
||||||
|
}
|
Loading…
Reference in New Issue