Removed WindowUpdater and BlitableSurface
This commit is contained in:
parent
8a8bcb2277
commit
3471eb4e8c
14
docs/log.txt
14
docs/log.txt
|
@ -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
|
||||
|
||||
|
|
|
@ -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"))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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};
|
||||
|
||||
|
|
|
@ -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>>);
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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))?;
|
||||
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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))?;
|
||||
|
||||
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
|
|
|
@ -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()));
|
||||
|
|
8
todo.txt
8
todo.txt
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue