Skip to content

Instantly share code, notes, and snippets.

@EmmaEwert
Created August 8, 2021 08:36
Show Gist options
  • Save EmmaEwert/9a95d8e69a8bfc0b9f05685f49053d2e to your computer and use it in GitHub Desktop.
Save EmmaEwert/9a95d8e69a8bfc0b9f05685f49053d2e to your computer and use it in GitHub Desktop.
Rust code for the CPU of a Game Boy
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