diff --git a/gb-vst/src/lib.rs b/gb-vst/src/lib.rs index 3a2918e..d1a6c5c 100644 --- a/gb-vst/src/lib.rs +++ b/gb-vst/src/lib.rs @@ -19,13 +19,13 @@ struct EmuParams {} struct EmuVars { rx: AsyncHeapConsumer<[f32; 2]>, sender: Sender, - emulator_core: EmulatorCore, + emulator_core: EmulatorCore<[u8; 4]>, } #[derive(Default)] pub struct GameboyEmu { vars: Option, - frame_receiver: Arc>>>>, + frame_receiver: Arc>>>>, } const ROM: &[u8; 32768] = include_bytes!("../../test-roms/Tetris.gb"); diff --git a/gb-vst/src/ui.rs b/gb-vst/src/ui.rs index 42e5bfe..9597434 100644 --- a/gb-vst/src/ui.rs +++ b/gb-vst/src/ui.rs @@ -14,11 +14,11 @@ use nih_plug::prelude::*; use pixels::{Pixels, SurfaceTexture}; pub struct Emulator { - frame_receiver: Arc>>>>, + frame_receiver: Arc>>>>, } impl Emulator { - pub fn new(frame_receiver: Arc>>>>) -> Self { + pub fn new(frame_receiver: Arc>>>>) -> Self { Self { frame_receiver } } } @@ -61,11 +61,14 @@ impl Editor for Emulator { pub struct EmulatorWindow { pix: Pixels, scale: usize, - frame_receiver: Arc>>>>, + frame_receiver: Arc>>>>, } impl EmulatorWindow { - fn new(window: &mut Window, frame_receiver: Arc>>>>) -> Self { + fn new( + window: &mut Window, + frame_receiver: Arc>>>>, + ) -> Self { let info = WindowInfo::from_logical_size(Size::new(WIDTH as f64, HEIGHT as f64), 1.); let (pix, scale) = init_pixbuf(info, window); @@ -105,7 +108,7 @@ impl WindowHandler for EmulatorWindow { for (pixel, source) in self.pix.get_frame_mut().chunks_exact_mut(4).zip(scaled_buf) { - pixel.copy_from_slice(&source.to_be_bytes()); + pixel.copy_from_slice(&source); } self.pix.render().unwrap(); } @@ -124,21 +127,21 @@ impl WindowHandler for EmulatorWindow { } pub struct EmulatorRenderer { - tx: Sender>, + tx: Sender>, } impl EmulatorRenderer { - pub(super) fn new() -> (Self, Receiver>) { - let (tx, rx) = mpsc::channel::>(); + pub(super) fn new() -> (Self, Receiver>) { + let (tx, rx) = mpsc::channel::>(); (Self { tx }, rx) } } -impl Renderer for EmulatorRenderer { +impl Renderer<[u8; 4]> for EmulatorRenderer { fn prepare(&mut self, _width: usize, _height: usize) {} #[allow(unused_must_use)] - fn display(&mut self, buffer: &[u32]) { + fn display(&mut self, buffer: &[[u8; 4]]) { self.tx.send(buffer.to_vec()); } diff --git a/lib/src/connect/mod.rs b/lib/src/connect/mod.rs index 9ad53a5..a5a99fd 100644 --- a/lib/src/connect/mod.rs +++ b/lib/src/connect/mod.rs @@ -1,3 +1,4 @@ +use crate::processor::memory::mmio::gpu::Colour; pub use crate::processor::memory::mmio::joypad::JoypadState; pub use crate::{HEIGHT, WIDTH}; use async_ringbuf::{AsyncHeapConsumer, AsyncHeapProducer, AsyncHeapRb}; @@ -12,10 +13,10 @@ pub enum RomFile { Raw(Vec), } -pub trait Renderer: Send { +pub trait Renderer>: Send { fn prepare(&mut self, width: usize, height: usize); - fn display(&mut self, buffer: &[u32]); + fn display(&mut self, buffer: &[Format]); fn set_title(&mut self, _title: String) {} diff --git a/lib/src/lib.rs b/lib/src/lib.rs index 743d5fe..0064478 100644 --- a/lib/src/lib.rs +++ b/lib/src/lib.rs @@ -3,13 +3,17 @@ let_chains, slice_flatten, async_closure, - bigint_helper_methods + bigint_helper_methods, + associated_type_defaults )] use crate::{processor::memory::Memory, util::pause}; use connect::{AudioOutput, EmulatorMessage, Renderer, RomFile}; use once_cell::sync::OnceCell; -use processor::{memory::Rom, Cpu}; +use processor::{ + memory::{mmio::gpu::Colour, Rom}, + Cpu, +}; use std::{ fs::{self}, io::{stdout, Write}, @@ -43,20 +47,20 @@ static VERBOSE: OnceCell = OnceCell::new(); pub const WIDTH: usize = 160; pub const HEIGHT: usize = 144; -pub struct EmulatorCore { +pub struct EmulatorCore + Clone> { receiver: Receiver, - cpu: Cpu, + cpu: Cpu, cycle_num: usize, cycle_count: bool, } -impl EmulatorCore { +impl + Clone> EmulatorCore { pub fn init( receiver: Receiver, options: Options, - mut window: Box, + mut window: Box>, output: AudioOutput, - tile_window: Option>, + tile_window: Option>>, ) -> Self { VERBOSE.set(options.verbose).unwrap(); @@ -116,7 +120,7 @@ impl EmulatorCore { ) } - fn new(receiver: Receiver, cpu: Cpu, cycle_count: bool) -> Self { + fn new(receiver: Receiver, cpu: Cpu, cycle_count: bool) -> Self { Self { receiver, cpu, diff --git a/lib/src/processor/instructions/instructions.rs b/lib/src/processor/instructions/instructions.rs index ed0c0b9..facb28d 100644 --- a/lib/src/processor/instructions/instructions.rs +++ b/lib/src/processor/instructions/instructions.rs @@ -1,9 +1,9 @@ use crate::{ - processor::{Cpu, Direction, Flags, Reg8, SplitRegister}, + processor::{memory::mmio::gpu::Colour, Cpu, Direction, Flags, Reg8, SplitRegister}, util::{clear_bit, get_bit, set_bit}, }; -impl Cpu { +impl + Clone> Cpu { pub(crate) fn and(&mut self, first: u8, second: u8) -> u8 { let result = first & second; self.set_or_clear_flag(Flags::Zero, result == 0x0); diff --git a/lib/src/processor/instructions/primitives.rs b/lib/src/processor/instructions/primitives.rs index 968a12b..9853f26 100644 --- a/lib/src/processor/instructions/primitives.rs +++ b/lib/src/processor/instructions/primitives.rs @@ -1,10 +1,10 @@ use crate::{ - processor::{Cpu, Direction, Flags, SplitRegister}, + processor::{memory::mmio::gpu::Colour, Cpu, Direction, Flags, SplitRegister}, util::{as_signed, get_bit, get_rotation_carry, rotate, Nibbles}, }; use std::ops::{BitAnd, BitOr}; -impl Cpu { +impl + Clone> Cpu { pub(crate) fn pop_word(&mut self) -> u16 { let mut word: u16 = 0x0; word.set_low(self.memory.get(self.reg.sp)); diff --git a/lib/src/processor/memory.rs b/lib/src/processor/memory.rs index 5473589..2e8ff56 100644 --- a/lib/src/processor/memory.rs +++ b/lib/src/processor/memory.rs @@ -1,4 +1,4 @@ -use self::mmio::{Apu, Gpu, Joypad, Serial, Timer}; +use self::mmio::{gpu::Colour, Apu, Gpu, Joypad, Serial, Timer}; pub use self::rom::Rom; use crate::{ connect::{AudioOutput, JoypadState, Renderer}, @@ -13,7 +13,7 @@ pub(crate) mod rom; pub(crate) type Address = u16; -pub struct Memory { +pub struct Memory + Clone> { bootrom: Option>, rom: Rom, ram: [u8; 8192], @@ -23,20 +23,20 @@ pub struct Memory { pub(super) ime_scheduled: u8, dma_addr: u8, joypad: Joypad, - gpu: Gpu, + gpu: Gpu, apu: Apu, serial: Serial, timers: Timer, } -impl Memory { +impl + Clone> Memory { pub fn init( bootrom: Option>, rom: Rom, - window: Box, + window: Box>, output: AudioOutput, connect_serial: bool, - tile_window: Option>, + tile_window: Option>>, ) -> Self { let serial = if connect_serial { Serial::default().connected() @@ -228,7 +228,7 @@ impl Memory { } } -impl Cpu { +impl + Clone> Cpu { pub fn increment_timers(&mut self, machine_cycles: u8) { let steps = (machine_cycles as usize) * 4; diff --git a/lib/src/processor/memory/mmio/gpu.rs b/lib/src/processor/memory/mmio/gpu.rs index 0e58f08..8d22f57 100644 --- a/lib/src/processor/memory/mmio/gpu.rs +++ b/lib/src/processor/memory/mmio/gpu.rs @@ -1,8 +1,8 @@ use self::{ tile_window::TileWindow, types::{ - Colour, DrawMode, GpuInterrupts, Lcdc, Oam, ObjPalette, ObjSize, Object, ObjectFlags, - Palette, Stat, TiledataArea, TilemapArea, Vram, + DrawMode, GpuInterrupts, Lcdc, Oam, ObjPalette, ObjSize, Object, ObjectFlags, Palette, + Stat, TiledataArea, TilemapArea, Vram, }, }; use crate::{ @@ -11,6 +11,7 @@ use crate::{ util::{clear_bit, get_bit}, HEIGHT, WIDTH, }; +pub use types::Colour; mod addresses; mod tile_window; @@ -19,18 +20,18 @@ mod types; const TILE_WINDOW_WIDTH: usize = 16 * 8; const TILE_WINDOW_HEIGHT: usize = 24 * 8; -pub struct Gpu { - pub buffer: Vec, +pub struct Gpu> { + pub buffer: Vec, pub vram: Vram, pub oam: Oam, - pub window: Box, + pub window: Box>, is_bg_zero: Vec, lcdc: Lcdc, stat: Stat, mode_clock: usize, scanline: u8, lyc: u8, - tile_window: Option, + tile_window: Option>, window_lc: u8, has_window_been_enabled: bool, bg_palette: Palette, @@ -43,17 +44,21 @@ pub struct Gpu { prev_stat: bool, } -impl Gpu { - pub fn new(window: Box, tile_window_renderer: Option>) -> Self { +impl + Clone> Gpu { + pub fn new( + window: Box>, + tile_window_renderer: Option>>, + ) -> Self { let tile_window = if let Some(mut tile_window_renderer) = tile_window_renderer { tile_window_renderer.prepare(TILE_WINDOW_WIDTH, TILE_WINDOW_HEIGHT); Some(TileWindow::new(tile_window_renderer)) } else { None }; + let buffer = vec![Colour::Error.into(); WIDTH * HEIGHT]; Self { - buffer: vec![0; WIDTH * HEIGHT], + buffer, vram: Vram::default(), oam: Oam::default(), window, @@ -162,7 +167,7 @@ impl Gpu { *e = true; } for x in 0..WIDTH { - self.buffer[(scanline as usize * WIDTH) + x] = Colour::from_u8_rgb(255, 0, 255); + self.buffer[(scanline as usize * WIDTH) + x] = Colour::Error.into(); } if self.lcdc.bg_window_enable { self.render_scanline_bg(scanline); @@ -175,7 +180,7 @@ impl Gpu { } } else { for x in 0..WIDTH { - self.buffer[(scanline as usize * WIDTH) + x] = Colour::from_u8_rgb(255, 255, 255); + self.buffer[(scanline as usize * WIDTH) + x] = Colour::Error.into(); } } if self.lcdc.obj_enable { @@ -295,7 +300,7 @@ impl Gpu { if x_coord < WIDTH { let buffer_index = (scanline as usize * WIDTH) + x_coord; if !object.flags.behind_bg_and_window || self.is_bg_zero[x_coord] { - self.buffer[buffer_index] = colour.as_rgb(); + self.buffer[buffer_index] = colour.into(); } } } @@ -340,7 +345,7 @@ impl Gpu { let (colour, is_zero) = self.bg_palette.map_bits(lsb, msb); self.is_bg_zero[x] = is_zero; - self.buffer[(scanline as usize * WIDTH) + x] = colour.as_rgb(); + self.buffer[(scanline as usize * WIDTH) + x] = colour.into(); } } diff --git a/lib/src/processor/memory/mmio/gpu/addresses.rs b/lib/src/processor/memory/mmio/gpu/addresses.rs index 72258de..a5b3bf2 100644 --- a/lib/src/processor/memory/mmio/gpu/addresses.rs +++ b/lib/src/processor/memory/mmio/gpu/addresses.rs @@ -2,10 +2,10 @@ use crate::util::{get_bit, set_or_clear_bit}; use super::{ types::{DrawMode, ObjSize, Palette, TiledataArea, TilemapArea}, - Gpu, + Colour, Gpu, }; -impl Gpu { +impl> Gpu { pub fn update_lcdc(&mut self, data: u8) { self.lcdc.enable = get_bit(data, 7); self.lcdc.window_tilemap = if get_bit(data, 6) { diff --git a/lib/src/processor/memory/mmio/gpu/tile_window.rs b/lib/src/processor/memory/mmio/gpu/tile_window.rs index 0699ddc..01e95cc 100644 --- a/lib/src/processor/memory/mmio/gpu/tile_window.rs +++ b/lib/src/processor/memory/mmio/gpu/tile_window.rs @@ -4,23 +4,24 @@ use crate::{ util::get_bit, }; -use super::types::Vram; +use super::{types::Vram, Colour}; -pub(super) struct TileWindow { - sprite_buffer: Vec, - sprite_renderer: Box, +pub(super) struct TileWindow> { + sprite_buffer: Vec, + sprite_renderer: Box>, } -impl TileWindow { - pub(super) fn new(window: Box) -> Self { +impl> TileWindow { + pub(super) fn new(window: Box>) -> Self { + let mut sprite_buffer = Vec::new(); + sprite_buffer.reserve_exact(TILE_WINDOW_WIDTH * TILE_WINDOW_HEIGHT); + Self { - sprite_buffer: vec![0; TILE_WINDOW_WIDTH * TILE_WINDOW_HEIGHT], + sprite_buffer, sprite_renderer: window, } } -} -impl TileWindow { pub(super) fn draw_sprite_window(&mut self, palette: Palette, memory: &Vram) { for tile_y in 0..16 { self.draw_row( @@ -66,7 +67,7 @@ impl TileWindow { let colour = palette.map_bits(lsb, msb); self.sprite_buffer[real_px_x + (real_px_y * TILE_WINDOW_WIDTH)] = - colour.0.as_rgb(); + colour.0.into(); } } } diff --git a/lib/src/processor/memory/mmio/gpu/types.rs b/lib/src/processor/memory/mmio/gpu/types.rs index abf829e..ba1eccd 100644 --- a/lib/src/processor/memory/mmio/gpu/types.rs +++ b/lib/src/processor/memory/mmio/gpu/types.rs @@ -84,28 +84,40 @@ impl Default for Lcdc { } #[derive(Clone, Copy, Debug, PartialEq)] -pub(super) enum Colour { +pub enum Colour { White, LightGray, DarkGray, Black, + Error, +} + +impl From for u32 { + fn from(value: Colour) -> Self { + let rgb = value.rgb_bytes(); + let (r, g, b) = (rgb.0 as u32, rgb.1 as u32, rgb.2 as u32); + (r << 16) | (g << 8) | b + } +} + +impl From for [u8; 4] { + fn from(value: Colour) -> Self { + let (r, g, b) = value.rgb_bytes(); + [r, g, b, 0xFF] + } } impl Colour { - pub(super) fn as_rgb(&self) -> u32 { + fn rgb_bytes(&self) -> (u8, u8, u8) { match self { - Colour::White => Self::from_u8_rgb(0xFF, 0xFF, 0xFF), - Colour::LightGray => Self::from_u8_rgb(0xAA, 0xAA, 0xAA), - Colour::DarkGray => Self::from_u8_rgb(0x55, 0x55, 0x55), - Colour::Black => Self::from_u8_rgb(0x00, 0x00, 0x00), + Colour::White => (0xFF, 0xFF, 0xFF), + Colour::LightGray => (0xAA, 0xAA, 0xAA), + Colour::DarkGray => (0x55, 0x55, 0x55), + Colour::Black => (0x00, 0x00, 0x00), + Colour::Error => (0xFF, 0x00, 0x00), } } - pub(super) fn from_u8_rgb(r: u8, g: u8, b: u8) -> u32 { - let (r, g, b) = (r as u32, g as u32, b as u32); - (r << 16) | (g << 8) | b - } - pub(super) fn from_bits(first: bool, second: bool) -> Colour { match (first, second) { (true, true) => Colour::Black, @@ -121,6 +133,7 @@ impl Colour { Colour::LightGray => 0b10, Colour::DarkGray => 0b01, Colour::Black => 0b11, + Colour::Error => 0b00, } } } diff --git a/lib/src/processor/mod.rs b/lib/src/processor/mod.rs index 3a0d73b..b05c03e 100644 --- a/lib/src/processor/mod.rs +++ b/lib/src/processor/mod.rs @@ -1,4 +1,4 @@ -use self::memory::{Interrupt, Memory}; +use self::memory::{mmio::gpu::Colour, Interrupt, Memory}; use crate::verbose_println; mod instructions; @@ -18,8 +18,8 @@ pub(crate) enum Direction { Right, } -pub struct Cpu { - pub memory: Memory, +pub struct Cpu + Clone> { + pub memory: Memory, pub reg: Registers, pub last_instruction: u8, last_instruction_addr: u16, @@ -27,8 +27,8 @@ pub struct Cpu { should_halt_bug: bool, } -impl Cpu { - pub fn new(mut memory: Memory, run_bootrom: bool) -> Self { +impl + Clone> Cpu { + pub fn new(mut memory: Memory, run_bootrom: bool) -> Self { if !run_bootrom { memory.cpu_ram_init(); } diff --git a/lib/src/processor/opcodes.rs b/lib/src/processor/opcodes.rs index c452714..e858e88 100644 --- a/lib/src/processor/opcodes.rs +++ b/lib/src/processor/opcodes.rs @@ -6,7 +6,9 @@ use crate::{ util::as_signed, }; -impl Cpu { +use super::memory::mmio::gpu::Colour; + +impl + Clone> Cpu { pub fn run_opcode(&mut self, opcode: u8) -> u8 { match opcode { 0x00 => { diff --git a/lib/src/util.rs b/lib/src/util.rs index 0c000b9..eeda7bc 100644 --- a/lib/src/util.rs +++ b/lib/src/util.rs @@ -1,4 +1,7 @@ -use crate::{processor::Direction, PAUSE_ENABLED, PAUSE_QUEUED, VERBOSE}; +use crate::{ + processor::{memory::mmio::gpu::Colour, Direction}, + PAUSE_ENABLED, PAUSE_QUEUED, VERBOSE, +}; use std::{io, mem::transmute}; #[macro_export] @@ -122,7 +125,12 @@ impl Nibbles for u8 { } } -pub fn scale_buffer(buffer: &[u32], width: usize, height: usize, factor: usize) -> Vec { +pub fn scale_buffer + Copy>( + buffer: &[T], + width: usize, + height: usize, + factor: usize, +) -> Vec { let mut v = vec![]; for y in 0..height { for _ in 0..factor {