Changed the way frontend works and added keyboard support

This commit is contained in:
transistor 2021-11-06 21:44:25 -07:00
parent 2c6a1a1b3a
commit 7e999d4c3a
10 changed files with 269 additions and 55 deletions

View File

@ -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<Mutex<MiniFrontendBuilder>>) {
while frontend.lock().unwrap().finalized == false {
thread::sleep(Duration::from_millis(10));
}
}

View File

@ -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,
}
}

View File

@ -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<Vec<u32>>,
pub updater: Mutex<Option<Box<dyn WindowUpdater>>>,
pub input: Mutex<Option<Box<dyn JoystickUpdater>>>,
pub struct MiniFrontendBuilder {
pub window: Option<Box<dyn WindowUpdater>>,
pub joystick: Option<Box<dyn JoystickUpdater>>,
pub keyboard: Option<Box<dyn KeyboardUpdater>>,
pub finalized: bool,
}
impl Host for MiniFrontend {
fn add_window(&self, updater: Box<dyn WindowUpdater>) -> 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<dyn WindowUpdater>) -> 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<dyn JoystickUpdater>) -> Result<(), Error> {
fn register_joystick(&mut self, device: JoystickDevice, input: Box<dyn JoystickUpdater>) -> 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<dyn KeyboardUpdater>) -> 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<u32>,
pub window: Option<Box<dyn WindowUpdater>>,
pub joystick: Option<Box<dyn JoystickUpdater>>,
pub keyboard: Option<Box<dyn KeyboardUpdater>>,
}
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<Box<dyn WindowUpdater>>, joystick: Option<Box<dyn JoystickUpdater>>, keyboard: Option<Box<dyn KeyboardUpdater>>) -> 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<System>) {
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();
}
}
}

View File

@ -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<Mutex<FrameSwapper>> {
Arc::new(Mutex::new(FrameSwapper::new()))
pub fn new_shared(width: u32, height: u32) -> Arc<Mutex<FrameSwapper>> {
Arc::new(Mutex::new(FrameSwapper::new(width, height)))
}
pub fn to_boxed(swapper: Arc<Mutex<FrameSwapper>>) -> Box<dyn WindowUpdater> {

View File

@ -5,4 +5,5 @@ pub mod traits;
pub mod tty;
pub mod gfx;
pub mod keys;

View File

@ -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<Box<dyn Tty>, Error>;
fn add_window(&self, updater: Box<dyn WindowUpdater>) -> Result<(), Error>;
fn register_joystick(&self, device: JoystickDevice, input: Box<dyn JoystickUpdater>) -> Result<(), Error> { Err(Error::new("Not supported")) }
fn add_window(&mut self, updater: Box<dyn WindowUpdater>) -> Result<(), Error>;
fn register_joystick(&mut self, device: JoystickDevice, input: Box<dyn JoystickUpdater>) -> Result<(), Error> { Err(Error::new("Not supported")) }
fn register_keyboard(&mut self, input: Box<dyn KeyboardUpdater>) -> 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<B: Iterator<Item=u32>>(&mut self, pos_x: u32, pos_y: u32, bitmap: B, width: u32, height: u32);
fn clear(&mut self, value: u32);
}

View File

@ -35,7 +35,7 @@ pub fn build_computie<H: Host>(host: &H) -> Result<System, Error> {
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);

View File

@ -10,7 +10,7 @@ use crate::peripherals::genesis;
use crate::host::traits::{Host, WindowUpdater};
pub fn build_genesis<H: Host>(host: &H) -> Result<System, Error> {
pub fn build_genesis<H: Host>(host: &mut H) -> Result<System, Error> {
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<H: Host>(host: &H) -> Result<System, Error> {
//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<H: Host>(host: &H) -> Result<System, Error> {
//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();

View File

@ -104,7 +104,7 @@ impl GenesisController {
}
}
pub fn create<H: Host>(host: &H) -> Result<Self, Error> {
pub fn create<H: Host>(host: &mut H) -> Result<Self, Error> {
let controller = GenesisController::new();
let joystick1 = Box::new(GenesisControllerUpdater(controller.port_1.data.clone(), controller.interrupt.clone()));

View File

@ -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