initial gpu impl
This commit is contained in:
parent
28fef84118
commit
976ee24d6a
14
src/main.rs
14
src/main.rs
|
@ -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
112
src/processor/gpu.rs
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue