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 hidden or 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