initial gpu impl

This commit is contained in:
Alex Janka 2023-02-05 18:46:55 +11:00
parent 28fef84118
commit 976ee24d6a
3 changed files with 147 additions and 29 deletions

View file

@ -11,7 +11,7 @@ use std::{
sync::RwLock, sync::RwLock,
}; };
use crate::processor::Registers; use crate::processor::{gpu::GPU, Registers};
#[macro_export] #[macro_export]
macro_rules! verbose_println { macro_rules! verbose_println {
@ -257,14 +257,14 @@ fn main() {
*v = args.verbose; *v = args.verbose;
} }
let buffer: Vec<u32> = vec![0; WIDTH * HEIGHT]; // let buffer: Vec<u32> = ;
let mut window = Window::new("Gameboy", WIDTH, HEIGHT, WindowOptions::default()) let window =
.unwrap_or_else(|e| { Window::new("Gameboy", WIDTH, HEIGHT, WindowOptions::default()).unwrap_or_else(|e| {
panic!("{}", e); panic!("{}", e);
}); });
window.update_with_buffer(&buffer, WIDTH, HEIGHT).unwrap(); window.topmost(true);
let rom: ROM = fs::read(args.rom).expect("Could not load ROM"); let rom: ROM = fs::read(args.rom).expect("Could not load ROM");
let bootrom: ROM = fs::read(args.bootrom).expect("Could not load BootROM"); let bootrom: ROM = fs::read(args.bootrom).expect("Could not load BootROM");
@ -278,10 +278,8 @@ fn main() {
reg, reg,
last_instruction: 0x0, last_instruction: 0x0,
last_instruction_addr: 0x0, last_instruction_addr: 0x0,
vblank_timer: 0.,
vblank_remaining: 0,
window, window,
buffer, gpu: GPU::default(),
}; };
cpu_ram_init(&mut cpu); cpu_ram_init(&mut cpu);
let mut cycle_num = 0; let mut cycle_num = 0;

112
src/processor/gpu.rs Normal file
View file

@ -0,0 +1,112 @@
use crate::{HEIGHT, WIDTH};
use super::{set_bit, set_or_clear_bit, CPU};
#[derive(PartialEq)]
enum DrawMode {
HBlank,
VBlank,
Mode2,
Mode3,
}
pub struct GPU {
pub buffer: Vec<u32>,
mode: DrawMode,
mode_clock: usize,
scanline: usize,
}
impl Default for GPU {
fn default() -> Self {
Self {
buffer: vec![0; WIDTH * HEIGHT],
mode: DrawMode::Mode2,
mode_clock: 0,
scanline: 0,
}
}
}
impl CPU {
pub fn advance_gpu_clock(&mut self, steps: u8) {
let real_steps = (steps as usize) * 4;
self.gpu.mode_clock += real_steps;
match self.gpu.mode {
DrawMode::HBlank => {
// mode 0: hblank
if self.gpu.mode_clock >= 204 {
self.gpu.mode_clock = 0;
self.gpu.scanline += 1;
if self.gpu.scanline == 143 {
self.enter_vblank();
} else {
self.gpu.mode = DrawMode::Mode2;
}
}
}
DrawMode::VBlank => {
// mode 1: vblank
if self.gpu.mode_clock >= 456 {
self.gpu.mode_clock = 0;
self.gpu.scanline += 1;
if self.gpu.scanline == 153 {
self.gpu.mode = DrawMode::Mode2;
self.gpu.scanline = 0;
}
}
}
DrawMode::Mode2 => {
// search oam for sprites on this line
// we dont really have to emulate this
if self.gpu.mode_clock >= 80 {
self.gpu.mode_clock = 0;
self.gpu.mode = DrawMode::Mode3;
}
}
DrawMode::Mode3 => {
// generate scanline
if self.gpu.mode_clock >= 172 {
self.gpu.mode_clock = 0;
self.enter_hblank();
}
}
}
self.set_lcd_status();
}
fn enter_hblank(&mut self) {
self.gpu.mode = DrawMode::HBlank;
self.render_scanline(self.gpu.scanline);
}
fn enter_vblank(&mut self) {
self.gpu.mode = DrawMode::VBlank;
self.render_window();
self.memory.set(0xFF0F, set_bit(self.memory.get(0xFF0F), 0));
}
fn set_lcd_status(&mut self) {
let mut stat = self.memory.get(0xFF41);
stat = set_or_clear_bit(stat, 2, self.memory.get(0xFF44) == self.memory.get(0xFF45));
stat = set_or_clear_bit(
stat,
1,
(self.gpu.mode == DrawMode::Mode2) || (self.gpu.mode == DrawMode::Mode3),
);
stat = set_or_clear_bit(
stat,
0,
(self.gpu.mode == DrawMode::VBlank) || (self.gpu.mode == DrawMode::Mode3),
);
self.memory.set(0xFF41, stat);
}
fn render_scanline(&mut self, _scanline: usize) {}
fn render_window(&mut self) {
self.window
.update_with_buffer(&self.gpu.buffer, WIDTH, HEIGHT)
.unwrap();
}
}

