Implement jump immediate instruction
parent
710e293a0e
commit
409e46eea9
|
@ -34,6 +34,7 @@ impl Processor {
|
||||||
parse::next_instruction(&memory_view).expect("invalid instruction");
|
parse::next_instruction(&memory_view).expect("invalid instruction");
|
||||||
|
|
||||||
let interrupt_enable_pending_already = self.interrupt_enable_pending;
|
let interrupt_enable_pending_already = self.interrupt_enable_pending;
|
||||||
|
let pc_before_instruction_run = self.registers.program_counter;
|
||||||
self.run_instruction(instruction);
|
self.run_instruction(instruction);
|
||||||
|
|
||||||
// EI does not work until after the following instruction, so we check after running an instruction if
|
// EI does not work until after the following instruction, so we check after running an instruction if
|
||||||
|
@ -43,8 +44,12 @@ impl Processor {
|
||||||
self.registers.enable_interrupts();
|
self.registers.enable_interrupts();
|
||||||
}
|
}
|
||||||
|
|
||||||
let next_pc = pc.wrapping_add(bytes_read);
|
if pc_before_instruction_run == self.registers.program_counter {
|
||||||
self.registers.program_counter = next_pc;
|
// 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.
|
/// Run a single instruction on the CPU.
|
||||||
|
|
|
@ -6,6 +6,7 @@ use super::parse::{self, Error};
|
||||||
|
|
||||||
pub mod arith16;
|
pub mod arith16;
|
||||||
pub mod arith8;
|
pub mod arith8;
|
||||||
|
pub mod control;
|
||||||
pub mod load16;
|
pub mod load16;
|
||||||
pub mod load8;
|
pub mod load8;
|
||||||
pub mod misc;
|
pub mod misc;
|
||||||
|
@ -19,6 +20,7 @@ pub enum Instruction {
|
||||||
EightBitArithmetic(arith8::EightBitArithmeticInstruction),
|
EightBitArithmetic(arith8::EightBitArithmeticInstruction),
|
||||||
StackPointerAdjust(arith8::AdjustStackPointerInstruction),
|
StackPointerAdjust(arith8::AdjustStackPointerInstruction),
|
||||||
SixteenBitArithmetic(arith16::SixteenBitArithmeticInstruction),
|
SixteenBitArithmetic(arith16::SixteenBitArithmeticInstruction),
|
||||||
|
ControlFlow(control::ControlFlowInstruction),
|
||||||
Misc(misc::MiscInstruction),
|
Misc(misc::MiscInstruction),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
#[allow(clippy::module_name_repetitions)]
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub enum ControlFlowInstruction {
|
||||||
|
JumpToImmediate { addr: u16 },
|
||||||
|
}
|
|
@ -7,6 +7,7 @@ use thiserror::Error;
|
||||||
|
|
||||||
mod arith16;
|
mod arith16;
|
||||||
mod arith8;
|
mod arith8;
|
||||||
|
mod control;
|
||||||
mod load16;
|
mod load16;
|
||||||
mod load8;
|
mod load8;
|
||||||
mod misc;
|
mod misc;
|
||||||
|
@ -45,6 +46,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,
|
||||||
|
control::Parser::parse_opcode,
|
||||||
misc::Parser::parse_opcode,
|
misc::Parser::parse_opcode,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
|
)
|
||||||
|
}
|
|
@ -7,6 +7,7 @@ use thiserror::Error;
|
||||||
mod arith16;
|
mod arith16;
|
||||||
mod arith8;
|
mod arith8;
|
||||||
mod arithutil;
|
mod arithutil;
|
||||||
|
mod control;
|
||||||
mod load16;
|
mod load16;
|
||||||
mod load8;
|
mod load8;
|
||||||
mod misc;
|
mod misc;
|
||||||
|
@ -48,6 +49,7 @@ pub fn run_instruction(
|
||||||
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::ControlFlow(control_instruction) => control_instruction.run_on(processor),
|
||||||
Instruction::Misc(instruction) => instruction.run_on(processor),
|
Instruction::Misc(instruction) => instruction.run_on(processor),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
|
@ -73,6 +73,7 @@ fn test_jsmoo_test(filename: &str) {
|
||||||
processor.registers.l, test_case.r#final.l,
|
processor.registers.l, test_case.r#final.l,
|
||||||
"register l value was incorrect"
|
"register l value was incorrect"
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
processor.registers.program_counter, test_case.r#final.pc,
|
processor.registers.program_counter, test_case.r#final.pc,
|
||||||
"program counter was incorrect"
|
"program counter was incorrect"
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
mod arith16;
|
mod arith16;
|
||||||
mod arith8;
|
mod arith8;
|
||||||
|
mod control;
|
||||||
mod jsmoo;
|
mod jsmoo;
|
||||||
mod load16;
|
mod load16;
|
||||||
mod load8;
|
mod load8;
|
||||||
|
|
Loading…
Reference in New Issue