gb-emu/lib/src/connect/mod.rs

256 lines
5.8 KiB
Rust

use std::marker::PhantomData;
use std::sync::{Arc, Mutex, RwLock};
pub use crate::processor::memory::mmio::gpu::Colour;
pub use crate::processor::memory::mmio::joypad::{JoypadButtons, JoypadState};
pub use crate::processor::memory::mmio::serial::SerialTarget;
pub use crate::{HEIGHT, WIDTH};
use async_ringbuf::{AsyncHeapConsumer, AsyncHeapProducer, AsyncHeapRb};
pub enum EmulatorMessage {
Stop,
}
#[derive(Clone, Copy)]
pub enum DownsampleType {
Linear,
ZeroOrderHold,
}
pub enum RomFile {
Path(String),
Raw(Vec<u8>),
}
pub trait Renderer<Format: From<Colour>> {
fn prepare(&mut self, width: usize, height: usize);
fn resize(&mut self, width: usize, height: usize) {
self.prepare(width, height)
}
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) {}
}
pub struct AudioOutput {
pub sample_rate: f32,
pub send_rb: AsyncHeapProducer<[f32; 2]>,
pub downsample_type: DownsampleType,
}
impl AudioOutput {
pub fn new(
sample_rate: f32,
buffers_per_frame: usize,
downsample_type: DownsampleType,
) -> (Self, AsyncHeapConsumer<[f32; 2]>) {
let rb_len = (sample_rate as usize / 60) / buffers_per_frame;
let rb = AsyncHeapRb::<[f32; 2]>::new(rb_len);
let (send_rb, rx) = rb.split();
(
Self {
sample_rate,
send_rb,
downsample_type,
},
rx,
)
}
}
pub trait PocketCamera {
// resolution - 128x128
fn get_image(&mut self) -> [u8; 128 * 128];
fn begin_capture(&mut self);
fn init(&mut self);
}
pub struct NoCamera {
buf: [u8; 128 * 128],
}
impl Default for NoCamera {
fn default() -> Self {
Self {
buf: [0; 128 * 128],
}
}
}
impl PocketCamera for NoCamera {
fn get_image(&mut self) -> [u8; 128 * 128] {
self.buf
}
fn begin_capture(&mut self) {
for v in &mut self.buf {
*v = rand::random();
}
}
fn init(&mut self) {}
}
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,
}
}
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()
}
}
#[derive(Debug)]
pub enum SramType {
File(String),
RawBuffer(Arc<RwLock<Vec<u8>>>),
}
#[non_exhaustive]
pub struct EmulatorOptions<ColourFormat, R>
where
ColourFormat: From<Colour> + Clone,
R: Renderer<ColourFormat>,
{
pub(crate) window: R,
pub(crate) tile_window: Option<R>,
pub(crate) rom: RomFile,
pub(crate) output: AudioOutput,
pub(crate) save: Option<SramType>,
pub(crate) no_save: bool,
pub(crate) bootrom: Option<RomFile>,
pub(crate) show_bootrom: bool,
pub(crate) serial_target: SerialTarget,
pub(crate) cgb_mode: bool,
spooky: PhantomData<ColourFormat>,
}
impl<ColourFormat, R> EmulatorOptions<ColourFormat, R>
where
ColourFormat: From<Colour> + Clone,
R: Renderer<ColourFormat>,
{
pub fn new(window: R, rom: RomFile, output: AudioOutput) -> Self {
Self {
window,
tile_window: None,
rom,
output,
save: None,
no_save: false,
bootrom: None,
show_bootrom: false,
serial_target: SerialTarget::None,
cgb_mode: true,
spooky: PhantomData,
}
}
pub fn with_save_path(mut self, path: Option<String>) -> Self {
self.save = path.map(SramType::File);
self
}
pub fn with_sram_buffer(mut self, buffer: Arc<RwLock<Vec<u8>>>) -> Self {
self.save = Some(SramType::RawBuffer(buffer));
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>, show_bootrom: bool) -> Self {
self.bootrom = bootrom;
self.show_bootrom = show_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
}
pub fn with_cgb_mode(mut self, cgb_mode: bool) -> Self {
self.cgb_mode = cgb_mode;
self
}
}
pub trait EmulatorCoreTrait {
fn replace_output(&mut self, new: AudioOutput);
fn cycle_count(&self) -> usize;
fn pc(&self) -> u16;
fn print_reg(&self) -> String;
fn get_memory(&self, address: u16) -> u8;
fn run(&mut self);
fn run_stepped(&mut self, step_size: usize);
fn run_until_buffer_full(&mut self);
}