diff --git a/frontends/moa-minifb/src/bin/moa-genesis.rs b/frontends/moa-minifb/src/bin/moa-genesis.rs index 66c8125..1f1bb98 100644 --- a/frontends/moa-minifb/src/bin/moa-genesis.rs +++ b/frontends/moa-minifb/src/bin/moa-genesis.rs @@ -1,28 +1,43 @@ use std::thread; -use std::sync::Arc; +use std::time::Duration; +use std::sync::{Arc, Mutex}; use moa::machines::genesis::build_genesis; -use moa_minifb::MiniFrontend; +use moa_minifb::MiniFrontendBuilder; fn main() { - /* - let mut frontend = Arc::new(MiniFrontend::init_frontend()); + let mut frontend = Arc::new(Mutex::new(MiniFrontendBuilder::new())); { let frontend = frontend.clone(); thread::spawn(move || { - let mut system = build_genesis(&*frontend).unwrap(); + let mut system = build_genesis(&mut *(frontend.lock().unwrap())).unwrap(); + frontend.lock().unwrap().finalize(); system.run_loop(); }); } - frontend.start(); + wait_until_initialized(frontend.clone()); + + frontend + .lock().unwrap() + .build() + .start(None); + + /* + let mut frontend = MiniFrontendBuilder::new(); + let mut system = build_genesis(&mut frontend).unwrap(); + + frontend + .build() + .start(Some(system)); */ - - let mut frontend = MiniFrontend::init_frontend(); - let mut system = build_genesis(&frontend).unwrap(); - - frontend.start(system); +} + +fn wait_until_initialized(frontend: Arc>) { + while frontend.lock().unwrap().finalized == false { + thread::sleep(Duration::from_millis(10)); + } } diff --git a/frontends/moa-minifb/src/keys.rs b/frontends/moa-minifb/src/keys.rs new file mode 100644 index 0000000..e4eed00 --- /dev/null +++ b/frontends/moa-minifb/src/keys.rs @@ -0,0 +1,112 @@ + +use minifb::Key as MiniKey; +use moa::host::keys::Key; + +pub fn map_key(key: MiniKey) -> Key { + match key { + MiniKey::Key0 => Key::Num0, + MiniKey::Key1 => Key::Num1, + MiniKey::Key2 => Key::Num2, + MiniKey::Key3 => Key::Num3, + MiniKey::Key4 => Key::Num4, + MiniKey::Key5 => Key::Num5, + MiniKey::Key6 => Key::Num6, + MiniKey::Key7 => Key::Num7, + MiniKey::Key8 => Key::Num8, + MiniKey::Key9 => Key::Num9, + MiniKey::A => Key::A, + MiniKey::B => Key::B, + MiniKey::C => Key::C, + MiniKey::D => Key::D, + MiniKey::E => Key::E, + MiniKey::F => Key::F, + MiniKey::G => Key::G, + MiniKey::H => Key::H, + MiniKey::I => Key::I, + MiniKey::J => Key::J, + MiniKey::K => Key::K, + MiniKey::L => Key::L, + MiniKey::M => Key::M, + MiniKey::N => Key::N, + MiniKey::O => Key::O, + MiniKey::P => Key::P, + MiniKey::Q => Key::Q, + MiniKey::R => Key::R, + MiniKey::S => Key::S, + MiniKey::T => Key::T, + MiniKey::U => Key::U, + MiniKey::V => Key::V, + MiniKey::W => Key::W, + MiniKey::X => Key::X, + MiniKey::Y => Key::Y, + MiniKey::Z => Key::Z, + MiniKey::F1 => Key::F1, + MiniKey::F2 => Key::F2, + MiniKey::F3 => Key::F3, + MiniKey::F4 => Key::F4, + MiniKey::F5 => Key::F5, + MiniKey::F6 => Key::F6, + MiniKey::F7 => Key::F7, + MiniKey::F8 => Key::F8, + MiniKey::F9 => Key::F9, + MiniKey::F10 => Key::F10, + MiniKey::F11 => Key::F11, + MiniKey::F12 => Key::F12, + MiniKey::Down => Key::Down, + MiniKey::Left => Key::Left, + MiniKey::Right => Key::Right, + MiniKey::Up => Key::Up, + MiniKey::Apostrophe => Key::Apostrophe, + MiniKey::Backquote => Key::Backquote, + MiniKey::Backslash => Key::Backslash, + MiniKey::Comma => Key::Comma, + MiniKey::Equal => Key::Equals, + MiniKey::LeftBracket => Key::LeftBracket, + MiniKey::Minus => Key::Minus, + MiniKey::Period => Key::Period, + MiniKey::RightBracket => Key::RightBracket, + MiniKey::Semicolon => Key::Semicolon, + MiniKey::Slash => Key::Slash, + MiniKey::Backspace => Key::Backspace, + MiniKey::Delete => Key::Delete, + MiniKey::End => Key::End, + MiniKey::Enter => Key::Enter, + MiniKey::Escape => Key::Escape, + MiniKey::Home => Key::Home, + MiniKey::Insert => Key::Insert, + MiniKey::PageDown => Key::PageDown, + MiniKey::PageUp => Key::PageUp, + MiniKey::Pause => Key::Pause, + MiniKey::Space => Key::Space, + MiniKey::Tab => Key::Tab, + MiniKey::NumLock => Key::NumLock, + MiniKey::CapsLock => Key::CapsLock, + MiniKey::ScrollLock => Key::ScrollLock, + MiniKey::LeftShift => Key::LeftShift, + MiniKey::RightShift => Key::RightShift, + MiniKey::LeftCtrl => Key::LeftCtrl, + MiniKey::RightCtrl => Key::RightCtrl, + MiniKey::NumPad0 => Key::NumPad0, + MiniKey::NumPad1 => Key::NumPad1, + MiniKey::NumPad2 => Key::NumPad2, + MiniKey::NumPad3 => Key::NumPad3, + MiniKey::NumPad4 => Key::NumPad4, + MiniKey::NumPad5 => Key::NumPad5, + MiniKey::NumPad6 => Key::NumPad6, + MiniKey::NumPad7 => Key::NumPad7, + MiniKey::NumPad8 => Key::NumPad8, + MiniKey::NumPad9 => Key::NumPad9, + MiniKey::NumPadDot => Key::NumPadDot, + MiniKey::NumPadSlash => Key::NumPadSlash, + MiniKey::NumPadAsterisk => Key::NumPadAsterisk, + MiniKey::NumPadMinus => Key::NumPadMinus, + MiniKey::NumPadPlus => Key::NumPadPlus, + MiniKey::NumPadEnter => Key::NumPadEnter, + MiniKey::LeftAlt => Key::LeftAlt, + MiniKey::RightAlt => Key::RightAlt, + MiniKey::LeftSuper => Key::LeftSuper, + MiniKey::RightSuper => Key::RightSuper, + _ => Key::Unknown, + } +} + diff --git a/frontends/moa-minifb/src/lib.rs b/frontends/moa-minifb/src/lib.rs index dc4b13c..a59c963 100644 --- a/frontends/moa-minifb/src/lib.rs +++ b/frontends/moa-minifb/src/lib.rs @@ -1,4 +1,6 @@ +mod keys; + use std::time::Duration; use std::sync::{Arc, Mutex}; @@ -6,55 +8,94 @@ use minifb::{self, Key}; use moa::error::Error; use moa::system::System; -use moa::host::traits::{Host, JoystickDevice, JoystickUpdater, WindowUpdater}; +use moa::host::traits::{Host, JoystickDevice, JoystickUpdater, KeyboardUpdater, WindowUpdater}; + +use crate::keys::map_key; const WIDTH: usize = 320; const HEIGHT: usize = 224; -pub struct MiniFrontend { - pub buffer: Mutex>, - pub updater: Mutex>>, - pub input: Mutex>>, +pub struct MiniFrontendBuilder { + pub window: Option>, + pub joystick: Option>, + pub keyboard: Option>, + pub finalized: bool, } -impl Host for MiniFrontend { - fn add_window(&self, updater: Box) -> Result<(), Error> { - let mut unlocked = self.updater.lock().unwrap(); - if unlocked.is_some() { +impl MiniFrontendBuilder { + pub fn new() -> Self { + Self { + window: None, + joystick: None, + keyboard: None, + finalized: false, + } + } + + pub fn finalize(&mut self) { + self.finalized = true; + } + + pub fn build(&mut self) -> MiniFrontend { + let window = std::mem::take(&mut self.window); + let joystick = std::mem::take(&mut self.joystick); + let keyboard = std::mem::take(&mut self.keyboard); + MiniFrontend::new(window, joystick, keyboard) + } +} + +impl Host for MiniFrontendBuilder { + fn add_window(&mut self, updater: Box) -> Result<(), Error> { + if self.window.is_some() { return Err(Error::new("A window updater has already been registered with the frontend")); } - *unlocked = Some(updater); + self.window = Some(updater); Ok(()) } - fn register_joystick(&self, device: JoystickDevice, input: Box) -> Result<(), Error> { + fn register_joystick(&mut self, device: JoystickDevice, input: Box) -> Result<(), Error> { if device != JoystickDevice::A { return Ok(()) } - let mut unlocked = self.input.lock().unwrap(); - if unlocked.is_some() { - return Err(Error::new("A window updater has already been registered with the frontend")); + if self.joystick.is_some() { + return Err(Error::new("A joystick updater has already been registered with the frontend")); } - *unlocked = Some(input); + self.joystick = Some(input); + Ok(()) + } + + fn register_keyboard(&mut self, input: Box) -> Result<(), Error> { + if self.keyboard.is_some() { + return Err(Error::new("A keyboard updater has already been registered with the frontend")); + } + self.keyboard = Some(input); Ok(()) } } + +pub struct MiniFrontend { + pub buffer: Vec, + pub window: Option>, + pub joystick: Option>, + pub keyboard: Option>, +} + impl MiniFrontend { - pub fn init_frontend() -> MiniFrontend { - MiniFrontend { - buffer: Mutex::new(vec![0; WIDTH * HEIGHT]), - updater: Mutex::new(None), - input: Mutex::new(None), + pub fn new(window: Option>, joystick: Option>, keyboard: Option>) -> Self { + Self { + buffer: vec![0; WIDTH * HEIGHT], + window, + joystick, + keyboard, } } - //pub fn start(&self) { - pub fn start(&self, mut system: System) { + pub fn start(&mut self, mut system: Option) { let mut options = minifb::WindowOptions::default(); - options.scale = minifb::Scale::X4; + options.scale = minifb::Scale::X2; let mut window = minifb::Window::new( "Test - ESC to exit", @@ -70,26 +111,37 @@ impl MiniFrontend { window.limit_update_rate(Some(Duration::from_micros(16600))); while window.is_open() && !window.is_key_down(Key::Escape) { - system.run_for(16_600_000).unwrap(); + if let Some(system) = system.as_mut() { + system.run_for(16_600_000).unwrap(); + } if let Some(keys) = window.get_keys_pressed(minifb::KeyRepeat::Yes) { let mut modifiers: u16 = 0; for key in keys { + if let Some(mut updater) = self.keyboard.as_mut() { + updater.update_keyboard(map_key(key), true); + } match key { Key::Enter => { modifiers |= 0xffff; }, - Key::D => { system.enable_debugging(); }, + Key::D => { system.as_ref().map(|s| s.enable_debugging()); }, _ => { }, } } - if let Some(updater) = &mut *self.input.lock().unwrap() { + if let Some(mut updater) = self.joystick.as_mut() { updater.update_joystick(modifiers); } } + if let Some(keys) = window.get_keys_released() { + for key in keys { + if let Some(mut updater) = self.keyboard.as_mut() { + updater.update_keyboard(map_key(key), false); + } + } + } - if let Some(updater) = &mut *self.updater.lock().unwrap() { - let mut buffer = self.buffer.lock().unwrap(); - updater.update_frame(WIDTH as u32, HEIGHT as u32, &mut buffer); - window.update_with_buffer(&buffer, WIDTH, HEIGHT).unwrap(); + if let Some(mut updater) = self.window.as_mut() { + updater.update_frame(WIDTH as u32, HEIGHT as u32, &mut self.buffer); + window.update_with_buffer(&self.buffer, WIDTH, HEIGHT).unwrap(); } } } diff --git a/src/host/gfx.rs b/src/host/gfx.rs index 4806bfb..86d7629 100644 --- a/src/host/gfx.rs +++ b/src/host/gfx.rs @@ -29,6 +29,12 @@ impl BlitableSurface for Frame { } } } + + fn clear(&mut self, value: u32) { + for i in 0..((self.width as usize) * (self.height as usize)) { + self.bitmap[i] = value; + } + } } @@ -38,15 +44,15 @@ pub struct FrameSwapper { } impl FrameSwapper { - pub fn new() -> FrameSwapper { + pub fn new(width: u32, height: u32) -> FrameSwapper { FrameSwapper { - current: Frame { width: 0, height: 0, bitmap: vec![] }, - previous: Frame { width: 0, height: 0, bitmap: vec![] }, + current: Frame { width, height, bitmap: vec![0; (width * height) as usize] }, + previous: Frame { width, height, bitmap: vec![0; (width * height) as usize] }, } } - pub fn new_shared() -> Arc> { - Arc::new(Mutex::new(FrameSwapper::new())) + pub fn new_shared(width: u32, height: u32) -> Arc> { + Arc::new(Mutex::new(FrameSwapper::new(width, height))) } pub fn to_boxed(swapper: Arc>) -> Box { diff --git a/src/host/mod.rs b/src/host/mod.rs index 2cafe27..43da817 100644 --- a/src/host/mod.rs +++ b/src/host/mod.rs @@ -5,4 +5,5 @@ pub mod traits; pub mod tty; pub mod gfx; +pub mod keys; diff --git a/src/host/traits.rs b/src/host/traits.rs index ccebc7b..b69733c 100644 --- a/src/host/traits.rs +++ b/src/host/traits.rs @@ -2,6 +2,7 @@ use std::sync::{Arc, Mutex}; use crate::error::Error; +use crate::host::keys::Key; #[derive(Copy, Clone, Debug, PartialEq)] pub enum JoystickDevice { @@ -13,8 +14,9 @@ pub enum JoystickDevice { pub trait Host { //fn create_pty(&self) -> Result, Error>; - fn add_window(&self, updater: Box) -> Result<(), Error>; - fn register_joystick(&self, device: JoystickDevice, input: Box) -> Result<(), Error> { Err(Error::new("Not supported")) } + fn add_window(&mut self, updater: Box) -> Result<(), Error>; + fn register_joystick(&mut self, device: JoystickDevice, input: Box) -> Result<(), Error> { Err(Error::new("Not supported")) } + fn register_keyboard(&mut self, input: Box) -> Result<(), Error> { Err(Error::new("Not supported")) } } pub trait Tty { @@ -31,9 +33,14 @@ pub trait JoystickUpdater: Send { fn update_joystick(&mut self, modifiers: u16); } +pub trait KeyboardUpdater: Send { + fn update_keyboard(&mut self, key: Key, state: bool); +} + pub trait BlitableSurface { fn set_size(&mut self, width: u32, height: u32); fn blit>(&mut self, pos_x: u32, pos_y: u32, bitmap: B, width: u32, height: u32); + fn clear(&mut self, value: u32); } diff --git a/src/machines/computie.rs b/src/machines/computie.rs index 337e3ac..a570969 100644 --- a/src/machines/computie.rs +++ b/src/machines/computie.rs @@ -35,7 +35,7 @@ pub fn build_computie(host: &H) -> Result { let mut cpu = M68k::new(M68kType::MC68010, 10_000_000, BusPort::new(0, 24, 16, system.bus.clone())); //cpu.enable_tracing(); - cpu.add_breakpoint(0x10781a); + //cpu.add_breakpoint(0x10781a); //cpu.add_breakpoint(0x10bc9c); //cpu.add_breakpoint(0x106a94); //cpu.add_breakpoint(0x1015b2); diff --git a/src/machines/genesis.rs b/src/machines/genesis.rs index 8fe130e..eaa37ed 100644 --- a/src/machines/genesis.rs +++ b/src/machines/genesis.rs @@ -10,7 +10,7 @@ use crate::peripherals::genesis; use crate::host::traits::{Host, WindowUpdater}; -pub fn build_genesis(host: &H) -> Result { +pub fn build_genesis(host: &mut H) -> Result { let mut system = System::new(); //let mut rom = MemoryBlock::load("binaries/genesis/Sonic The Hedgehog (W) (REV 00) [!].bin").unwrap(); @@ -19,7 +19,7 @@ pub fn build_genesis(host: &H) -> Result { //let mut rom = MemoryBlock::load("binaries/genesis/Sonic the Hedgehog 3 (U) [!].bin").unwrap(); //let mut rom = MemoryBlock::load("binaries/genesis/Earthworm Jim (U) [h1].bin").unwrap(); //let mut rom = MemoryBlock::load("binaries/genesis/Home Alone (beta).bin").unwrap(); - //let mut mut rom = MemoryBlock::load("binaries/genesis/F1 World Championship (JUE) [!].bin").unwrap(); + //let mut rom = MemoryBlock::load("binaries/genesis/F1 World Championship (JUE) [!].bin").unwrap(); //let mut rom = MemoryBlock::load("binaries/genesis/Ren and Stimpy's Invention (U) [!].bin").unwrap(); //let mut rom = MemoryBlock::load("binaries/genesis/Out of this World (U) [!].bin").unwrap(); //let mut rom = MemoryBlock::load("binaries/genesis/Ghostbusters (REV 00) (JUE).bin").unwrap(); @@ -58,8 +58,11 @@ pub fn build_genesis(host: &H) -> Result { //cpu.add_breakpoint(0x16a0e); //cpu.add_breakpoint(0x16812); //cpu.add_breakpoint(0x166ec); - cpu.add_breakpoint(0x13e18); - cpu.add_breakpoint(0x16570); + //cpu.add_breakpoint(0x13e18); + //cpu.add_breakpoint(0x16570); + //cpu.add_breakpoint(0x1714); + + //cpu.add_breakpoint(0x43c2); system.add_interruptable_device("cpu", wrap_transmutable(cpu)).unwrap(); diff --git a/src/peripherals/genesis/controllers.rs b/src/peripherals/genesis/controllers.rs index ed613ae..8b6350c 100644 --- a/src/peripherals/genesis/controllers.rs +++ b/src/peripherals/genesis/controllers.rs @@ -104,7 +104,7 @@ impl GenesisController { } } - pub fn create(host: &H) -> Result { + pub fn create(host: &mut H) -> Result { let controller = GenesisController::new(); let joystick1 = Box::new(GenesisControllerUpdater(controller.port_1.data.clone(), controller.interrupt.clone())); diff --git a/todo.txt b/todo.txt index 79e33bc..dd53622 100644 --- a/todo.txt +++ b/todo.txt @@ -1,11 +1,25 @@ +* there is a problem where something writes to the rom area which causes a crash At 0x16cde, a move writes an invalid value to 0 via an indirect %a2 reg. The value of the reg might have changed during an interrupt, but it definitely breaks when the next interrupt occurs Before the loop is 0x16a0e which then calculates the count and such 0x16584 is where the memory address 0xffd11a is updated, which is then used for the bad 0x0000 address which causes the improper write. 0x16570 is a better start On broken cycle: %a1 = 1df40, moves that location + 1 to %d0 -* there is a problem where something writes to the rom area which causes a crash +* 0x1650e is where 0xffac08 is changed to 0xd100 +* 0x16572 is where 0xffd100 is changed to 0 +* what if it's a problem when turning the 0 into a full address (which should be ff0000 or ffff00 but instead ends up being 000000) + + + + +* the overflow bit is only correct for addition but not subtraction... in subtraction, two positives can result in a negative and vice versa + + + + + + * fix ym7101 to better handle V/H interrupts (right now it sets and then the next step will clear, but it'd be nice if it could 'edge trigger') @@ -18,6 +32,10 @@ On broken cycle: %a1 = 1df40, moves that location + 1 to %d0 So both could share the same Signal, one setting it and the other reading it, but how would you actually configure/build that? +* make it possible to set the frame sizes when creating the frame swapper +* should you rename devices.rs traits.rs? + + * implement a Z80 * maybe see about a Mac 128k or something