Refactored how UI interfacing will work

This commit is contained in:
transistor 2021-10-21 21:55:27 -07:00
parent fab763a867
commit f9e018742b
11 changed files with 166 additions and 373 deletions

View File

@ -4,10 +4,11 @@ version = "0.1.0"
edition = "2018"
[workspace]
members = [".", "frontends/moa-console", "frontends/moa-piston"]
members = [".", "frontends/moa-console"]
default-members = ["frontends/moa-console"]
[features]
default = ["tty"]
tty = ["nix"]
[dependencies]

View File

@ -0,0 +1,52 @@
use std::thread;
use std::time::Duration;
use moa::error::Error;
use moa::system::System;
use moa::memory::MemoryBlock;
use moa::devices::wrap_transmutable;
use moa::cpus::m68k::{M68k, M68kType};
use moa::peripherals::ata::AtaDevice;
use moa::peripherals::mc68681::MC68681;
use moa::machines::computie::build_computie;
fn main() {
thread::spawn(|| {
let mut system = System::new();
let monitor = MemoryBlock::load("binaries/monitor.bin").unwrap();
system.add_addressable_device(0x00000000, wrap_transmutable(monitor)).unwrap();
let mut ram = MemoryBlock::new(vec![0; 0x00100000]);
ram.load_at(0, "binaries/kernel.bin").unwrap();
system.add_addressable_device(0x00100000, wrap_transmutable(ram)).unwrap();
let mut ata = AtaDevice::new();
ata.load("binaries/disk-with-partition-table.img").unwrap();
system.add_addressable_device(0x00600000, wrap_transmutable(ata)).unwrap();
let mut serial = MC68681::new();
system.add_addressable_device(0x00700000, wrap_transmutable(serial)).unwrap();
let mut cpu = M68k::new(M68kType::MC68030);
//cpu.enable_tracing();
//cpu.add_breakpoint(0x10781a);
//cpu.add_breakpoint(0x10bc9c);
//cpu.add_breakpoint(0x106a94);
//cpu.add_breakpoint(0x1015b2);
//cpu.add_breakpoint(0x103332);
//cpu.decoder.dump_disassembly(&mut system, 0x100000, 0x2000);
//cpu.decoder.dump_disassembly(&mut system, 0x2ac, 0x200);
system.add_interruptable_device(wrap_transmutable(cpu)).unwrap();
system.run_loop();
});
thread::sleep(Duration::from_secs(10));
}

View File

@ -1,10 +1,11 @@
use moa_console::ConsoleFrontend;
use moa::machines::computie::run_computie;
use moa::machines::computie::build_computie;
fn main() {
let mut frontend = ConsoleFrontend;
run_computie(&mut frontend);
let mut system = build_computie(&mut frontend).unwrap();
system.run_loop();
}

View File

