Implement jump immediate instruction

old-bit-manip
Nick Krichevsky 2023-11-21 14:55:40 -05:00
parent 710e293a0e
commit 409e46eea9
11 changed files with 85 additions and 2 deletions

View File

@ -34,6 +34,7 @@ impl Processor {
parse::next_instruction(&memory_view).expect("invalid instruction");
let interrupt_enable_pending_already = self.interrupt_enable_pending;
let pc_before_instruction_run = self.registers.program_counter;
self.run_instruction(instruction);
// EI does not work until after the following instruction, so we check after running an instruction if
@ -43,9 +44,13 @@ impl Processor {
self.registers.enable_interrupts();
}
if pc_before_instruction_run == self.registers.program_counter {
// The program counter should be advanced *ONLY* if the instruction did not modify the PC register
let next_pc = pc.wrapping_add(bytes_read);
self.registers.program_counter = next_pc;
}
}
/// Run a single instruction on the CPU.
///

View File

@ -6,6 +6,7 @@ use super::parse::{self, Error};
pub mod arith16;
pub mod arith8;
pub mod control;
pub mod load16;
pub mod load8;
pub mod misc;
@ -19,6 +20,7 @@ pub enum Instruction {
EightBitArithmetic(arith8::EightBitArithmeticInstruction),
StackPointerAdjust(arith8::AdjustStackPointerInstruction),
SixteenBitArithmetic(arith16::SixteenBitArithmeticInstruction),
ControlFlow(control::ControlFlowInstruction),
Misc(misc::MiscInstruction),
}

View File

@ -0,0 +1,5 @@
#[allow(clippy::module_name_repetitions)]
#[derive(Debug, Clone, Copy)]
pub enum ControlFlowInstruction {
JumpToImmediate { addr: u16 },
}

View File

@ -7,6 +7,7 @@ use thiserror::Error;
mod arith16;
mod arith8;
mod control;
mod load16;
mod load8;
mod misc;
@ -45,6 +46,7 @@ pub fn next_instruction(data: &View) -> ParseResult {
load16::stack::Parser::parse_opcode,
arith8::Parser::parse_opcode,
arith16::Parser::parse_opcode,
control::Parser::parse_opcode,
misc::Parser::parse_opcode,
];

33
src/cpu/parse/control.rs Normal file
View File

@ -0,0 +1,33 @@
use crate::{
cpu::{
instructions::{control::ControlFlowInstruction, Instruction},
parse,
},
memory::{GetViewTuple, View},
};
use super::{OpcodeParser, ParseOutput, ParseResult};
pub struct Parser;
impl OpcodeParser for Parser {
fn parse_opcode(data: &View) -> ParseResult {
let opcode = parse::get_opcode_from_data(data);
match opcode {
0xC3 => Ok(build_jump_to_immediate_data(data)),
_ => Err(parse::Error::UnknownOpcode(opcode)),
}
}
}
fn build_jump_to_immediate_data(data: &View) -> ParseOutput {
let (_opcode, lower_bytes, upper_bytes) = data.get_tuple();
// manual doesn't state this should be LE, but some inspection of games and googling
// indicates it should be.
let addr = u16::from_le_bytes([lower_bytes, upper_bytes]);
(
Instruction::ControlFlow(ControlFlowInstruction::JumpToImmediate { addr }),
3,
)
}

View File

@ -7,6 +7,7 @@ use thiserror::Error;
mod arith16;
mod arith8;
mod arithutil;
mod control;
mod load16;
mod load8;
mod misc;
@ -48,6 +49,7 @@ pub fn run_instruction(
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::ControlFlow(control_instruction) => control_instruction.run_on(processor),
Instruction::Misc(instruction) => instruction.run_on(processor),
}
}

17
src/cpu/run/control.rs Normal file
View File

@ -0,0 +1,17 @@
use crate::cpu::{instructions::control::ControlFlowInstruction, register, Processor};
use super::{Cycles, Run};
impl Run for ControlFlowInstruction {
fn run_on(&self, processor: &mut Processor) -> Result<Cycles, super::Error> {
match *self {
Self::JumpToImmediate { addr } => {
processor
.registers
.set_single_16bit_register(register::SingleSixteenBit::ProgramCounter, addr);
Ok(Cycles(16))
}
}
}
}

15
tests/cpu/control.rs Normal file
View File

@ -0,0 +1,15 @@
use ferris_boi::cpu::{instructions::Instruction, Processor};
#[test]
fn test_can_jump_to_immediate() {
let mut processor = Processor::default();
let data = [0xC3, 0x37, 0x13, 0x06];
let (ins, extra_data) = Instruction::from_data(&data).expect("could not parse instruction");
assert_eq!(extra_data, &[0x06]);
processor.run_instruction(ins);
assert_eq!(0x1337, processor.registers.program_counter);
}

View File

@ -73,6 +73,7 @@ fn test_jsmoo_test(filename: &str) {
processor.registers.l, test_case.r#final.l,
"register l value was incorrect"
);
assert_eq!(
processor.registers.program_counter, test_case.r#final.pc,
"program counter was incorrect"

View File

@ -1,5 +1,6 @@
mod arith16;
mod arith8;
mod control;
mod jsmoo;
mod load16;
mod load8;