Created
August 8, 2021 08:36
-
-
Save EmmaEwert/9a95d8e69a8bfc0b9f05685f49053d2e to your computer and use it in GitHub Desktop.
Rust code for the CPU of a Game Boy
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use std::mem::MaybeUninit; | |
use crate::log; | |
use crate::emulator::*; | |
pub struct CPU { | |
pub emulator: *mut Emulator, | |
b: u8, | |
c: u8, | |
d: u8, | |
e: u8, | |
h: u8, | |
l: u8, | |
a: u8, | |
f_z: bool, | |
f_n: bool, | |
f_h: bool, | |
f_c: bool, | |
sp: u16, | |
pc: u16, | |
} | |
impl CPU { | |
pub fn new() -> CPU { | |
CPU { | |
emulator: unsafe { MaybeUninit::uninit().assume_init() }, | |
b: 0, | |
c: 0, | |
d: 0, | |
e: 0, | |
h: 0, | |
l: 0, | |
a: 0, | |
f_z: false, | |
f_n: false, | |
f_h: false, | |
f_c: false, | |
sp: 0, | |
pc: 0, | |
} | |
} | |
fn get_opcode(&mut self) -> u8 { | |
let opcode = unsafe { self.emulator.as_ref().unwrap() }.read_memory(self.pc); | |
self.pc += 1; | |
opcode | |
} | |
fn push_stack(&mut self, n: u8) { | |
self.sp -= 1; | |
unsafe { self.emulator.as_mut().unwrap() }.write_memory(self.sp, n) | |
} | |
fn pop_stack(&mut self) -> u8 { | |
let result = unsafe { self.emulator.as_ref().unwrap() }.read_memory(self.sp); | |
self.sp += 1; | |
result | |
} | |
fn add(&mut self, r: u8) { | |
let temp = r as i32; | |
let temp2 = self.a as i32 + temp; | |
self.f_z = temp2 as u8 == 0; | |
self.f_n = false; | |
self.f_h = (self.a as i32 ^ temp ^ temp2) & 0x10 != 0; | |
self.f_c = temp2 & 0x100 != 0; | |
self.a = temp2 as u8; | |
} | |
fn sub(&mut self, r: u8) { | |
let temp = r as i32; | |
let temp2 = self.a as i32 - temp; | |
self.f_z = temp2 == 0; | |
self.f_n = true; | |
self.f_h = (self.a as i32 ^ temp ^ temp2) & 0x10 != 0; | |
self.f_c = temp2 & 0x100 != 0; | |
self.a = temp2 as u8; | |
} | |
fn xor(&mut self, r: u8) { | |
self.a ^= r; | |
self.f_z = self.a == 0; | |
self.f_n = false; | |
self.f_h = false; | |
self.f_c = false; | |
} | |
fn cp(&mut self, r: u8) { | |
let temp = r as i32; | |
let temp2 = self.a as i32 - temp; | |
self.f_z = temp2 == 0; | |
self.f_n = true; | |
self.f_h = (self.a as i32 ^ temp ^ temp2) & 0x10 != 0; | |
self.f_c = temp2 & 0x100 != 0; | |
} | |
fn rl(&mut self, r: u8) -> u8 { | |
let temp: i32 = if self.f_c { 1 } else { 0 }; | |
self.f_c = r & 0x80 != 0; | |
let res = (r << 1) as i32 | temp; | |
self.f_z = res == 0; | |
self.f_n = false; | |
self.f_h = false; | |
res as u8 | |
} | |
fn bit(&mut self, b: usize, r: u8) { | |
self.f_z = ((r >> b) & 0x01) ^ 0x01 == 1; | |
self.f_n = false; | |
self.f_h = true; | |
} | |
fn bc(&self) -> u16 { | |
(self.b as u16) << 8 | self.c as u16 | |
} | |
fn de(&self) -> u16 { | |
(self.d as u16) << 8 | self.e as u16 | |
} | |
fn hl(&self) -> u16 { | |
(self.h as u16) << 8 | self.l as u16 | |
} | |
pub fn init(&self) { | |
} | |
pub fn step(&mut self) -> i32 { | |
// if halt return cycle_clock; | |
// if gdma_num | |
//log("CPU:", &format!("PC: {:02X?}, DE: {:04X?}", self.pc, self.de())); | |
let mut cycle: i32 = 1; // TODO: 0 | |
match self.get_opcode() { | |
0x00 => { } // NOP | |
0x01 => { // LD BC,d16 | |
self.c = self.get_opcode(); | |
self.b = self.get_opcode(); | |
} | |
0x02 => { // LD (BC),A | |
unsafe { self.emulator.as_mut().unwrap() }.write_memory(self.bc(), self.a); | |
} | |
0x03 => { // INC BC | |
self.c += 1; | |
if self.c == 0 { | |
self.b += 1; | |
} | |
} | |
0x04 => { // INC B | |
self.b += 1; | |
self.f_z = self.b == 0; | |
self.f_n = false; | |
self.f_h = self.b & 0x0F == 0; | |
} | |
0x05 => { // DEC B | |
self.b -= 1; | |
self.f_z = self.b == 0; | |
self.f_n = true; | |
self.f_h = self.b & 0x0F == 0x0F; | |
} | |
0x06 => { self.b = self.get_opcode(); } // LD B,d8 | |
0x07 => { // RLCA | |
self.a = ((self.a as u16) << 1) as u8 | self.a >> 7; | |
self.f_z = false; | |
self.f_n = false; | |
self.f_h = false; | |
self.f_c = self.a & 0x01 == 0x01; | |
} | |
0x08 => { // LD (a16),SP | |
let mut temp = self.get_opcode() as u16; | |
temp |= (self.get_opcode() as u16) << 8; | |
unsafe { self.emulator.as_mut().unwrap() }.write_memory(temp, (self.sp & 0xFF) as u8); | |
temp += 1; | |
unsafe { self.emulator.as_mut().unwrap() }.write_memory(temp, (self.sp >> 8 & 0xFF) as u8); | |
} | |
0x09 => { // ADD HL,BC | |
let temp = self.hl() as u32 + self.bc() as u32; | |
self.f_n = false; | |
self.f_h = self.hl() as u32 ^ self.bc() as u32 ^ temp & 0x01000 != 0; | |
self.f_c = temp & 0x10000 != 0; | |
self.h = (temp >> 8 & 0xFF) as u8; | |
self.l = (temp & 0xFF) as u8; | |
} | |
0x0A => { // LD A,(BC) | |
self.a = unsafe { self.emulator.as_ref().unwrap() }.read_memory(self.bc()); | |
} | |
0x0B => { // DEC BC | |
self.c -= 1; | |
if self.c == 0xFF { | |
self.b -= 1; | |
} | |
} | |
0x0C => { // INC C | |
self.c += 1; | |
self.f_z = self.c == 0; | |
self.f_n = false; | |
self.f_h = self.c & 0x0F == 0; | |
} | |
0x0D => { // DEC C | |
self.c -= 1; | |
self.f_z = self.c == 0; | |
self.f_n = true; | |
self.f_h = self.c & 0x0F == 0x0F; | |
} | |
0x0E => { self.c = self.get_opcode(); } // LD C,d8 | |
0x0F => { // RRCA | |
self.f_z = false; | |
self.f_n = false; | |
self.f_h = false; | |
self.f_c = self.a & 0x01 == 0x01; | |
self.a = self.a >> 1 | ((self.a as u16) << 7) as u8; | |
} | |
0x10 => { unsafe { self.emulator.as_mut().unwrap() }.stop(); } // STOP | |
0x11 => { // LD DE,d16 | |
self.e = self.get_opcode(); | |
self.d = self.get_opcode(); | |
} | |
0x13 => { // INC DE | |
self.e += 1; | |
if self.e == 0 { | |
self.d += 1; | |
} | |
} | |
0x15 => { // DEC D | |
self.d -= 1; | |
self.f_z = self.d == 0; | |
self.f_n = true; | |
self.f_h = self.d & 0x0F == 0x0F; | |
} | |
0x16 => { self.d = self.get_opcode(); } // LD D,d8 | |
0x17 => { // RLA | |
let temp: u8 = if self.f_c { 1 } else { 0 }; | |
self.f_z = false; | |
self.f_n = false; | |
self.f_h = false; | |
self.f_c = self.a & 0x80 != 0; | |
self.a = (self.a << 1) | temp; | |
} | |
0x18 => { // JR r8 | |
self.pc = (self.get_opcode() as i8 as i16 + self.pc as i16) as u16; | |
} | |
0x1A => { // LD A,(DE) | |
self.a = unsafe { self.emulator.as_ref().unwrap() }.read_memory(self.de()); | |
} | |
0x1D => { // DEC E | |
self.e -= 1; | |
self.f_z = self.e == 0; | |
self.f_n = true; | |
self.f_h = self.e & 0x0F == 0x0F; | |
} | |
0x1E => { self.e = self.get_opcode(); } // LD E,d8 | |
0x20 => { // JR NZ,r8 | |
if !self.f_z { | |
self.pc = (self.get_opcode() as i8 as i16 + self.pc as i16) as u16; | |
cycle = 1; | |
} else { | |
self.pc += 1; | |
} | |
} | |
0x21 => { // LD HL,d16 | |
self.l = self.get_opcode(); | |
self.h = self.get_opcode(); | |
} | |
0x22 => { // LDI (HL),A | |
unsafe { self.emulator.as_mut().unwrap() }.write_memory(self.hl(), self.a); | |
self.l += 1; | |
if self.l == 0x00 { | |
self.h += 1; | |
} | |
} | |
0x23 => { // INC HL | |
self.l += 1; | |
if self.l == 0x00 { | |
self.h += 1; | |
} | |
} | |
0x24 => { // INC H | |
self.h += 1; | |
self.f_z = self.h == 0; | |
self.f_n = false; | |
self.f_h = self.h & 0x0F == 0; | |
} | |
0x28 => { // JR Z,r8 | |
if self.f_z { | |
self.pc = (self.get_opcode() as i8 as i16 + self.pc as i16) as u16; | |
cycle = 1; | |
} else { | |
self.pc += 1; | |
} | |
} | |
0x2E => { // LD L,d8 | |
self.l = self.get_opcode(); | |
} | |
0x31 => { // LD SP,d16 | |
let mut temp = self.get_opcode() as u16; | |
temp |= (self.get_opcode() as u16) << 8; | |
self.sp = temp; | |
} | |
0x32 => { // LDD (HL),A | |
unsafe { self.emulator.as_mut().unwrap() }.write_memory(self.hl(), self.a); | |
self.l -= 1; | |
if self.l == 0xFF { | |
self.h -= 1; | |
} | |
} | |
0x3D => { // DEC A | |
self.a -= 1; | |
self.f_z = self.a == 0; | |
self.f_n = true; | |
self.f_h = self.a & 0x0F == 0x0F; | |
} | |
0x3E => { self.a = self.get_opcode(); } // LD A,d8 | |
0x4F => { self.c = self.a } // LD C,A | |
0x57 => { self.d = self.a } // LD D,A | |
0x58 => { self.e = self.b } // LD E,B | |
0x59 => { self.e = self.c } // LD E,C | |
0x5A => { self.e = self.d } // LD E,D | |
0x5B => { self.e = self.e } // LD E,E | |
0x5C => { self.e = self.h } // LD E,H | |
0x5D => { self.e = self.l } // LD E,L | |
0x67 => { self.h = self.a; } // LD H,A | |
0x68 => { self.l = self.b; } // LD L,B | |
0x69 => { self.l = self.c; } // LD L,C | |
0x6A => { self.l = self.d; } // LD L,D | |
0x6B => { self.l = self.e; } // LD L,E | |
0x6C => { self.l = self.h; } // LD L,H | |
0x6D => { self.l = self.l; } // LD L,L | |
0x77 => { // LD (HL),A | |
unsafe { self.emulator.as_mut().unwrap() }.write_memory(self.hl(), self.a); | |
} | |
0x78 => { self.a = self.b } // LD A,B | |
0x79 => { self.a = self.c } // LD A,C | |
0x7A => { self.a = self.d } // LD A,D | |
0x7B => { self.a = self.e } // LD A,E | |
0x7C => { self.a = self.h } // LD A,H | |
0x7D => { self.a = self.l } // LD A,L | |
0x86 => { // ADD (HL) | |
self.add(unsafe { self.emulator.as_ref().unwrap() }.read_memory(self.hl())) | |
} | |
0x90 => { self.sub(self.b); } // SUB B | |
0xAF => { self.xor(self.a); } // XOR A | |
0xBE => { // CP (HL) | |
self.cp(unsafe { self.emulator.as_ref().unwrap() }.read_memory(self.hl())); | |
} | |
0xC1 => { // POP BC | |
self.c = self.pop_stack(); | |
self.b = self.pop_stack(); | |
} | |
0xC5 => { // PUSH BC | |
self.push_stack(self.b); | |
self.push_stack(self.c); | |
} | |
0xC9 => { // RET | |
let mut temp = self.pop_stack() as u16; | |
temp |= (self.pop_stack() as u16) << 8; | |
self.pc = temp; | |
} | |
0xCB => { | |
match self.get_opcode() { | |
0x11 => { self.c = self.rl(self.c); } // RL C | |
0x7C => { self.bit(7, self.h); } // BIT 7,H | |
cb => { | |
log("UNKNOWN CB:", &format!("{:02X?}", cb)); | |
unsafe { self.emulator.as_mut().unwrap() }.stop(); | |
} | |
} | |
} | |
0xCD => { // CALL a16 | |
let mut temp = self.get_opcode() as u16; | |
temp |= (self.get_opcode() as u16) << 8; | |
self.push_stack((self.pc >> 8) as u8); | |
self.push_stack((self.pc & 0xFF) as u8); | |
self.pc = temp; | |
} | |
0xE0 => { // LDH (d8),A | |
let temp = self.get_opcode() as u16 | 0xFF00; | |
unsafe { self.emulator.as_mut().unwrap() }.write_memory(temp, self.a); | |
} | |
0xE2 => { // LDH (C),A | |
let temp = self.c as u16 | 0xFF00; | |
unsafe { self.emulator.as_mut().unwrap() }.write_memory(temp, self.a); | |
} | |
0xEA => { // LD (a16),A | |
let mut temp = self.get_opcode() as u16; | |
temp |= (self.get_opcode() as u16) << 8; | |
unsafe { self.emulator.as_mut().unwrap() }.write_memory(temp, self.a); | |
} | |
0xF0 => { // LDH A,(a8) | |
let temp = self.get_opcode() as u16 | 0xFF00; | |
self.a = unsafe { self.emulator.as_mut().unwrap() }.read_memory(temp); | |
} | |
0xFE => { // CP d8 | |
let temp = self.get_opcode(); | |
let temp2 = self.a - temp; | |
self.f_z = temp2 == 0; | |
self.f_n = true; | |
self.f_h = (self.a ^ temp ^ temp2) & 0x10 != 0; | |
self.f_c = temp2 /* & 0x100 */ != 0; // TODO: Actually check carry | |
} | |
opcode => { | |
log("UNKNOWN OPCODE:", &format!("{:02X?}", opcode)); | |
unsafe { self.emulator.as_mut().unwrap() }.stop(); | |
} | |
} | |
cycle | |
} | |
pub fn info(&self) -> String { | |
let opcode = unsafe { self.emulator.as_ref().unwrap() }.read_memory(self.pc); | |
format!("SP: {:04X?}\tPC: {:04X?}\tOPCODE: {:02X?}\nBC: {:04X?}\tDE: {:04X?}\tHL: {:04X?}\tA: {:02X?}", self.sp, self.pc, opcode, self.bc(), self.de(), self.hl(), self.a) | |
} | |
} | |
pub const BIT_IEIF_TIMERINTR: u8 = 1 << 2; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment