use std::{fs, env}; use instructions::arithmetic; mod instructions; pub struct EmulatorState { a: u8, b: u8, c: u8, d: u8, e: u8, h: u8, l: u8, cc: ConditionCodes, /// Stack pointer sp: u16, /// Memory pointer pc: u16, memory: [u8; 8000], } pub struct ConditionCodes { /// Zero (Z), set if the result is zero. z: bool, /// Sign (S), set if the result is negative. s: bool, /// Parity (P), set if the number of 1 bits in the result is even. p: bool, /// Carry (C), set if the last addition operation resulted in a carry or if the last subtraction operation required a borrow. c: bool, // Auxiliary carry (AC or H), used for binary-coded decimal arithmetic (BCD). // Can't test this so I won't implement it // ac: bool, } #[derive(PartialEq)] pub enum Register { B, C, D, E, H, L, M, A, } /// Returns a Register enum based on the input number 0..7 in the order B,C,D,E,H,L,M,A fn register_from_num(b: u8) -> Register { match b { 0 => Register::B, 1 => Register::C, 2 => Register::D, 3 => Register::E, 4 => Register::H, 5 => Register::L, 6 => Register::M, 7 => Register::A, _ => panic!("'{b}' cannot be converted to register enum"), } } fn get_register(register: Register, state: &EmulatorState) -> u16 { match register { Register::B => state.b as u16, Register::C => state.c as u16, Register::D => state.d as u16, Register::E => state.e as u16, Register::H => state.h as u16, Register::L => state.l as u16, Register::A => state.a as u16, Register::M => (state.memory[(state.h as usize)] as u16) << 8 | (state.memory[state.l as usize] as u16), } } fn set_register(register: Register, value: u8, state: &mut EmulatorState) { match register { Register::B => state.b = value, Register::C => state.c = value, Register::D => state.d = value, Register::E => state.e = value, Register::H => state.h = value, Register::L => state.l = value, Register::A => state.a = value, Register::M => panic!("Cannot set pseudoregister 'M'"), }; } fn main() { let mut state = EmulatorState { a: 0, b: 0, c: 0, d: 0, e: 0, h: 0, l: 0, sp: 0, cc: ConditionCodes { z: true, s: true, p: true, c: true }, pc: 0, memory: [0; 8000], }; // Load the ROM into memory let mut args = env::args(); let filename = args .nth(1) .expect("Provide a path to a ROM file to emulate as an argument"); let file = fs::read(filename).expect("where file"); for i in 0..8000 { state.memory[i] = file[i]; } while state.pc < 8000 { tick(&mut state); } } fn tick(state: &mut EmulatorState) { let instruction = state.memory[state.pc as usize]; let mut next_byte = || { state.pc += 1; return state.memory[state.pc as usize]; }; match instruction { 0x00 => {} // NOP 0x80..=0x87 => arithmetic::add(register_from_num(instruction & 0xf), state), // ADD 0xc6 => arithmetic::adi(next_byte(), state), // ADI _ => not_implemented(state), } state.pc += 1; } fn not_implemented(state: &EmulatorState) { let instruction = state.memory[state.pc as usize]; panic!("Unimplemented instruction {:#02X} at {:#04X}", instruction, state.pc); }