diff --git a/emulator/core/src/host/controllers.rs b/emulator/core/src/host/controllers.rs index c2af7f0..cb96fc2 100644 --- a/emulator/core/src/host/controllers.rs +++ b/emulator/core/src/host/controllers.rs @@ -8,7 +8,7 @@ pub enum ControllerDevice { } #[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub enum ControllerEvent { +pub enum ControllerInput { DpadUp(bool), DpadDown(bool), DpadLeft(bool), @@ -23,3 +23,18 @@ pub enum ControllerEvent { Mode(bool), } +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub struct ControllerEvent { + pub device: ControllerDevice, + pub input: ControllerInput, +} + +impl ControllerEvent { + pub fn new(device: ControllerDevice, input: ControllerInput) -> Self { + Self { + device, + input, + } + } +} + diff --git a/emulator/core/src/host/gfx.rs b/emulator/core/src/host/gfx.rs index 2817e85..3a36618 100644 --- a/emulator/core/src/host/gfx.rs +++ b/emulator/core/src/host/gfx.rs @@ -1,6 +1,5 @@ use std::sync::{Arc, Mutex}; -use crate::Error; use crate::ClockTime; use crate::host::traits::ClockedQueue; @@ -113,9 +112,8 @@ impl Frame { pub fn frame_queue(width: u32, height: u32) -> (FrameSender, FrameReceiver) { let sender = FrameSender { - max_size: (width, height), encoding: Arc::new(Mutex::new(PixelEncoding::RGBA)), - queue: Default::default(), + queue: ClockedQueue::new(10), }; let receiver = FrameReceiver { @@ -128,20 +126,11 @@ pub fn frame_queue(width: u32, height: u32) -> (FrameSender, FrameReceiver) { } pub struct FrameSender { - max_size: (u32, u32), encoding: Arc>, queue: ClockedQueue, } impl FrameSender { - pub fn new(width: u32, height: u32) -> Self { - Self { - max_size: (width, height), - encoding: Arc::new(Mutex::new(PixelEncoding::RGBA)), - queue: Default::default(), - } - } - pub fn encoding(&self) -> PixelEncoding { *self.encoding.lock().unwrap() } diff --git a/emulator/core/src/host/keys.rs b/emulator/core/src/host/keys.rs index 52bc8d5..9751bb9 100644 --- a/emulator/core/src/host/keys.rs +++ b/emulator/core/src/host/keys.rs @@ -110,6 +110,7 @@ pub enum Key { Unknown, } +#[derive(Copy, Clone, Debug, PartialEq, Eq)] pub struct KeyEvent { pub key: Key, pub state: bool, diff --git a/emulator/core/src/host/mod.rs b/emulator/core/src/host/mod.rs index 0e051ea..525b4cd 100644 --- a/emulator/core/src/host/mod.rs +++ b/emulator/core/src/host/mod.rs @@ -2,12 +2,14 @@ mod traits; mod keys; mod gfx; +mod input; mod controllers; mod mouse; pub use self::gfx::{Pixel, PixelEncoding, Frame, FrameSender, FrameReceiver, frame_queue}; pub use self::keys::{Key, KeyEvent}; pub use self::mouse::{MouseButton, MouseEventType, MouseEvent, MouseState}; -pub use self::controllers::{ControllerDevice, ControllerEvent}; -pub use self::traits::{Host, Tty, ControllerUpdater, KeyboardUpdater, MouseUpdater, Audio, HostData, ClockedQueue, DummyAudio}; +pub use self::controllers::{ControllerDevice, ControllerInput, ControllerEvent}; +pub use self::input::{EventSender, EventReceiver, event_queue}; +pub use self::traits::{Host, Tty, Audio, HostData, ClockedQueue, DummyAudio}; diff --git a/emulator/core/src/host/mouse.rs b/emulator/core/src/host/mouse.rs index 08a69e7..3b2fe29 100644 --- a/emulator/core/src/host/mouse.rs +++ b/emulator/core/src/host/mouse.rs @@ -1,22 +1,25 @@ +#[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum MouseButton { Left, Right, Middle, } +#[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum MouseEventType { Down(MouseButton), Up(MouseButton), Move, } +#[derive(Copy, Clone, Debug, PartialEq, Eq)] pub struct MouseEvent { pub etype: MouseEventType, pub pos: (u32, u32), } -#[derive(Clone, Default, PartialEq, Eq)] +#[derive(Copy, Clone, Default, PartialEq, Eq)] pub struct MouseState { pub buttons: [bool; 3], pub pos: (u32, u32), diff --git a/emulator/core/src/host/traits.rs b/emulator/core/src/host/traits.rs index 85d8ca6..2cf4087 100644 --- a/emulator/core/src/host/traits.rs +++ b/emulator/core/src/host/traits.rs @@ -3,10 +3,11 @@ use std::collections::VecDeque; use std::sync::{Arc, Mutex, MutexGuard}; use crate::{ClockTime, Error}; -use crate::host::gfx::{PixelEncoding, Pixel, Frame, FrameReceiver}; +use crate::host::gfx::FrameReceiver; use crate::host::keys::KeyEvent; -use crate::host::controllers::{ControllerDevice, ControllerEvent}; +use crate::host::controllers::ControllerEvent; use crate::host::mouse::MouseEvent; +use crate::host::input::EventSender; pub trait Host { fn add_pty(&self) -> Result, Error> { @@ -21,15 +22,15 @@ pub trait Host { Err(Error::new("This frontend doesn't support the sound")) } - fn register_controller(&mut self, _device: ControllerDevice, _input: Box) -> Result<(), Error> { + fn register_controllers(&mut self, _sender: EventSender) -> Result<(), Error> { Err(Error::new("This frontend doesn't support game controllers")) } - fn register_keyboard(&mut self, _input: Box) -> Result<(), Error> { + fn register_keyboard(&mut self, _sender: EventSender) -> Result<(), Error> { Err(Error::new("This frontend doesn't support the keyboard")) } - fn register_mouse(&mut self, _input: Box) -> Result<(), Error> { + fn register_mouse(&mut self, _sender: EventSender) -> Result<(), Error> { Err(Error::new("This frontend doesn't support the mouse")) } } @@ -41,18 +42,6 @@ pub trait Tty { fn write(&mut self, output: u8) -> bool; } -pub trait ControllerUpdater: Send { - fn update_controller(&self, event: ControllerEvent); -} - -pub trait KeyboardUpdater: Send { - fn update_keyboard(&self, event: KeyEvent); -} - -pub trait MouseUpdater: Send { - fn update_mouse(&self, event: MouseEvent); -} - pub trait Audio { fn samples_per_second(&self) -> usize; fn space_available(&self) -> usize; @@ -84,12 +73,22 @@ impl HostData { } } + #[derive(Clone, Default)] -pub struct ClockedQueue(Arc>>); +pub struct ClockedQueue(Arc>>, usize); impl ClockedQueue { + pub fn new(max: usize) -> Self { + Self(Arc::new(Mutex::new(VecDeque::new())), max) + } + pub fn push(&self, clock: ClockTime, data: T) { - self.0.lock().unwrap().push_back((clock, data)); + let mut queue = self.0.lock().unwrap(); + if queue.len() > self.1 { + //log::warn!("dropping data from queue due to limit of {} items", self.1); + queue.pop_front(); + } + queue.push_back((clock, data)); } pub fn pop_next(&self) -> Option<(ClockTime, T)> { @@ -100,7 +99,7 @@ impl ClockedQueue { self.0.lock().unwrap().drain(..).last() } - pub fn unpop(&mut self, clock: ClockTime, data: T) { + pub fn unpop(&self, clock: ClockTime, data: T) { self.0.lock().unwrap().push_front((clock, data)); } diff --git a/emulator/frontends/common/src/audio.rs b/emulator/frontends/common/src/audio.rs index beff67e..517dcef 100644 --- a/emulator/frontends/common/src/audio.rs +++ b/emulator/frontends/common/src/audio.rs @@ -8,8 +8,11 @@ use moa_core::host::{Audio, ClockedQueue}; pub const SAMPLE_RATE: usize = 48000; +pub struct Sample(f32, f32); + #[derive(Clone, Default)] pub struct AudioFrame { + //pub sample_rate: usize, pub data: Vec<(f32, f32)>, } @@ -23,7 +26,7 @@ pub struct AudioSource { impl AudioSource { pub fn new(mixer: Arc>) -> Self { - let queue = ClockedQueue::default(); + let queue = ClockedQueue::new(5000); let (id, sample_rate, frame_size) = { let mut mixer = mixer.lock().unwrap(); let id = mixer.add_source(queue.clone()); @@ -124,6 +127,10 @@ impl AudioMixer { self.sources.len() - 1 } + pub fn num_sources(&self) -> usize { + self.sources.len() + } + pub fn get_sink(&mut self) -> Arc> { self.output.clone() } @@ -214,11 +221,9 @@ impl AudioMixer { } } -#[allow(dead_code)] pub struct AudioOutput { frame_size: usize, sequence_num: usize, - last_frame: Option, output: VecDeque, } @@ -227,7 +232,6 @@ impl AudioOutput { Arc::new(Mutex::new(Self { frame_size: 0, sequence_num: 0, - last_frame: None, output: VecDeque::with_capacity(2), })) } diff --git a/emulator/frontends/common/src/cpal.rs b/emulator/frontends/common/src/cpal.rs index cc2e11a..8cc5ac5 100644 --- a/emulator/frontends/common/src/cpal.rs +++ b/emulator/frontends/common/src/cpal.rs @@ -1,6 +1,6 @@ use std::sync::{Arc, Mutex}; -use cpal::{Stream, SampleRate, SampleFormat, StreamConfig, traits::{DeviceTrait, HostTrait, StreamTrait}}; +use cpal::{Stream, SampleRate, SampleFormat, StreamConfig, StreamInstant, OutputCallbackInfo, traits::{DeviceTrait, HostTrait, StreamTrait}}; use moa_core::{warn, error}; @@ -25,7 +25,7 @@ impl CpalAudioOutput { .with_sample_rate(SampleRate(SAMPLE_RATE as u32)) .into(); - let data_callback = move |data: &mut [f32], _: &cpal::OutputCallbackInfo| { + let data_callback = move |data: &mut [f32], info: &OutputCallbackInfo| { let result = if let Ok(mut output) = output.lock() { output.set_frame_size(data.len() / 2); output.pop_next() @@ -59,5 +59,13 @@ impl CpalAudioOutput { stream, } } + + pub fn set_mute(&self, mute: bool) { + if mute { + self.stream.pause().unwrap(); + } else { + self.stream.play().unwrap(); + } + } } diff --git a/emulator/frontends/minifb/src/bin/moa-synth.rs b/emulator/frontends/minifb/src/bin/moa-synth.rs index 6f00091..5ea20f5 100644 --- a/emulator/frontends/minifb/src/bin/moa-synth.rs +++ b/emulator/frontends/minifb/src/bin/moa-synth.rs @@ -1,43 +1,29 @@ -use std::sync::mpsc; - use moa_peripherals_yamaha::{Ym2612, Sn76489}; -use moa_core::host::{Host, Frame, FrameQueue, PixelEncoding, WindowUpdater, KeyboardUpdater, Key, KeyEvent /*, MouseUpdater, MouseState, MouseEvent*/}; +use moa_core::host::{self, Host, Frame, FrameSender, PixelEncoding, Key, KeyEvent, EventReceiver}; use moa_core::{System, Error, ClockTime, ClockDuration, Frequency, Address, Addressable, Steppable, Transmutable, TransmutableBox, wrap_transmutable}; - -pub struct SynthControlsUpdater(mpsc::Sender); - -impl KeyboardUpdater for SynthControlsUpdater { - fn update_keyboard(&mut self, event: KeyEvent) { - self.0.send(event).unwrap(); - } -} - -//impl MouseUpdater for SynthControlsUpdater { -// fn update_mouse(&mut self, event: MouseEvent) { -// self.0.send(event).unwrap(); -// } -//} +const SCREEN_WIDTH: u32 = 384; +const SCREEN_HEIGHT: u32 = 128; struct SynthControl { - queue: FrameQueue, - receiver: mpsc::Receiver, + key_receiver: EventReceiver, + frame_sender: FrameSender, } impl SynthControl { - pub fn new(queue: FrameQueue, receiver: mpsc::Receiver) -> Self { + pub fn new(key_receiver: EventReceiver, frame_sender: FrameSender) -> Self { Self { - queue, - receiver, + key_receiver, + frame_sender, } } } impl Steppable for SynthControl { fn step(&mut self, system: &System) -> Result { - if let Ok(event) = self.receiver.try_recv() { + if let Some(event) = self.key_receiver.receive() { match event.key { Key::Enter => { @@ -55,11 +41,10 @@ impl Steppable for SynthControl { } } - let size = self.queue.max_size(); - let frame = Frame::new(size.0, size.1, PixelEncoding::RGBA); - self.queue.add(system.clock, frame); + let frame = Frame::new(SCREEN_WIDTH, SCREEN_HEIGHT, PixelEncoding::RGBA); + self.frame_sender.add(system.clock, frame); - Ok(ClockDuration::from_millis(1)) + Ok(ClockDuration::from_micros(100)) } } @@ -98,21 +83,20 @@ fn main() { moa_minifb::run(matches, |host| { let mut system = System::default(); - let queue = FrameQueue::new(384, 128); - let (sender, receiver) = mpsc::channel(); - let control = wrap_transmutable(SynthControl::new(queue.clone(), receiver)); + let (frame_sender, frame_receiver) = host::frame_queue(SCREEN_WIDTH, SCREEN_HEIGHT); + let (key_sender, key_receiver) = host::event_queue(); + let control = wrap_transmutable(SynthControl::new(key_receiver, frame_sender)); system.add_device("control", control)?; - let ym_sound = wrap_transmutable(Ym2612::create(host, Frequency::from_hz(7_670_454))?); + let ym_sound = wrap_transmutable(Ym2612::new(host, Frequency::from_hz(7_670_454))?); initialize_ym(ym_sound.clone())?; system.add_addressable_device(0x00, ym_sound)?; - let sn_sound = wrap_transmutable(Sn76489::create(host, Frequency::from_hz(3_579_545))?); + let sn_sound = wrap_transmutable(Sn76489::new(host, Frequency::from_hz(3_579_545))?); system.add_addressable_device(0x10, sn_sound)?; - host.add_window(Box::new(queue))?; - host.register_keyboard(Box::new(SynthControlsUpdater(sender)))?; - //host.register_mouse(Box::new(SynthControlsUpdater(sender)))?; + host.add_video_source(frame_receiver)?; + host.register_keyboard(key_sender)?; Ok(system) }); diff --git a/emulator/frontends/minifb/src/controllers.rs b/emulator/frontends/minifb/src/controllers.rs index 00060f9..ce56b3f 100644 --- a/emulator/frontends/minifb/src/controllers.rs +++ b/emulator/frontends/minifb/src/controllers.rs @@ -1,18 +1,18 @@ use minifb::Key as MiniKey; -use moa_core::host::ControllerEvent; +use moa_core::host::ControllerInput; -pub fn map_controller_a(key: MiniKey, state: bool) -> Option { +pub fn map_controller_a(key: MiniKey, state: bool) -> Option { match key { - MiniKey::A => { Some(ControllerEvent::ButtonA(state)) }, - MiniKey::O => { Some(ControllerEvent::ButtonB(state)) }, - MiniKey::E => { Some(ControllerEvent::ButtonC(state)) }, - MiniKey::Up => { Some(ControllerEvent::DpadUp(state)) }, - MiniKey::Down => { Some(ControllerEvent::DpadDown(state)) }, - MiniKey::Left => { Some(ControllerEvent::DpadLeft(state)) }, - MiniKey::Right => { Some(ControllerEvent::DpadRight(state)) }, - MiniKey::Enter => { Some(ControllerEvent::Start(state)) }, - MiniKey::M => { Some(ControllerEvent::Mode(state)) }, + MiniKey::A => { Some(ControllerInput::ButtonA(state)) }, + MiniKey::O => { Some(ControllerInput::ButtonB(state)) }, + MiniKey::E => { Some(ControllerInput::ButtonC(state)) }, + MiniKey::Up => { Some(ControllerInput::DpadUp(state)) }, + MiniKey::Down => { Some(ControllerInput::DpadDown(state)) }, + MiniKey::Left => { Some(ControllerInput::DpadLeft(state)) }, + MiniKey::Right => { Some(ControllerInput::DpadRight(state)) }, + MiniKey::Enter => { Some(ControllerInput::Start(state)) }, + MiniKey::M => { Some(ControllerInput::Mode(state)) }, _ => None, } } diff --git a/emulator/frontends/minifb/src/lib.rs b/emulator/frontends/minifb/src/lib.rs index ffaeb3d..9ca63c5 100644 --- a/emulator/frontends/minifb/src/lib.rs +++ b/emulator/frontends/minifb/src/lib.rs @@ -8,7 +8,7 @@ use minifb::{self, Key, MouseMode, MouseButton}; use clap::{App, Arg, ArgMatches}; use moa_core::{System, Error, ClockDuration}; -use moa_core::host::{Host, ControllerUpdater, KeyboardUpdater, KeyEvent, MouseUpdater, MouseState, Audio, ControllerDevice, PixelEncoding, Frame, FrameReceiver}; +use moa_core::host::{Host, Audio, KeyEvent, MouseEvent, MouseState, ControllerDevice, ControllerEvent, EventSender, PixelEncoding, Frame, FrameReceiver}; use moa_common::{AudioMixer, AudioSource}; use moa_common::CpalAudioOutput; @@ -96,9 +96,9 @@ fn wait_until_initialized(frontend: Arc>) { pub struct MiniFrontendBuilder { video: Option, - controller: Option>, - keyboard: Option>, - mouse: Option>, + controllers: Option>, + keyboard: Option>, + mouse: Option>, mixer: Option>>, finalized: bool, } @@ -107,7 +107,7 @@ impl Default for MiniFrontendBuilder { fn default() -> Self { Self { video: None, - controller: None, + controllers: None, keyboard: None, mouse: None, mixer: Some(AudioMixer::with_default_rate()), @@ -123,11 +123,11 @@ impl MiniFrontendBuilder { pub fn build(&mut self) -> MiniFrontend { let video = std::mem::take(&mut self.video); - let controller = std::mem::take(&mut self.controller); + let controllers = std::mem::take(&mut self.controllers); let keyboard = std::mem::take(&mut self.keyboard); let mouse = std::mem::take(&mut self.mouse); let mixer = std::mem::take(&mut self.mixer); - MiniFrontend::new(video, controller, keyboard, mouse, mixer.unwrap()) + MiniFrontend::new(video, controllers, keyboard, mouse, mixer.unwrap()) } } @@ -145,31 +145,27 @@ impl Host for MiniFrontendBuilder { Ok(Box::new(source)) } - fn register_controller(&mut self, device: ControllerDevice, input: Box) -> Result<(), Error> { - if device != ControllerDevice::A { - return Ok(()) - } - - if self.controller.is_some() { + fn register_controllers(&mut self, sender: EventSender) -> Result<(), Error> { + if self.controllers.is_some() { return Err(Error::new("A controller updater has already been registered with the frontend")); } - self.controller = Some(input); + self.controllers = Some(sender); Ok(()) } - fn register_keyboard(&mut self, input: Box) -> Result<(), Error> { + fn register_keyboard(&mut self, sender: EventSender) -> Result<(), Error> { if self.keyboard.is_some() { return Err(Error::new("A keyboard updater has already been registered with the frontend")); } - self.keyboard = Some(input); + self.keyboard = Some(sender); Ok(()) } - fn register_mouse(&mut self, input: Box) -> Result<(), Error> { + fn register_mouse(&mut self, sender: EventSender) -> 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); + self.mouse = Some(sender); Ok(()) } } @@ -179,9 +175,9 @@ pub struct MiniFrontend { pub modifiers: u16, pub mouse_state: MouseState, pub video: Option, - pub controller: Option>, - pub keyboard: Option>, - pub mouse: Option>, + pub controllers: Option>, + pub keyboard: Option>, + pub mouse: Option>, pub audio: Option, pub mixer: Arc>, } @@ -189,16 +185,16 @@ pub struct MiniFrontend { impl MiniFrontend { pub fn new( video: Option, - controller: Option>, - keyboard: Option>, - mouse: Option>, + controllers: Option>, + keyboard: Option>, + mouse: Option>, mixer: Arc>, ) -> Self { Self { modifiers: 0, mouse_state: Default::default(), video, - controller, + controllers, keyboard, mouse, audio: None, @@ -218,7 +214,7 @@ impl MiniFrontend { } } - if matches.occurrences_of("disable-audio") == 0 { + if self.mixer.lock().unwrap().num_sources() != 0 && matches.occurrences_of("disable-audio") == 0 { self.audio = Some(CpalAudioOutput::create_audio_output(self.mixer.lock().unwrap().get_sink())); } @@ -292,7 +288,7 @@ impl MiniFrontend { } } - if let Some(updater) = self.mouse.as_mut() { + if let Some(sender) = 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); @@ -302,7 +298,7 @@ impl MiniFrontend { self.mouse_state .to_events(next_state) .into_iter() - .for_each(|event| updater.update_mouse(event)); + .for_each(|event| sender.send(event)); } } @@ -316,13 +312,14 @@ impl MiniFrontend { } fn check_key(&mut self, key: Key, state: bool) { - if let Some(updater) = self.keyboard.as_mut() { - updater.update_keyboard(KeyEvent::new(map_key(key), state)); + if let Some(sender) = self.keyboard.as_mut() { + sender.send(KeyEvent::new(map_key(key), state)); } - if let Some(updater) = self.controller.as_mut() { - if let Some(event) = map_controller_a(key, state) { - updater.update_controller(event); + if let Some(sender) = self.controllers.as_mut() { + if let Some(input) = map_controller_a(key, state) { + let event = ControllerEvent::new(ControllerDevice::A, input); + sender.send(event); } } } diff --git a/emulator/frontends/pixels/assets/moa-genesis/index.html b/emulator/frontends/pixels/assets/moa-genesis/index.html index 23a1655..0c4b22c 100644 --- a/emulator/frontends/pixels/assets/moa-genesis/index.html +++ b/emulator/frontends/pixels/assets/moa-genesis/index.html @@ -1,24 +1,19 @@ + Sega Genesis - Moa - Sega Genesis - Moa
- + + - - - +
@@ -27,7 +22,19 @@
- + +
+
+ + + + + + + + + +
diff --git a/emulator/frontends/pixels/assets/moa-genesis/interface.js b/emulator/frontends/pixels/assets/moa-genesis/interface.js index 1ddc658..1e48afe 100644 --- a/emulator/frontends/pixels/assets/moa-genesis/interface.js +++ b/emulator/frontends/pixels/assets/moa-genesis/interface.js @@ -5,6 +5,7 @@ function initialize_emulator() { let host = Emulator.new_host(); let system = Emulator.load_system(host, Emulator.get_load_system_fn()); + //Emulator.start_system(system); let last_update = performance.now(); setTimeout(function refreshFrame() { let current = performance.now(); @@ -22,6 +23,12 @@ function initialize_emulator() { Emulator.host_run_loop(host); } +// Update the frame rate display +var frame_rate_el = document.getElementById("frame-rate"); +var frame_rate = setInterval(function () { + frame_rate_el.value = Emulator.get_frames_since(); +}, 1000); + window.addEventListener("load", () => { Emulator.default(); }); @@ -55,14 +62,31 @@ document.getElementById("power").addEventListener("click", () => { initialize_emulator(); }); -document.getElementById("speed").addEventListener("change", (e) => { - document.getElementById("video").focus(); - Emulator.set_speed(e.target.value); +var mute_state = false; +var mute = document.getElementById("mute"); +mute.addEventListener("click", () => { + mute_state = !mute_state; + if (mute_state) { + mute.value = "\uD83D\uDD07"; + } else { + mute.value = "\uD83D\uDD08"; + } + Emulator.set_mute(mute_state); }); -// Update the frame rate display -var frame_rate_el = document.getElementById("frame-rate"); -var frame_rate = setInterval(function () { - frame_rate_el.value = Emulator.get_frames_since(); -}, 1000); +function button_event(e) { + var state; + if (e.type == 'mousedown' || e.type == 'touchstart') { + state = true; + } else { + state = false; + } + Emulator.button_press(e.target.name, state); +} +document.getElementById("controller").querySelectorAll('button').forEach(function (button) { + button.addEventListener('mousedown', button_event); + button.addEventListener('mouseup', button_event); + button.addEventListener('touchstart', button_event); + button.addEventListener('touchend', button_event); +}); diff --git a/emulator/frontends/pixels/assets/moa-genesis/style.css b/emulator/frontends/pixels/assets/moa-genesis/style.css index 4ba1de8..12a8ad3 100644 --- a/emulator/frontends/pixels/assets/moa-genesis/style.css +++ b/emulator/frontends/pixels/assets/moa-genesis/style.css @@ -1,4 +1,5 @@ body { + font-family: sans; background-color: #000; margin: 0; overflow: hidden; @@ -19,3 +20,16 @@ body { color: #DDD; background-color: #000; } + +#mute { + cursor: pointer; +} + +#controller .updown { + overflow: hidden; +} + +#controller button[name="left"] { + +} + diff --git a/emulator/frontends/pixels/src/frontend.rs b/emulator/frontends/pixels/src/frontend.rs index 42bba1a..ca5ed56 100644 --- a/emulator/frontends/pixels/src/frontend.rs +++ b/emulator/frontends/pixels/src/frontend.rs @@ -7,7 +7,7 @@ use winit::event::{Event, VirtualKeyCode, WindowEvent, ElementState}; use winit::event_loop::{ControlFlow, EventLoop}; use moa_core::{System, Error}; -use moa_core::host::{Host, PixelEncoding, Frame, ControllerDevice, ControllerEvent, ControllerUpdater, Audio, DummyAudio, FrameReceiver}; +use moa_core::host::{Host, PixelEncoding, Frame, ControllerDevice, ControllerInput, ControllerEvent, EventSender, Audio, DummyAudio, FrameReceiver}; use moa_common::{AudioMixer, AudioSource, CpalAudioOutput}; use crate::settings; @@ -21,22 +21,22 @@ pub type LoadSystemFn = fn (&mut PixelsFrontend, Vec) -> Result, - controller: Option>, - //mixer: Arc>, - //audio_output: CpalAudioOutput, + controllers: Option>, + mixer: Arc>, + audio_output: CpalAudioOutput, } impl PixelsFrontend { pub fn new() -> PixelsFrontend { settings::get().run = true; - //let mixer = AudioMixer::with_default_rate(); - //let audio_output = CpalAudioOutput::create_audio_output(mixer.lock().unwrap().get_sink()); + let mixer = AudioMixer::with_default_rate(); + let audio_output = CpalAudioOutput::create_audio_output(mixer.lock().unwrap().get_sink()); PixelsFrontend { video: None, - controller: None, - //mixer, - //audio_output, + controllers: None, + mixer, + audio_output, } } } @@ -47,19 +47,15 @@ impl Host for PixelsFrontend { Ok(()) } - fn register_controller(&mut self, device: ControllerDevice, input: Box) -> Result<(), Error> { - if device != ControllerDevice::A { - return Ok(()) - } - - self.controller = Some(input); + fn register_controllers(&mut self, sender: EventSender) -> Result<(), Error> { + self.controllers = Some(sender); Ok(()) } fn add_audio_source(&mut self) -> Result, Error> { - //let source = AudioSource::new(self.mixer.clone()); - //Ok(Box::new(source)) - Ok(Box::new(DummyAudio())) + let source = AudioSource::new(self.mixer.clone()); + Ok(Box::new(source)) + //Ok(Box::new(DummyAudio())) } } @@ -68,8 +64,8 @@ pub async fn run_loop(host: PixelsFrontend) { let window = create_window(&event_loop); - if let Some(recevier) = host.video.as_ref() { - recevier.request_encoding(PixelEncoding::ABGR); + if let Some(receiver) = host.video.as_ref() { + receiver.request_encoding(PixelEncoding::ABGR); } let mut pixels = { @@ -81,6 +77,7 @@ pub async fn run_loop(host: PixelsFrontend) { .expect("Pixels error") }; + let mut mute = false; let mut last_size = (WIDTH, HEIGHT); let mut last_frame = Frame::new(WIDTH, HEIGHT, PixelEncoding::ABGR); //let mut update_timer = Instant::now(); @@ -131,14 +128,22 @@ pub async fn run_loop(host: PixelsFrontend) { } }; - if let Some(updater) = host.controller.as_ref() { + if let Some(sender) = host.controllers.as_ref() { if let Some(key) = key { - updater.update_controller(key); + let event = ControllerEvent::new(ControllerDevice::A, key); + sender.send(event); } } } } + let requested_mute = settings::get().mute; + if requested_mute != mute { + mute = requested_mute; + host.audio_output.set_mute(mute); + log::info!("setting mute to {}", mute); + } + // Check if the run flag is no longer true, and exit the loop if !settings::get().run { // Clear the screen @@ -161,17 +166,17 @@ pub async fn run_loop(host: PixelsFrontend) { }); } -pub fn map_controller_a(key: VirtualKeyCode, state: bool) -> Option { +pub fn map_controller_a(key: VirtualKeyCode, state: bool) -> Option { match key { - VirtualKeyCode::A => { Some(ControllerEvent::ButtonA(state)) }, - VirtualKeyCode::S => { Some(ControllerEvent::ButtonB(state)) }, - VirtualKeyCode::D => { Some(ControllerEvent::ButtonC(state)) }, - VirtualKeyCode::Up => { Some(ControllerEvent::DpadUp(state)) }, - VirtualKeyCode::Down => { Some(ControllerEvent::DpadDown(state)) }, - VirtualKeyCode::Left => { Some(ControllerEvent::DpadLeft(state)) }, - VirtualKeyCode::Right => { Some(ControllerEvent::DpadRight(state)) }, - VirtualKeyCode::Return => { Some(ControllerEvent::Start(state)) }, - VirtualKeyCode::M => { Some(ControllerEvent::Mode(state)) }, + VirtualKeyCode::A => { Some(ControllerInput::ButtonA(state)) }, + VirtualKeyCode::S => { Some(ControllerInput::ButtonB(state)) }, + VirtualKeyCode::D => { Some(ControllerInput::ButtonC(state)) }, + VirtualKeyCode::Up => { Some(ControllerInput::DpadUp(state)) }, + VirtualKeyCode::Down => { Some(ControllerInput::DpadDown(state)) }, + VirtualKeyCode::Left => { Some(ControllerInput::DpadLeft(state)) }, + VirtualKeyCode::Right => { Some(ControllerInput::DpadRight(state)) }, + VirtualKeyCode::Return => { Some(ControllerInput::Start(state)) }, + VirtualKeyCode::M => { Some(ControllerInput::Mode(state)) }, _ => None, } } diff --git a/emulator/frontends/pixels/src/settings.rs b/emulator/frontends/pixels/src/settings.rs index d77fa7a..1fe5e89 100644 --- a/emulator/frontends/pixels/src/settings.rs +++ b/emulator/frontends/pixels/src/settings.rs @@ -7,7 +7,9 @@ pub struct EmulatorSettings { pub rom_data: Vec, pub run: bool, pub speed: f32, + pub size: (u32, u32), pub frames_since: usize, + pub mute: bool, } impl EmulatorSettings { @@ -16,7 +18,9 @@ impl EmulatorSettings { rom_data: vec![], run: false, speed: 4.0, + size: (640, 448), frames_since: 0, + mute: false, } } } @@ -29,6 +33,10 @@ pub fn set_rom_data(rom_data: Vec) { get().rom_data = rom_data; } +pub fn set_size(width: u32, height: u32) { + get().size = (width, height); +} + pub fn get_frames_since() -> usize { let mut options = get(); let frames_since = options.frames_since; diff --git a/emulator/frontends/pixels/src/web.rs b/emulator/frontends/pixels/src/web.rs index f19e1c2..ec7e2fb 100644 --- a/emulator/frontends/pixels/src/web.rs +++ b/emulator/frontends/pixels/src/web.rs @@ -1,12 +1,17 @@ #![cfg(target_arch = "wasm32")] use std::rc::Rc; +use std::cell::RefCell; use instant::Instant; use wasm_bindgen::prelude::*; use winit::dpi::LogicalSize; use winit::event_loop::EventLoop; use winit::window::{Window, WindowBuilder}; +use web_sys::Event; +use wasm_bindgen::JsCast; +use wasm_bindgen::closure::Closure; + use moa_core::{ClockDuration, System}; use crate::settings; @@ -26,6 +31,11 @@ pub fn set_rom_data(rom_data: Vec) { settings::set_rom_data(rom_data); } +#[wasm_bindgen] +pub fn set_size(width: u32, height: u32) { + settings::set_size(width, height); +} + #[wasm_bindgen] pub fn request_stop() { settings::request_stop(); @@ -56,6 +66,17 @@ pub fn get_frames_since() -> usize { settings::get_frames_since() } +#[wasm_bindgen] +pub fn set_mute(mute: bool) { + settings::get().mute = mute; +} + +#[wasm_bindgen] +pub fn button_press(name: String, state: bool) { + +} + + #[wasm_bindgen] pub struct HostHandle(PixelsFrontend); @@ -115,7 +136,7 @@ pub fn create_window(event_loop: &EventLoop) -> Rc { let size = LogicalSize::new(frontend::WIDTH as f64, frontend::HEIGHT as f64); WindowBuilder::new() .with_canvas(Some(canvas)) - .with_title("Hello Pixels + Web") + .with_title("Moa Emulator") //.with_inner_size(size) //.with_min_inner_size(size) .build(event_loop) @@ -171,6 +192,24 @@ pub fn create_window(event_loop: &EventLoop) -> Rc { } */ + /* + fn ycombinator(f: &F) + where + F: Fn(&F) + { + f(f) + } + + + let closure: Closure = Closure::new(move |_e: Event| { + + }); + client_window + .set_interval_with_callback_and_timeout_and_arguments_0(closure.as_ref().unchecked_ref(), 17) + .unwrap(); + closure.forget(); + */ + /* let mut update_timer = Instant::now(); let mut system = load(&mut host, settings::get().rom_data.clone()).unwrap(); @@ -196,6 +235,64 @@ pub fn create_window(event_loop: &EventLoop) -> Rc { closure.forget(); */ + window } + +#[wasm_bindgen] +pub fn start_system(handle: SystemHandle) -> Handle { + let emulator = Emulator::new(handle.0); + set_timeout(emulator.clone(), 17); + Handle(emulator) +} + +#[wasm_bindgen] +pub struct Handle(Rc>); + +pub struct Emulator { + running: bool, + //frontend: PixelsFrontend, + system: System, +} + +impl Emulator { + pub fn new(system: System) -> Rc> { + Rc::new(RefCell::new(Self { + running: false, + system, + })) + } + + pub fn stop(&mut self) { + self.running = false; + } +} + +fn update(emulator: Rc>) { + let run_timer = Instant::now(); + let nanoseconds_per_frame = (16_600_000 as f32 * settings::get().speed) as u64; + if let Err(err) = emulator.borrow_mut().system.run_for(ClockDuration::from_nanos(nanoseconds_per_frame)) { + log::error!("{:?}", err); + } + log::info!("ran simulation for {:?}ms in {:?}ms", nanoseconds_per_frame / 1_000_000, run_timer.elapsed().as_millis()); + + let running = emulator.borrow().running; + if running { + set_timeout(emulator, 17); + } +} + +fn set_timeout(emulator: Rc>, timeout: i32) { + emulator.borrow_mut().running = true; + let closure: Closure = Closure::new(move |_e: Event| { + update(emulator.clone()); + }); + + let client_window = web_sys::window().unwrap(); + client_window + .set_timeout_with_callback_and_timeout_and_arguments_0(closure.as_ref().unchecked_ref(), timeout) + .unwrap(); + closure.forget(); +} + diff --git a/emulator/peripherals/yamaha/src/ym2612.rs b/emulator/peripherals/yamaha/src/ym2612.rs index f0de109..22a3143 100644 --- a/emulator/peripherals/yamaha/src/ym2612.rs +++ b/emulator/peripherals/yamaha/src/ym2612.rs @@ -249,8 +249,7 @@ impl EnvelopeGenerator { calculate_rate(self.rates[etype as usize], rate_adjust) } - fn notify_key_change(&mut self, state: bool, envelope_clock: EnvelopeClock, rate_adjust: usize) { - + fn notify_key_change(&mut self, state: bool, _envelope_clock: EnvelopeClock, rate_adjust: usize) { if state { let rate = self.get_scaled_rate(EnvelopeState::Attack, rate_adjust); if rate < 62 { @@ -262,9 +261,6 @@ impl EnvelopeGenerator { } else { self.envelope_state = EnvelopeState::Release; } -//if self.debug_name == "ch 2, op 1" { -//println!("change: {} {:?} {}", state, self.envelope_state, self.envelope); -//} } fn update_envelope(&mut self, envelope_clock: EnvelopeClock, rate_adjust: usize) { @@ -403,7 +399,7 @@ impl PhaseGenerator { let increment = if self.block == 0 { increment >> 1 } else { - increment << self.block - 1 + increment << (self.block - 1) }; // Apply detune @@ -539,7 +535,7 @@ impl Operator { // If the original phase was in the negative portion, invert the output // since the sine wave's second half is a mirror of the first half if mod_phase & 0x200 != 0 { - output = output * -1; + output *= -1; } // The output is now represented with a 16-bit signed number in the range of -0x1FFF and 0x1FFF diff --git a/emulator/systems/genesis/src/peripherals/controllers.rs b/emulator/systems/genesis/src/peripherals/controllers.rs index bc68151..504441f 100644 --- a/emulator/systems/genesis/src/peripherals/controllers.rs +++ b/emulator/systems/genesis/src/peripherals/controllers.rs @@ -1,10 +1,7 @@ -use std::sync::Arc; -use std::sync::atomic::{AtomicBool, AtomicU16, Ordering}; - use moa_core::{warn, info}; use moa_core::{System, Error, ClockTime, ClockDuration, Address, Addressable, Steppable, Transmutable}; -use moa_core::host::{Host, ControllerUpdater, HostData, ControllerDevice, ControllerEvent}; +use moa_core::host::{self, Host, HostData, ControllerDevice, ControllerInput, ControllerEvent, EventReceiver}; const REG_VERSION: Address = 0x01; @@ -25,7 +22,7 @@ pub struct GenesisControllerPort { /// Data contains bits: /// 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 /// X | Y | Z | MODE | START | A | C | B | RIGHT | LEFT | DOWN | UP - buttons: Arc, + buttons: u16, ctrl: u8, outputs: u8, @@ -37,7 +34,7 @@ pub struct GenesisControllerPort { impl Default for GenesisControllerPort { fn default() -> Self { Self { - buttons: Arc::new(AtomicU16::new(0xffff)), + buttons: 0xffff, ctrl: 0, outputs: 0, th_count: 0, @@ -48,7 +45,7 @@ impl Default for GenesisControllerPort { impl GenesisControllerPort { pub fn get_data(&mut self) -> u8 { - let inputs = self.buttons.load(Ordering::Relaxed); + let inputs = self.buttons; let th_state = (self.outputs & 0x40) != 0; match (th_state, self.th_count) { @@ -88,70 +85,60 @@ impl GenesisControllerPort { } } -pub struct GenesisControllersUpdater(Arc, Arc); - -impl ControllerUpdater for GenesisControllersUpdater { - fn update_controller(&self, event: ControllerEvent) { - let (mask, state) = match event { - ControllerEvent::ButtonA(state) => (0x0040, state), - ControllerEvent::ButtonB(state) => (0x0010, state), - ControllerEvent::ButtonC(state) => (0x0020, state), - ControllerEvent::DpadUp(state) => (0x0001, state), - ControllerEvent::DpadDown(state) => (0x0002, state), - ControllerEvent::DpadLeft(state) => (0x0004, state), - ControllerEvent::DpadRight(state) => (0x0008, state), - ControllerEvent::Start(state) => (0x0080, state), - ControllerEvent::Mode(state) => (0x0100, state), - _ => (0x0000, false), - }; - - let buttons = (self.0.load(Ordering::Acquire) & !mask) | (if !state { mask } else { 0 }); - self.0.store(buttons, Ordering::Release); - if buttons != 0 { - self.1.store(true, Ordering::Release); - } - } -} - - pub struct GenesisControllers { + receiver: EventReceiver, port_1: GenesisControllerPort, port_2: GenesisControllerPort, expansion: GenesisControllerPort, - has_changed: Arc, interrupt: HostData, reset_timer: ClockDuration, } -impl Default for GenesisControllers { - fn default() -> Self { - Self { +impl GenesisControllers { + pub fn new(host: &mut H) -> Result { + let (sender, receiver) = host::event_queue(); + host.register_controllers(sender)?; + + Ok(Self { + receiver, port_1: GenesisControllerPort::default(), port_2: GenesisControllerPort::default(), expansion: GenesisControllerPort::default(), - has_changed: Arc::new(AtomicBool::new(false)), interrupt: HostData::new(false), reset_timer: ClockDuration::ZERO, - } - } -} - -impl GenesisControllers { - pub fn new(host: &mut H) -> Result { - let controller = GenesisControllers::default(); - - let controller1 = Box::new(GenesisControllersUpdater(controller.port_1.buttons.clone(), controller.has_changed.clone())); - host.register_controller(ControllerDevice::A, controller1)?; - let controller2 = Box::new(GenesisControllersUpdater(controller.port_2.buttons.clone(), controller.has_changed.clone())); - host.register_controller(ControllerDevice::B, controller2)?; - - Ok(controller) + }) } pub fn get_interrupt_signal(&self) -> HostData { self.interrupt.clone() } + + fn process_event(&mut self, event: ControllerEvent) { + let (mask, state) = match event.input { + ControllerInput::ButtonA(state) => (0x0040, state), + ControllerInput::ButtonB(state) => (0x0010, state), + ControllerInput::ButtonC(state) => (0x0020, state), + ControllerInput::DpadUp(state) => (0x0001, state), + ControllerInput::DpadDown(state) => (0x0002, state), + ControllerInput::DpadLeft(state) => (0x0004, state), + ControllerInput::DpadRight(state) => (0x0008, state), + ControllerInput::Start(state) => (0x0080, state), + ControllerInput::Mode(state) => (0x0100, state), + _ => (0x0000, false), + }; + + let prev_state = match event.device { + ControllerDevice::A => &mut self.port_1.buttons, + ControllerDevice::B => &mut self.port_2.buttons, + _ => return, + }; + + *prev_state = (*prev_state & !mask) | (if !state { mask } else { 0 }); + if *prev_state != 0 { + self.interrupt.set(true); + } + } } impl Addressable for GenesisControllers { @@ -208,9 +195,8 @@ impl Steppable for GenesisControllers { fn step(&mut self, _system: &System) -> Result { let duration = ClockDuration::from_micros(100); // Update every 100us - if self.has_changed.load(Ordering::Acquire) { - self.has_changed.store(false, Ordering::Release); - self.interrupt.set(true); + while let Some(event) = self.receiver.receive() { + self.process_event(event); } self.reset_timer += duration; diff --git a/emulator/systems/trs80/src/peripherals/model1.rs b/emulator/systems/trs80/src/peripherals/model1.rs index 2ce5069..59730aa 100644 --- a/emulator/systems/trs80/src/peripherals/model1.rs +++ b/emulator/systems/trs80/src/peripherals/model1.rs @@ -1,8 +1,6 @@ -use std::sync::{Arc, Mutex}; - use moa_core::{System, Error, ClockTime, ClockDuration, Address, Addressable, Steppable, Transmutable, debug, warn}; -use moa_core::host::{self, Host, Frame, FrameSender, KeyboardUpdater, KeyEvent}; +use moa_core::host::{self, Host, Frame, FrameSender, KeyEvent, EventReceiver}; use super::keymap; use super::charset::CharacterGenerator; @@ -11,38 +9,94 @@ use super::charset::CharacterGenerator; const DEV_NAME: &str = "model1"; const SCREEN_SIZE: (u32, u32) = (384, 128); -pub struct Model1Peripherals { + +pub struct Model1Keyboard { + receiver: EventReceiver, + keyboard_mem: [u8; 8], +} + +impl Model1Keyboard { + pub fn new(host: &mut H) -> Result { + let (sender, receiver) = host::event_queue(); + host.register_keyboard(sender)?; + + Ok(Self { + receiver, + keyboard_mem: [0; 8], + }) + } +} + +impl Addressable for Model1Keyboard { + fn len(&self) -> usize { + 0x420 + } + + fn read(&mut self, _clock: ClockTime, addr: Address, data: &mut [u8]) -> Result<(), Error> { + if (0x20..=0xA0).contains(&addr) { + let offset = addr - 0x20; + data[0] = 0; + if (offset & 0x01) != 0 { data[0] |= self.keyboard_mem[0]; } + if (offset & 0x02) != 0 { data[0] |= self.keyboard_mem[1]; } + if (offset & 0x04) != 0 { data[0] |= self.keyboard_mem[2]; } + if (offset & 0x08) != 0 { data[0] |= self.keyboard_mem[3]; } + if (offset & 0x10) != 0 { data[0] |= self.keyboard_mem[4]; } + if (offset & 0x20) != 0 { data[0] |= self.keyboard_mem[5]; } + if (offset & 0x40) != 0 { data[0] |= self.keyboard_mem[6]; } + if (offset & 0x80) != 0 { data[0] |= self.keyboard_mem[7]; } + //info!("{}: read from keyboard {:x} of {:?}", DEV_NAME, addr, data); + } else { + warn!("{}: !!! unhandled read from {:0x}", DEV_NAME, addr); + } + debug!("{}: read from register {:x} of {:?}", DEV_NAME, addr, data); + Ok(()) + } + + fn write(&mut self, _clock: ClockTime, addr: Address, data: &[u8]) -> Result<(), Error> { + warn!("{}: !!! unhandled write {:0x} to {:0x}", DEV_NAME, data[0], addr); + Ok(()) + } +} + +impl Steppable for Model1Keyboard { + fn step(&mut self, _system: &System) -> Result { + while let Some(event) = self.receiver.receive() { + keymap::record_key_press(&mut self.keyboard_mem, event.key, event.state); + } + + Ok(ClockDuration::from_millis(1)) + } +} + +impl Transmutable for Model1Keyboard { + fn as_addressable(&mut self) -> Option<&mut dyn Addressable> { + Some(self) + } + + fn as_steppable(&mut self) -> Option<&mut dyn Steppable> { + Some(self) + } +} + +pub struct Model1Video { frame_sender: FrameSender, - keyboard_mem: Arc>, video_mem: [u8; 1024], } -impl Model1Peripherals { +impl Model1Video { pub fn new(host: &mut H) -> Result { let (frame_sender, frame_receiver) = host::frame_queue(SCREEN_SIZE.0, SCREEN_SIZE.1); - let keyboard_mem = Arc::new(Mutex::new([0; 8])); host.add_video_source(frame_receiver)?; - host.register_keyboard(Box::new(Model1KeyboardUpdater(keyboard_mem.clone())))?; Ok(Self { frame_sender, - keyboard_mem, video_mem: [0x20; 1024], }) } } -pub struct Model1KeyboardUpdater(Arc>); - -impl KeyboardUpdater for Model1KeyboardUpdater { - fn update_keyboard(&self, event: KeyEvent) { - println!(">>> {:?}", event.key); - keymap::record_key_press(&mut self.0.lock().unwrap(), event.key, event.state); - } -} - -impl Steppable for Model1Peripherals { +impl Steppable for Model1Video { fn step(&mut self, system: &System) -> Result { let mut frame = Frame::new(SCREEN_SIZE.0, SCREEN_SIZE.1, self.frame_sender.encoding()); for y in 0..16 { @@ -58,45 +112,25 @@ impl Steppable for Model1Peripherals { } } -impl Addressable for Model1Peripherals { +impl Addressable for Model1Video { fn len(&self) -> usize { - 0x820 + 0x400 } fn read(&mut self, _clock: ClockTime, addr: Address, data: &mut [u8]) -> Result<(), Error> { - if (0x20..=0xA0).contains(&addr) { - let offset = addr - 0x20; - data[0] = 0; - if (offset & 0x01) != 0 { data[0] |= self.keyboard_mem.lock().unwrap()[0]; } - if (offset & 0x02) != 0 { data[0] |= self.keyboard_mem.lock().unwrap()[1]; } - if (offset & 0x04) != 0 { data[0] |= self.keyboard_mem.lock().unwrap()[2]; } - if (offset & 0x08) != 0 { data[0] |= self.keyboard_mem.lock().unwrap()[3]; } - if (offset & 0x10) != 0 { data[0] |= self.keyboard_mem.lock().unwrap()[4]; } - if (offset & 0x20) != 0 { data[0] |= self.keyboard_mem.lock().unwrap()[5]; } - if (offset & 0x40) != 0 { data[0] |= self.keyboard_mem.lock().unwrap()[6]; } - if (offset & 0x80) != 0 { data[0] |= self.keyboard_mem.lock().unwrap()[7]; } - //info!("{}: read from keyboard {:x} of {:?}", DEV_NAME, addr, data); - } else if (0x420..=0x820).contains(&addr) { - data[0] = self.video_mem[addr as usize - 0x420]; - } else { - warn!("{}: !!! unhandled read from {:0x}", DEV_NAME, addr); - } + data[0] = self.video_mem[addr as usize]; debug!("{}: read from register {:x} of {:?}", DEV_NAME, addr, data); Ok(()) } fn write(&mut self, _clock: ClockTime, addr: Address, data: &[u8]) -> Result<(), Error> { debug!("{}: write to register {:x} with {:x}", DEV_NAME, addr, data[0]); - if (0x420..0x820).contains(&addr) { - self.video_mem[addr as usize - 0x420] = data[0]; - } else { - warn!("{}: !!! unhandled write {:0x} to {:0x}", DEV_NAME, data[0], addr); - } + self.video_mem[addr as usize] = data[0]; Ok(()) } } -impl Transmutable for Model1Peripherals { +impl Transmutable for Model1Video { fn as_addressable(&mut self) -> Option<&mut dyn Addressable> { Some(self) } diff --git a/emulator/systems/trs80/src/system.rs b/emulator/systems/trs80/src/system.rs index 56b3113..b76ddf4 100644 --- a/emulator/systems/trs80/src/system.rs +++ b/emulator/systems/trs80/src/system.rs @@ -4,7 +4,7 @@ use moa_core::host::Host; use moa_z80::{Z80, Z80Type}; -use crate::peripherals::model1::Model1Peripherals; +use crate::peripherals::model1::{Model1Keyboard, Model1Video}; pub struct Trs80Options { @@ -37,8 +37,10 @@ pub fn build_trs80(host: &mut H, options: Trs80Options) -> Result