2021-10-28 04:01:18 +00:00
|
|
|
|
2021-11-11 04:10:46 +00:00
|
|
|
use std::thread;
|
2021-12-29 06:36:52 +00:00
|
|
|
use std::str::FromStr;
|
2021-10-28 04:01:18 +00:00
|
|
|
use std::sync::{Arc, Mutex};
|
2022-10-01 19:12:25 +00:00
|
|
|
use std::time::{Duration, Instant};
|
2021-10-28 04:01:18 +00:00
|
|
|
|
2022-10-03 03:39:02 +00:00
|
|
|
use minifb::{self, Key, MouseMode, MouseButton};
|
2022-09-13 05:43:55 +00:00
|
|
|
use clap::{App, Arg, ArgMatches};
|
2021-10-28 04:01:18 +00:00
|
|
|
|
2022-10-09 04:32:59 +00:00
|
|
|
use moa_core::{System, Error};
|
2022-10-08 20:26:17 +00:00
|
|
|
use moa_core::host::{Host, ControllerUpdater, KeyboardUpdater, KeyEvent, MouseUpdater, MouseState, WindowUpdater, Audio, ControllerDevice};
|
2022-10-02 20:07:15 +00:00
|
|
|
use moa_core::host::gfx::Frame;
|
2021-11-07 04:44:25 +00:00
|
|
|
|
2022-10-09 04:32:59 +00:00
|
|
|
use moa_common::audio::{AudioMixer, AudioSource, CpalAudioOutput};
|
2021-12-12 23:20:09 +00:00
|
|
|
|
2021-11-11 17:52:18 +00:00
|
|
|
mod keys;
|
2021-12-04 22:41:27 +00:00
|
|
|
mod controllers;
|
|
|
|
|
2021-11-07 04:44:25 +00:00
|
|
|
use crate::keys::map_key;
|
2021-12-04 22:41:27 +00:00
|
|
|
use crate::controllers::map_controller_a;
|
2021-10-28 04:01:18 +00:00
|
|
|
|
|
|
|
|
2021-11-11 17:52:18 +00:00
|
|
|
const WIDTH: u32 = 320;
|
|
|
|
const HEIGHT: u32 = 224;
|
|
|
|
|
|
|
|
|
|
|
|
pub fn new(name: &str) -> App {
|
|
|
|
App::new(name)
|
2022-09-13 05:43:55 +00:00
|
|
|
.arg(Arg::new("scale")
|
|
|
|
.short('s')
|
|
|
|
.long("scale")
|
|
|
|
.takes_value(true)
|
|
|
|
.help("Scale the screen"))
|
|
|
|
.arg(Arg::new("threaded")
|
|
|
|
.short('t')
|
|
|
|
.long("threaded")
|
|
|
|
.help("Run the simulation in a separate thread"))
|
|
|
|
.arg(Arg::new("speed")
|
|
|
|
.short('x')
|
|
|
|
.long("speed")
|
|
|
|
.takes_value(true)
|
|
|
|
.help("Adjust the speed of the simulation"))
|
|
|
|
.arg(Arg::new("debugger")
|
|
|
|
.short('d')
|
|
|
|
.long("debugger")
|
|
|
|
.help("Start the debugger before running machine"))
|
|
|
|
.arg(Arg::new("disable-audio")
|
|
|
|
.short('a')
|
|
|
|
.long("disable-audio")
|
|
|
|
.help("Disable audio output"))
|
2021-11-11 17:52:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn run<I>(matches: ArgMatches, init: I) where I: FnOnce(&mut MiniFrontendBuilder) -> Result<System, Error> + Send + 'static {
|
2021-11-18 20:13:36 +00:00
|
|
|
if matches.occurrences_of("threaded") > 0 {
|
2021-11-11 17:52:18 +00:00
|
|
|
run_threaded(matches, init);
|
2021-11-16 04:51:33 +00:00
|
|
|
} else {
|
|
|
|
run_inline(matches, init);
|
2021-11-11 17:52:18 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn run_inline<I>(matches: ArgMatches, init: I) where I: FnOnce(&mut MiniFrontendBuilder) -> Result<System, Error> {
|
2023-03-06 04:19:49 +00:00
|
|
|
let mut frontend = MiniFrontendBuilder::default();
|
2021-11-11 17:52:18 +00:00
|
|
|
let system = init(&mut frontend).unwrap();
|
|
|
|
|
|
|
|
frontend
|
|
|
|
.build()
|
|
|
|
.start(matches, Some(system));
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn run_threaded<I>(matches: ArgMatches, init: I) where I: FnOnce(&mut MiniFrontendBuilder) -> Result<System, Error> + Send + 'static {
|
2023-03-06 04:19:49 +00:00
|
|
|
let frontend = Arc::new(Mutex::new(MiniFrontendBuilder::default()));
|
2021-11-11 17:52:18 +00:00
|
|
|
|
|
|
|
{
|
|
|
|
let frontend = frontend.clone();
|
|
|
|
thread::spawn(move || {
|
2023-03-06 04:34:30 +00:00
|
|
|
let mut system = init(&mut frontend.lock().unwrap()).unwrap();
|
2021-11-11 17:52:18 +00:00
|
|
|
frontend.lock().unwrap().finalize();
|
|
|
|
system.run_loop();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
wait_until_initialized(frontend.clone());
|
|
|
|
|
|
|
|
frontend
|
|
|
|
.lock().unwrap()
|
|
|
|
.build()
|
|
|
|
.start(matches, None);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn wait_until_initialized(frontend: Arc<Mutex<MiniFrontendBuilder>>) {
|
2023-03-06 04:19:49 +00:00
|
|
|
while !frontend.lock().unwrap().finalized {
|
2021-11-11 17:52:18 +00:00
|
|
|
thread::sleep(Duration::from_millis(10));
|
|
|
|
}
|
|
|
|
}
|
2021-11-11 04:10:46 +00:00
|
|
|
|
2021-10-28 04:01:18 +00:00
|
|
|
|
2021-11-07 04:44:25 +00:00
|
|
|
pub struct MiniFrontendBuilder {
|
|
|
|
pub window: Option<Box<dyn WindowUpdater>>,
|
2021-12-02 23:04:41 +00:00
|
|
|
pub controller: Option<Box<dyn ControllerUpdater>>,
|
2021-11-07 04:44:25 +00:00
|
|
|
pub keyboard: Option<Box<dyn KeyboardUpdater>>,
|
2022-10-03 03:39:02 +00:00
|
|
|
pub mouse: Option<Box<dyn MouseUpdater>>,
|
2022-10-08 20:26:17 +00:00
|
|
|
pub mixer: Option<Arc<Mutex<AudioMixer>>>,
|
2021-11-07 04:44:25 +00:00
|
|
|
pub finalized: bool,
|
2021-10-28 04:01:18 +00:00
|
|
|
}
|
|
|
|
|
2023-03-06 04:19:49 +00:00
|
|
|
impl Default for MiniFrontendBuilder {
|
|
|
|
fn default() -> Self {
|
2021-11-07 04:44:25 +00:00
|
|
|
Self {
|
|
|
|
window: None,
|
2021-12-02 23:04:41 +00:00
|
|
|
controller: None,
|
2021-11-07 04:44:25 +00:00
|
|
|
keyboard: None,
|
2022-10-03 03:39:02 +00:00
|
|
|
mouse: None,
|
2022-10-08 20:26:17 +00:00
|
|
|
mixer: Some(AudioMixer::with_default_rate()),
|
2021-11-07 04:44:25 +00:00
|
|
|
finalized: false,
|
|
|
|
}
|
|
|
|
}
|
2023-03-06 04:19:49 +00:00
|
|
|
}
|
2021-11-07 04:44:25 +00:00
|
|
|
|
2023-03-06 04:19:49 +00:00
|
|
|
impl MiniFrontendBuilder {
|
2021-11-07 04:44:25 +00:00
|
|
|
pub fn finalize(&mut self) {
|
|
|
|
self.finalized = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn build(&mut self) -> MiniFrontend {
|
|
|
|
let window = std::mem::take(&mut self.window);
|
2021-12-02 23:04:41 +00:00
|
|
|
let controller = std::mem::take(&mut self.controller);
|
2021-11-07 04:44:25 +00:00
|
|
|
let keyboard = std::mem::take(&mut self.keyboard);
|
2022-10-03 03:39:02 +00:00
|
|
|
let mouse = std::mem::take(&mut self.mouse);
|
2021-12-12 23:20:09 +00:00
|
|
|
let mixer = std::mem::take(&mut self.mixer);
|
2022-10-03 03:39:02 +00:00
|
|
|
MiniFrontend::new(window, controller, keyboard, mouse, mixer.unwrap())
|
2021-11-07 04:44:25 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Host for MiniFrontendBuilder {
|
|
|
|
fn add_window(&mut self, updater: Box<dyn WindowUpdater>) -> Result<(), Error> {
|
|
|
|
if self.window.is_some() {
|
2021-10-28 04:01:18 +00:00
|
|
|
return Err(Error::new("A window updater has already been registered with the frontend"));
|
|
|
|
}
|
2021-11-07 04:44:25 +00:00
|
|
|
self.window = Some(updater);
|
2021-10-28 04:01:18 +00:00
|
|
|
Ok(())
|
|
|
|
}
|
2021-10-31 18:00:14 +00:00
|
|
|
|
2021-12-02 23:04:41 +00:00
|
|
|
fn register_controller(&mut self, device: ControllerDevice, input: Box<dyn ControllerUpdater>) -> Result<(), Error> {
|
|
|
|
if device != ControllerDevice::A {
|
2021-10-31 18:00:14 +00:00
|
|
|
return Ok(())
|
|
|
|
}
|
|
|
|
|
2021-12-02 23:04:41 +00:00
|
|
|
if self.controller.is_some() {
|
|
|
|
return Err(Error::new("A controller updater has already been registered with the frontend"));
|
2021-11-07 04:44:25 +00:00
|
|
|
}
|
2021-12-02 23:04:41 +00:00
|
|
|
self.controller = Some(input);
|
2021-11-07 04:44:25 +00:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn register_keyboard(&mut self, input: Box<dyn KeyboardUpdater>) -> Result<(), Error> {
|
|
|
|
if self.keyboard.is_some() {
|
|
|
|
return Err(Error::new("A keyboard updater has already been registered with the frontend"));
|
2021-10-31 18:00:14 +00:00
|
|
|
}
|
2021-11-07 04:44:25 +00:00
|
|
|
self.keyboard = Some(input);
|
2021-10-31 18:00:14 +00:00
|
|
|
Ok(())
|
|
|
|
}
|
2021-12-12 23:20:09 +00:00
|
|
|
|
2022-10-03 03:39:02 +00:00
|
|
|
fn register_mouse(&mut self, input: Box<dyn MouseUpdater>) -> Result<(), Error> {
|
|
|
|
if self.mouse.is_some() {
|
|
|
|
return Err(Error::new("A mouse updater has already been registered with the frontend"));
|
|
|
|
}
|
|
|
|
self.mouse = Some(input);
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2021-12-12 23:20:09 +00:00
|
|
|
fn create_audio_source(&mut self) -> Result<Box<dyn Audio>, Error> {
|
|
|
|
let source = AudioSource::new(self.mixer.as_ref().unwrap().clone());
|
|
|
|
Ok(Box::new(source))
|
|
|
|
}
|
2021-10-28 04:01:18 +00:00
|
|
|
}
|
|
|
|
|
2021-11-07 04:44:25 +00:00
|
|
|
|
|
|
|
pub struct MiniFrontend {
|
2021-12-04 22:41:27 +00:00
|
|
|
pub modifiers: u16,
|
2022-10-03 03:39:02 +00:00
|
|
|
pub mouse_state: MouseState,
|
2021-11-07 04:44:25 +00:00
|
|
|
pub window: Option<Box<dyn WindowUpdater>>,
|
2021-12-02 23:04:41 +00:00
|
|
|
pub controller: Option<Box<dyn ControllerUpdater>>,
|
2021-11-07 04:44:25 +00:00
|
|
|
pub keyboard: Option<Box<dyn KeyboardUpdater>>,
|
2022-10-03 03:39:02 +00:00
|
|
|
pub mouse: Option<Box<dyn MouseUpdater>>,
|
2022-10-08 20:26:17 +00:00
|
|
|
pub audio: Option<CpalAudioOutput>,
|
|
|
|
pub mixer: Arc<Mutex<AudioMixer>>,
|
2021-11-07 04:44:25 +00:00
|
|
|
}
|
|
|
|
|
2021-10-28 04:01:18 +00:00
|
|
|
impl MiniFrontend {
|
2022-10-03 03:39:02 +00:00
|
|
|
pub fn new(
|
|
|
|
window: Option<Box<dyn WindowUpdater>>,
|
|
|
|
controller: Option<Box<dyn ControllerUpdater>>,
|
|
|
|
keyboard: Option<Box<dyn KeyboardUpdater>>,
|
|
|
|
mouse: Option<Box<dyn MouseUpdater>>,
|
2022-10-08 20:26:17 +00:00
|
|
|
mixer: Arc<Mutex<AudioMixer>>,
|
2022-10-03 03:39:02 +00:00
|
|
|
) -> Self {
|
2021-11-07 04:44:25 +00:00
|
|
|
Self {
|
2021-12-04 22:41:27 +00:00
|
|
|
modifiers: 0,
|
2022-10-03 03:39:02 +00:00
|
|
|
mouse_state: Default::default(),
|
2021-11-07 04:44:25 +00:00
|
|
|
window,
|
2021-12-02 23:04:41 +00:00
|
|
|
controller,
|
2021-11-07 04:44:25 +00:00
|
|
|
keyboard,
|
2022-10-03 03:39:02 +00:00
|
|
|
mouse,
|
2022-01-17 05:42:07 +00:00
|
|
|
audio: None,
|
2022-09-13 23:57:56 +00:00
|
|
|
mixer,
|
2021-10-28 04:01:18 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-11 17:52:18 +00:00
|
|
|
pub fn start(&mut self, matches: ArgMatches, mut system: Option<System>) {
|
2022-10-09 16:40:20 +00:00
|
|
|
simple_logger::SimpleLogger::new()
|
|
|
|
.with_level(log::Level::Warn.to_level_filter())
|
|
|
|
.without_timestamps()
|
|
|
|
.init().unwrap();
|
|
|
|
|
2021-11-18 20:13:36 +00:00
|
|
|
if matches.occurrences_of("debugger") > 0 {
|
2023-03-06 04:19:49 +00:00
|
|
|
if let Some(system) = system.as_mut() {
|
|
|
|
system.enable_debugging();
|
|
|
|
}
|
2021-11-18 20:13:36 +00:00
|
|
|
}
|
|
|
|
|
2023-03-06 04:19:49 +00:00
|
|
|
if matches.occurrences_of("disable-audio") == 0 {
|
2022-10-08 20:26:17 +00:00
|
|
|
self.audio = Some(CpalAudioOutput::create_audio_output(self.mixer.lock().unwrap().get_sink()));
|
2022-01-17 05:42:07 +00:00
|
|
|
}
|
|
|
|
|
2023-03-06 04:19:49 +00:00
|
|
|
let options = minifb::WindowOptions {
|
|
|
|
scale: match matches.value_of("scale").map(|s| s.parse::<u8>().unwrap()) {
|
|
|
|
Some(1) => minifb::Scale::X1,
|
|
|
|
Some(2) => minifb::Scale::X2,
|
|
|
|
Some(4) => minifb::Scale::X4,
|
|
|
|
Some(8) => minifb::Scale::X8,
|
|
|
|
_ => minifb::Scale::X2,
|
|
|
|
},
|
|
|
|
..Default::default()
|
2021-11-11 17:52:18 +00:00
|
|
|
};
|
|
|
|
|
2021-12-29 06:36:52 +00:00
|
|
|
let speed = match matches.value_of("speed") {
|
|
|
|
Some(x) => f32::from_str(x).unwrap(),
|
|
|
|
None => 1.0,
|
|
|
|
};
|
|
|
|
|
2021-11-11 17:52:18 +00:00
|
|
|
let mut size = (WIDTH, HEIGHT);
|
|
|
|
if let Some(updater) = self.window.as_mut() {
|
2022-10-01 19:12:25 +00:00
|
|
|
size = updater.max_size();
|
2021-11-11 17:52:18 +00:00
|
|
|
}
|
2021-11-03 03:57:19 +00:00
|
|
|
|
2021-10-28 04:01:18 +00:00
|
|
|
let mut window = minifb::Window::new(
|
|
|
|
"Test - ESC to exit",
|
2021-11-11 17:52:18 +00:00
|
|
|
size.0 as usize,
|
|
|
|
size.1 as usize,
|
2021-11-03 03:57:19 +00:00
|
|
|
options,
|
2021-10-28 04:01:18 +00:00
|
|
|
)
|
|
|
|
.unwrap_or_else(|e| {
|
|
|
|
panic!("{}", e);
|
|
|
|
});
|
|
|
|
|
|
|
|
// Limit to max ~60 fps update rate
|
|
|
|
window.limit_update_rate(Some(Duration::from_micros(16600)));
|
|
|
|
|
2022-10-02 04:01:56 +00:00
|
|
|
let mut update_timer = Instant::now();
|
2022-10-02 20:07:15 +00:00
|
|
|
let mut last_frame = Frame::new(size.0, size.1);
|
2021-10-28 04:01:18 +00:00
|
|
|
while window.is_open() && !window.is_key_down(Key::Escape) {
|
2022-10-08 20:26:17 +00:00
|
|
|
let frame_time = update_timer.elapsed();
|
2022-10-02 04:01:56 +00:00
|
|
|
update_timer = Instant::now();
|
2022-10-08 23:17:17 +00:00
|
|
|
//println!("new frame after {:?}us", frame_time.as_micros());
|
2022-10-02 04:01:56 +00:00
|
|
|
|
2022-10-09 04:32:59 +00:00
|
|
|
//let run_timer = Instant::now();
|
2021-11-07 04:44:25 +00:00
|
|
|
if let Some(system) = system.as_mut() {
|
2022-10-08 20:26:17 +00:00
|
|
|
//system.run_for(nanoseconds_per_frame).unwrap();
|
2022-10-09 04:32:59 +00:00
|
|
|
system.run_for((frame_time.as_nanos() as f32 * speed) as u64).unwrap();
|
2021-12-12 23:20:09 +00:00
|
|
|
//system.run_until_break().unwrap();
|
2021-11-07 04:44:25 +00:00
|
|
|
}
|
2022-10-09 04:32:59 +00:00
|
|
|
//let sim_time = run_timer.elapsed().as_micros();
|
2022-10-11 17:28:59 +00:00
|
|
|
//println!("ran simulation for {:?}us in {:?}us (avg: {:?}us)", frame_time.as_micros(), sim_time, frame_time.as_micros() as f64 / sim_time as f64);
|
2021-10-28 04:01:18 +00:00
|
|
|
|
2021-12-05 22:26:21 +00:00
|
|
|
if let Some(keys) = window.get_keys_pressed(minifb::KeyRepeat::No) {
|
2021-10-31 18:00:14 +00:00
|
|
|
for key in keys {
|
2021-12-04 22:41:27 +00:00
|
|
|
self.check_key(key, true);
|
|
|
|
|
|
|
|
// Process special keys
|
2023-03-06 04:19:49 +00:00
|
|
|
if let Key::D = key {
|
|
|
|
if let Some(system) = system.as_ref() {
|
|
|
|
system.enable_debugging();
|
|
|
|
}
|
2021-10-31 18:00:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-11-07 04:44:25 +00:00
|
|
|
if let Some(keys) = window.get_keys_released() {
|
|
|
|
for key in keys {
|
2021-12-04 22:41:27 +00:00
|
|
|
self.check_key(key, false);
|
2021-11-07 04:44:25 +00:00
|
|
|
}
|
|
|
|
}
|
2021-10-31 18:00:14 +00:00
|
|
|
|
2022-10-03 03:39:02 +00:00
|
|
|
if let Some(updater) = self.mouse.as_mut() {
|
|
|
|
if let Some((x, y)) = window.get_mouse_pos(MouseMode::Clamp) {
|
|
|
|
let left = window.get_mouse_down(MouseButton::Left);
|
|
|
|
let right = window.get_mouse_down(MouseButton::Right);
|
|
|
|
let middle = window.get_mouse_down(MouseButton::Middle);
|
|
|
|
|
|
|
|
let next_state = MouseState::with(left, right, middle, x as u32, y as u32);
|
|
|
|
self.mouse_state
|
|
|
|
.to_events(next_state)
|
|
|
|
.into_iter()
|
|
|
|
.for_each(|event| updater.update_mouse(event));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-11 04:10:46 +00:00
|
|
|
if let Some(updater) = self.window.as_mut() {
|
2022-10-01 19:12:25 +00:00
|
|
|
if let Ok(frame) = updater.take_frame() {
|
2022-10-02 20:07:15 +00:00
|
|
|
last_frame = frame
|
2022-10-01 19:12:25 +00:00
|
|
|
}
|
2022-10-02 20:07:15 +00:00
|
|
|
window.update_with_buffer(&last_frame.bitmap, last_frame.width as usize, last_frame.height as usize).unwrap();
|
2021-10-28 04:01:18 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-12-04 22:41:27 +00:00
|
|
|
|
|
|
|
fn check_key(&mut self, key: Key, state: bool) {
|
|
|
|
if let Some(updater) = self.keyboard.as_mut() {
|
2022-10-03 03:39:02 +00:00
|
|
|
updater.update_keyboard(KeyEvent::new(map_key(key), state));
|
2021-12-04 22:41:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if let Some(updater) = self.controller.as_mut() {
|
|
|
|
if let Some(event) = map_controller_a(key, state) {
|
|
|
|
updater.update_controller(event);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-10-28 04:01:18 +00:00
|
|
|
}
|
|
|
|
|