gb-emu/src/processor/mod.rs

182 lines
3.9 KiB
Rust
Raw Normal View History

2023-02-02 16:52:33 +11:00
use std::{mem::transmute, time::Duration};
2023-01-16 19:28:03 +11:00
2023-02-01 17:18:08 +11:00
use crate::{verbose_println, Memory};
2023-01-16 12:13:53 +11:00
2023-01-31 10:05:36 +11:00
mod instructions;
2023-01-22 09:39:45 +11:00
mod opcodes;
2023-01-22 09:18:07 +11:00
#[derive(PartialEq)]
2023-02-01 20:04:05 +11:00
pub(crate) enum Flags {
2023-01-22 13:09:31 +11:00
Zero = 7,
NSubtract = 6,
HalfCarry = 5,
Carry = 4,
2023-01-16 14:23:06 +11:00
}
2023-01-31 10:05:36 +11:00
pub(crate) enum Direction {
2023-01-18 13:14:22 +11:00
Left,
Right,
}
2023-01-16 12:13:53 +11:00
pub struct CPU {
pub memory: Memory,
2023-02-01 17:18:08 +11:00
pub reg: Registers,
2023-01-18 12:45:56 +11:00
pub last_instruction: u8,
pub last_instruction_addr: u16,
2023-01-16 12:13:53 +11:00
}
2023-02-02 16:52:33 +11:00
// MHz
const CLOCK_SPEED: f64 = 4.194304 * 1000000.;
const SPEEDUP: f64 = 1.;
2023-01-16 12:13:53 +11:00
impl CPU {
2023-01-17 09:09:53 +11:00
pub fn exec_next(&mut self) {
2023-02-01 17:18:08 +11:00
self.last_instruction_addr = self.reg.pc;
2023-01-16 12:13:53 +11:00
let opcode = self.next_opcode();
2023-01-18 12:45:56 +11:00
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;
}
}
2023-01-22 12:13:02 +11:00
verbose_println!(
2023-01-22 09:32:19 +11:00
"exec {:#4X} from pc: {:#X}",
2023-01-22 12:13:02 +11:00
opcode,
self.last_instruction_addr
2023-01-22 09:32:19 +11:00
);
2023-02-02 16:52:33 +11:00
let cycles = self.run_opcode(opcode);
spin_sleep::sleep(Duration::from_secs_f64(
(cycles * 4) as f64 / (CLOCK_SPEED * SPEEDUP),
));
2023-01-16 12:13:53 +11:00
}
fn next_opcode(&mut self) -> u8 {
2023-02-01 17:18:08 +11:00
let opcode = self.memory.get(self.reg.pc);
self.reg.pc = self.reg.pc.wrapping_add(0x1);
return opcode;
}
}
2023-02-01 20:04:05 +11:00
#[derive(Clone, Copy)]
2023-02-01 17:18:08 +11:00
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: 0x0100,
}
}
}
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;
2023-01-16 12:13:53 +11:00
}
}
2023-01-17 09:09:53 +11:00
fn as_signed(unsigned: u8) -> i8 {
unsafe {
return transmute(unsigned);
}
}
2023-01-18 12:45:56 +11:00
fn get_bit(byte: u8, flag: u8) -> bool {
let mask = 1 << flag;
let got = byte & mask;
return got > 0x0;
}
2023-01-18 13:14:22 +11:00
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,
}
}