shrooms-vb-native/src/emulator.rs

174 lines
4.5 KiB
Rust
Raw Normal View History

2024-11-03 16:32:53 +00:00
use std::{
fs,
2024-11-04 14:59:58 +00:00
path::{Path, PathBuf},
2024-11-05 03:18:57 +00:00
sync::{
atomic::{AtomicBool, Ordering},
mpsc::{self, TryRecvError},
Arc,
},
2024-11-03 16:32:53 +00:00
};
use anyhow::Result;
2024-11-05 05:07:48 +00:00
use crate::{
audio::Audio,
renderer::GameRenderer,
shrooms_vb_core::{CoreVB, VBKey},
};
2024-11-04 14:59:58 +00:00
pub struct EmulatorBuilder {
rom: Option<PathBuf>,
commands: mpsc::Receiver<EmulatorCommand>,
2024-11-05 03:18:57 +00:00
running: Arc<AtomicBool>,
2024-11-04 14:59:58 +00:00
}
impl EmulatorBuilder {
pub fn new() -> (Self, EmulatorClient) {
let (queue, commands) = mpsc::channel();
let builder = Self {
rom: None,
commands,
2024-11-05 03:18:57 +00:00
running: Arc::new(AtomicBool::new(false)),
};
let client = EmulatorClient {
queue,
running: builder.running.clone(),
2024-11-04 14:59:58 +00:00
};
(builder, client)
}
pub fn with_rom(self, path: &Path) -> Self {
Self {
rom: Some(path.into()),
..self
}
}
pub fn build(self) -> Result<Emulator> {
2024-11-05 03:18:57 +00:00
let mut emulator = Emulator::new(self.commands, self.running)?;
2024-11-04 14:59:58 +00:00
if let Some(path) = self.rom {
emulator.load_rom(&path)?;
}
Ok(emulator)
}
}
2024-11-03 16:32:53 +00:00
pub struct Emulator {
sim: CoreVB,
2024-11-04 14:59:58 +00:00
audio: Audio,
2024-11-03 16:32:53 +00:00
commands: mpsc::Receiver<EmulatorCommand>,
renderer: Option<GameRenderer>,
2024-11-05 03:18:57 +00:00
running: Arc<AtomicBool>,
has_game: bool,
2024-11-03 16:32:53 +00:00
}
impl Emulator {
2024-11-05 03:18:57 +00:00
fn new(commands: mpsc::Receiver<EmulatorCommand>, running: Arc<AtomicBool>) -> Result<Self> {
2024-11-04 14:59:58 +00:00
Ok(Self {
2024-11-03 16:32:53 +00:00
sim: CoreVB::new(),
2024-11-04 14:59:58 +00:00
audio: Audio::init()?,
commands,
2024-11-03 16:32:53 +00:00
renderer: None,
2024-11-05 03:18:57 +00:00
running,
has_game: false,
2024-11-04 14:59:58 +00:00
})
2024-11-03 16:32:53 +00:00
}
pub fn load_rom(&mut self, path: &Path) -> Result<()> {
let bytes = fs::read(path)?;
2024-11-05 03:18:57 +00:00
self.sim.reset();
2024-11-03 16:32:53 +00:00
self.sim.load_rom(bytes)?;
2024-11-05 03:18:57 +00:00
self.has_game = true;
self.running.store(true, Ordering::Release);
2024-11-03 16:32:53 +00:00
Ok(())
}
pub fn run(&mut self) {
let mut eye_contents = vec![0u8; 384 * 224 * 2];
2024-11-04 14:59:58 +00:00
let mut audio_samples = vec![];
2024-11-03 16:32:53 +00:00
loop {
2024-11-05 03:18:57 +00:00
if self.running.load(Ordering::Acquire) {
self.sim.emulate_frame();
}
2024-11-03 16:32:53 +00:00
if let Some(renderer) = &mut self.renderer {
if self.sim.read_pixels(&mut eye_contents) {
renderer.render(&eye_contents);
}
}
2024-11-04 14:59:58 +00:00
self.sim.read_samples(&mut audio_samples);
if !audio_samples.is_empty() {
self.audio.update(&audio_samples);
audio_samples.clear();
}
2024-11-03 16:32:53 +00:00
loop {
match self.commands.try_recv() {
Ok(command) => self.handle_command(command),
Err(TryRecvError::Empty) => {
break;
}
Err(TryRecvError::Disconnected) => {
return;
}
}
}
}
}
fn handle_command(&mut self, command: EmulatorCommand) {
match command {
EmulatorCommand::SetRenderer(renderer) => {
self.renderer = Some(renderer);
}
2024-11-05 03:18:57 +00:00
EmulatorCommand::LoadGame(path) => {
if let Err(error) = self.load_rom(&path) {
eprintln!("error loading rom: {}", error);
}
}
EmulatorCommand::Pause => {
self.running.store(false, Ordering::Release);
}
EmulatorCommand::Resume => {
if self.has_game {
self.running.store(true, Ordering::Relaxed);
}
}
EmulatorCommand::Reset => {
self.sim.reset();
self.running.store(true, Ordering::Release);
}
2024-11-05 05:07:48 +00:00
EmulatorCommand::SetKeys(keys) => {
self.sim.set_keys(keys);
2024-11-03 16:32:53 +00:00
}
}
}
}
#[derive(Debug)]
pub enum EmulatorCommand {
SetRenderer(GameRenderer),
2024-11-05 03:18:57 +00:00
LoadGame(PathBuf),
Pause,
Resume,
Reset,
2024-11-05 05:07:48 +00:00
SetKeys(VBKey),
2024-11-03 16:32:53 +00:00
}
pub struct EmulatorClient {
queue: mpsc::Sender<EmulatorCommand>,
2024-11-05 03:18:57 +00:00
running: Arc<AtomicBool>,
2024-11-03 16:32:53 +00:00
}
impl EmulatorClient {
2024-11-05 03:18:57 +00:00
pub fn is_running(&self) -> bool {
self.running.load(Ordering::Acquire)
}
2024-11-03 16:32:53 +00:00
pub fn send_command(&self, command: EmulatorCommand) {
if let Err(err) = self.queue.send(command) {
eprintln!(
"could not send command {:?} as emulator is shut down",
err.0
);
}
}
}