Add parsing of ldnn instructions

jsmoo
Nick Krichevsky 2022-04-02 11:01:04 -04:00
parent ac6097794d
commit 92b779d02d
1 changed files with 96 additions and 0 deletions

96
src/run.rs Normal file
View File

@ -0,0 +1,96 @@
use std::time::Instant;
use crate::register::{self, Registers};
#[derive(Debug, Default, Clone)]
pub struct Processor {
num_cycles: u64,
registers: Registers,
}
// these are indexed with the instruction numbers in this manual
// http://marc.rawer.de/Gameboy/Docs/GBCPUman.pdf
pub enum Instruction {
// 3.3.1.1
LDnnn {
value: u8,
register: register::Single,
},
}
struct RunnableInstruction {
instruction: Instruction,
cycles: u8,
}
impl RunnableInstruction {
/// `from_data` will produce an instruction from the given data and return the data after
/// processing that operation.
pub fn from_data(data: &[u8]) -> (Self, &[u8]) {
// TODO: Maybe we should return an err? I'm somewhat of the mind maybe we should
// panic with bad data because what really can we do?
let opcode = data[0];
match opcode {
0x06 => Self::make_ldnn_data(register::Single::B, data),
0x0E => Self::make_ldnn_data(register::Single::C, data),
0x16 => Self::make_ldnn_data(register::Single::D, data),
0x1E => Self::make_ldnn_data(register::Single::E, data),
0x26 => Self::make_ldnn_data(register::Single::H, data),
0x2E => Self::make_ldnn_data(register::Single::L, data),
_ => panic!("invalid opcode {opcode:0x}"),
}
}
fn make_ldnn_data(register: register::Single, data: &[u8]) -> (RunnableInstruction, &[u8]) {
(
RunnableInstruction {
instruction: Instruction::LDnnn {
register,
value: data[1],
},
cycles: 8,
},
&data[2..],
)
}
}
impl Processor {
fn run(&mut self, instruction: &RunnableInstruction) {
match instruction.instruction {
Instruction::LDnnn { value, register } => {
self.registers.set_single_register(register, value);
}
}
self.num_cycles += u64::from(instruction.cycles);
}
}
#[cfg(test)]
mod tests {
use super::*;
use test_case::test_case;
#[test_case(0x06, register::Single::B)]
#[test_case(0x0E, register::Single::C)]
#[test_case(0x16, register::Single::D)]
#[test_case(0x1E, register::Single::E)]
#[test_case(0x26, register::Single::H)]
#[test_case(0x2E, register::Single::L)]
fn test_load_immediate(ld_opcode: u8, expected_register: register::Single) {
let mut processor = Processor::default();
let data = [ld_opcode, 0x23, 0x00, 0x01];
let (ins, extra_data) = RunnableInstruction::from_data(&data);
// assert our extra data after the instruction is returned
// this data is just garbage; no idea if it's a valid instruction
assert_eq!(extra_data, &[0x00, 0x01]);
processor.run(&ins);
assert_eq!(
0x23,
processor.registers.get_single_register(expected_register)
);
}
}