Removed WindowUpdater and BlitableSurface

This commit is contained in:
transistor 2023-05-03 22:42:24 -07:00
parent 8a8bcb2277
commit 3471eb4e8c
18 changed files with 200 additions and 150 deletions

View File

@ -387,3 +387,17 @@ General Work
- now it actually sounds pretty close for the high pitch tones, but the base tones are completely
missing
2023-05-03
- it still doesn't sound quite right but it's much better. The drum sounds are provided by the DAC
which explains why they aren't working properly. The DAC is not synchronized to the FM output
because the fm output is generated in 1 millisecond batches, so I'll have to change that
- one of the issues that came up was that in C code that has the attack calculation, the same as
detailed on page 28 of the Nemesis forum posts, the result is different. It turns out C is using
arithmetic shift right instead of logical shift right, despite the types of the inputs being
unsigned explicitly, however in rust, it will use the appropriate operation based on the sign of
the strongly typed numbers, so unsigned shift right will insert 0s in the upper bits, which makes
the number result be bigger than the previous version, so the attack phase ends on the first pass.
Forcing the numbers to be signed for the shift in Rust makes it sign-extend the number (insert 1s
because the upper bit is 1), so the number is negative. Even an unsigned addition will result in
the correct number at the end. It's just that shift that caused the issue

View File

