camera works-ish!

This commit is contained in:
Alex Janka 2023-03-19 12:43:10 +11:00
parent bceb8b0ea4
commit 47e2210b88
7 changed files with 149 additions and 42 deletions

View file

@ -21,7 +21,7 @@ impl Webcam {
} }
impl PocketCamera for Webcam { impl PocketCamera for Webcam {
fn capture_greyscale(&mut self) -> [u8; 128 * 128] { fn get_image(&mut self) -> [u8; 128 * 128] {
let frame = self let frame = self
.camera .camera
.frame() .frame()
@ -40,8 +40,4 @@ impl PocketCamera for Webcam {
fn begin_capture(&mut self) { fn begin_capture(&mut self) {
todo!() todo!()
} }
fn is_capturing(&self) -> bool {
todo!()
}
} }

View file

@ -1,4 +1,5 @@
use std::marker::PhantomData; use std::marker::PhantomData;
use std::sync::{Arc, Mutex};
use crate::processor::memory::mmio::gpu::Colour; use crate::processor::memory::mmio::gpu::Colour;
pub use crate::processor::memory::mmio::joypad::{JoypadButtons, JoypadState}; pub use crate::processor::memory::mmio::joypad::{JoypadButtons, JoypadState};
@ -67,24 +68,81 @@ impl AudioOutput {
pub trait PocketCamera { pub trait PocketCamera {
// resolution - 128x128 // resolution - 128x128
fn capture_greyscale(&mut self) -> [u8; 128 * 128]; fn get_image(&mut self) -> [u8; 128 * 128];
fn begin_capture(&mut self); 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 { impl PocketCamera for NoCamera {
fn capture_greyscale(&mut self) -> [u8; 128 * 128] { fn get_image(&mut self) -> [u8; 128 * 128] {
[0; 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 { pub(crate) type CameraWrapperRef<C> = Arc<Mutex<CameraWrapper<C>>>;
false
pub(crate) struct CameraWrapper<C>
where
C: PocketCamera,
{
pub(crate) inner: C,
counter: usize,
next_image: Option<[u8; 128 * 128]>,
}
impl<C> CameraWrapper<C>
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, rom,
output, output,
save_path: None, save_path: None,
camera: NoCamera, camera: NoCamera::default(),
no_save: false, no_save: false,
bootrom: None, bootrom: None,
serial_target: SerialTarget::None, serial_target: SerialTarget::None,

View file

@ -2,8 +2,8 @@
use crate::{processor::memory::Memory, util::pause}; use crate::{processor::memory::Memory, util::pause};
use connect::{ use connect::{
AudioOutput, EmulatorMessage, EmulatorOptions, NoCamera, PocketCamera, Renderer, RomFile, AudioOutput, CameraWrapper, CameraWrapperRef, EmulatorMessage, EmulatorOptions, NoCamera,
SerialTarget, PocketCamera, Renderer, RomFile, SerialTarget,
}; };
use once_cell::sync::OnceCell; use once_cell::sync::OnceCell;
use processor::{ use processor::{
@ -17,7 +17,7 @@ use std::{
path::PathBuf, path::PathBuf,
process::exit, process::exit,
str::FromStr, str::FromStr,
sync::mpsc::Receiver, sync::{mpsc::Receiver, Arc, Mutex},
}; };
use util::pause_then_step; use util::pause_then_step;
@ -59,6 +59,8 @@ where
VERBOSE.set(true).unwrap(); VERBOSE.set(true).unwrap();
} }
let camera: CameraWrapperRef<C> = Arc::new(Mutex::new(CameraWrapper::new(options.camera)));
let rom = match options.rom { let rom = match options.rom {
RomFile::Path(path) => { RomFile::Path(path) => {
let maybe_save = if options.no_save { let maybe_save = if options.no_save {
@ -72,14 +74,14 @@ where
}; };
match fs::read(path) { match fs::read(path) {
Ok(data) => Rom::load(data, maybe_save, options.camera), Ok(data) => Rom::load(data, maybe_save, camera.clone()),
Err(e) => { Err(e) => {
println!("Error reading ROM: {e}"); println!("Error reading ROM: {e}");
exit(1); 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); options.window.prepare(WIDTH, HEIGHT);
@ -109,6 +111,7 @@ where
options.output, options.output,
options.serial_target, options.serial_target,
options.tile_window, options.tile_window,
camera,
), ),
bootrom_enabled, bootrom_enabled,
), ),
@ -206,7 +209,14 @@ where
}; };
Self { Self {
receiver, 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, spooky: PhantomData,
} }
} }

View file

@ -9,7 +9,7 @@ use self::{
rom::RomSaveState, rom::RomSaveState,
}; };
use crate::{ use crate::{
connect::{AudioOutput, JoypadState, PocketCamera, Renderer, SerialTarget}, connect::{AudioOutput, CameraWrapperRef, JoypadState, PocketCamera, Renderer, SerialTarget},
processor::SplitRegister, processor::SplitRegister,
verbose_println, Cpu, verbose_println, Cpu,
}; };
@ -41,6 +41,7 @@ where
apu: Apu, apu: Apu,
serial: Serial, serial: Serial,
timers: Timer, timers: Timer,
camera: CameraWrapperRef<C>,
} }
#[serde_with::serde_as] #[serde_with::serde_as]
@ -96,13 +97,14 @@ where
R: Renderer<ColourFormat>, R: Renderer<ColourFormat>,
C: PocketCamera + Send + 'static, C: PocketCamera + Send + 'static,
{ {
pub fn init( pub(crate) fn init(
bootrom: Option<Vec<u8>>, bootrom: Option<Vec<u8>>,
rom: Rom<C>, rom: Rom<C>,
window: R, window: R,
output: AudioOutput, output: AudioOutput,
serial_target: SerialTarget, serial_target: SerialTarget,
tile_window: Option<R>, tile_window: Option<R>,
camera: CameraWrapperRef<C>,
) -> Self { ) -> Self {
Self { Self {
bootrom, bootrom,
@ -118,6 +120,7 @@ where
apu: Apu::new(output), apu: Apu::new(output),
serial: Serial::new(serial_target), serial: Serial::new(serial_target),
timers: Timer::init(), timers: Timer::init(),
camera,
} }
} }
@ -292,17 +295,17 @@ where
self.apu.replace_output(new); self.apu.replace_output(new);
} }
pub fn from_save_state( pub(crate) fn from_save_state(
state: MemorySaveState<ColourFormat, R>, state: MemorySaveState<ColourFormat, R>,
data: Vec<u8>, data: Vec<u8>,
window: R, window: R,
output: AudioOutput, output: AudioOutput,
serial_target: SerialTarget, serial_target: SerialTarget,
camera: C, camera: CameraWrapperRef<C>,
) -> Self { ) -> Self {
Self { Self {
bootrom: None, bootrom: None,
rom: Rom::from_save_state(state.rom, data, camera), rom: Rom::from_save_state(state.rom, data, camera.clone()),
ram: state.ram, ram: state.ram,
cpu_ram: state.cpu_ram, cpu_ram: state.cpu_ram,
interrupts: state.interrupts, interrupts: state.interrupts,
@ -314,6 +317,7 @@ where
apu: Apu::from_save_state(state.apu, output), apu: Apu::from_save_state(state.apu, output),
serial: Serial::from_save_state(state.serial, serial_target), serial: Serial::from_save_state(state.serial, serial_target),
timers: state.timers, timers: state.timers,
camera,
} }
} }
} }
@ -327,6 +331,8 @@ where
pub fn increment_timers(&mut self, machine_cycles: u8) { pub fn increment_timers(&mut self, machine_cycles: u8) {
let steps = (machine_cycles as usize) * 4; let steps = (machine_cycles as usize) * 4;
self.memory.camera.lock().unwrap().tick(steps);
let timer_return = self.memory.timers.tick(steps); let timer_return = self.memory.timers.tick(steps);
for _ in 0..timer_return.num_apu_ticks { for _ in 0..timer_return.num_apu_ticks {

View file

@ -1,6 +1,9 @@
use serde::{Deserialize, Serialize}; 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::{ use std::{
fs::{File, OpenOptions}, fs::{File, OpenOptions},
io::{Read, Seek, SeekFrom, Write}, io::{Read, Seek, SeekFrom, Write},
@ -160,7 +163,7 @@ impl MbcSaveState {
fn get_mbc<C: PocketCameraTrait + Send + 'static>( fn get_mbc<C: PocketCameraTrait + Send + 'static>(
self, self,
data: Vec<u8>, data: Vec<u8>,
camera: C, camera: CameraWrapperRef<C>,
) -> Box<dyn Mbc> { ) -> Box<dyn Mbc> {
match self { match self {
MbcSaveState::Mbc1(state) => Box::new(Mbc1::from_save_state(state, data)), MbcSaveState::Mbc1(state) => Box::new(Mbc1::from_save_state(state, data)),
@ -179,7 +182,11 @@ impl<C> Rom<C>
where where
C: PocketCameraTrait + Send + 'static, C: PocketCameraTrait + Send + 'static,
{ {
pub fn load(data: Vec<u8>, save_path: Option<PathBuf>, camera: C) -> Self { pub(crate) fn load(
data: Vec<u8>,
save_path: Option<PathBuf>,
camera: CameraWrapperRef<C>,
) -> Self {
let mut title_length = 0x143; let mut title_length = 0x143;
for (i, val) in data.iter().enumerate().take(0x143).skip(0x134) { for (i, val) in data.iter().enumerate().take(0x143).skip(0x134) {
title_length = i; title_length = i;
@ -224,7 +231,11 @@ where
} }
} }
pub fn from_save_state(state: RomSaveState, data: Vec<u8>, camera: C) -> Self { pub(crate) fn from_save_state(
state: RomSaveState,
data: Vec<u8>,
camera: CameraWrapperRef<C>,
) -> Self {
Self { Self {
title: state.title, title: state.title,
mbc: state.mbc.get_mbc(data, camera), mbc: state.mbc.get_mbc(data, camera),

View file

@ -1,6 +1,6 @@
use super::{ram_size_kb, rom_banks, Mbc, KB, RAM_BANK_SIZE, ROM_BANK_SIZE}; use super::{ram_size_kb, rom_banks, Mbc, KB, RAM_BANK_SIZE, ROM_BANK_SIZE};
use crate::{ use crate::{
connect::PocketCamera as PocketCameraTrait, connect::{CameraWrapperRef, PocketCamera as PocketCameraTrait},
processor::memory::{ processor::memory::{
rom::{MaybeBufferedSram, MbcSaveState}, rom::{MaybeBufferedSram, MbcSaveState},
Address, Address,
@ -25,9 +25,9 @@ where
ram_bank: RamBank, ram_bank: RamBank,
ram_size: usize, ram_size: usize,
ram_enabled: bool, ram_enabled: bool,
camera: C, camera: CameraWrapperRef<C>,
extra_bits_a000: u8, extra_bits_a000: u8,
camera_ram: [u8; 52], camera_ram: [u8; 53],
} }
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
@ -37,12 +37,12 @@ impl<C> PocketCamera<C>
where where
C: PocketCameraTrait, C: PocketCameraTrait,
{ {
pub fn init( pub(crate) fn init(
data: Vec<u8>, data: Vec<u8>,
rom_size: u8, rom_size: u8,
ram_size: u8, ram_size: u8,
save_file: Option<PathBuf>, save_file: Option<PathBuf>,
camera: C, camera: CameraWrapperRef<C>,
) -> Self { ) -> Self {
let ram = ram_size_kb(ram_size).map(|s| MaybeBufferedSram::new(save_file, s * KB)); let ram = ram_size_kb(ram_size).map(|s| MaybeBufferedSram::new(save_file, s * KB));
Self { Self {
@ -55,7 +55,7 @@ where
ram_enabled: false, ram_enabled: false,
camera, camera,
extra_bits_a000: 0, 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 ((address as usize - 0xA000) + (RAM_BANK_SIZE * bank as usize)) % self.ram_size
} }
pub fn from_save_state(_state: PocketCameraSaveState, _data: Vec<u8>, _camera: C) -> Self { pub(crate) fn from_save_state(
_state: PocketCameraSaveState,
_data: Vec<u8>,
_camera: CameraWrapperRef<C>,
) -> Self {
todo!(); todo!();
} }
fn get_cam_reg(&self, address: Address) -> u8 { fn get_cam_reg(&self, address: Address) -> u8 {
match address { 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, _ => 0x00,
} }
} }
@ -89,13 +100,26 @@ where
match address { match address {
0xA000 => { 0xA000 => {
if data & 0x1 == 0x1 { if data & 0x1 == 0x1 {
self.camera.begin_capture(); self.camera.lock().unwrap().begin_capture();
} }
self.extra_bits_a000 = data & 0b110; 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<C> Mbc for PocketCamera<C> impl<C> Mbc for PocketCamera<C>
@ -120,6 +144,7 @@ where
} }
fn set(&mut self, address: Address, data: u8) { fn set(&mut self, address: Address, data: u8) {
self.check_for_new_image();
match address { match address {
0x0..0x2000 => { 0x0..0x2000 => {
if (data & 0xF) == 0xA { if (data & 0xF) == 0xA {
@ -146,6 +171,7 @@ where
} }
fn set_ram(&mut self, address: Address, data: u8) { fn set_ram(&mut self, address: Address, data: u8) {
self.check_for_new_image();
match self.ram_bank { match self.ram_bank {
RamBank::Ram(bank) => { RamBank::Ram(bank) => {
let real_addr = self.get_ram_addr(address, bank); let real_addr = self.get_ram_addr(address, bank);

View file

@ -2,7 +2,7 @@ use serde::{Deserialize, Serialize};
use self::memory::{mmio::gpu::Colour, Interrupt, Memory, MemorySaveState}; use self::memory::{mmio::gpu::Colour, Interrupt, Memory, MemorySaveState};
use crate::{ use crate::{
connect::{AudioOutput, PocketCamera, Renderer, SerialTarget}, connect::{AudioOutput, CameraWrapperRef, PocketCamera, Renderer, SerialTarget},
verbose_println, verbose_println,
}; };
@ -172,13 +172,13 @@ where
self.memory.ime = false; self.memory.ime = false;
} }
pub fn from_save_state( pub(crate) fn from_save_state(
state: CpuSaveState<ColourFormat, R>, state: CpuSaveState<ColourFormat, R>,
data: Vec<u8>, data: Vec<u8>,
window: R, window: R,
output: AudioOutput, output: AudioOutput,
serial_target: SerialTarget, serial_target: SerialTarget,
camera: C, camera: CameraWrapperRef<C>,
) -> Self { ) -> Self {
Self { Self {
memory: Memory::from_save_state( memory: Memory::from_save_state(