use self::gpu::GPU; use self::memory::Memory; use crate::{processor::instructions::instructions::set, verbose_println}; use minifb::Window; use std::{mem::transmute, time::Duration}; pub mod gpu; mod instructions; pub mod memory; mod opcodes; #[derive(PartialEq)] pub(crate) enum Flags { Zero = 7, NSubtract = 6, HalfCarry = 5, Carry = 4, } pub(crate) enum Direction { Left, Right, } pub struct CPU { pub memory: Memory, pub reg: Registers, pub last_instruction: u8, pub last_instruction_addr: u16, pub window: Window, pub gpu: GPU, } // Hz const CLOCK_SPEED: f64 = 4.194304 * 1000000.; const SPEEDUP: f64 = 1.; const FF04_SPEED: f64 = 16384.; impl CPU { pub fn new(memory: Memory, window: Window) -> Self { Self { memory, reg: Registers::default(), last_instruction: 0x0, last_instruction_addr: 0x0, window, gpu: GPU::default(), } } pub fn exec_next(&mut self) { self.last_instruction_addr = self.reg.pc; let opcode = self.next_opcode(); self.last_instruction = opcode; if self.memory.ime_scheduled > 0 { self.memory.ime_scheduled = self.memory.ime_scheduled.saturating_sub(1); if self.memory.ime_scheduled == 0 { self.memory.ime = true; } } verbose_println!( "exec {:#4X} from pc: {:#X}", opcode, self.last_instruction_addr ); self.memory.user_mode = true; let cycles = self.run_opcode(opcode); self.memory.user_mode = false; self.increment_timers(cycles); let interrupt_cycles = self.handle_interrupts(); self.increment_timers(interrupt_cycles); } fn increment_timers(&mut self, cycles: u8) { let secs = (cycles * 4) as f64 / CLOCK_SPEED; self.advance_gpu_clock(cycles); self.memory.set( 0xFF04, self.memory .get(0xFF04) .wrapping_add((FF04_SPEED * secs) as u8), ); let (timer_enabled, timer_rate) = self.timer_scale(); if timer_enabled { let (val, wrap) = self .memory .get(0xFF05) .overflowing_add((secs * timer_rate as f64) as u8); if wrap { self.memory.set(0xFF05, self.memory.get(0xFF06)); self.memory.set(0xFF0F, set(self.memory.get(0xFF0F), 2)); } else { self.memory.set(0xFF05, val); } } spin_sleep::sleep(Duration::from_secs_f64(secs / SPEEDUP)); } fn next_opcode(&mut self) -> u8 { let opcode = self.memory.get(self.reg.pc); self.reg.pc = self.reg.pc.wrapping_add(0x1); return opcode; } fn handle_interrupts(&mut self) -> u8 { if self.memory.ime { let req_and_enabled = self.memory.get(0xFF0F) & self.memory.get(0xFFFF); // all interrupts should last 5 cycles? if get_bit(req_and_enabled, 0) { // vblank self.service_interrupt(0x40); self.memory .set(0xFF0F, clear_bit(self.memory.get(0xFF0F), 0)); 5 } else if get_bit(req_and_enabled, 1) { // lcd stat self.service_interrupt(0x48); self.memory .set(0xFF0F, clear_bit(self.memory.get(0xFF0F), 1)); 5 } else if get_bit(req_and_enabled, 2) { // timer self.service_interrupt(0x50); self.memory .set(0xFF0F, clear_bit(self.memory.get(0xFF0F), 2)); 5 } else if get_bit(req_and_enabled, 3) { // serial self.service_interrupt(0x58); self.memory .set(0xFF0F, clear_bit(self.memory.get(0xFF0F), 3)); 5 } else if get_bit(req_and_enabled, 4) { // joypad self.service_interrupt(0x60); self.memory .set(0xFF0F, clear_bit(self.memory.get(0xFF0F), 4)); 5 } else { 0 } } else { 0 } } fn service_interrupt(&mut self, addr: u16) { self.push(self.reg.pc); self.reg.pc = addr; self.memory.ime = false; } fn timer_scale(&self) -> (bool, usize) { let timer_control = self.memory.get(0xFF07); let timer_enable = get_bit(timer_control, 2); let timer_rate = match (get_bit(timer_control, 1), get_bit(timer_control, 0)) { (true, true) => 256, (true, false) => 64, (false, true) => 16, (false, false) => 1024, }; (timer_enable, timer_rate) } } #[derive(Clone, Copy)] pub enum Reg8 { A, B, C, D, E, H, L, } #[derive(Clone, Copy)] pub struct Registers { pub af: u16, pub bc: u16, pub de: u16, pub hl: u16, pub sp: u16, pub pc: u16, } impl Default for Registers { fn default() -> Self { // default post-bootrom values Self { af: 0x00B0, // af: 0x01B0, bc: 0x0013, de: 0x00D8, hl: 0x014D, sp: 0xFFFE, pc: 0x0000, } } } impl Registers { fn get_8(&self, register: Reg8) -> u8 { match register { Reg8::A => self.af.get_high(), Reg8::B => self.bc.get_high(), Reg8::C => self.bc.get_low(), Reg8::D => self.de.get_high(), Reg8::E => self.de.get_low(), Reg8::H => self.hl.get_high(), Reg8::L => self.hl.get_low(), } } fn set_8(&mut self, register: Reg8, val: u8) { match register { Reg8::A => self.af.set_high(val), Reg8::B => self.bc.set_high(val), Reg8::C => self.bc.set_low(val), Reg8::D => self.de.set_high(val), Reg8::E => self.de.set_low(val), Reg8::H => self.hl.set_high(val), Reg8::L => self.hl.set_low(val), } } } trait SplitRegister { fn get_low(&self) -> u8; fn get_high(&self) -> u8; fn set_low(&mut self, val: u8); fn set_high(&mut self, val: u8); } impl SplitRegister for u16 { fn get_low(&self) -> u8 { (*self & 0xFF) as u8 } fn get_high(&self) -> u8 { ((*self >> 8) & 0xFF) as u8 } fn set_low(&mut self, val: u8) { *self = (*self & !0xff) | val as u16; } fn set_high(&mut self, val: u8) { *self = (*self & !0xff00) | (val as u16) << 8; } } fn as_signed(unsigned: u8) -> i8 { unsafe { return transmute(unsigned); } } fn get_bit(byte: u8, flag: u8) -> bool { let mask = 1 << flag; let got = byte & mask; return got > 0x0; } fn set_or_clear_bit(byte: u8, flag: u8, condition: bool) -> u8 { if condition { set_bit(byte, flag) } else { clear_bit(byte, flag) } } fn set_bit(byte: u8, flag: u8) -> u8 { byte | (1 << flag) } fn clear_bit(byte: u8, flag: u8) -> u8 { byte & (!(1 << flag)) } fn rotate(byte: u8, direction: &Direction) -> (u8, bool) { match direction { Direction::Left => { let carry = get_bit(byte, 7); let r = byte << 1; return (r, carry); } Direction::Right => { let carry = get_bit(byte, 0); let r = byte >> 1; return (r, carry); } } } fn get_rotation_carry(direction: &Direction) -> u8 { match direction { Direction::Left => 0b1, Direction::Right => 0b10000000, } }