split renderer works!!! no keyboard input any more though lolllll

This commit is contained in:
Alex Janka 2023-02-26 10:18:58 +11:00
parent c54027e5f6
commit a36d161939
9 changed files with 118 additions and 128 deletions

2
src/connect/mod.rs Normal file
View file

@ -0,0 +1,2 @@
mod renderer;
pub use renderer::Renderer;

7
src/connect/renderer.rs Normal file
View file

@ -0,0 +1,7 @@
pub trait Renderer {
fn prepare(&mut self, width: usize, height: usize);
fn display(&mut self, buffer: &[u32]);
fn set_title(&mut self, _title: String) {}
}

View file

@ -10,8 +10,8 @@ use crate::{
processor::memory::Memory, processor::memory::Memory,
util::{pause, print_cycles}, util::{pause, print_cycles},
}; };
use connect::Renderer;
use gilrs::Gilrs; use gilrs::Gilrs;
use minifb::Window;
use once_cell::sync::OnceCell; use once_cell::sync::OnceCell;
use processor::{memory::Rom, Cpu}; use processor::{memory::Rom, Cpu};
use std::{ use std::{
@ -20,6 +20,7 @@ use std::{
}; };
use util::pause_then_step; use util::pause_then_step;
pub mod connect;
mod constants; mod constants;
mod processor; mod processor;
pub mod util; pub mod util;
@ -31,7 +32,6 @@ pub struct Options {
pub verbose: bool, pub verbose: bool,
pub step_by: Option<usize>, pub step_by: Option<usize>,
pub cycle_count: bool, pub cycle_count: bool,
pub factor: usize,
} }
static mut PAUSE_ENABLED: bool = false; static mut PAUSE_ENABLED: bool = false;
@ -41,13 +41,13 @@ static VERBOSE: OnceCell<bool> = OnceCell::new();
pub const WIDTH: usize = 160; pub const WIDTH: usize = 160;
pub const HEIGHT: usize = 144; pub const HEIGHT: usize = 144;
static FACTOR: OnceCell<usize> = OnceCell::new();
#[allow(clippy::too_many_arguments)] pub fn init(
pub fn init(options: Options, mut window: Window, tile_window: bool) { options: Options,
mut window: Box<dyn Renderer>,
tile_window: Option<Box<dyn Renderer>>,
) {
VERBOSE.set(options.verbose).unwrap(); VERBOSE.set(options.verbose).unwrap();
FACTOR.set(options.factor).unwrap();
crate::processor::memory::mmio::gpu::init_statics();
let rom: Rom = match fs::read(options.rom_path) { let rom: Rom = match fs::read(options.rom_path) {
Ok(data) => Rom::load(data), Ok(data) => Rom::load(data),
@ -56,7 +56,9 @@ pub fn init(options: Options, mut window: Window, tile_window: bool) {
return; return;
} }
}; };
window.set_title(format!("{} on {}", rom.get_title(), rom.mbc_type()).as_str());
window.prepare(WIDTH, HEIGHT);
window.set_title(format!("{} on {}", rom.get_title(), rom.mbc_type()));
let bootrom_enabled = options.bootrom_path.is_some(); let bootrom_enabled = options.bootrom_path.is_some();
let bootrom: Option<Vec<u8>> = if let Some(path) = options.bootrom_path { let bootrom: Option<Vec<u8>> = if let Some(path) = options.bootrom_path {

View file

@ -1,5 +1,5 @@
use clap::{ArgGroup, Parser}; use clap::{ArgGroup, Parser};
use gb_emu::{HEIGHT, WIDTH}; use gb_emu::{connect::Renderer, util::scale_buffer};
use minifb::{Window, WindowOptions}; use minifb::{Window, WindowOptions};
@ -50,20 +50,6 @@ fn main() {
3 3
}; };
let mut window = Window::new(
"emu",
WIDTH * factor,
HEIGHT * factor,
WindowOptions::default(),
)
.unwrap_or_else(|e| {
panic!("{e}");
});
window.set_position(500, 50);
window.topmost(true);
let options = gb_emu::Options { let options = gb_emu::Options {
rom_path: args.rom, rom_path: args.rom,
bootrom_path: args.bootrom, bootrom_path: args.bootrom,
@ -71,8 +57,68 @@ fn main() {
verbose: args.verbose, verbose: args.verbose,
step_by: args.step_by, step_by: args.step_by,
cycle_count: args.cycle_count, cycle_count: args.cycle_count,
factor,
}; };
gb_emu::init(options, window, args.tile_window); let tile_window: Option<Box<dyn Renderer>> = if args.tile_window {
Some(Box::new(WindowRenderer::new(factor)))
} else {
None
};
gb_emu::init(options, Box::new(WindowRenderer::new(factor)), tile_window);
}
struct WindowRenderer {
window: Option<Window>,
scaled_buf: Vec<u32>,
width: usize,
height: usize,
factor: usize,
}
impl WindowRenderer {
fn new(factor: usize) -> Self {
Self {
window: None,
scaled_buf: vec![],
width: 0,
height: 0,
factor,
}
}
}
impl Renderer for WindowRenderer {
fn prepare(&mut self, width: usize, height: usize) {
self.width = width;
self.height = height;
self.window = Some(
Window::new(
"Gameboy",
width * self.factor,
height * self.factor,
WindowOptions::default(),
)
.unwrap(),
);
}
fn display(&mut self, buffer: &[u32]) {
if let Some(ref mut window) = self.window {
self.scaled_buf = scale_buffer(buffer, self.width, self.height, self.factor);
window
.update_with_buffer(
&self.scaled_buf,
self.width * self.factor,
self.height * self.factor,
)
.unwrap();
}
}
fn set_title(&mut self, title: String) {
if let Some(ref mut window) = self.window {
window.set_title(&title);
}
}
} }