@ -1,16 +1,13 @@
use moa::error::Error;
use moa::host::frontend::{Frontend, SharedCanvas, SharedAudio};
use moa::host::traits::{Host, WindowUpdater};
pub struct ConsoleFrontend;
impl Frontend for ConsoleFrontend {
fn get_canvas(&mut self) -> Result<SharedCanvas, Error> {
Err(Error::new("Console frontend doesn't support canvas"))
}
fn get_audio(&mut self) -> Result<SharedAudio, Error> {
Err(Error::new("Console frontend doesn't support audio"))
impl Host for ConsoleFrontend {
fn add_window(&self, updater: Box<dyn WindowUpdater>) -> Result<(), Error> {
println!("console: add_window() is not supported from the console; ignoring request...");
Ok(())
}
}

View File

@ -1,11 +0,0 @@
pub mod traits;
pub use self::traits::{
Frontend,
Canvas,
SharedCanvas,
Audio,
SharedAudio,
};

View File

@ -1,345 +0,0 @@
use std::sync::{Arc, Mutex};
use crate::error::Error;
//pub trait Canvas: Send {
//}
//pub trait Audio {
//
//}
//pub type SharedCanvas = Arc<Mutex<Box<dyn Canvas>>>;
//pub type SharedAudio = Arc<Mutex<Box<dyn Audio>>>;
// TODO instead make something like HostAdapter, or a representation of the backend, which it's given to the builder function
pub trait Frontend {
//fn set_size(&mut self, x: u32, y: u32);
//fn draw_bitmap(&mut self, x: u32, y: u32, bitmap: &[u8]);
//fn set_update_callback(&mut self, update: Box<fn(&mut [u8]) -> ()>);
fn request_update(&self, x: u32, y: u32, bitmap: &[u8]);
}
struct HostAdapter {
bitmap: Mutex<Vec<u32>>,
}
// Types:
// Window (gfx out + input)
// Audio
// TTY
// Network
/*
// Opt 1 - Simple Callback
pub trait Window {
fn draw_bitmap(&self, x: u32, y: u32, bitmap: &[u32]);
}
pub trait Host {
fn register_update<T, W: Window>(&mut self, func: fn(T, W), data: T);
}
// TODO how will the object data be shared with the device
*/
/*
// Opt 4 - The Host Generic Device Method
pub trait Window {
fn request_update(&self, x: u32, y: u32, bitmap: &[u32]);
}
pub trait Host {
fn create_window<W: Window>(&mut self) -> W;
}
pub struct YmDevice<W: Window> {
pub window: W,
pub buffer: Vec<u32>,
}
impl<W: Window> YmDevice<W> {
pub fn new<H: Host>(host: &mut H) -> YmDevice<W> {
YmDevice {
window: host.create_window(),
buffer: vec![0; 200 * 200],
}
}
pub fn step(&mut self) {
self.window.request_update(200, 200, self.buffer.as_slice());
}
}
pub struct CustomWindow {
pub buffer: Mutex<Vec<u32>>,
}
impl CustomWindow {
fn request_update(&self, x: u32, y: u32, bitmap: &[u32]) {
let mut target = self.buffer.lock().unwrap();
for i in 0..target.len() {
target[i] = bitmap[i];
}
}
}
*/
/*
// Opt 2 - The Callback Through Trait Method
pub trait Window {
fn render(&self, x: u32, y: u32, bitmap: &mut [u8]);
}
pub trait Host {
fn register_window<W: Window>(&mut self, window: Arc<W>) -> Result<(), Error>;
}
pub struct YmDevice(Arc<YmDeviceInternal>);
pub struct YmDeviceInternal {
//pub window: Window,
// some things
}
impl YmDevice {
pub fn new<H: Host>(host: &mut H) -> YmDevice {
let device = Arc::new(YmDeviceInternal {
});
host.register_window(device.clone()).unwrap();
YmDevice(device)
}
}
impl Window for YmDeviceInternal {
fn render(&self, x: u32, y: u32, bitmap: &mut [u8]) {
println!("here");
}
}
impl Addressable for YmDevice {
}
*/
/*
// Opt 3 - The Callback Through Common Backend-defined Object Method
pub struct Window {
width: u32,
height: u32,
buffer: Vec<u32>
}
impl Window {
fn render(&self, x: u32, y: u32, bitmap: &mut [u8]) {
}
}
pub trait Host {
fn register_window<W: Window>(&mut self, window: Arc<W>) -> Result<(), Error>;
}
pub struct YmDevice(Arc<YmDeviceInternal>);
pub struct YmDeviceInternal {
//pub window: Window,
// some things
}
impl YmDevice {
pub fn new<H: Host>(host: &mut H) -> YmDevice {
let device = Arc::new(YmDeviceInternal {
});
host.register_window(device.clone()).unwrap();
YmDevice(device)
}
}
impl Window for YmDeviceInternal {
fn render(&self, x: u32, y: u32, bitmap: &mut [u8]) {
println!("here");
}
}
//impl Addressable for YmDevice {
//
//}
*/
pub trait Host {
fn add_window(&self, window: Box<dyn Window>);
//fn create_pty(&self) -> Tty;
}
// TODO should you rename this Drawable, FrameUpdater, WindowUpdater?
pub trait Window: Send {
fn update_frame(&mut self, width: u32, height: u32, bitmap: &mut [u32]);
}
#[derive(Clone)]
pub struct Frame {
pub width: u32,
pub height: u32,
pub bitmap: Vec<u32>,
}
pub struct FrameSwapper {
pub current: Frame,
pub previous: Frame,
}
impl FrameSwapper {
pub fn new() -> FrameSwapper {
FrameSwapper {
current: Frame { width: 0, height: 0, bitmap: vec![] },
previous: Frame { width: 0, height: 0, bitmap: vec![] },
}
}
}
impl Window for FrameSwapper {
fn update_frame(&mut self, width: u32, height: u32, bitmap: &mut [u32]) {
std::mem::swap(&mut self.current, &mut self.previous);
println!("{} {}", self.current.width, self.current.height);
if self.current.width != width || self.current.height != height {
self.current.width = width;
self.current.height = height;
self.current.bitmap.resize((width * height) as usize, 0);
self.previous = self.current.clone();
return;
}
for i in 0..(width as usize * height as usize) {
bitmap[i] = self.current.bitmap[i];
}
}
}
pub struct FrameSwapperWrapper(Arc<Mutex<FrameSwapper>>);
impl Window for FrameSwapperWrapper {
fn update_frame(&mut self, width: u32, height: u32, bitmap: &mut [u32]) {
self.0.lock().map(|mut swapper| swapper.update_frame(width, height, bitmap));
}
}
pub struct YmDeviceTransmutable(YmDevice);
pub struct YmDevice {
pub count2: u32,
pub count: Mutex<u32>,
pub frame_swapper: Arc<Mutex<FrameSwapper>>,
}
impl YmDevice {
pub fn new<H: Host>(host: &H) -> YmDeviceTransmutable {
let frame_swapper = Arc::new(Mutex::new(FrameSwapper::new()));
let device = YmDevice {
count2: 0,
count: Mutex::new(0),
frame_swapper,
};
host.add_window(Box::new(FrameSwapperWrapper(device.frame_swapper.clone())));
YmDeviceTransmutable(device)
}
}
use crate::system::System;
use crate::devices::{Clock, Address, Transmutable, Steppable, Addressable, MAX_READ};
impl Transmutable for YmDeviceTransmutable {
fn as_addressable(&mut self) -> Option<&mut dyn Addressable> {
Some(self)
}
fn as_steppable(&mut self) -> Option<&mut dyn Steppable> {
Some(self)
}
}
impl Steppable for YmDeviceTransmutable {
fn step(&mut self, system: &System) -> Result<Clock, Error> {
self.0.count2 += 1;
if self.0.count2 > 1000 {
self.0.count2 = 0;
let value = match self.0.count.lock() {
Ok(mut value) => { *value = *value + 1; *value }
_ => { 0 },
};
let mut frame = self.0.frame_swapper.lock().unwrap();
for i in 0..(frame.current.width * frame.current.height) {
if i == value {
frame.current.bitmap[i as usize] = 0;
} else {
frame.current.bitmap[i as usize] = 12465;
}
}
}
Ok(1)
}
}
impl Addressable for YmDeviceTransmutable {
fn len(&self) -> usize {
0x20
}
fn read(&mut self, addr: Address, _count: usize) -> Result<[u8; MAX_READ], Error> {
let mut data = [0; MAX_READ];
debug!("read from register {:x}", addr);
Ok(data)
}
fn write(&mut self, addr: Address, data: &[u8]) -> Result<(), Error> {
debug!("write to register {:x} with {:x}", addr, data[0]);
Ok(())
}
}
/*
pub struct CustomWindow {
pub buffer: Mutex<Vec<u32>>,
}
impl CustomWindow {
fn request_update(&self, x: u32, y: u32, bitmap: &[u32]) {
let mut target = self.buffer.lock().unwrap();
for i in 0..target.len() {
target[i] = bitmap[i];
}
}
}
*/

View File

@ -1,5 +1,5 @@
pub mod frontend;
pub mod traits;
#[cfg(feature = "tty")]
pub mod tty;

73
src/host/traits.rs Normal file
View File

@ -0,0 +1,73 @@
use std::sync::{Arc, Mutex};
use crate::error::Error;
pub trait Host {
fn add_window(&self, updater: Box<dyn WindowUpdater>) -> Result<(), Error>;
//fn create_pty(&self) -> Tty;
}
// TODO should you rename this Drawable, FrameUpdater, WindowUpdater?
pub trait WindowUpdater: Send {
fn update_frame(&mut self, width: u32, height: u32, bitmap: &mut [u32]);
}
#[derive(Clone)]
pub struct Frame {
pub width: u32,
pub height: u32,
pub bitmap: Vec<u32>,
}
pub struct FrameSwapper {
pub current: Frame,
pub previous: Frame,
}
impl FrameSwapper {
pub fn new() -> FrameSwapper {
FrameSwapper {
current: Frame { width: 0, height: 0, bitmap: vec![] },
previous: Frame { width: 0, height: 0, bitmap: vec![] },
}
}
pub fn new_shared() -> Arc<Mutex<FrameSwapper>> {
Arc::new(Mutex::new(FrameSwapper::new()))
}
pub fn to_boxed(swapper: Arc<Mutex<FrameSwapper>>) -> Box<dyn WindowUpdater> {
Box::new(FrameSwapperWrapper(swapper))
}
}
impl WindowUpdater for FrameSwapper {
fn update_frame(&mut self, width: u32, height: u32, bitmap: &mut [u32]) {
std::mem::swap(&mut self.current, &mut self.previous);
println!("{} {}", self.current.width, self.current.height);
if self.current.width != width || self.current.height != height {
self.current.width = width;
self.current.height = height;
self.current.bitmap.resize((width * height) as usize, 0);
self.previous = self.current.clone();
return;
}
for i in 0..(width as usize * height as usize) {
bitmap[i] = self.current.bitmap[i];
}
}
}
pub struct FrameSwapperWrapper(Arc<Mutex<FrameSwapper>>);
impl WindowUpdater for FrameSwapperWrapper {
fn update_frame(&mut self, width: u32, height: u32, bitmap: &mut [u32]) {
self.0.lock().map(|mut swapper| swapper.update_frame(width, height, bitmap));
}
}

View File

@ -1,15 +1,17 @@
use crate::error::Error;
use crate::system::System;
use crate::memory::MemoryBlock;
use crate::host::frontend::Frontend;
use crate::devices::wrap_transmutable;
use crate::cpus::m68k::{M68k, M68kType};
use crate::peripherals::ata::AtaDevice;
use crate::peripherals::mc68681::MC68681;
use crate::host::traits::Host;
pub fn run_computie(frontend: &mut dyn Frontend) {
pub fn build_computie<H: Host>(host: &H) -> Result<System, Error> {
let mut system = System::new();
let monitor = MemoryBlock::load("binaries/monitor.bin").unwrap();
@ -44,10 +46,11 @@ pub fn run_computie(frontend: &mut dyn Frontend) {
//cpu.decoder.dump_disassembly(&mut system, 0x2ac, 0x200);
system.add_interruptable_device(wrap_transmutable(cpu)).unwrap();
system.run_loop();
Ok(system)
}
pub fn run_computie_k30(frontend: &mut dyn Frontend) {
pub fn build_computie_k30<H: Host>(host: &H) -> Result<System, Error> {
let mut system = System::new();
let monitor = MemoryBlock::load("binaries/monitor-68030.bin").unwrap();
@ -79,7 +82,8 @@ pub fn run_computie_k30(frontend: &mut dyn Frontend) {
//cpu.decoder.dump_disassembly(&mut system, 0x2ac, 0x200);
system.add_interruptable_device(wrap_transmutable(cpu)).unwrap();
system.run_loop();
Ok(system)
}
pub fn launch_terminal_emulator(name: String) {

View File

@ -88,5 +88,20 @@ impl System {
}
}
}
pub fn run_for(&mut self, clocks: Clock) -> Result<(), Error> {
let target = self.clock + clocks;
while self.clock < target {
match self.step() {
Ok(()) => { },
Err(err) => {
self.exit_error();
println!("{:?}", err);
return Err(err);
},
}
}
Ok(())
}
}

View File

@ -1,4 +1,10 @@
* generics for the frontend that's passed to the builder functions is much better than a trait object
* you could possibly use a backend representation that is given to the frontened, rather than vice versa
* you could use callbacks in some way (ie. callbacks from the gui loop to the system)
* should the frontend and simulator parts be separated so that the simulator part can be a library package?
* it should be call something other than canvas because it'll have input as well
* how will you get the canvas/app shared object between the sim thread and the io thread? It kind of has to be passed through the system-creation