2023-03-17 13:58:47 +11:00
|
|
|
use std::marker::PhantomData;
|
2023-03-19 12:43:10 +11:00
|
|
|
use std::sync::{Arc, Mutex};
|
2023-03-17 13:58:47 +11:00
|
|
|
|
2023-03-08 11:01:18 +11:00
|
|
|
use crate::processor::memory::mmio::gpu::Colour;
|
2023-03-08 15:19:10 +11:00
|
|
|
pub use crate::processor::memory::mmio::joypad::{JoypadButtons, JoypadState};
|
2023-03-15 17:45:56 +11:00
|
|
|
pub use crate::processor::memory::mmio::serial::SerialTarget;
|
2023-03-15 13:15:32 +11:00
|
|
|
pub use crate::processor::CpuSaveState;
|
2023-03-07 18:05:06 +11:00
|
|
|
pub use crate::{HEIGHT, WIDTH};
|
2023-03-03 18:08:28 +11:00
|
|
|
use async_ringbuf::{AsyncHeapConsumer, AsyncHeapProducer, AsyncHeapRb};
|
2023-03-02 10:20:50 +11:00
|
|
|
|
2023-03-02 11:29:54 +11:00
|
|
|
pub enum EmulatorMessage {
|
|
|
|
Stop,
|
|
|
|
}
|
|
|
|
|
2023-03-09 10:50:18 +11:00
|
|
|
#[derive(Clone, Copy)]
|
|
|
|
pub enum DownsampleType {
|
|
|
|
Linear,
|
|
|
|
ZeroOrderHold,
|
|
|
|
}
|
|
|
|
|
2023-03-06 17:23:46 +11:00
|
|
|
pub enum RomFile {
|
|
|
|
Path(String),
|
|
|
|
Raw(Vec<u8>),
|
|
|
|
}
|
|
|
|
|
2023-03-08 11:28:32 +11:00
|
|
|
pub trait Renderer<Format: From<Colour>> {
|
|
|
|
fn prepare(&mut self, width: usize, height: usize);
|
|
|
|
|
|
|
|
fn display(&mut self, buffer: &[Format]);
|
|
|
|
|
|
|
|
fn set_title(&mut self, _title: String) {}
|
|
|
|
|
|
|
|
fn latest_joypad_state(&mut self) -> JoypadState;
|
|
|
|
|
|
|
|
fn set_rumble(&mut self, _rumbling: bool) {}
|
|
|
|
}
|
|
|
|
|
2023-03-03 18:08:28 +11:00
|
|
|
pub struct AudioOutput {
|
2023-03-06 21:10:52 +11:00
|
|
|
pub sample_rate: f32,
|
|
|
|
pub send_rb: AsyncHeapProducer<[f32; 2]>,
|
2023-03-08 11:28:32 +11:00
|
|
|
pub wait_for_output: bool,
|
2023-03-09 10:50:18 +11:00
|
|
|
pub downsample_type: DownsampleType,
|
2023-03-03 18:08:28 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
impl AudioOutput {
|
2023-03-09 10:25:29 +11:00
|
|
|
pub fn new(
|
|
|
|
sample_rate: f32,
|
|
|
|
wait_for_output: bool,
|
|
|
|
frames_to_buffer: usize,
|
2023-03-09 10:50:18 +11:00
|
|
|
downsample_type: DownsampleType,
|
2023-03-08 11:28:32 +11:00
|
|
|
) -> (Self, AsyncHeapConsumer<[f32; 2]>) {
|
2023-03-09 10:25:29 +11:00
|
|
|
let rb_len = (sample_rate as usize / 60) * frames_to_buffer;
|
2023-03-03 18:08:28 +11:00
|
|
|
|
2023-03-06 21:10:52 +11:00
|
|
|
let rb = AsyncHeapRb::<[f32; 2]>::new(rb_len);
|
2023-03-07 08:51:23 +11:00
|
|
|
let (send_rb, rx) = rb.split();
|
2023-03-03 18:08:28 +11:00
|
|
|
|
|
|
|
(
|
|
|
|
Self {
|
|
|
|
sample_rate,
|
|
|
|
send_rb,
|
2023-03-08 11:28:32 +11:00
|
|
|
wait_for_output,
|
2023-03-09 10:50:18 +11:00
|
|
|
downsample_type,
|
2023-03-03 18:08:28 +11:00
|
|
|
},
|
|
|
|
rx,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
2023-03-17 13:58:47 +11:00
|
|
|
|
2023-03-19 10:24:03 +11:00
|
|
|
pub trait PocketCamera {
|
|
|
|
// resolution - 128x128
|
2023-03-19 12:43:10 +11:00
|
|
|
fn get_image(&mut self) -> [u8; 128 * 128];
|
2023-03-19 11:33:53 +11:00
|
|
|
|
|
|
|
fn begin_capture(&mut self);
|
2023-03-19 15:44:45 +11:00
|
|
|
|
|
|
|
fn init(&mut self);
|
2023-03-19 12:43:10 +11:00
|
|
|
}
|
2023-03-19 11:33:53 +11:00
|
|
|
|
2023-03-19 12:43:10 +11:00
|
|
|
pub struct NoCamera {
|
|
|
|
buf: [u8; 128 * 128],
|
2023-03-19 10:24:03 +11:00
|
|
|
}
|
|
|
|
|
2023-03-19 12:43:10 +11:00
|
|
|
impl Default for NoCamera {
|
|
|
|
fn default() -> Self {
|
|
|
|
Self {
|
|
|
|
buf: [0; 128 * 128],
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-03-19 10:39:57 +11:00
|
|
|
|
|
|
|
impl PocketCamera for NoCamera {
|
2023-03-19 12:43:10 +11:00
|
|
|
fn get_image(&mut self) -> [u8; 128 * 128] {
|
|
|
|
self.buf
|
|
|
|
}
|
|
|
|
|
|
|
|
fn begin_capture(&mut self) {
|
|
|
|
for v in &mut self.buf {
|
|
|
|
*v = rand::random();
|
|
|
|
}
|
|
|
|
}
|
2023-03-19 15:44:45 +11:00
|
|
|
|
|
|
|
fn init(&mut self) {}
|
2023-03-19 12:43:10 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) type CameraWrapperRef<C> = Arc<Mutex<CameraWrapper<C>>>;
|
|
|
|
|
|
|
|
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,
|
|
|
|
}
|
2023-03-19 10:39:57 +11:00
|
|
|
}
|
2023-03-19 11:33:53 +11:00
|
|
|
|
2023-03-19 12:43:10 +11:00
|
|
|
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();
|
|
|
|
}
|
2023-03-19 11:33:53 +11:00
|
|
|
|
2023-03-19 12:43:10 +11:00
|
|
|
pub(crate) fn get_next(&mut self) -> Option<[u8; 128 * 128]> {
|
|
|
|
self.next_image.take()
|
2023-03-19 11:33:53 +11:00
|
|
|
}
|
2023-03-19 10:39:57 +11:00
|
|
|
}
|
|
|
|
|
2023-03-17 13:58:47 +11:00
|
|
|
#[non_exhaustive]
|
2023-03-19 10:39:57 +11:00
|
|
|
pub struct EmulatorOptions<ColourFormat, R, C>
|
2023-03-19 10:26:56 +11:00
|
|
|
where
|
|
|
|
ColourFormat: From<Colour> + Clone,
|
|
|
|
R: Renderer<ColourFormat>,
|
2023-03-19 11:33:53 +11:00
|
|
|
C: PocketCamera + Send + 'static,
|
2023-03-19 10:26:56 +11:00
|
|
|
{
|
2023-03-17 13:58:47 +11:00
|
|
|
pub(crate) window: R,
|
|
|
|
pub(crate) tile_window: Option<R>,
|
|
|
|
pub(crate) rom: RomFile,
|
|
|
|
pub(crate) output: AudioOutput,
|
|
|
|
pub(crate) save_path: Option<String>,
|
2023-03-19 10:39:57 +11:00
|
|
|
pub(crate) camera: C,
|
2023-03-17 13:58:47 +11:00
|
|
|
pub(crate) no_save: bool,
|
|
|
|
pub(crate) bootrom: Option<RomFile>,
|
|
|
|
pub(crate) serial_target: SerialTarget,
|
2023-04-20 12:29:22 +10:00
|
|
|
pub(crate) cgb_mode: bool,
|
2023-03-19 10:38:08 +11:00
|
|
|
spooky: PhantomData<ColourFormat>,
|
2023-03-17 13:58:47 +11:00
|
|
|
}
|
|
|
|
|
2023-03-19 10:39:57 +11:00
|
|
|
impl<ColourFormat, R> EmulatorOptions<ColourFormat, R, NoCamera>
|
2023-03-19 10:26:56 +11:00
|
|
|
where
|
|
|
|
ColourFormat: From<Colour> + Clone,
|
|
|
|
R: Renderer<ColourFormat>,
|
2023-03-17 13:58:47 +11:00
|
|
|
{
|
|
|
|
pub fn new(window: R, rom: RomFile, output: AudioOutput) -> Self {
|
|
|
|
Self {
|
|
|
|
window,
|
|
|
|
tile_window: None,
|
|
|
|
rom,
|
|
|
|
output,
|
|
|
|
save_path: None,
|
2023-03-19 12:43:10 +11:00
|
|
|
camera: NoCamera::default(),
|
2023-03-19 10:39:57 +11:00
|
|
|
no_save: false,
|
|
|
|
bootrom: None,
|
|
|
|
serial_target: SerialTarget::None,
|
2023-04-20 12:29:22 +10:00
|
|
|
cgb_mode: false,
|
2023-03-19 10:39:57 +11:00
|
|
|
spooky: PhantomData,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<ColourFormat, R, C> EmulatorOptions<ColourFormat, R, C>
|
|
|
|
where
|
|
|
|
ColourFormat: From<Colour> + Clone,
|
|
|
|
R: Renderer<ColourFormat>,
|
2023-03-19 11:33:53 +11:00
|
|
|
C: PocketCamera + Send + 'static,
|
2023-03-19 10:39:57 +11:00
|
|
|
{
|
|
|
|
pub fn new_with_camera(window: R, rom: RomFile, output: AudioOutput, camera: C) -> Self {
|
|
|
|
Self {
|
|
|
|
window,
|
|
|
|
tile_window: None,
|
|
|
|
rom,
|
|
|
|
output,
|
|
|
|
save_path: None,
|
|
|
|
camera,
|
2023-03-17 13:58:47 +11:00
|
|
|
no_save: false,
|
|
|
|
bootrom: None,
|
|
|
|
serial_target: SerialTarget::None,
|
2023-04-20 12:29:22 +10:00
|
|
|
cgb_mode: false,
|
2023-03-17 13:58:47 +11:00
|
|
|
spooky: PhantomData,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn with_save_path(mut self, path: Option<String>) -> Self {
|
|
|
|
self.save_path = path;
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn force_no_save(mut self) -> Self {
|
|
|
|
self.no_save = true;
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn with_no_save(mut self, no_save: bool) -> Self {
|
|
|
|
self.no_save = no_save;
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn with_bootrom(mut self, bootrom: Option<RomFile>) -> Self {
|
|
|
|
self.bootrom = bootrom;
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn with_stdout(mut self) -> Self {
|
|
|
|
self.serial_target = SerialTarget::Stdout;
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn with_serial_target(mut self, target: SerialTarget) -> Self {
|
|
|
|
self.serial_target = target;
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn with_tile_window(mut self, window: Option<R>) -> Self {
|
|
|
|
self.tile_window = window;
|
|
|
|
self
|
|
|
|
}
|
2023-04-20 12:29:22 +10:00
|
|
|
|
|
|
|
pub fn with_cgb_mode(mut self, cgb_mode: bool) -> Self {
|
|
|
|
self.cgb_mode = cgb_mode;
|
|
|
|
self
|
|
|
|
}
|
2023-03-17 13:58:47 +11:00
|
|
|
}
|