diff --git a/gb-emu/src/camera.rs b/gb-emu/src/camera.rs index 5c7c46f..e9dbe61 100644 --- a/gb-emu/src/camera.rs +++ b/gb-emu/src/camera.rs @@ -21,7 +21,7 @@ impl Webcam { } impl PocketCamera for Webcam { - fn capture_greyscale(&mut self) -> [u8; 128 * 128] { + fn get_image(&mut self) -> [u8; 128 * 128] { let frame = self .camera .frame() @@ -40,8 +40,4 @@ impl PocketCamera for Webcam { fn begin_capture(&mut self) { todo!() } - - fn is_capturing(&self) -> bool { - todo!() - } } diff --git a/lib/src/connect/mod.rs b/lib/src/connect/mod.rs index 7eae20f..6d7e60e 100644 --- a/lib/src/connect/mod.rs +++ b/lib/src/connect/mod.rs @@ -1,4 +1,5 @@ use std::marker::PhantomData; +use std::sync::{Arc, Mutex}; use crate::processor::memory::mmio::gpu::Colour; pub use crate::processor::memory::mmio::joypad::{JoypadButtons, JoypadState}; @@ -67,24 +68,81 @@ impl AudioOutput { pub trait PocketCamera { // resolution - 128x128 - fn capture_greyscale(&mut self) -> [u8; 128 * 128]; + fn get_image(&mut self) -> [u8; 128 * 128]; fn begin_capture(&mut self); - - fn is_capturing(&self) -> bool; } -pub struct NoCamera; +pub struct NoCamera { + buf: [u8; 128 * 128], +} + +impl Default for NoCamera { + fn default() -> Self { + Self { + buf: [0; 128 * 128], + } + } +} impl PocketCamera for NoCamera { - fn capture_greyscale(&mut self) -> [u8; 128 * 128] { - [0; 128 * 128] + fn get_image(&mut self) -> [u8; 128 * 128] { + self.buf } - fn begin_capture(&mut self) {} + fn begin_capture(&mut self) { + for v in &mut self.buf { + *v = rand::random(); + } + } +} - fn is_capturing(&self) -> bool { - false +pub(crate) type CameraWrapperRef = Arc>>; + +pub(crate) struct CameraWrapper +where + C: PocketCamera, +{ + pub(crate) inner: C, + counter: usize, + next_image: Option<[u8; 128 * 128]>, +} + +impl CameraWrapper +where + C: PocketCamera, +{ + pub(crate) fn new(camera: C) -> Self { + Self { + inner: camera, + counter: 0, + next_image: None, + } + } + + pub(crate) fn is_capturing(&self) -> bool { + self.counter > 0 + } + + pub(crate) fn tick(&mut self, steps: usize) { + if self.counter > 0 { + self.counter = match self.counter.checked_sub(steps) { + Some(num) => num, + None => { + self.next_image = Some(self.inner.get_image()); + 0 + } + }; + } + } + + pub(crate) fn begin_capture(&mut self) { + self.counter = 32446 * 4; + self.inner.begin_capture(); + } + + pub(crate) fn get_next(&mut self) -> Option<[u8; 128 * 128]> { + self.next_image.take() } } @@ -120,7 +178,7 @@ where rom, output, save_path: None, - camera: NoCamera, + camera: NoCamera::default(), no_save: false, bootrom: None, serial_target: SerialTarget::None, diff --git a/lib/src/lib.rs b/lib/src/lib.rs index fe0f077..91db467 100644 --- a/lib/src/lib.rs +++ b/lib/src/lib.rs @@ -2,8 +2,8 @@ use crate::{processor::memory::Memory, util::pause}; use connect::{ - AudioOutput, EmulatorMessage, EmulatorOptions, NoCamera, PocketCamera, Renderer, RomFile, - SerialTarget, + AudioOutput, CameraWrapper, CameraWrapperRef, EmulatorMessage, EmulatorOptions, NoCamera, + PocketCamera, Renderer, RomFile, SerialTarget, }; use once_cell::sync::OnceCell; use processor::{ @@ -17,7 +17,7 @@ use std::{ path::PathBuf, process::exit, str::FromStr, - sync::mpsc::Receiver, + sync::{mpsc::Receiver, Arc, Mutex}, }; use util::pause_then_step; @@ -59,6 +59,8 @@ where VERBOSE.set(true).unwrap(); } + let camera: CameraWrapperRef = Arc::new(Mutex::new(CameraWrapper::new(options.camera))); + let rom = match options.rom { RomFile::Path(path) => { let maybe_save = if options.no_save { @@ -72,14 +74,14 @@ where }; match fs::read(path) { - Ok(data) => Rom::load(data, maybe_save, options.camera), + Ok(data) => Rom::load(data, maybe_save, camera.clone()), Err(e) => { println!("Error reading ROM: {e}"); exit(1); } } } - RomFile::Raw(data) => Rom::load(data, None, options.camera), + RomFile::Raw(data) => Rom::load(data, None, camera.clone()), }; options.window.prepare(WIDTH, HEIGHT); @@ -109,6 +111,7 @@ where options.output, options.serial_target, options.tile_window, + camera, ), bootrom_enabled, ), @@ -206,7 +209,14 @@ where }; Self { receiver, - cpu: Cpu::from_save_state(state, data, window, output, serial_target, NoCamera), + cpu: Cpu::from_save_state( + state, + data, + window, + output, + serial_target, + Arc::new(Mutex::new(CameraWrapper::new(NoCamera::default()))), + ), spooky: PhantomData, } } diff --git a/lib/src/processor/memory.rs b/lib/src/processor/memory.rs index 6cfc032..803e3a0 100644 --- a/lib/src/processor/memory.rs +++ b/lib/src/processor/memory.rs @@ -9,7 +9,7 @@ use self::{ rom::RomSaveState, }; use crate::{ - connect::{AudioOutput, JoypadState, PocketCamera, Renderer, SerialTarget}, + connect::{AudioOutput, CameraWrapperRef, JoypadState, PocketCamera, Renderer, SerialTarget}, processor::SplitRegister, verbose_println, Cpu, }; @@ -41,6 +41,7 @@ where apu: Apu, serial: Serial, timers: Timer, + camera: CameraWrapperRef, } #[serde_with::serde_as] @@ -96,13 +97,14 @@ where R: Renderer, C: PocketCamera + Send + 'static, { - pub fn init( + pub(crate) fn init( bootrom: Option>, rom: Rom, window: R, output: AudioOutput, serial_target: SerialTarget, tile_window: Option, + camera: CameraWrapperRef, ) -> Self { Self { bootrom, @@ -118,6 +120,7 @@ where apu: Apu::new(output), serial: Serial::new(serial_target), timers: Timer::init(), + camera, } } @@ -292,17 +295,17 @@ where self.apu.replace_output(new); } - pub fn from_save_state( + pub(crate) fn from_save_state( state: MemorySaveState, data: Vec, window: R, output: AudioOutput, serial_target: SerialTarget, - camera: C, + camera: CameraWrapperRef, ) -> Self { Self { bootrom: None, - rom: Rom::from_save_state(state.rom, data, camera), + rom: Rom::from_save_state(state.rom, data, camera.clone()), ram: state.ram, cpu_ram: state.cpu_ram, interrupts: state.interrupts, @@ -314,6 +317,7 @@ where apu: Apu::from_save_state(state.apu, output), serial: Serial::from_save_state(state.serial, serial_target), timers: state.timers, + camera, } } } @@ -327,6 +331,8 @@ where pub fn increment_timers(&mut self, machine_cycles: u8) { let steps = (machine_cycles as usize) * 4; + self.memory.camera.lock().unwrap().tick(steps); + let timer_return = self.memory.timers.tick(steps); for _ in 0..timer_return.num_apu_ticks { diff --git a/lib/src/processor/memory/rom.rs b/lib/src/processor/memory/rom.rs index da44fba..2d436a2 100644 --- a/lib/src/processor/memory/rom.rs +++ b/lib/src/processor/memory/rom.rs @@ -1,6 +1,9 @@ use serde::{Deserialize, Serialize}; -use crate::{connect::PocketCamera as PocketCameraTrait, processor::memory::Address}; +use crate::{ + connect::{CameraWrapperRef, PocketCamera as PocketCameraTrait}, + processor::memory::Address, +}; use std::{ fs::{File, OpenOptions}, io::{Read, Seek, SeekFrom, Write}, @@ -160,7 +163,7 @@ impl MbcSaveState { fn get_mbc( self, data: Vec, - camera: C, + camera: CameraWrapperRef, ) -> Box { match self { MbcSaveState::Mbc1(state) => Box::new(Mbc1::from_save_state(state, data)), @@ -179,7 +182,11 @@ impl Rom where C: PocketCameraTrait + Send + 'static, { - pub fn load(data: Vec, save_path: Option, camera: C) -> Self { + pub(crate) fn load( + data: Vec, + save_path: Option, + camera: CameraWrapperRef, + ) -> Self { let mut title_length = 0x143; for (i, val) in data.iter().enumerate().take(0x143).skip(0x134) { title_length = i; @@ -224,7 +231,11 @@ where } } - pub fn from_save_state(state: RomSaveState, data: Vec, camera: C) -> Self { + pub(crate) fn from_save_state( + state: RomSaveState, + data: Vec, + camera: CameraWrapperRef, + ) -> Self { Self { title: state.title, mbc: state.mbc.get_mbc(data, camera), diff --git a/lib/src/processor/memory/rom/mbcs/pocketcamera.rs b/lib/src/processor/memory/rom/mbcs/pocketcamera.rs index e9f63f9..dc73991 100644 --- a/lib/src/processor/memory/rom/mbcs/pocketcamera.rs +++ b/lib/src/processor/memory/rom/mbcs/pocketcamera.rs @@ -1,6 +1,6 @@ use super::{ram_size_kb, rom_banks, Mbc, KB, RAM_BANK_SIZE, ROM_BANK_SIZE}; use crate::{ - connect::PocketCamera as PocketCameraTrait, + connect::{CameraWrapperRef, PocketCamera as PocketCameraTrait}, processor::memory::{ rom::{MaybeBufferedSram, MbcSaveState}, Address, @@ -25,9 +25,9 @@ where ram_bank: RamBank, ram_size: usize, ram_enabled: bool, - camera: C, + camera: CameraWrapperRef, extra_bits_a000: u8, - camera_ram: [u8; 52], + camera_ram: [u8; 53], } #[derive(Serialize, Deserialize)] @@ -37,12 +37,12 @@ impl PocketCamera where C: PocketCameraTrait, { - pub fn init( + pub(crate) fn init( data: Vec, rom_size: u8, ram_size: u8, save_file: Option, - camera: C, + camera: CameraWrapperRef, ) -> Self { let ram = ram_size_kb(ram_size).map(|s| MaybeBufferedSram::new(save_file, s * KB)); Self { @@ -55,7 +55,7 @@ where ram_enabled: false, camera, extra_bits_a000: 0, - camera_ram: [0; 52], + camera_ram: [0; 53], } } @@ -74,13 +74,24 @@ where ((address as usize - 0xA000) + (RAM_BANK_SIZE * bank as usize)) % self.ram_size } - pub fn from_save_state(_state: PocketCameraSaveState, _data: Vec, _camera: C) -> Self { + pub(crate) fn from_save_state( + _state: PocketCameraSaveState, + _data: Vec, + _camera: CameraWrapperRef, + ) -> Self { todo!(); } fn get_cam_reg(&self, address: Address) -> u8 { match address { - 0xA000 => (if self.camera.is_capturing() { 0x1 } else { 0x0 }) | self.extra_bits_a000, + 0xA000 => { + (if self.camera.lock().unwrap().is_capturing() { + 0x1 + } else { + 0x0 + }) | self.extra_bits_a000 + } + 0xA001..=0xA035 => self.camera_ram[(address - 0xA001) as usize], _ => 0x00, } } @@ -89,13 +100,26 @@ where match address { 0xA000 => { if data & 0x1 == 0x1 { - self.camera.begin_capture(); + self.camera.lock().unwrap().begin_capture(); } self.extra_bits_a000 = data & 0b110; } + 0xA001..=0xA035 => { + self.camera_ram[(address - 0xA001) as usize] = data; + } _ => {} } } + + fn check_for_new_image(&mut self) { + if let Some(image) = self.camera.lock().unwrap().get_next() { + if let Some(ram) = &mut self.ram { + for (i, v) in image.iter().enumerate() { + ram.set(0x100 + i, *v); + } + } + } + } } impl Mbc for PocketCamera @@ -120,6 +144,7 @@ where } fn set(&mut self, address: Address, data: u8) { + self.check_for_new_image(); match address { 0x0..0x2000 => { if (data & 0xF) == 0xA { @@ -146,6 +171,7 @@ where } fn set_ram(&mut self, address: Address, data: u8) { + self.check_for_new_image(); match self.ram_bank { RamBank::Ram(bank) => { let real_addr = self.get_ram_addr(address, bank); diff --git a/lib/src/processor/mod.rs b/lib/src/processor/mod.rs index c91bafc..29cf39c 100644 --- a/lib/src/processor/mod.rs +++ b/lib/src/processor/mod.rs @@ -2,7 +2,7 @@ use serde::{Deserialize, Serialize}; use self::memory::{mmio::gpu::Colour, Interrupt, Memory, MemorySaveState}; use crate::{ - connect::{AudioOutput, PocketCamera, Renderer, SerialTarget}, + connect::{AudioOutput, CameraWrapperRef, PocketCamera, Renderer, SerialTarget}, verbose_println, }; @@ -172,13 +172,13 @@ where self.memory.ime = false; } - pub fn from_save_state( + pub(crate) fn from_save_state( state: CpuSaveState, data: Vec, window: R, output: AudioOutput, serial_target: SerialTarget, - camera: C, + camera: CameraWrapperRef, ) -> Self { Self { memory: Memory::from_save_state(