@ -1,8 +1,8 @@
use std::sync::{Arc, Mutex};
use crate::host::traits::{BlitableSurface, ClockedQueue, WindowUpdater};
use crate::ClockTime;
use crate::Error;
use crate::ClockTime;
use crate::host::traits::ClockedQueue;
pub const MASK_COLOUR: u32 = 0xFFFFFFFF;
@ -62,16 +62,15 @@ impl Frame {
pub fn new_shared(width: u32, height: u32, encoding: PixelEncoding) -> Arc<Mutex<Frame>> {
Arc::new(Mutex::new(Frame::new(width, height, encoding)))
}
}
impl BlitableSurface for Frame {
fn set_size(&mut self, width: u32, height: u32) {
pub fn set_size(&mut self, width: u32, height: u32) {
self.width = width;
self.height = height;
self.bitmap.resize((width * height) as usize, 0);
}
fn set_pixel(&mut self, pos_x: u32, pos_y: u32, pixel: Pixel) {
#[inline]
pub fn set_pixel(&mut self, pos_x: u32, pos_y: u32, pixel: Pixel) {
match pixel {
Pixel::Mask => {}
value if pos_x < self.width && pos_y < self.height => {
@ -82,7 +81,7 @@ impl BlitableSurface for Frame {
}
#[inline]
fn set_encoded_pixel(&mut self, pos_x: u32, pos_y: u32, pixel: u32) {
pub fn set_encoded_pixel(&mut self, pos_x: u32, pos_y: u32, pixel: u32) {
match pixel {
MASK_COLOUR => { },
value if pos_x < self.width && pos_y < self.height => {
@ -92,7 +91,7 @@ impl BlitableSurface for Frame {
}
}
fn blit<B: Iterator<Item = Pixel>>(&mut self, pos_x: u32, pos_y: u32, mut bitmap: B, width: u32, height: u32) {
pub fn blit<B: Iterator<Item = Pixel>>(&mut self, pos_x: u32, pos_y: u32, mut bitmap: B, width: u32, height: u32) {
for y in pos_y..(pos_y + height) {
for x in pos_x..(pos_x + width) {
match bitmap.next().unwrap() {
@ -106,20 +105,35 @@ impl BlitableSurface for Frame {
}
}
fn clear(&mut self, value: Pixel) {
pub fn clear(&mut self, value: Pixel) {
let value = value.encode(self.encoding);
self.bitmap.iter_mut().for_each(|pixel| *pixel = value);
}
}
#[derive(Clone)]
pub struct FrameQueue {
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(),
};
let receiver = FrameReceiver {
max_size: (width, height),
encoding: sender.encoding.clone(),
queue: sender.queue.clone(),
};
(sender, receiver)
}
pub struct FrameSender {
max_size: (u32, u32),
encoding: Arc<Mutex<PixelEncoding>>,
queue: ClockedQueue<Frame>,
}
impl FrameQueue {
impl FrameSender {
pub fn new(width: u32, height: u32) -> Self {
Self {
max_size: (width, height),
@ -128,31 +142,32 @@ impl FrameQueue {
}
}
pub fn encoding(&mut self) -> PixelEncoding {
pub fn encoding(&self) -> PixelEncoding {
*self.encoding.lock().unwrap()
}
pub fn add(&self, clock: ClockTime, frame: Frame) {
self.queue.push(clock, frame);
}
}
pub struct FrameReceiver {
max_size: (u32, u32),
encoding: Arc<Mutex<PixelEncoding>>,
queue: ClockedQueue<Frame>,
}
impl FrameReceiver {
pub fn max_size(&self) -> (u32, u32) {
self.max_size
}
pub fn request_encoding(&self, encoding: PixelEncoding) {
*self.encoding.lock().unwrap() = encoding;
}
pub fn latest(&self) -> Option<(ClockTime, Frame)> {
self.queue.pop_latest()
}
}
impl WindowUpdater for FrameQueue {
fn max_size(&self) -> (u32, u32) {
self.max_size
}
fn request_encoding(&mut self, encoding: PixelEncoding) {
*self.encoding.lock().unwrap() = encoding;
}
fn take_frame(&mut self) -> Result<Frame, Error> {
self.latest()
.map(|(_, f)| f)
.ok_or_else(|| Error::new("No frame available"))
}
}

View File

@ -5,9 +5,9 @@ mod gfx;
mod controllers;
mod mouse;
pub use self::gfx::{Pixel, PixelEncoding, Frame, FrameQueue};
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, WindowUpdater, ControllerUpdater, KeyboardUpdater, MouseUpdater, Audio, BlitableSurface, HostData, ClockedQueue, DummyAudio};
pub use self::traits::{Host, Tty, ControllerUpdater, KeyboardUpdater, MouseUpdater, Audio, HostData, ClockedQueue, DummyAudio};

View File

@ -3,20 +3,24 @@ use std::collections::VecDeque;
use std::sync::{Arc, Mutex, MutexGuard};
use crate::{ClockTime, Error};
use crate::host::gfx::{PixelEncoding, Pixel, Frame};
use crate::host::gfx::{PixelEncoding, Pixel, Frame, FrameReceiver};
use crate::host::keys::KeyEvent;
use crate::host::controllers::{ControllerDevice, ControllerEvent};
use crate::host::mouse::MouseEvent;
pub trait Host {
fn create_pty(&self) -> Result<Box<dyn Tty>, Error> {
fn add_pty(&self) -> Result<Box<dyn Tty>, Error> {
Err(Error::new("This frontend doesn't support PTYs"))
}
fn add_window(&mut self, _updater: Box<dyn WindowUpdater>) -> Result<(), Error> {
fn add_video_source(&mut self, _receiver: FrameReceiver) -> Result<(), Error> {
Err(Error::new("This frontend doesn't support windows"))
}
fn add_audio_source(&mut self) -> Result<Box<dyn Audio>, Error> {
Err(Error::new("This frontend doesn't support the sound"))
}
fn register_controller(&mut self, _device: ControllerDevice, _input: Box<dyn ControllerUpdater>) -> Result<(), Error> {
Err(Error::new("This frontend doesn't support game controllers"))
}
@ -28,10 +32,6 @@ pub trait Host {
fn register_mouse(&mut self, _input: Box<dyn MouseUpdater>) -> Result<(), Error> {
Err(Error::new("This frontend doesn't support the mouse"))
}
fn create_audio_source(&mut self) -> Result<Box<dyn Audio>, Error> {
Err(Error::new("This frontend doesn't support the sound"))
}
}
@ -41,22 +41,16 @@ pub trait Tty {
fn write(&mut self, output: u8) -> bool;
}
pub trait WindowUpdater: Send {
fn max_size(&self) -> (u32, u32);
fn request_encoding(&mut self, encoding: PixelEncoding);
fn take_frame(&mut self) -> Result<Frame, Error>;
}
pub trait ControllerUpdater: Send {
fn update_controller(&mut self, event: ControllerEvent);
fn update_controller(&self, event: ControllerEvent);
}
pub trait KeyboardUpdater: Send {
fn update_keyboard(&mut self, event: KeyEvent);
fn update_keyboard(&self, event: KeyEvent);
}
pub trait MouseUpdater: Send {
fn update_mouse(&mut self, event: MouseEvent);
fn update_mouse(&self, event: MouseEvent);
}
pub trait Audio {
@ -66,14 +60,6 @@ pub trait Audio {
fn flush(&mut self);
}
pub trait BlitableSurface {
fn set_size(&mut self, width: u32, height: u32);
fn set_pixel(&mut self, pos_x: u32, pos_y: u32, pixel: Pixel);
fn set_encoded_pixel(&mut self, pos_x: u32, pos_y: u32, pixel: u32);
fn blit<B: Iterator<Item=Pixel>>(&mut self, pos_x: u32, pos_y: u32, bitmap: B, width: u32, height: u32);
fn clear(&mut self, value: Pixel);
}
#[derive(Clone, Debug)]
pub struct HostData<T>(Arc<Mutex<T>>);

View File

@ -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, WindowUpdater, Audio, ControllerDevice, PixelEncoding, Frame};
use moa_core::host::{Host, ControllerUpdater, KeyboardUpdater, KeyEvent, MouseUpdater, MouseState, Audio, ControllerDevice, PixelEncoding, Frame, FrameReceiver};
use moa_common::{AudioMixer, AudioSource};
use moa_common::CpalAudioOutput;
@ -95,18 +95,18 @@ fn wait_until_initialized(frontend: Arc<Mutex<MiniFrontendBuilder>>) {
pub struct MiniFrontendBuilder {
pub window: Option<Box<dyn WindowUpdater>>,
pub controller: Option<Box<dyn ControllerUpdater>>,
pub keyboard: Option<Box<dyn KeyboardUpdater>>,
pub mouse: Option<Box<dyn MouseUpdater>>,
pub mixer: Option<Arc<Mutex<AudioMixer>>>,
pub finalized: bool,
video: Option<FrameReceiver>,
controller: Option<Box<dyn ControllerUpdater>>,
keyboard: Option<Box<dyn KeyboardUpdater>>,
mouse: Option<Box<dyn MouseUpdater>>,
mixer: Option<Arc<Mutex<AudioMixer>>>,
finalized: bool,
}
impl Default for MiniFrontendBuilder {
fn default() -> Self {
Self {
window: None,
video: None,
controller: None,
keyboard: None,
mouse: None,
@ -122,24 +122,29 @@ impl MiniFrontendBuilder {
}
pub fn build(&mut self) -> MiniFrontend {
let window = std::mem::take(&mut self.window);
let video = std::mem::take(&mut self.video);
let controller = std::mem::take(&mut self.controller);
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(window, controller, keyboard, mouse, mixer.unwrap())
MiniFrontend::new(video, controller, keyboard, mouse, mixer.unwrap())
}
}
impl Host for MiniFrontendBuilder {
fn add_window(&mut self, updater: Box<dyn WindowUpdater>) -> Result<(), Error> {
if self.window.is_some() {
return Err(Error::new("A window updater has already been registered with the frontend"));
fn add_video_source(&mut self, receiver: FrameReceiver) -> Result<(), Error> {
if self.video.is_some() {
return Err(Error::new("Only one video source can be registered with this frontend"));
}
self.window = Some(updater);
self.video = Some(receiver);
Ok(())
}
fn add_audio_source(&mut self) -> Result<Box<dyn Audio>, Error> {
let source = AudioSource::new(self.mixer.as_ref().unwrap().clone());
Ok(Box::new(source))
}
fn register_controller(&mut self, device: ControllerDevice, input: Box<dyn ControllerUpdater>) -> Result<(), Error> {
if device != ControllerDevice::A {
return Ok(())
@ -167,18 +172,13 @@ impl Host for MiniFrontendBuilder {
self.mouse = Some(input);
Ok(())
}
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))
}
}
pub struct MiniFrontend {
pub modifiers: u16,
pub mouse_state: MouseState,
pub window: Option<Box<dyn WindowUpdater>>,
pub video: Option<FrameReceiver>,
pub controller: Option<Box<dyn ControllerUpdater>>,
pub keyboard: Option<Box<dyn KeyboardUpdater>>,
pub mouse: Option<Box<dyn MouseUpdater>>,
@ -188,7 +188,7 @@ pub struct MiniFrontend {
impl MiniFrontend {
pub fn new(
window: Option<Box<dyn WindowUpdater>>,
video: Option<FrameReceiver>,
controller: Option<Box<dyn ControllerUpdater>>,
keyboard: Option<Box<dyn KeyboardUpdater>>,
mouse: Option<Box<dyn MouseUpdater>>,
@ -197,7 +197,7 @@ impl MiniFrontend {
Self {
modifiers: 0,
mouse_state: Default::default(),
window,
video,
controller,
keyboard,
mouse,
@ -239,9 +239,9 @@ impl MiniFrontend {
};
let mut size = (WIDTH, HEIGHT);
if let Some(updater) = self.window.as_mut() {
size = updater.max_size();
updater.request_encoding(PixelEncoding::ARGB);
if let Some(queue) = self.video.as_mut() {
size = queue.max_size();
queue.request_encoding(PixelEncoding::ARGB);
}
let mut window = minifb::Window::new(
@ -306,8 +306,8 @@ impl MiniFrontend {
}
}
if let Some(updater) = self.window.as_mut() {
if let Ok(frame) = updater.take_frame() {
if let Some(queue) = self.video.as_mut() {
if let Some((clock, frame)) = queue.latest() {
last_frame = frame
}
window.update_with_buffer(&last_frame.bitmap, last_frame.width as usize, last_frame.height as usize).unwrap();

View File

@ -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, WindowUpdater, ControllerDevice, ControllerEvent, ControllerUpdater, Audio, DummyAudio};
use moa_core::host::{Host, PixelEncoding, Frame, ControllerDevice, ControllerEvent, ControllerUpdater, Audio, DummyAudio, FrameReceiver};
use moa_common::{AudioMixer, AudioSource, CpalAudioOutput};
use crate::settings;
@ -20,7 +20,7 @@ pub const HEIGHT: u32 = 224;
pub type LoadSystemFn = fn (&mut PixelsFrontend, Vec<u8>) -> Result<System, Error>;
pub struct PixelsFrontend {
updater: Option<Box<dyn WindowUpdater>>,
video: Option<FrameReceiver>,
controller: Option<Box<dyn ControllerUpdater>>,
//mixer: Arc<Mutex<AudioMixer>>,
//audio_output: CpalAudioOutput,
@ -33,8 +33,8 @@ impl PixelsFrontend {
//let audio_output = CpalAudioOutput::create_audio_output(mixer.lock().unwrap().get_sink());
PixelsFrontend {
video: None,
controller: None,
updater: None,
//mixer,
//audio_output,
}
@ -42,8 +42,8 @@ impl PixelsFrontend {
}
impl Host for PixelsFrontend {
fn add_window(&mut self, updater: Box<dyn WindowUpdater>) -> Result<(), Error> {
self.updater = Some(updater);
fn add_video_source(&mut self, receiver: FrameReceiver) -> Result<(), Error> {
self.video = Some(receiver);
Ok(())
}
@ -56,20 +56,20 @@ impl Host for PixelsFrontend {
Ok(())
}
fn create_audio_source(&mut self) -> Result<Box<dyn Audio>, Error> {
fn add_audio_source(&mut self) -> Result<Box<dyn Audio>, Error> {
//let source = AudioSource::new(self.mixer.clone());
//Ok(Box::new(source))
Ok(Box::new(DummyAudio()))
}
}
pub async fn run_loop(mut host: PixelsFrontend) {
pub async fn run_loop(host: PixelsFrontend) {
let event_loop = EventLoop::new();
let window = create_window(&event_loop);
if let Some(updater) = host.updater.as_mut() {
updater.request_encoding(PixelEncoding::ABGR);
if let Some(recevier) = host.video.as_ref() {
recevier.request_encoding(PixelEncoding::ABGR);
}
let mut pixels = {
@ -93,8 +93,8 @@ pub async fn run_loop(mut host: PixelsFrontend) {
//log::warn!("updated after {:4}ms", update_timer.elapsed().as_millis());
//update_timer = Instant::now();
if let Some(updater) = host.updater.as_mut() {
if let Ok(frame) = updater.take_frame() {
if let Some(updater) = host.video.as_ref() {
if let Some((clock, frame)) = updater.latest() {
last_frame = frame;
}
@ -131,7 +131,7 @@ pub async fn run_loop(mut host: PixelsFrontend) {
}
};
if let Some(updater) = host.controller.as_mut() {
if let Some(updater) = host.controller.as_ref() {
if let Some(key) = key {
updater.update_controller(key);
}

View File

@ -94,8 +94,8 @@ pub struct Sn76489 {
}
impl Sn76489 {
pub fn create<H: Host>(host: &mut H, clock_frequency: Frequency) -> Result<Self, Error> {
let source = host.create_audio_source()?;
pub fn new<H: Host>(host: &mut H, clock_frequency: Frequency) -> Result<Self, Error> {
let source = host.add_audio_source()?;
let sample_rate = source.samples_per_second();
Ok(Self {

View File

@ -5,14 +5,14 @@
//! source code that emulates the chip. It is still very much a work in progress
//!
//! Resources:
//! - Registers: https://www.smspower.org/maxim/Documents/YM2612
//! - Internal Implementation: https://gendev.spritesmind.net/forum/viewtopic.php?t=386 [Nemesis]
//! - Registers: <https://www.smspower.org/maxim/Documents/YM2612>
//! - Internal Implementation: <https://gendev.spritesmind.net/forum/viewtopic.php?t=386> (Nemesis)
//! * Envelope Generator and Corrections:
//! http://gendev.spritesmind.net/forum/viewtopic.php?p=5716#5716
//! http://gendev.spritesmind.net/forum/viewtopic.php?t=386&postdays=0&postorder=asc&start=417
//! <http://gendev.spritesmind.net/forum/viewtopic.php?p=5716#5716>
//! <http://gendev.spritesmind.net/forum/viewtopic.php?t=386&postdays=0&postorder=asc&start=417>
//! * Phase Generator and Output:
//! http://gendev.spritesmind.net/forum/viewtopic.php?f=24&t=386&start=150
//! http://gendev.spritesmind.net/forum/viewtopic.php?p=6224#6224
//! <http://gendev.spritesmind.net/forum/viewtopic.php?f=24&t=386&start=150>
//! <http://gendev.spritesmind.net/forum/viewtopic.php?p=6224#6224>
use std::f32;
use std::num::NonZeroU8;
@ -262,6 +262,9 @@ 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) {
@ -272,13 +275,24 @@ impl EnvelopeGenerator {
let rate = self.get_scaled_rate(self.envelope_state, rate_adjust);
let counter_shift = COUNTER_SHIFT_VALUES[rate];
//if self.debug_name == "ch 2, op 0" {
//println!("{:4x} {:4x} {:4x}", envelope_clock, counter_shift, envelope_clock % (1 << counter_shift));
//}
if envelope_clock % (1 << counter_shift) == 0 {
let update_cycle = (envelope_clock >> counter_shift) & 0x07;
let increment = RATE_TABLE[rate * 8 + update_cycle as usize];
match self.envelope_state {
EnvelopeState::Attack => {
let new_envelope = self.envelope + ((!self.envelope * increment) >> 4) & 0xFFC;
// NOTE: the adjustment added to the envelope is negative, but the envelope is an unsigned number, so
// it's converted to signed to ensure an arithmetic (sign-extending) shift right is used. The addition
// will work the same regardless due to the magic of two's complement numbers. It would have also worked
// to bitwise-and with 0xFFC instead, which will wrap the number to a 12-bit signed number, which when
// clamped to MAX_ENVELOPE will produce the same results
let new_envelope = self.envelope + (((!self.envelope * increment) as i16) >> 4) as u16;
if self.debug_name == "ch 2, op 0" {
println!("{:4x} {:4x} {:4x} {:4x} {:4x}", self.envelope, update_cycle, rate * 8 + update_cycle as usize, (((!self.envelope * increment) as i16) >> 4) as u16 & 0xFFFC, new_envelope);
}
if new_envelope > self.envelope {
self.envelope_state = EnvelopeState::Decay;
self.envelope = 0;
@ -296,6 +310,9 @@ impl EnvelopeGenerator {
}
},
}
//if self.debug_name == "ch 2, op 0" {
//println!("{:4x} {:4x} {:4x} {:4x} {:4x}", rate, counter_shift, self.envelope_state as usize, increment, self.envelope);
//}
}
}
@ -506,6 +523,10 @@ impl Operator {
let mod_phase = phase + modulator;
//if self.debug_name == "ch 2, op 0" {
//println!("{:4x} = {:4x} + {:4x} + {:4x}, e: {:x}, {:4x} {:4x}", mod_phase, phase, self.phase.increment, modulator, self.envelope.envelope_state as usize, envelope, self.envelope.envelope);
//}
// The sine table contains the first half of the wave as an attenuation value
// Use the phase with the sign truncated to get the attenuation, plus the
// attenuation from the envelope, to get the total attenuation at this point
@ -601,7 +622,9 @@ impl Channel {
//let output = sign_extend_u16(output, 14);
//let output = output * 2 / 3;
//if self.debug_name == "ch 2" {
//println!("{:6x}", output);
//}
let sample = output as f32 / (1 << 13) as f32;
let left = if self.enabled.0 { sample } else { 0.0 };
@ -726,8 +749,8 @@ pub struct Ym2612 {
}
impl Ym2612 {
pub fn create<H: Host>(host: &mut H, clock_frequency: Frequency) -> Result<Self, Error> {
let source = host.create_audio_source()?;
pub fn new<H: Host>(host: &mut H, clock_frequency: Frequency) -> Result<Self, Error> {
let source = host.add_audio_source()?;
let fm_clock = clock_frequency / (6 * 24);
let fm_clock_period = fm_clock.period_duration();
@ -817,6 +840,7 @@ impl Ym2612 {
pub fn set_register(&mut self, clock: ClockTime, bank: u8, reg: u8, data: u8) {
// Keep a copy for debugging purposes, and if the original values are needed
self.registers[bank as usize * 256 + reg as usize] = data;
//println!("set {:x} to {:x}", bank as usize * 256 + reg as usize, data);
//warn!("{}: set reg {}{:x} to {:x}", DEV_NAME, bank, reg, data);
match reg {

View File

@ -22,8 +22,8 @@ pub fn build_computie<H: Host>(host: &H) -> Result<System, Error> {
system.add_addressable_device(0x00600000, wrap_transmutable(ata))?;
let mut serial = MC68681::default();
launch_terminal_emulator(serial.port_a.connect(host.create_pty()?)?);
launch_slip_connection(serial.port_b.connect(host.create_pty()?)?);
launch_terminal_emulator(serial.port_a.connect(host.add_pty()?)?);
launch_slip_connection(serial.port_b.connect(host.add_pty()?)?);
system.add_addressable_device(0x00700000, wrap_transmutable(serial))?;
@ -60,8 +60,8 @@ pub fn build_computie_k30<H: Host>(host: &H) -> Result<System, Error> {
system.add_addressable_device(0x00600000, wrap_transmutable(ata))?;
let mut serial = MC68681::default();
launch_terminal_emulator(serial.port_a.connect(host.create_pty()?)?);
//launch_slip_connection(serial.port_b.connect(host.create_pty()?)?);
launch_terminal_emulator(serial.port_a.connect(host.add_pty()?)?);
//launch_slip_connection(serial.port_b.connect(host.add_pty()?)?);
system.add_addressable_device(0x00700000, wrap_transmutable(serial))?;

View File

@ -1,6 +1,6 @@
use std::sync::Arc;
use std::sync::atomic::{AtomicU16, Ordering};
use std::sync::atomic::{AtomicBool, AtomicU16, Ordering};
use moa_core::{warn, info};
use moa_core::{System, Error, ClockTime, ClockDuration, Address, Addressable, Steppable, Transmutable};
@ -88,10 +88,10 @@ impl GenesisControllerPort {
}
}
pub struct GenesisControllersUpdater(Arc<AtomicU16>, HostData<bool>);
pub struct GenesisControllersUpdater(Arc<AtomicU16>, Arc<AtomicBool>);
impl ControllerUpdater for GenesisControllersUpdater {
fn update_controller(&mut self, event: ControllerEvent) {
fn update_controller(&self, event: ControllerEvent) {
let (mask, state) = match event {
ControllerEvent::ButtonA(state) => (0x0040, state),
ControllerEvent::ButtonB(state) => (0x0010, state),
@ -108,7 +108,7 @@ impl ControllerUpdater for GenesisControllersUpdater {
let buttons = (self.0.load(Ordering::Acquire) & !mask) | (if !state { mask } else { 0 });
self.0.store(buttons, Ordering::Release);
if buttons != 0 {
self.1.set(true);
self.1.store(true, Ordering::Release);
}
}
}
@ -119,6 +119,7 @@ pub struct GenesisControllers {
port_1: GenesisControllerPort,
port_2: GenesisControllerPort,
expansion: GenesisControllerPort,
has_changed: Arc<AtomicBool>,
interrupt: HostData<bool>,
reset_timer: ClockDuration,
}
@ -129,6 +130,7 @@ impl Default for GenesisControllers {
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,
}
@ -136,12 +138,12 @@ impl Default for GenesisControllers {
}
impl GenesisControllers {
pub fn create<H: Host>(host: &mut H) -> Result<Self, Error> {
pub fn new<H: Host>(host: &mut H) -> Result<Self, Error> {
let controller = GenesisControllers::default();
let controller1 = Box::new(GenesisControllersUpdater(controller.port_1.buttons.clone(), controller.interrupt.clone()));
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.interrupt.clone()));
let controller2 = Box::new(GenesisControllersUpdater(controller.port_2.buttons.clone(), controller.has_changed.clone()));
host.register_controller(ControllerDevice::B, controller2)?;
Ok(controller)
@ -206,6 +208,11 @@ impl Steppable for GenesisControllers {
fn step(&mut self, _system: &System) -> Result<ClockDuration, Error> {
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);
}
self.reset_timer += duration;
if self.reset_timer >= ClockDuration::from_micros(1_500) {
self.port_1.reset_count();

View File

@ -1,7 +1,7 @@
use moa_core::{debug, warn, error};
use moa_core::{System, Error, EdgeSignal, ClockTime, ClockDuration, Frequency, Address, Addressable, Steppable, Inspectable, Transmutable, TransmutableBox, read_beu16, dump_slice};
use moa_core::host::{Host, Pixel, PixelEncoding, Frame, FrameQueue, BlitableSurface, HostData};
use moa_core::host::{self, Host, Pixel, PixelEncoding, Frame, FrameSender, HostData};
const REG_MODE_SET_1: usize = 0x00;
@ -659,9 +659,9 @@ impl Steppable for Ym7101 {
}
if (self.state.mode_1 & MODE1_BF_DISABLE_DISPLAY) == 0 {
let mut frame = Frame::new(self.state.screen_size.0 as u32 * 8, self.state.screen_size.1 as u32 * 8, self.queue.encoding());
let mut frame = Frame::new(self.state.screen_size.0 as u32 * 8, self.state.screen_size.1 as u32 * 8, self.sender.encoding());
self.state.draw_frame(&mut frame);
self.queue.add(system.clock, frame);
self.sender.add(system.clock, frame);
}
self.frame_complete.signal();
@ -681,7 +681,7 @@ impl Steppable for Ym7101 {
pub struct Ym7101 {
queue: FrameQueue,
sender: FrameSender,
state: Ym7101State,
sn_sound: TransmutableBox,
@ -691,11 +691,11 @@ pub struct Ym7101 {
impl Ym7101 {
pub fn new<H: Host>(host: &mut H, external_interrupt: HostData<bool>, sn_sound: TransmutableBox) -> Ym7101 {
let queue = FrameQueue::new(320, 224);
host.add_window(Box::new(queue.clone())).unwrap();
let (sender, receiver) = host::frame_queue(320, 224);
host.add_video_source(receiver).unwrap();
Ym7101 {
queue,
sender,
state: Ym7101State::default(),
sn_sound,
external_interrupt,

View File

@ -70,8 +70,8 @@ pub fn build_genesis<H: Host>(host: &mut H, mut options: SegaGenesisOptions) ->
// Build the Coprocessor's Bus
let bank_register = Signal::new(0);
let coproc_ram = wrap_transmutable(MemoryBlock::new(vec![0; 0x00002000]));
let coproc_ym_sound = wrap_transmutable(Ym2612::create(host, Frequency::from_hz(7_670_454))?);
let coproc_sn_sound = wrap_transmutable(Sn76489::create(host, Frequency::from_hz(3_579_545))?);
let coproc_ym_sound = wrap_transmutable(Ym2612::new(host, Frequency::from_hz(7_670_454))?);
let coproc_sn_sound = wrap_transmutable(Sn76489::new(host, Frequency::from_hz(3_579_545))?);
let coproc_register = wrap_transmutable(CoprocessorBankRegister::new(bank_register.clone()));
let coproc_area = wrap_transmutable(CoprocessorBankArea::new(bank_register, system.bus.clone()));
@ -98,7 +98,7 @@ pub fn build_genesis<H: Host>(host: &mut H, mut options: SegaGenesisOptions) ->
system.add_device("coproc", wrap_transmutable(coproc))?;
let controllers = GenesisControllers::create(host)?;
let controllers = GenesisControllers::new(host)?;
let interrupt = controllers.get_interrupt_signal();
system.add_addressable_device(0x00a10000, wrap_transmutable(controllers)).unwrap();

View File

@ -22,7 +22,7 @@ pub struct Mainboard {
}
impl Mainboard {
pub fn create(ram: TransmutableBox, rom: TransmutableBox) -> Result<Self, Error> {
pub fn new(ram: TransmutableBox, rom: TransmutableBox) -> Result<Self, Error> {
let scc1 = Z8530::default();
let scc2 = Z8530::default();
let iwm = IWM::default();

View File

@ -1,23 +1,23 @@
use moa_core::{System, Error, ClockDuration, Address, Addressable, Steppable, Transmutable};
use moa_core::host::{Host, BlitableSurface, Frame, FrameQueue, Pixel};
use moa_core::host::{self, Host, Frame, FrameSender, Pixel};
const SCRN_BASE: u32 = 0x07A700;
const SCRN_SIZE: (u32, u32) = (512, 342);
pub struct MacVideo {
frame_queue: FrameQueue,
frame_sender: FrameSender,
}
impl MacVideo {
pub fn create<H: Host>(host: &mut H) -> Result<Self, Error> {
let frame_queue = FrameQueue::new(SCRN_SIZE.0, SCRN_SIZE.1);
pub fn new<H: Host>(host: &mut H) -> Result<Self, Error> {
let (frame_sender, frame_receiver) = host::frame_queue(SCRN_SIZE.0, SCRN_SIZE.1);
host.add_window(Box::new(frame_queue.clone()))?;
host.add_video_source(frame_receiver)?;
Ok(Self {
frame_queue,
frame_sender,
})
}
}
@ -58,7 +58,7 @@ impl Iterator for BitIter {
impl Steppable for MacVideo {
fn step(&mut self, system: &System) -> Result<ClockDuration, Error> {
let mut memory = system.get_bus();
let mut frame = Frame::new(SCRN_SIZE.0, SCRN_SIZE.1, self.frame_queue.encoding());
let mut frame = Frame::new(SCRN_SIZE.0, SCRN_SIZE.1, self.frame_sender.encoding());
for y in 0..SCRN_SIZE.1 {
for x in 0..(SCRN_SIZE.0 / 16) {
let word = memory.read_beu16(system.clock, (SCRN_BASE + (x * 2) + (y * (SCRN_SIZE.0 / 8))) as Address)?;
@ -66,7 +66,7 @@ impl Steppable for MacVideo {
}
}
self.frame_queue.add(system.clock, frame);
self.frame_sender.add(system.clock, frame);
Ok(ClockDuration::from_micros(16_600))
}
}

View File

@ -32,7 +32,7 @@ pub fn build_macintosh_512k<H: Host>(host: &mut H) -> Result<System, Error> {
let misc = MemoryBlock::new(vec![0; 0x100000]);
system.add_addressable_device(0x00f00000, wrap_transmutable(misc))?;
let video = MacVideo::create(host)?;
let video = MacVideo::new(host)?;
system.add_device("video", wrap_transmutable(video)).unwrap();
let scc1 = Z8530::new();
@ -62,10 +62,10 @@ pub fn build_macintosh_512k<H: Host>(host: &mut H) -> Result<System, Error> {
let mut rom = MemoryBlock::load("binaries/macintosh/Macintosh 512k.rom")?;
rom.read_only();
let video = MacVideo::create(host)?;
let video = MacVideo::new(host)?;
system.add_device("video", wrap_transmutable(video)).unwrap();
let mainboard = Mainboard::create(wrap_transmutable(ram), wrap_transmutable(rom))?;
let mainboard = Mainboard::new(wrap_transmutable(ram), wrap_transmutable(rom))?;
system.add_addressable_device(0x00000000, wrap_transmutable(mainboard))?;

View File

@ -2,7 +2,7 @@
use std::sync::{Arc, Mutex};
use moa_core::{System, Error, ClockTime, ClockDuration, Address, Addressable, Steppable, Transmutable, debug, warn};
use moa_core::host::{Host, Frame, FrameQueue, BlitableSurface, KeyboardUpdater, KeyEvent};
use moa_core::host::{self, Host, Frame, FrameSender, KeyboardUpdater, KeyEvent};
use super::keymap;
use super::charset::CharacterGenerator;
@ -12,21 +12,21 @@ const DEV_NAME: &str = "model1";
const SCREEN_SIZE: (u32, u32) = (384, 128);
pub struct Model1Peripherals {
frame_queue: FrameQueue,
frame_sender: FrameSender,
keyboard_mem: Arc<Mutex<[u8; 8]>>,
video_mem: [u8; 1024],
}
impl Model1Peripherals {
pub fn create<H: Host>(host: &mut H) -> Result<Self, Error> {
let frame_queue = FrameQueue::new(SCREEN_SIZE.0, SCREEN_SIZE.1);
pub fn new<H: Host>(host: &mut H) -> Result<Self, Error> {
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_window(Box::new(frame_queue.clone()))?;
host.add_video_source(frame_receiver)?;
host.register_keyboard(Box::new(Model1KeyboardUpdater(keyboard_mem.clone())))?;
Ok(Self {
frame_queue,
frame_sender,
keyboard_mem,
video_mem: [0x20; 1024],
})
@ -36,7 +36,7 @@ impl Model1Peripherals {
pub struct Model1KeyboardUpdater(Arc<Mutex<[u8; 8]>>);
impl KeyboardUpdater for Model1KeyboardUpdater {
fn update_keyboard(&mut self, event: KeyEvent) {
fn update_keyboard(&self, event: KeyEvent) {
println!(">>> {:?}", event.key);
keymap::record_key_press(&mut self.0.lock().unwrap(), event.key, event.state);
}
@ -44,7 +44,7 @@ impl KeyboardUpdater for Model1KeyboardUpdater {
impl Steppable for Model1Peripherals {
fn step(&mut self, system: &System) -> Result<ClockDuration, Error> {
let mut frame = Frame::new(SCREEN_SIZE.0, SCREEN_SIZE.1, self.frame_queue.encoding());
let mut frame = Frame::new(SCREEN_SIZE.0, SCREEN_SIZE.1, self.frame_sender.encoding());
for y in 0..16 {
for x in 0..64 {
let ch = self.video_mem[x + (y * 64)];
@ -52,7 +52,7 @@ impl Steppable for Model1Peripherals {
frame.blit((x * 6) as u32, (y * 8) as u32, iter, 6, 8);
}
}
self.frame_queue.add(system.clock, frame);
self.frame_sender.add(system.clock, frame);
Ok(ClockDuration::from_micros(16_630))
}

View File

@ -37,7 +37,7 @@ pub fn build_trs80<H: Host>(host: &mut H, options: Trs80Options) -> Result<Syste
let ram = MemoryBlock::new(vec![0; options.memory as usize]);
system.add_addressable_device(0x4000, wrap_transmutable(ram))?;
let model1 = Model1Peripherals::create(host)?;
let model1 = Model1Peripherals::new(host)?;
system.add_addressable_device(0x37E0, wrap_transmutable(model1)).unwrap();
let cpu = Z80::new(Z80Type::Z80, options.frequency, BusPort::new(0, 16, 8, system.bus.clone()));

View File

@ -1,7 +1,11 @@
* add runtime checks for arithmetic to look for errors
* make the ym generate audio in sync so the DAC timings can be more accurate
* change the host things to use queues instead
* add rust runtime checks for math to look for overflow errors
* I think the overflowing add and subs return the original number and not the overflowed result. I might have already checked that
in the m68k impl but I should check again
* the first 512 entries are 0 for some reason, in the log table, but otherwise seems ok
* you need to scale the output sample to be +/- 1.0 instead of 0-1.0
* AudioFrame (and possibly the mixer and source) should be moved to the core, it should probably have the sample rate