Add parsing of ldnn instructions
parent
ac6097794d
commit
92b779d02d
|
@ -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)
|
||||
);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue