Split some parts of moa-core into libraries/
This commit is contained in:
parent
5653cf47a1
commit
55efc4f406
|
@ -577,12 +577,6 @@ version = "1.0.6"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6"
|
||||
|
||||
[[package]]
|
||||
name = "iz80"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "76e13cd358184c4b647f19a858246b82e3327ed29c2893d232f01ed016ac5d3c"
|
||||
|
||||
[[package]]
|
||||
name = "jni"
|
||||
version = "0.19.0"
|
||||
|
@ -744,6 +738,7 @@ dependencies = [
|
|||
"femtos",
|
||||
"log",
|
||||
"moa-core",
|
||||
"moa-host",
|
||||
"nix 0.25.1",
|
||||
]
|
||||
|
||||
|
@ -770,15 +765,23 @@ version = "0.1.0"
|
|||
dependencies = [
|
||||
"femtos",
|
||||
"log",
|
||||
"moa-host",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "moa-iz80"
|
||||
name = "moa-debugger"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"moa-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "moa-host"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"femtos",
|
||||
"iz80",
|
||||
"moa-core",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -789,6 +792,7 @@ dependencies = [
|
|||
"log",
|
||||
"moa-core",
|
||||
"moa-parsing",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -801,6 +805,8 @@ dependencies = [
|
|||
"minifb",
|
||||
"moa-common",
|
||||
"moa-core",
|
||||
"moa-debugger",
|
||||
"moa-host",
|
||||
"moa-peripherals-yamaha",
|
||||
"moa-systems-computie",
|
||||
"moa-systems-genesis",
|
||||
|
@ -832,6 +838,7 @@ dependencies = [
|
|||
"femtos",
|
||||
"log",
|
||||
"moa-core",
|
||||
"moa-signals",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -841,6 +848,7 @@ dependencies = [
|
|||
"femtos",
|
||||
"log",
|
||||
"moa-core",
|
||||
"moa-host",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -852,6 +860,7 @@ dependencies = [
|
|||
"log",
|
||||
"moa-audio",
|
||||
"moa-core",
|
||||
"moa-host",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -863,6 +872,13 @@ dependencies = [
|
|||
"moa-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "moa-signals"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"femtos",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "moa-systems-computie"
|
||||
version = "0.1.0"
|
||||
|
@ -870,6 +886,7 @@ dependencies = [
|
|||
"femtos",
|
||||
"log",
|
||||
"moa-core",
|
||||
"moa-host",
|
||||
"moa-m68k",
|
||||
"moa-peripherals-generic",
|
||||
"moa-peripherals-motorola",
|
||||
|
@ -882,8 +899,10 @@ dependencies = [
|
|||
"femtos",
|
||||
"log",
|
||||
"moa-core",
|
||||
"moa-host",
|
||||
"moa-m68k",
|
||||
"moa-peripherals-yamaha",
|
||||
"moa-signals",
|
||||
"moa-z80",
|
||||
]
|
||||
|
||||
|
@ -894,9 +913,11 @@ dependencies = [
|
|||
"femtos",
|
||||
"log",
|
||||
"moa-core",
|
||||
"moa-host",
|
||||
"moa-m68k",
|
||||
"moa-peripherals-mos",
|
||||
"moa-peripherals-zilog",
|
||||
"moa-signals",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -906,6 +927,7 @@ dependencies = [
|
|||
"femtos",
|
||||
"log",
|
||||
"moa-core",
|
||||
"moa-host",
|
||||
"moa-z80",
|
||||
]
|
||||
|
||||
|
@ -916,6 +938,7 @@ dependencies = [
|
|||
"femtos",
|
||||
"log",
|
||||
"moa-core",
|
||||
"moa-signals",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
|
@ -6,4 +6,5 @@ edition = "2021"
|
|||
[dependencies]
|
||||
log = "0.4"
|
||||
femtos = "0.1"
|
||||
|
||||
thiserror = "1.0"
|
||||
moa-host = { path = "../libraries/host" }
|
||||
|
|
|
@ -1,16 +1,21 @@
|
|||
|
||||
use std::fmt;
|
||||
use std::error::{Error as StdError};
|
||||
use moa_host::HostError;
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub enum EmulatorErrorKind {
|
||||
Misc,
|
||||
MemoryAlignment,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Debug, thiserror::Error)]
|
||||
pub enum Error {
|
||||
Assertion(String),
|
||||
Breakpoint(String),
|
||||
Emulator(EmulatorErrorKind, String),
|
||||
Processor(u32),
|
||||
Other(String),
|
||||
}
|
||||
|
||||
impl Error {
|
||||
|
@ -50,9 +55,28 @@ impl Error {
|
|||
match self {
|
||||
Error::Assertion(msg) |
|
||||
Error::Breakpoint(msg) |
|
||||
Error::Other(msg) |
|
||||
Error::Emulator(_, msg) => msg.as_str(),
|
||||
Error::Processor(_) => "native exception",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
Error::Assertion(msg) |
|
||||
Error::Breakpoint(msg) |
|
||||
Error::Other(msg) |
|
||||
Error::Emulator(_, msg) => write!(f, "{}", msg),
|
||||
Error::Processor(_) => write!(f, "native exception"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<E> From<HostError<E>> for Error {
|
||||
fn from(err: HostError<E>) -> Self {
|
||||
Self::Other(format!("other"))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,17 +0,0 @@
|
|||
|
||||
mod audio;
|
||||
mod controllers;
|
||||
mod gfx;
|
||||
mod input;
|
||||
mod keys;
|
||||
mod mouse;
|
||||
mod traits;
|
||||
|
||||
pub use self::audio::{Sample, AudioFrame};
|
||||
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, ControllerInput, ControllerEvent};
|
||||
pub use self::input::{EventSender, EventReceiver, event_queue};
|
||||
pub use self::traits::{Host, Tty, Audio, ClockedQueue, DummyAudio};
|
||||
|
|
@ -2,21 +2,15 @@
|
|||
#[macro_use]
|
||||
mod error;
|
||||
|
||||
mod debugger;
|
||||
mod devices;
|
||||
mod interrupts;
|
||||
mod memory;
|
||||
mod signals;
|
||||
mod system;
|
||||
|
||||
pub mod host;
|
||||
|
||||
pub use crate::debugger::{DebugControl, Debugger};
|
||||
pub use crate::devices::{Address, Addressable, Steppable, Interruptable, Debuggable, Inspectable, Transmutable, TransmutableBox, Device};
|
||||
pub use crate::devices::{read_beu16, read_beu32, read_leu16, read_leu32, write_beu16, write_beu32, write_leu16, write_leu32, wrap_transmutable};
|
||||
pub use crate::error::Error;
|
||||
pub use crate::interrupts::InterruptController;
|
||||
pub use crate::memory::{MemoryBlock, AddressTranslator, AddressRepeater, Bus, BusPort, dump_slice};
|
||||
pub use crate::signals::{Observable, Signal, EdgeSignal, ObservableSignal, ObservableEdgeSignal};
|
||||
pub use crate::system::System;
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ use std::cell::{RefCell, RefMut};
|
|||
use std::collections::HashMap;
|
||||
use femtos::{Instant, Duration};
|
||||
|
||||
use crate::{Bus, EdgeSignal, Error, InterruptController, Address, Device};
|
||||
use crate::{Bus, Error, InterruptController, Address, Device};
|
||||
|
||||
|
||||
pub struct System {
|
||||
|
@ -17,8 +17,6 @@ pub struct System {
|
|||
pub bus: Rc<RefCell<Bus>>,
|
||||
pub buses: HashMap<String, Rc<RefCell<Bus>>>,
|
||||
pub interrupt_controller: RefCell<InterruptController>,
|
||||
|
||||
pub break_signal: Option<EdgeSignal>,
|
||||
}
|
||||
|
||||
impl Default for System {
|
||||
|
@ -33,8 +31,6 @@ impl Default for System {
|
|||
bus: Rc::new(RefCell::new(Bus::default())),
|
||||
buses: HashMap::new(),
|
||||
interrupt_controller: RefCell::new(InterruptController::default()),
|
||||
|
||||
break_signal: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ edition = "2021"
|
|||
|
||||
[dependencies]
|
||||
log = "0.4"
|
||||
thiserror = "1.0"
|
||||
femtos = "0.1"
|
||||
moa-core = { path = "../../core" }
|
||||
moa-parsing = { path = "../../libraries/parsing" }
|
||||
|
|
|
@ -9,5 +9,5 @@ pub mod memory;
|
|||
pub mod timing;
|
||||
pub mod tests;
|
||||
|
||||
pub use self::state::{M68k, M68kType};
|
||||
pub use self::state::{M68k, M68kType, M68kError};
|
||||
|
||||
|
|
|
@ -7,3 +7,4 @@ edition = "2021"
|
|||
log = "0.4"
|
||||
femtos = "0.1"
|
||||
moa-core = { path = "../../core" }
|
||||
moa-signals = { path = "../../libraries/signals" }
|
||||
|
|
|
@ -3,7 +3,8 @@ use std::rc::Rc;
|
|||
use std::cell::RefCell;
|
||||
use femtos::{Instant, Frequency};
|
||||
|
||||
use moa_core::{Address, Bus, BusPort, Signal};
|
||||
use moa_core::{Address, Bus, BusPort};
|
||||
use moa_signals::Signal;
|
||||
|
||||
use crate::decode::Z80Decoder;
|
||||
use crate::debugger::Z80Debugger;
|
||||
|
|
|
@ -11,6 +11,7 @@ audio = ["cpal"]
|
|||
log = "0.4"
|
||||
femtos = "0.1"
|
||||
moa-core = { path = "../../core" }
|
||||
moa-host = { path = "../../libraries/host" }
|
||||
nix = { version = "0.25", optional = true }
|
||||
|
||||
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
use std::sync::{Arc, Mutex, MutexGuard};
|
||||
use femtos::{Instant, Duration};
|
||||
|
||||
use moa_core::host::{Audio, Sample, AudioFrame, ClockedQueue};
|
||||
use moa_host::{Audio, Sample, AudioFrame, ClockedQueue};
|
||||
|
||||
|
||||
pub const SAMPLE_RATE: usize = 48000;
|
||||
|
|
|
@ -9,22 +9,24 @@ use moa_core::host::{Host, Tty, ControllerEvent, Audio, DummyAudio, FrameReceive
|
|||
pub struct ConsoleFrontend;
|
||||
|
||||
impl Host for ConsoleFrontend {
|
||||
fn add_pty(&self) -> Result<Box<dyn Tty>, Error> {
|
||||
type Error = Error;
|
||||
|
||||
fn add_pty(&self) -> Result<Box<dyn Tty>, HostError<Self::Error>> {
|
||||
use moa_common::tty::SimplePty;
|
||||
Ok(Box::new(SimplePty::open()?))
|
||||
}
|
||||
|
||||
fn add_video_source(&mut self, _receiver: FrameReceiver) -> Result<(), Error> {
|
||||
fn add_video_source(&mut self, _receiver: FrameReceiver) -> Result<(), HostError<Self::Error>> {
|
||||
println!("console: add_window() is not supported from the console; ignoring request...");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn register_controllers(&mut self, _sender: EventSender<ControllerEvent>) -> Result<(), Error> {
|
||||
fn register_controllers(&mut self, _sender: EventSender<ControllerEvent>) -> Result<(), HostError<Self::Error>> {
|
||||
println!("console: register_controller() is not supported from the console; ignoring request...");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn add_audio_source(&mut self) -> Result<Box<dyn Audio>, Error> {
|
||||
fn add_audio_source(&mut self) -> Result<Box<dyn Audio>, HostError<Self::Error>> {
|
||||
println!("console: create_audio_source() is not supported from the console; returning dummy device...");
|
||||
Ok(Box::new(DummyAudio()))
|
||||
}
|
||||
|
|
|
@ -12,8 +12,10 @@ simple_logger = "^2"
|
|||
femtos = "0.1"
|
||||
|
||||
moa-core = { path = "../../core" }
|
||||
moa-host = { path = "../../libraries/host" }
|
||||
moa-common = { path = "../common", features = ["audio"] }
|
||||
|
||||
moa-debugger = { path = "../../libraries/debugger" }
|
||||
moa-systems-genesis = { path = "../../systems/genesis" }
|
||||
moa-systems-computie = { path = "../../systems/computie" }
|
||||
moa-systems-trs80 = { path = "../../systems/trs80" }
|
||||
|
|
|
@ -3,7 +3,7 @@ use femtos::{Instant, Duration, Frequency};
|
|||
|
||||
use moa_peripherals_yamaha::{Ym2612, Sn76489};
|
||||
|
||||
use moa_core::host::{self, Host, Frame, FrameSender, PixelEncoding, Key, KeyEvent, EventReceiver};
|
||||
use moa_host::{self, Host, Frame, FrameSender, PixelEncoding, Key, KeyEvent, EventReceiver};
|
||||
use moa_core::{System, Error, Address, Addressable, Steppable, Transmutable, Device};
|
||||
|
||||
const SCREEN_WIDTH: u32 = 384;
|
||||
|
@ -85,8 +85,8 @@ fn main() {
|
|||
moa_minifb::run(matches, |host| {
|
||||
let mut system = System::default();
|
||||
|
||||
let (frame_sender, frame_receiver) = host::frame_queue(SCREEN_WIDTH, SCREEN_HEIGHT);
|
||||
let (key_sender, key_receiver) = host::event_queue();
|
||||
let (frame_sender, frame_receiver) = moa_host::frame_queue(SCREEN_WIDTH, SCREEN_HEIGHT);
|
||||
let (key_sender, key_receiver) = moa_host::event_queue();
|
||||
let control = Device::new(SynthControl::new(key_receiver, frame_sender));
|
||||
system.add_device("control", control)?;
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
|
||||
use minifb::Key as MiniKey;
|
||||
use moa_core::host::ControllerInput;
|
||||
use moa_host::ControllerInput;
|
||||
|
||||
pub fn map_controller_a(key: MiniKey, state: bool) -> Option<ControllerInput> {
|
||||
match key {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
|
||||
use minifb::Key as MiniKey;
|
||||
use moa_core::host::Key;
|
||||
use moa_host::Key;
|
||||
|
||||
pub fn map_key(key: MiniKey) -> Key {
|
||||
match key {
|
||||
|
|
|
@ -8,8 +8,9 @@ use minifb::{self, Key, MouseMode, MouseButton};
|
|||
use clap::{Command, Arg, ArgAction, ArgMatches};
|
||||
use femtos::{Duration as FemtosDuration};
|
||||
|
||||
use moa_core::{System, Error, Device, Debugger, DebugControl};
|
||||
use moa_core::host::{Host, Audio, KeyEvent, MouseEvent, MouseState, ControllerDevice, ControllerEvent, EventSender, PixelEncoding, Frame, FrameReceiver};
|
||||
use moa_core::{System, Error, Device};
|
||||
use moa_debugger::{Debugger, DebugControl};
|
||||
use moa_host::{Host, HostError, Audio, KeyEvent, MouseEvent, MouseState, ControllerDevice, ControllerEvent, EventSender, PixelEncoding, Frame, FrameReceiver};
|
||||
|
||||
use moa_common::{AudioMixer, AudioSource};
|
||||
use moa_common::CpalAudioOutput;
|
||||
|
@ -138,38 +139,40 @@ impl MiniFrontendBuilder {
|
|||
}
|
||||
|
||||
impl Host for MiniFrontendBuilder {
|
||||
fn add_video_source(&mut self, receiver: FrameReceiver) -> Result<(), Error> {
|
||||
type Error = Error;
|
||||
|
||||
fn add_video_source(&mut self, receiver: FrameReceiver) -> Result<(), HostError<Self::Error>> {
|
||||
if self.video.is_some() {
|
||||
return Err(Error::new("Only one video source can be registered with this frontend"));
|
||||
return Err(HostError::Specific(Error::new("Only one video source can be registered with this frontend")));
|
||||
}
|
||||
self.video = Some(receiver);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn add_audio_source(&mut self) -> Result<Box<dyn Audio>, Error> {
|
||||
fn add_audio_source(&mut self) -> Result<Box<dyn Audio>, HostError<Self::Error>> {
|
||||
let source = AudioSource::new(self.mixer.as_ref().unwrap().clone());
|
||||
Ok(Box::new(source))
|
||||
}
|
||||
|
||||
fn register_controllers(&mut self, sender: EventSender<ControllerEvent>) -> Result<(), Error> {
|
||||
fn register_controllers(&mut self, sender: EventSender<ControllerEvent>) -> Result<(), HostError<Self::Error>> {
|
||||
if self.controllers.is_some() {
|
||||
return Err(Error::new("A controller updater has already been registered with the frontend"));
|
||||
return Err(HostError::Specific(Error::new("A controller updater has already been registered with the frontend")));
|
||||
}
|
||||
self.controllers = Some(sender);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn register_keyboard(&mut self, sender: EventSender<KeyEvent>) -> Result<(), Error> {
|
||||
fn register_keyboard(&mut self, sender: EventSender<KeyEvent>) -> Result<(), HostError<Self::Error>> {
|
||||
if self.keyboard.is_some() {
|
||||
return Err(Error::new("A keyboard updater has already been registered with the frontend"));
|
||||
return Err(HostError::Specific(Error::new("A keyboard updater has already been registered with the frontend")));
|
||||
}
|
||||
self.keyboard = Some(sender);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn register_mouse(&mut self, sender: EventSender<MouseEvent>) -> Result<(), Error> {
|
||||
fn register_mouse(&mut self, sender: EventSender<MouseEvent>) -> Result<(), HostError<Self::Error>> {
|
||||
if self.mouse.is_some() {
|
||||
return Err(Error::new("A mouse updater has already been registered with the frontend"));
|
||||
return Err(HostError::Specific(Error::new("A mouse updater has already been registered with the frontend")));
|
||||
}
|
||||
self.mouse = Some(sender);
|
||||
Ok(())
|
||||
|
|
|
@ -11,6 +11,7 @@ femtos = "0.1"
|
|||
|
||||
moa-core = { path = "../../core" }
|
||||
moa-common = { path = "../common", features = ["audio"] }
|
||||
moa-host = { path = "../../libraries/host" }
|
||||
|
||||
moa-systems-genesis = { path = "../../systems/genesis" }
|
||||
|
||||
|
|
|
@ -5,7 +5,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, ControllerInput, ControllerEvent, EventSender, Audio, DummyAudio, FrameReceiver};
|
||||
use moa_host::{Host, HostError, PixelEncoding, Frame, ControllerDevice, ControllerInput, ControllerEvent, EventSender, Audio, DummyAudio, FrameReceiver};
|
||||
use moa_common::{AudioMixer, AudioSource, CpalAudioOutput};
|
||||
|
||||
use crate::settings;
|
||||
|
@ -45,17 +45,19 @@ impl PixelsFrontend {
|
|||
}
|
||||
|
||||
impl Host for PixelsFrontend {
|
||||
fn add_video_source(&mut self, receiver: FrameReceiver) -> Result<(), Error> {
|
||||
type Error = Error;
|
||||
|
||||
fn add_video_source(&mut self, receiver: FrameReceiver) -> Result<(), HostError<Self::Error>> {
|
||||
self.video = Some(receiver);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn register_controllers(&mut self, sender: EventSender<ControllerEvent>) -> Result<(), Error> {
|
||||
fn register_controllers(&mut self, sender: EventSender<ControllerEvent>) -> Result<(), HostError<Self::Error>> {
|
||||
self.controllers = Some(sender);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn add_audio_source(&mut self) -> Result<Box<dyn Audio>, Error> {
|
||||
fn add_audio_source(&mut self) -> Result<Box<dyn Audio>, HostError<Self::Error>> {
|
||||
let source = AudioSource::new(self.mixer.clone());
|
||||
Ok(Box::new(source))
|
||||
//Ok(Box::new(DummyAudio()))
|
||||
|
|
|
@ -14,7 +14,7 @@ use wasm_bindgen::closure::Closure;
|
|||
|
||||
use femtos::{Duration as FemtosDuration};
|
||||
use moa_core::{System, Device};
|
||||
use moa_core::host::{ControllerInput, ControllerDevice, ControllerEvent, EventSender};
|
||||
use moa_host::{ControllerInput, ControllerDevice, ControllerEvent, EventSender};
|
||||
|
||||
use crate::settings;
|
||||
use crate::frontend::{self, PixelsFrontend, LoadSystemFn};
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
[package]
|
||||
name = "moa-debugger"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
moa-core = { path = "../../core" }
|
|
@ -1,5 +1,5 @@
|
|||
|
||||
use crate::{Error, System, Address, Addressable};
|
||||
use moa_core::{Error, System, Address, Addressable};
|
||||
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
|
@ -0,0 +1,16 @@
|
|||
[package]
|
||||
name = "moa-host"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
rust-version = "1.60"
|
||||
categories = ["emulators"]
|
||||
keywords = ["emulators"]
|
||||
description = "traits for abstracting the I/O of an emulated system to the host"
|
||||
authors = ["transistor fet <trans@jabberwocky.ca>"]
|
||||
license = "MIT OR Apache-2.0"
|
||||
repository = "https://github.com/transistorfet/moa"
|
||||
readme = "README.md"
|
||||
|
||||
[dependencies]
|
||||
femtos = "0.1"
|
||||
thiserror = "1.0"
|
|
@ -1,7 +1,7 @@
|
|||
use std::sync::{Arc, Mutex};
|
||||
use femtos::Instant;
|
||||
|
||||
use crate::host::traits::ClockedQueue;
|
||||
use crate::traits::ClockedQueue;
|
||||
|
||||
pub const MASK_COLOUR: u32 = 0xFFFFFFFF;
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
|
||||
mod audio;
|
||||
mod controllers;
|
||||
mod gfx;
|
||||
mod input;
|
||||
mod keys;
|
||||
mod mouse;
|
||||
mod traits;
|
||||
|
||||
pub use crate::audio::{Sample, AudioFrame};
|
||||
pub use crate::gfx::{Pixel, PixelEncoding, Frame, FrameSender, FrameReceiver, frame_queue};
|
||||
pub use crate::keys::{Key, KeyEvent};
|
||||
pub use crate::mouse::{MouseButton, MouseEventType, MouseEvent, MouseState};
|
||||
pub use crate::controllers::{ControllerDevice, ControllerInput, ControllerEvent};
|
||||
pub use crate::input::{EventSender, EventReceiver, event_queue};
|
||||
pub use crate::traits::{Host, HostError, Tty, Audio, ClockedQueue, DummyAudio};
|
||||
|
|
@ -1,40 +1,70 @@
|
|||
|
||||
use std::fmt;
|
||||
use std::error::Error;
|
||||
use std::collections::VecDeque;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use femtos::Instant;
|
||||
|
||||
use crate::Error;
|
||||
use crate::host::gfx::FrameReceiver;
|
||||
use crate::host::audio::Sample;
|
||||
use crate::host::keys::KeyEvent;
|
||||
use crate::host::controllers::ControllerEvent;
|
||||
use crate::host::mouse::MouseEvent;
|
||||
use crate::host::input::EventSender;
|
||||
use crate::gfx::FrameReceiver;
|
||||
use crate::audio::Sample;
|
||||
use crate::keys::KeyEvent;
|
||||
use crate::controllers::ControllerEvent;
|
||||
use crate::mouse::MouseEvent;
|
||||
use crate::input::EventSender;
|
||||
|
||||
#[derive(Clone, Debug, thiserror::Error)]
|
||||
pub enum HostError<E> {
|
||||
TTYNotSupported,
|
||||
VideoSourceNotSupported,
|
||||
AudioSourceNotSupported,
|
||||
ControllerNotSupported,
|
||||
KeyboardNotSupported,
|
||||
MouseNotSupported,
|
||||
#[from(E)]
|
||||
Specific(E),
|
||||
}
|
||||
|
||||
/*
|
||||
impl<E> fmt::Display for HostError<E> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
HostError::TTYNotSupported => write!(f, "This frontend doesn't support PTYs"),
|
||||
HostError::VideoSourceNotSupported => write!(f, "This frontend doesn't support windows"),
|
||||
HostError::AudioSourceNotSupported => write!(f, "This frontend doesn't support the sound"),
|
||||
HostError::ControllerNotSupported => write!(f, "This frontend doesn't support game controllers"),
|
||||
HostError::KeyboardNotSupported => write!(f, "This frontend doesn't support the keyboard"),
|
||||
HostError::MouseNotSupported => write!(f, "This frontend doesn't support the mouse"),
|
||||
HostError::Specific(err) => write!(f, "{}", err),
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
pub trait Host {
|
||||
fn add_pty(&self) -> Result<Box<dyn Tty>, Error> {
|
||||
Err(Error::new("This frontend doesn't support PTYs"))
|
||||
type Error: Error;
|
||||
|
||||
fn add_pty(&self) -> Result<Box<dyn Tty>, HostError<Self::Error>> {
|
||||
Err(HostError::TTYNotSupported)
|
||||
}
|
||||
|
||||
fn add_video_source(&mut self, _receiver: FrameReceiver) -> Result<(), Error> {
|
||||
Err(Error::new("This frontend doesn't support windows"))
|
||||
fn add_video_source(&mut self, _receiver: FrameReceiver) -> Result<(), HostError<Self::Error>> {
|
||||
Err(HostError::VideoSourceNotSupported)
|
||||
}
|
||||
|
||||
fn add_audio_source(&mut self) -> Result<Box<dyn Audio>, Error> {
|
||||
Err(Error::new("This frontend doesn't support the sound"))
|
||||
fn add_audio_source(&mut self) -> Result<Box<dyn Audio>, HostError<Self::Error>> {
|
||||
Err(HostError::AudioSourceNotSupported)
|
||||
}
|
||||
|
||||
fn register_controllers(&mut self, _sender: EventSender<ControllerEvent>) -> Result<(), Error> {
|
||||
Err(Error::new("This frontend doesn't support game controllers"))
|
||||
fn register_controllers(&mut self, _sender: EventSender<ControllerEvent>) -> Result<(), HostError<Self::Error>> {
|
||||
Err(HostError::ControllerNotSupported)
|
||||
}
|
||||
|
||||
fn register_keyboard(&mut self, _sender: EventSender<KeyEvent>) -> Result<(), Error> {
|
||||
Err(Error::new("This frontend doesn't support the keyboard"))
|
||||
fn register_keyboard(&mut self, _sender: EventSender<KeyEvent>) -> Result<(), HostError<Self::Error>> {
|
||||
Err(HostError::KeyboardNotSupported)
|
||||
}
|
||||
|
||||
fn register_mouse(&mut self, _sender: EventSender<MouseEvent>) -> Result<(), Error> {
|
||||
Err(Error::new("This frontend doesn't support the mouse"))
|
||||
fn register_mouse(&mut self, _sender: EventSender<MouseEvent>) -> Result<(), HostError<Self::Error>> {
|
||||
Err(HostError::MouseNotSupported)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
[package]
|
||||
name = "moa-signals"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
femtos = "0.1"
|
|
@ -7,3 +7,4 @@ edition = "2021"
|
|||
log = "0.4"
|
||||
femtos = "0.1"
|
||||
moa-core = { path = "../../core" }
|
||||
moa-signals = { path = "../../libraries/signals" }
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
|
||||
use femtos::{Instant, Duration};
|
||||
|
||||
use moa_core::{Error, System, Address, Addressable, Steppable, Transmutable, Signal, ObservableSignal, Observable};
|
||||
|
||||
use moa_core::{Error, System, Address, Addressable, Steppable, Transmutable};
|
||||
use moa_signals::{Signal, ObservableSignal, Observable};
|
||||
|
||||
const REG_OUTPUT_B: Address = 0x00;
|
||||
const REG_OUTPUT_A: Address = 0x01;
|
||||
|
|
|
@ -7,3 +7,4 @@ edition = "2021"
|
|||
log = "0.4"
|
||||
femtos = "0.1"
|
||||
moa-core = { path = "../../core" }
|
||||
moa-host = { path = "../../libraries/host" }
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
use femtos::{Instant, Duration, Frequency};
|
||||
|
||||
use moa_core::{System, Error, Address, Steppable, Addressable, Transmutable};
|
||||
use moa_core::host::Tty;
|
||||
use moa_host::Tty;
|
||||
|
||||
|
||||
const REG_MR1A_MR2A: Address = 0x01;
|
||||
|
|
|
@ -7,5 +7,6 @@ edition = "2021"
|
|||
log = "^0.4"
|
||||
femtos = "0.1"
|
||||
moa-core = { path = "../../core" }
|
||||
moa-host = { path = "../../libraries/host" }
|
||||
moa-audio = { path = "../../libraries/audio" }
|
||||
lazy_static = "1.4.0"
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
use femtos::{Instant, Duration, Frequency};
|
||||
|
||||
use moa_core::{System, Error, Address, Addressable, Steppable, Transmutable};
|
||||
use moa_core::host::{Host, Audio, Sample};
|
||||
use moa_host::{Host, HostError, Audio, Sample};
|
||||
use moa_audio::SquareWave;
|
||||
|
||||
|
||||
|
@ -94,7 +94,10 @@ pub struct Sn76489 {
|
|||
}
|
||||
|
||||
impl Sn76489 {
|
||||
pub fn new<H: Host>(host: &mut H, _clock_frequency: Frequency) -> Result<Self, Error> {
|
||||
pub fn new<H, E>(host: &mut H, _clock_frequency: Frequency) -> Result<Self, HostError<E>>
|
||||
where
|
||||
H: Host<Error = E>
|
||||
{
|
||||
let source = host.add_audio_source()?;
|
||||
let sample_rate = source.samples_per_second();
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ use lazy_static::lazy_static;
|
|||
use femtos::{Instant, Duration, Frequency};
|
||||
|
||||
use moa_core::{System, Error, Address, Addressable, Steppable, Transmutable};
|
||||
use moa_core::host::{Host, Audio, Sample};
|
||||
use moa_host::{Host, HostError, Audio, Sample};
|
||||
|
||||
|
||||
/// Table of shift values for each possible rate angle
|
||||
|
@ -739,7 +739,10 @@ pub struct Ym2612 {
|
|||
}
|
||||
|
||||
impl Ym2612 {
|
||||
pub fn new<H: Host>(host: &mut H, clock_frequency: Frequency) -> Result<Self, Error> {
|
||||
pub fn new<H, E>(host: &mut H, clock_frequency: Frequency) -> Result<Self, HostError<E>>
|
||||
where
|
||||
H: Host<Error = E>,
|
||||
{
|
||||
let source = host.add_audio_source()?;
|
||||
let fm_clock = clock_frequency / (6 * 24);
|
||||
let fm_clock_period = fm_clock.period_duration();
|
||||
|
|
|
@ -7,6 +7,7 @@ edition = "2021"
|
|||
log = "0.4"
|
||||
femtos = "0.1"
|
||||
moa-core = { path = "../../core" }
|
||||
moa-host = { path = "../../libraries/host" }
|
||||
moa-m68k = { path = "../../cpus/m68k" }
|
||||
moa-peripherals-generic = { path = "../../peripherals/generic" }
|
||||
moa-peripherals-motorola = { path = "../../peripherals/motorola" }
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
use femtos::Frequency;
|
||||
|
||||
use moa_core::{System, Error, Debuggable, MemoryBlock, Device};
|
||||
use moa_core::host::Host;
|
||||
use moa_host::Host;
|
||||
|
||||
use moa_m68k::{M68k, M68kType};
|
||||
use moa_peripherals_generic::AtaDevice;
|
||||
|
|
|
@ -7,6 +7,8 @@ edition = "2021"
|
|||
log = "0.4"
|
||||
femtos = "0.1"
|
||||
moa-core = { path = "../../core" }
|
||||
moa-signals = { path = "../../libraries/signals" }
|
||||
moa-host = { path = "../../libraries/host" }
|
||||
moa-peripherals-yamaha = { path = "../../peripherals/yamaha" }
|
||||
moa-m68k = { path = "../../cpus/m68k" }
|
||||
moa-z80 = { path = "../../cpus/z80" }
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
|
||||
use femtos::{Instant, Duration};
|
||||
|
||||
use moa_core::{System, Error, Signal, Address, Addressable, Steppable, Transmutable};
|
||||
use moa_core::host::{self, Host, ControllerDevice, ControllerInput, ControllerEvent, EventReceiver};
|
||||
|
||||
use moa_core::{System, Error, Address, Addressable, Steppable, Transmutable};
|
||||
use moa_host::{self, Host, HostError, ControllerDevice, ControllerInput, ControllerEvent, EventReceiver};
|
||||
use moa_signals::{Signal};
|
||||
|
||||
const REG_VERSION: Address = 0x01;
|
||||
const REG_DATA1: Address = 0x03;
|
||||
|
@ -97,8 +97,11 @@ pub struct GenesisControllers {
|
|||
}
|
||||
|
||||
impl GenesisControllers {
|
||||
pub fn new<H: Host>(host: &mut H) -> Result<Self, Error> {
|
||||
let (sender, receiver) = host::event_queue();
|
||||
pub fn new<H, E>(host: &mut H) -> Result<Self, HostError<E>>
|
||||
where
|
||||
H: Host<Error = E>,
|
||||
{
|
||||
let (sender, receiver) = moa_host::event_queue();
|
||||
host.register_controllers(sender)?;
|
||||
|
||||
Ok(Self {
|
||||
|
|
|
@ -3,8 +3,8 @@ use std::rc::Rc;
|
|||
use std::cell::{Cell, RefCell};
|
||||
use femtos::Instant;
|
||||
|
||||
use moa_core::{Bus, Signal, Error, Address, Addressable, Transmutable};
|
||||
|
||||
use moa_core::{Bus, Error, Address, Addressable, Transmutable};
|
||||
use moa_signals::Signal;
|
||||
|
||||
const DEV_NAME: &str = "coprocessor";
|
||||
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
|
||||
use femtos::{Instant, Duration, Frequency};
|
||||
|
||||
use moa_core::{System, Error, EdgeSignal, Signal, Address, Addressable, Steppable, Inspectable, Transmutable, Device, read_beu16, dump_slice};
|
||||
use moa_core::host::{self, Host, Pixel, PixelEncoding, Frame, FrameSender};
|
||||
|
||||
use moa_core::{System, Error, Address, Addressable, Steppable, Inspectable, Transmutable, Device, read_beu16, dump_slice};
|
||||
use moa_host::{self, Host, HostError, Pixel, PixelEncoding, Frame, FrameSender};
|
||||
use moa_signals::{EdgeSignal, Signal};
|
||||
|
||||
const DEV_NAME: &str = "ym7101";
|
||||
|
||||
|
@ -717,17 +717,20 @@ pub struct Ym7101 {
|
|||
}
|
||||
|
||||
impl Ym7101 {
|
||||
pub fn new<H: Host>(host: &mut H, external_interrupt: Signal<bool>, sn_sound: Device) -> Ym7101 {
|
||||
let (sender, receiver) = host::frame_queue(320, 224);
|
||||
host.add_video_source(receiver).unwrap();
|
||||
pub fn new<H, E>(host: &mut H, external_interrupt: Signal<bool>, sn_sound: Device) -> Result<Ym7101, HostError<E>>
|
||||
where
|
||||
H: Host<Error = E>,
|
||||
{
|
||||
let (sender, receiver) = moa_host::frame_queue(320, 224);
|
||||
host.add_video_source(receiver)?;
|
||||
|
||||
Ym7101 {
|
||||
Ok(Ym7101 {
|
||||
sender,
|
||||
state: Ym7101State::default(),
|
||||
sn_sound,
|
||||
external_interrupt,
|
||||
vsync_interrupt: EdgeSignal::default(),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn set_register(&mut self, word: u16) {
|
||||
|
|
|
@ -6,7 +6,7 @@ use std::cell::RefCell;
|
|||
use femtos::Frequency;
|
||||
|
||||
use moa_core::{System, Error, MemoryBlock, Bus, Address, Addressable, Device};
|
||||
use moa_core::host::Host;
|
||||
use moa_host::Host;
|
||||
|
||||
use moa_m68k::{M68k, M68kType};
|
||||
use moa_z80::{Z80, Z80Type};
|
||||
|
@ -45,13 +45,13 @@ pub fn build_genesis<H: Host>(host: &mut H, mut options: SegaGenesisOptions) ->
|
|||
let rom = MemoryBlock::new(rom_data);
|
||||
//rom.read_only();
|
||||
let rom_end = rom.size();
|
||||
system.add_addressable_device(0x00000000, Device::new(rom)).unwrap();
|
||||
system.add_addressable_device(0x00000000, Device::new(rom))?;
|
||||
|
||||
let cartridge_nvram = MemoryBlock::new(vec![0; 0x400000 - rom_end]);
|
||||
system.add_addressable_device(rom_end as Address, Device::new(cartridge_nvram)).unwrap();
|
||||
system.add_addressable_device(rom_end as Address, Device::new(cartridge_nvram))?;
|
||||
|
||||
let ram = MemoryBlock::new(vec![0; 0x00010000]);
|
||||
system.add_addressable_device(0x00ff0000, Device::new(ram)).unwrap();
|
||||
system.add_addressable_device(0x00ff0000, Device::new(ram))?;
|
||||
|
||||
|
||||
// Build the Coprocessor's Bus
|
||||
|
@ -86,16 +86,16 @@ pub fn build_genesis<H: Host>(host: &mut H, mut options: SegaGenesisOptions) ->
|
|||
|
||||
let controllers = GenesisControllers::new(host)?;
|
||||
let interrupt = controllers.get_interrupt_signal();
|
||||
system.add_addressable_device(0x00a10000, Device::new(controllers)).unwrap();
|
||||
system.add_addressable_device(0x00a10000, Device::new(controllers))?;
|
||||
|
||||
let coproc = CoprocessorCoordinator::new(reset, bus_request);
|
||||
system.add_addressable_device(0x00a11000, Device::new(coproc)).unwrap();
|
||||
system.add_addressable_device(0x00a11000, Device::new(coproc))?;
|
||||
|
||||
let vdp = Ym7101::new(host, interrupt, coproc_sn_sound);
|
||||
system.add_peripheral("vdp", 0x00c00000, Device::new(vdp)).unwrap();
|
||||
let vdp = Ym7101::new(host, interrupt, coproc_sn_sound)?;
|
||||
system.add_peripheral("vdp", 0x00c00000, Device::new(vdp))?;
|
||||
|
||||
let cpu = M68k::from_type(M68kType::MC68000, Frequency::from_hz(7_670_454), system.bus.clone(), 0);
|
||||
system.add_interruptable_device("cpu", Device::new(cpu)).unwrap();
|
||||
system.add_interruptable_device("cpu", Device::new(cpu))?;
|
||||
|
||||
Ok(system)
|
||||
}
|
||||
|
|
|
@ -7,6 +7,8 @@ edition = "2021"
|
|||
log = "0.4"
|
||||
femtos = "0.1"
|
||||
moa-core = { path = "../../core" }
|
||||
moa-host = { path = "../../libraries/host" }
|
||||
moa-signals = { path = "../../libraries/signals" }
|
||||
moa-m68k = { path = "../../cpus/m68k" }
|
||||
moa-peripherals-mos = { path = "../../peripherals/mos" }
|
||||
moa-peripherals-zilog = { path = "../../peripherals/zilog" }
|
||||
|
|
|
@ -3,7 +3,8 @@ use std::rc::Rc;
|
|||
use std::cell::RefCell;
|
||||
use femtos::{Instant, Duration};
|
||||
|
||||
use moa_core::{System, Bus, Error, Observable, Address, Addressable, AddressRepeater, Steppable, Transmutable, Device};
|
||||
use moa_core::{System, Bus, Error, Address, Addressable, AddressRepeater, Steppable, Transmutable, Device};
|
||||
use moa_signals::Observable;
|
||||
|
||||
use moa_peripherals_mos::Mos6522;
|
||||
use moa_peripherals_zilog::Z8530;
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
use femtos::Duration;
|
||||
|
||||
use moa_core::{System, Error, Address, Addressable, Steppable, Transmutable};
|
||||
use moa_core::host::{self, Host, Frame, FrameSender, Pixel};
|
||||
use moa_host::{self, Host, HostError, Frame, FrameSender, Pixel};
|
||||
|
||||
|
||||
const SCRN_BASE: u32 = 0x07A700;
|
||||
|
@ -13,8 +13,11 @@ pub struct MacVideo {
|
|||
}
|
||||
|
||||
impl MacVideo {
|
||||
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);
|
||||
pub fn new<H, E>(host: &mut H) -> Result<Self, HostError<E>>
|
||||
where
|
||||
H: Host<Error = E>,
|
||||
{
|
||||
let (frame_sender, frame_receiver) = moa_host::frame_queue(SCRN_SIZE.0, SCRN_SIZE.1);
|
||||
|
||||
host.add_video_source(frame_receiver)?;
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
use femtos::Frequency;
|
||||
|
||||
use moa_core::{System, Error, MemoryBlock, Debuggable, Device};
|
||||
use moa_core::host::Host;
|
||||
use moa_host::Host;
|
||||
|
||||
use moa_m68k::{M68k, M68kType};
|
||||
|
||||
|
|
|
@ -7,5 +7,6 @@ edition = "2021"
|
|||
log = "0.4"
|
||||
femtos = "0.1"
|
||||
moa-core = { path = "../../core" }
|
||||
moa-host = { path = "../../libraries/host" }
|
||||
moa-z80 = { path = "../../cpus/z80" }
|
||||
|
||||
|
|
|
@ -650,7 +650,7 @@ const CHARACTERS: [[u8; 8]; 64] = [
|
|||
];
|
||||
|
||||
|
||||
use moa_core::host::Pixel;
|
||||
use moa_host::Pixel;
|
||||
|
||||
pub struct CharacterGenerator {
|
||||
pub row: i8,
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
|
||||
use moa_core::host::Key;
|
||||
use moa_host::Key;
|
||||
|
||||
#[inline(always)]
|
||||
pub fn set_bit(data: &mut [u8; 8], index: usize, bit: u8, state: bool) {
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
use femtos::{Instant, Duration};
|
||||
|
||||
use moa_core::{System, Error, Address, Addressable, Steppable, Transmutable};
|
||||
use moa_core::host::{self, Host, Frame, FrameSender, KeyEvent, EventReceiver};
|
||||
use moa_host::{self, Host, HostError, Frame, FrameSender, KeyEvent, EventReceiver};
|
||||
|
||||
use super::keymap;
|
||||
use super::charset::CharacterGenerator;
|
||||
|
@ -18,8 +18,11 @@ pub struct Model1Keyboard {
|
|||
}
|
||||
|
||||
impl Model1Keyboard {
|
||||
pub fn new<H: Host>(host: &mut H) -> Result<Self, Error> {
|
||||
let (sender, receiver) = host::event_queue();
|
||||
pub fn new<H, E>(host: &mut H) -> Result<Self, HostError<E>>
|
||||
where
|
||||
H: Host<Error = E>,
|
||||
{
|
||||
let (sender, receiver) = moa_host::event_queue();
|
||||
host.register_keyboard(sender)?;
|
||||
|
||||
Ok(Self {
|
||||
|
@ -86,8 +89,11 @@ pub struct Model1Video {
|
|||
}
|
||||
|
||||
impl Model1Video {
|
||||
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);
|
||||
pub fn new<H, E>(host: &mut H) -> Result<Self, HostError<E>>
|
||||
where
|
||||
H: Host<Error = E>,
|
||||
{
|
||||
let (frame_sender, frame_receiver) = moa_host::frame_queue(SCREEN_SIZE.0, SCREEN_SIZE.1);
|
||||
|
||||
host.add_video_source(frame_receiver)?;
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
use femtos::Frequency;
|
||||
|
||||
use moa_core::{System, Error, MemoryBlock, Device};
|
||||
use moa_core::host::Host;
|
||||
use moa_host::Host;
|
||||
|
||||
use moa_z80::{Z80, Z80Type};
|
||||
|
||||
|
|
Loading…
Reference in New Issue