View file

@ -4,6 +4,9 @@ use minifb::Window;
use crate::{processor::instructions::instructions::set, verbose_println, Memory}; use crate::{processor::instructions::instructions::set, verbose_println, Memory};
use self::gpu::GPU;
pub mod gpu;
mod instructions; mod instructions;
mod opcodes; mod opcodes;
@ -25,16 +28,14 @@ pub struct CPU {
pub reg: Registers, pub reg: Registers,
pub last_instruction: u8, pub last_instruction: u8,
pub last_instruction_addr: u16, pub last_instruction_addr: u16,
pub vblank_timer: f64,
pub vblank_remaining: u8,
pub window: Window, pub window: Window,
pub buffer: Vec<u32>, pub gpu: GPU,
} }
// Hz // Hz
const CLOCK_SPEED: f64 = 4.194304 * 1000000.; const CLOCK_SPEED: f64 = 4.194304 * 1000000.;
const SPEEDUP: f64 = 1.; const SPEEDUP: f64 = 1.;
const FF04_SPEED: f32 = 16384.; const FF04_SPEED: f64 = 16384.;
impl CPU { impl CPU {
pub fn exec_next(&mut self) { pub fn exec_next(&mut self) {
@ -49,11 +50,11 @@ impl CPU {
} }
} }
if self.vblank_timer >= (1. / 59.7) { // if self.vblank_timer >= (1. / 59.7) {
self.vblank_timer = 0.; // self.vblank_timer = 0.;
self.vblank_remaining = 20; // self.vblank_remaining = 20;
self.memory.set(0xFF0F, set_bit(self.memory.get(0xFF0F), 0)); // self.memory.set(0xFF0F, set_bit(self.memory.get(0xFF0F), 0));
} // }
verbose_println!( verbose_println!(
"exec {:#4X} from pc: {:#X}", "exec {:#4X} from pc: {:#X}",
@ -69,22 +70,23 @@ impl CPU {
fn increment_timers(&mut self, cycles: u8) { fn increment_timers(&mut self, cycles: u8) {
let secs = (cycles * 4) as f64 / CLOCK_SPEED; let secs = (cycles * 4) as f64 / CLOCK_SPEED;
self.advance_gpu_clock(cycles);
self.vblank_timer += secs; // self.vblank_timer += secs;
if self.vblank_remaining > 0 { // if self.vblank_remaining > 0 {
self.vblank_remaining = self.vblank_remaining.saturating_sub(cycles); // self.vblank_remaining = self.vblank_remaining.saturating_sub(cycles);
if self.vblank_remaining == 0 { // if self.vblank_remaining == 0 {
self.memory // self.memory
.set(0xFF0F, clear_bit(self.memory.get(0xFF0F), 0)); // .set(0xFF0F, clear_bit(self.memory.get(0xFF0F), 0));
self.draw(); // self.draw();
} // }
} // }
self.memory.set( self.memory.set(
0xFF04, 0xFF04,
self.memory self.memory
.get(0xFF04) .get(0xFF04)
.wrapping_add((FF04_SPEED * 16384.) as u8), .wrapping_add((FF04_SPEED * secs) as u8),
); );
let (timer_enabled, timer_rate) = self.timer_scale(); let (timer_enabled, timer_rate) = self.timer_scale();
if timer_enabled { if timer_enabled {
@ -102,8 +104,6 @@ impl CPU {
spin_sleep::sleep(Duration::from_secs_f64(secs / SPEEDUP)); spin_sleep::sleep(Duration::from_secs_f64(secs / SPEEDUP));
} }
fn draw(&mut self) {}
fn next_opcode(&mut self) -> u8 { fn next_opcode(&mut self) -> u8 {
let opcode = self.memory.get(self.reg.pc); let opcode = self.memory.get(self.reg.pc);
self.reg.pc = self.reg.pc.wrapping_add(0x1); self.reg.pc = self.reg.pc.wrapping_add(0x1);
@ -269,6 +269,14 @@ fn get_bit(byte: u8, flag: u8) -> bool {
return got > 0x0; 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 { fn set_bit(byte: u8, flag: u8) -> u8 {
byte | (1 << flag) byte | (1 << flag)
} }