View file

@ -1,8 +1,7 @@
use self::mmio::{Apu, Gpu, Joypad, Serial, Timer}; use self::mmio::{Apu, Gpu, Joypad, Serial, Timer};
pub use self::rom::Rom; pub use self::rom::Rom;
use crate::{processor::SplitRegister, verbose_println, Cpu}; use crate::{connect::Renderer, processor::SplitRegister, verbose_println, Cpu};
use gilrs::Gilrs; use gilrs::Gilrs;
use minifb::{Key, Window};
mod interrupts; mod interrupts;
pub use interrupts::{Interrupt, Interrupts}; pub use interrupts::{Interrupt, Interrupts};
@ -33,9 +32,9 @@ impl Memory {
pub fn init( pub fn init(
bootrom: Option<Vec<u8>>, bootrom: Option<Vec<u8>>,
rom: Rom, rom: Rom,
window: Window, window: Box<dyn Renderer>,
connect_serial: bool, connect_serial: bool,
enable_tile_window: bool, tile_window: Option<Box<dyn Renderer>>,
) -> Self { ) -> Self {
let serial = if connect_serial { let serial = if connect_serial {
Serial::default().connected() Serial::default().connected()
@ -53,7 +52,7 @@ impl Memory {
ime_scheduled: 0x0, ime_scheduled: 0x0,
dma_addr: 0xFF, dma_addr: 0xFF,
joypad: Joypad::default(), joypad: Joypad::default(),
gpu: Gpu::new(window, enable_tile_window), gpu: Gpu::new(window, tile_window),
apu: Apu::init_default(), apu: Apu::init_default(),
serial, serial,
timers: Timer::init(), timers: Timer::init(),
@ -185,8 +184,8 @@ impl Memory {
} }
} }
pub fn update_pressed_keys(&mut self, keys: Vec<Key>, gamepads: &mut Gilrs) -> bool { pub fn update_pressed_keys(&mut self, gamepads: &mut Gilrs) -> bool {
self.joypad.update_pressed_keys(keys, gamepads) self.joypad.update_pressed_keys(gamepads)
} }
pub(super) fn cpu_ram_init(&mut self) { pub(super) fn cpu_ram_init(&mut self) {
@ -247,9 +246,7 @@ impl Cpu {
.set_interrupt(Interrupt::LcdStat, gpu_interrupts.lcd_stat); .set_interrupt(Interrupt::LcdStat, gpu_interrupts.lcd_stat);
if gpu_interrupts.vblank { if gpu_interrupts.vblank {
let joypad_interrupt = self let joypad_interrupt = self.memory.update_pressed_keys(&mut self.gamepad_handler);
.memory
.update_pressed_keys(self.memory.gpu.window.get_keys(), &mut self.gamepad_handler);
self.memory self.memory
.interrupts .interrupts
.set_interrupt(Interrupt::Joypad, joypad_interrupt); .set_interrupt(Interrupt::Joypad, joypad_interrupt);

View file

@ -6,12 +6,11 @@ use self::{
}, },
}; };
use crate::{ use crate::{
connect::Renderer,
processor::SplitRegister, processor::SplitRegister,
util::{clear_bit, get_bit}, util::{clear_bit, get_bit},
FACTOR, HEIGHT, WIDTH, HEIGHT, WIDTH,
}; };
use minifb::{Window, WindowOptions};
use once_cell::sync::OnceCell;
mod addresses; mod addresses;
mod tile_window; mod tile_window;
@ -19,24 +18,12 @@ mod types;
const TILE_WINDOW_WIDTH: usize = 16 * 8; const TILE_WINDOW_WIDTH: usize = 16 * 8;
const TILE_WINDOW_HEIGHT: usize = 24 * 8; const TILE_WINDOW_HEIGHT: usize = 24 * 8;
static TILE_WINDOW_WIDTH_SCALED: OnceCell<usize> = OnceCell::new();
static TILE_WINDOW_HEIGHT_SCALED: OnceCell<usize> = OnceCell::new();
pub fn init_statics() {
TILE_WINDOW_WIDTH_SCALED
.set(TILE_WINDOW_WIDTH * FACTOR.get().unwrap())
.unwrap();
TILE_WINDOW_HEIGHT_SCALED
.set(TILE_WINDOW_HEIGHT * FACTOR.get().unwrap())
.unwrap();
}
pub struct Gpu { pub struct Gpu {
pub buffer: Vec<u32>, pub buffer: Vec<u32>,
pub vram: Vram, pub vram: Vram,
pub oam: Oam, pub oam: Oam,
pub window: Window, pub window: Box<dyn Renderer>,
scaled_buffer: Vec<u32>,
lcdc: Lcdc, lcdc: Lcdc,
stat: Stat, stat: Stat,
mode_clock: usize, mode_clock: usize,
@ -55,21 +42,10 @@ pub struct Gpu {
} }
impl Gpu { impl Gpu {
pub fn new(window: Window, enable_tile_window: bool) -> Self { pub fn new(window: Box<dyn Renderer>, tile_window_renderer: Option<Box<dyn Renderer>>) -> Self {
let tile_window = if enable_tile_window { let tile_window = if let Some(mut tile_window_renderer) = tile_window_renderer {
let mut window = Window::new( tile_window_renderer.prepare(TILE_WINDOW_WIDTH, TILE_WINDOW_HEIGHT);
"Tiles", Some(TileWindow::new(tile_window_renderer))
*TILE_WINDOW_WIDTH_SCALED.get().unwrap(),
*TILE_WINDOW_HEIGHT_SCALED.get().unwrap(),
WindowOptions::default(),
)
.unwrap_or_else(|e| {
panic!("{e}");
});
window.set_position((550 + (WIDTH * FACTOR.get().unwrap())) as isize, 50);
window.topmost(true);
Some(TileWindow::new(window))
} else { } else {
None None
}; };
@ -79,7 +55,6 @@ impl Gpu {
vram: Vram::default(), vram: Vram::default(),
oam: Oam::default(), oam: Oam::default(),
window, window,
scaled_buffer: vec![0; WIDTH * HEIGHT * 4],
lcdc: Lcdc::default(), lcdc: Lcdc::default(),
stat: Stat::default(), stat: Stat::default(),
mode_clock: 0, mode_clock: 0,
@ -370,27 +345,6 @@ impl Gpu {
} }
fn render_window(&mut self) { fn render_window(&mut self) {
self.scaled_buffer = scale_buffer(&self.buffer, WIDTH, HEIGHT, *FACTOR.get().unwrap()); self.window.display(&self.buffer);
self.window
.update_with_buffer(
&self.scaled_buffer,
WIDTH * FACTOR.get().unwrap(),
HEIGHT * FACTOR.get().unwrap(),
)
.unwrap();
} }
} }
fn scale_buffer(buffer: &[u32], width: usize, height: usize, factor: usize) -> Vec<u32> {
let mut v = vec![];
for y in 0..height {
for _ in 0..factor {
for x in 0..width {
for _ in 0..factor {
v.push(buffer[(y * width) + x]);
}
}
}
}
v
}

View file

@ -1,32 +1,21 @@
use minifb::Window;
use crate::{ use crate::{
processor::memory::mmio::gpu::{ connect::Renderer,
scale_buffer, Palette, TiledataArea, TILE_WINDOW_HEIGHT, TILE_WINDOW_HEIGHT_SCALED, processor::memory::mmio::gpu::{Palette, TiledataArea, TILE_WINDOW_HEIGHT, TILE_WINDOW_WIDTH},
TILE_WINDOW_WIDTH, TILE_WINDOW_WIDTH_SCALED,
},
util::get_bit, util::get_bit,
FACTOR,
}; };
use super::types::Vram; use super::types::Vram;
pub(super) struct TileWindow { pub(super) struct TileWindow {
sprite_buffer: Vec<u32>, sprite_buffer: Vec<u32>,
sprite_buffer_scaled: Vec<u32>, sprite_renderer: Box<dyn Renderer>,
sprite_window: Window,
} }
impl TileWindow { impl TileWindow {
pub(super) fn new(window: Window) -> Self { pub(super) fn new(window: Box<dyn Renderer>) -> Self {
Self { Self {
sprite_buffer: vec![0; TILE_WINDOW_WIDTH * TILE_WINDOW_HEIGHT], sprite_buffer: vec![0; TILE_WINDOW_WIDTH * TILE_WINDOW_HEIGHT],
sprite_buffer_scaled: vec![ sprite_renderer: window,
0;
TILE_WINDOW_WIDTH_SCALED.get().unwrap()
* TILE_WINDOW_HEIGHT_SCALED.get().unwrap()
],
sprite_window: window,
} }
} }
} }
@ -52,19 +41,7 @@ impl TileWindow {
); );
} }
self.sprite_buffer_scaled = scale_buffer( self.sprite_renderer.display(&self.sprite_buffer);
&self.sprite_buffer,
TILE_WINDOW_WIDTH,
TILE_WINDOW_HEIGHT,
*FACTOR.get().unwrap(),
);
self.sprite_window
.update_with_buffer(
&self.sprite_buffer_scaled,
*TILE_WINDOW_WIDTH_SCALED.get().unwrap(),
*TILE_WINDOW_HEIGHT_SCALED.get().unwrap(),
)
.unwrap();
} }
fn draw_row( fn draw_row(

View file

@ -1,6 +1,5 @@
use crate::util::{clear_bit, get_bit}; use crate::util::{clear_bit, get_bit};
use gilrs::{Button, Gilrs}; use gilrs::{Button, Gilrs};
use minifb::Key;
#[derive(Debug, Clone, Copy, PartialEq)] #[derive(Debug, Clone, Copy, PartialEq)]
pub struct Joypad { pub struct Joypad {
@ -57,7 +56,7 @@ impl Joypad {
self.sel_direction = !get_bit(data, 4); self.sel_direction = !get_bit(data, 4);
} }
pub fn update_pressed_keys(&mut self, keys: Vec<Key>, gamepad_handler: &mut Gilrs) -> bool { pub fn update_pressed_keys(&mut self, gamepad_handler: &mut Gilrs) -> bool {
let old = *self; let old = *self;
self.clear_buttons(); self.clear_buttons();
@ -87,14 +86,6 @@ impl Joypad {
self.a |= pad.is_pressed(Button::East); self.a |= pad.is_pressed(Button::East);
self.b |= pad.is_pressed(Button::South); self.b |= pad.is_pressed(Button::South);
} }
self.down |= keys.contains(&Key::Down) || keys.contains(&Key::S);
self.up |= keys.contains(&Key::Up) || keys.contains(&Key::W);
self.left |= keys.contains(&Key::Left) || keys.contains(&Key::A);
self.right |= keys.contains(&Key::Right) || keys.contains(&Key::D);
self.start |= keys.contains(&Key::Equal);
self.select |= keys.contains(&Key::Minus);
self.a |= keys.contains(&Key::Apostrophe);
self.b |= keys.contains(&Key::Semicolon);
*self != old *self != old
} }

View file

@ -137,3 +137,17 @@ impl Nibbles for u8 {
*self = (*self & 0x0F) | (val << 4); *self = (*self & 0x0F) | (val << 4);
} }
} }
pub fn scale_buffer(buffer: &[u32], width: usize, height: usize, factor: usize) -> Vec<u32> {
let mut v = vec![];
for y in 0..height {
for _ in 0..factor {
for x in 0..width {
for _ in 0..factor {
v.push(buffer[(y * width) + x]);
}
}
}
}
v
}