[kbd] Implement keyboard and adapter

This commit is contained in:
2024-03-05 18:37:57 +05:30
parent d1b3cff7eb
commit be03b5aff2
6 changed files with 138 additions and 12 deletions

View File

@@ -6,7 +6,9 @@ use std::thread::{JoinHandle, sleep};
use std::time::Duration; use std::time::Duration;
use rand::random; use rand::random;
use crate::device::instruction::Instruction; use crate::device::instruction::Instruction;
use crate::device::keyboard::Keyboard;
use crate::device::timer::Timer; use crate::device::timer::Timer;
use crate::util::EmulatorResult;
pub struct Device { pub struct Device {
@@ -16,6 +18,7 @@ pub struct Device {
pub stack: Vec<u16>, pub stack: Vec<u16>,
pub frame_buffer: Arc<Mutex<Box<[bool; 64 * 32]>>>, pub frame_buffer: Arc<Mutex<Box<[bool; 64 * 32]>>>,
pub super_chip8_mode: bool, pub super_chip8_mode: bool,
pub device_keyboard: Keyboard,
} }
impl Device { impl Device {
@@ -23,7 +26,7 @@ impl Device {
pub const FRAME_BUFFER_WIDTH: usize = 64; pub const FRAME_BUFFER_WIDTH: usize = 64;
pub const FRAME_BUFFER_HEIGHT: usize = 32; pub const FRAME_BUFFER_HEIGHT: usize = 32;
pub const FRAME_BUFFER_SIZE: usize = Self::FRAME_BUFFER_WIDTH * Self::FRAME_BUFFER_HEIGHT; pub const FRAME_BUFFER_SIZE: usize = Self::FRAME_BUFFER_WIDTH * Self::FRAME_BUFFER_HEIGHT;
pub fn new(timer: Timer, fb: Arc<Mutex<Box<[bool; Device::FRAME_BUFFER_SIZE]>>>) -> Device { pub fn new(timer: Timer, fb: Arc<Mutex<Box<[bool; Device::FRAME_BUFFER_SIZE]>>>, device_keyboard: Keyboard) -> Device {
let memory = vec![0u8; Self::DEVICE_MEMORY_SIZE].into_boxed_slice().try_into().unwrap(); let memory = vec![0u8; Self::DEVICE_MEMORY_SIZE].into_boxed_slice().try_into().unwrap();
log::trace!("Successfully initiated device memory"); log::trace!("Successfully initiated device memory");
Device { Device {
@@ -33,6 +36,7 @@ impl Device {
stack: Vec::with_capacity(16), stack: Vec::with_capacity(16),
timer, timer,
super_chip8_mode: false, super_chip8_mode: false,
device_keyboard
} }
} }
} }
@@ -59,13 +63,16 @@ impl Device {
const FONT_DEFAULT_MEM_LOCATION_START: usize = 0x50; const FONT_DEFAULT_MEM_LOCATION_START: usize = 0x50;
const FONT_DEFAULT_MEM_LOCATION_END: usize = 0x9F; const FONT_DEFAULT_MEM_LOCATION_END: usize = 0x9F;
const ROM_START: usize = 0x200; const ROM_START: usize = 0x200;
pub fn cycle(&mut self) { pub fn cycle(&mut self) ->EmulatorResult<()>{
self.device_keyboard.update_keyboard()?;
let pc = self.registers.pc as usize; let pc = self.registers.pc as usize;
let instr_slice = self.memory.get(pc..pc + 2).expect("Failed to get memory"); let instr_slice = self.memory.get(pc..pc + 2).expect("Failed to get memory");
self.registers.pc += 2; self.registers.pc += 2;
let instruction = Instruction::decode_instruction(instr_slice); let instruction = Instruction::decode_instruction(instr_slice);
self.execute_instruction(instruction); self.execute_instruction(instruction);
Ok(())
} }
/// convert the 2 indices into one /// convert the 2 indices into one
fn get_framebuffer_index(x: usize, y: usize) -> usize { fn get_framebuffer_index(x: usize, y: usize) -> usize {

68
src/device/keyboard.rs Normal file
View File

@@ -0,0 +1,68 @@
use std::sync::mpsc::TryRecvError;
use crate::util::{EmulatorError, EmulatorResult};
// display thread sends these to the device thread.
// device thread updates on key up and down?
pub struct Keyboard {
bitflags: u16,
keyboard_event_receiver: std::sync::mpsc::Receiver<KeyboardEvent>,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum KeyboardEvent {
KeyUp(u8),
KeyDown(u8),
}
impl Keyboard {
pub fn new(keyboard_event_receiver: std::sync::mpsc::Receiver<KeyboardEvent>) -> Keyboard {
Keyboard {
bitflags: 0,
keyboard_event_receiver,
}
}
/// Update keyboard based on pending keyboard events
pub fn update_keyboard(&mut self) -> EmulatorResult<()> {
loop {
let keyboard_event_recv_res = self.keyboard_event_receiver.try_recv();
match keyboard_event_recv_res {
Ok(event) => {
log::warn!("Processing {:?}",event);
self.update_keyboard_state(event);
}
Err(TryRecvError::Empty) => {
break Ok(());
}
Err(TryRecvError::Disconnected) => {
break Err(EmulatorError::IOError("Keyboard updater disconnected".into()));
}
}
}
}
/// Query if key is down
pub fn query_key_down(&self, key_num: u8) -> bool {
(self.bitflags | 1 << key_num) == (1 << key_num)
}
fn update_keyboard_state(&mut self, keyboard_event: KeyboardEvent) {
match keyboard_event {
KeyboardEvent::KeyUp(key) => { self.key_up(key) }
KeyboardEvent::KeyDown(key) => { self.key_down(key) }
}
}
fn key_down(&mut self, x: u8) {
self.bitflags |= 1 << x;
log::trace!("Key Down - state {}",self.bitflags);
}
fn key_up(&mut self, x: u8) {
self.bitflags &= !((1 << x) as u16);
log::debug!("Key Up - state {}",self.bitflags);
}
}

View File

@@ -1,6 +1,7 @@
pub mod timer; pub mod timer;
mod device; pub mod keyboard;
mod sound; mod sound;
pub mod instruction; pub mod instruction;
mod device;
pub use device::*; pub use device::*;

View File

@@ -16,15 +16,18 @@ use sdl2::render::WindowCanvas;
use simple_logger::SimpleLogger; use simple_logger::SimpleLogger;
use device::timer::Timer; use device::timer::Timer;
use crate::device::Device; use crate::device::Device;
use crate::device::keyboard::Keyboard;
use crate::util::EmulatorResult; use crate::util::EmulatorResult;
use crate::kb_map::get_key_index; use crate::kb_map::get_key_index;
use crate::sdl_graphics_adapter::SdlGraphicsAdapter; use crate::sdl_graphics_adapter::SdlGraphicsAdapter;
use crate::sdl_keyboard_adapter::SdlKeyboardAdapter;
mod args; mod args;
mod device; mod device;
mod kb_map; mod kb_map;
mod sdl_graphics_adapter; mod sdl_graphics_adapter;
mod util; mod util;
mod sdl_keyboard_adapter;
fn main() -> EmulatorResult<()> { fn main() -> EmulatorResult<()> {
SimpleLogger::new().with_level(LevelFilter::Info).env().init().unwrap(); SimpleLogger::new().with_level(LevelFilter::Info).env().init().unwrap();
@@ -33,13 +36,14 @@ fn main() -> EmulatorResult<()> {
let mut timer = Timer::new(); let mut timer = Timer::new();
timer.start(); timer.start();
let frame_buffer_for_display = get_frame_buffer(); let (frame_buffer_for_display, frame_buffer_for_device) = get_frame_buffer_references();
let frame_buffer_for_device = Arc::clone(&frame_buffer_for_display);
let (sdl_kb_adapter,device_keyboard) = SdlKeyboardAdapter::new_keyboard();
let (termination_signal_sender, termination_signal_sender_receiver) = std::sync::mpsc::channel(); let (termination_signal_sender, termination_signal_sender_receiver) = std::sync::mpsc::channel();
let compute_handle = thread::Builder::new().name("Compute".to_string()).spawn(move || { let compute_handle = thread::Builder::new().name("Compute".to_string()).spawn(move || {
do_device_loop(timer, frame_buffer_for_device, termination_signal_sender_receiver); do_device_loop(timer, frame_buffer_for_device, termination_signal_sender_receiver, device_keyboard);
})?; })?;
let (mut canvas, mut event_pump) = try_initiate_sdl(8f32)?; let (mut canvas, mut event_pump) = try_initiate_sdl(8f32)?;
@@ -59,11 +63,13 @@ fn main() -> EmulatorResult<()> {
} }
Event::KeyDown { keycode: Some(x), repeat: false, .. } => { Event::KeyDown { keycode: Some(x), repeat: false, .. } => {
if let Some(key_val) = get_key_index(x) { if let Some(key_val) = get_key_index(x) {
sdl_kb_adapter.send_key_down(key_val)?;
log::info!("Key+ {}",key_val) log::info!("Key+ {}",key_val)
} }
} }
Event::KeyUp { keycode: Some(x), repeat: false, .. } => { Event::KeyUp { keycode: Some(x), repeat: false, .. } => {
if let Some(key_val) = get_key_index(x) { if let Some(key_val) = get_key_index(x) {
sdl_kb_adapter.send_key_up(key_val)?;
log::info!("Key- {}",key_val) log::info!("Key- {}",key_val)
} }
} }
@@ -87,8 +93,8 @@ fn main() -> EmulatorResult<()> {
Ok(()) Ok(())
} }
fn do_device_loop(mut timer: Timer, frame_buffer: Arc<Mutex<Box<[bool; 2048]>>>, receiver: Receiver<()>) { fn do_device_loop(mut timer: Timer, frame_buffer: Arc<Mutex<Box<[bool; 2048]>>>, receiver: Receiver<()>, device_keyboard: Keyboard) {
let mut device = Device::new(timer, frame_buffer); let mut device = Device::new(timer, frame_buffer, device_keyboard);
device.set_default_font(); device.set_default_font();
{ {
let rom = load_rom(); let rom = load_rom();
@@ -102,15 +108,17 @@ fn do_device_loop(mut timer: Timer, frame_buffer: Arc<Mutex<Box<[bool; 2048]>>>,
} else if let Err(std::sync::mpsc::TryRecvError::Disconnected) = val { } else if let Err(std::sync::mpsc::TryRecvError::Disconnected) = val {
panic!("Disconnected"); panic!("Disconnected");
} }
device.cycle(); device.cycle().expect("Failed to execute");
// Put a bit of delay to slow down execution // Put a bit of delay to slow down execution
thread::sleep(Duration::from_nanos(500)) thread::sleep(Duration::from_nanos(500))
} }
} }
fn get_frame_buffer() -> Arc<Mutex<Box<[bool; 2048]>>> { fn get_frame_buffer_references() -> (Arc<Mutex<Box<[bool; 2048]>>>, Arc<Mutex<Box<[bool; 2048]>>>) {
Arc::new(Mutex::new(vec![false; Device::FRAME_BUFFER_SIZE].into_boxed_slice().try_into().unwrap())) let arc = Arc::new(Mutex::new(vec![false; Device::FRAME_BUFFER_SIZE].into_boxed_slice().try_into().unwrap()));
let arc2 = Arc::clone(&arc);
(arc,arc2)
} }
const ROM_SIZE: usize = 4096 - 0x200; const ROM_SIZE: usize = 4096 - 0x200;

View File

@@ -0,0 +1,34 @@
use std::sync::mpsc::Sender;
use crate::device::keyboard::{Keyboard, KeyboardEvent};
use crate::device::keyboard::KeyboardEvent::{KeyDown, KeyUp};
use crate::util::EmulatorResult;
#[derive(Debug)]
pub struct SdlKeyboardAdapter {
keyboard_event_sender: Sender<KeyboardEvent>,
}
impl SdlKeyboardAdapter {
fn new(keyboard_event_sender: Sender<KeyboardEvent>) -> SdlKeyboardAdapter {
SdlKeyboardAdapter {
keyboard_event_sender
}
}
/// Creates a paired keyboard and adapter.
pub fn new_keyboard()->(SdlKeyboardAdapter, Keyboard){
let (sender,receiver) = std::sync::mpsc::channel();
let sdl2_kb_adapter = Self::new(sender);
let device_kb = Keyboard::new(receiver);
(sdl2_kb_adapter,device_kb)
}
pub fn send_key_up(&self, keycode: u8) -> EmulatorResult<u8> {
self.keyboard_event_sender.send(KeyUp(keycode))?;
Ok(keycode)
}
pub fn send_key_down(&self, keycode: u8) -> EmulatorResult<u8> {
self.keyboard_event_sender.send(KeyDown(keycode))?;
Ok(keycode)
}
}

View File

@@ -1,6 +1,8 @@
use std::sync::mpsc::SendError;
use std::sync::PoisonError; use std::sync::PoisonError;
use sdl2::IntegerOrSdlError; use sdl2::IntegerOrSdlError;
use sdl2::video::WindowBuildError; use sdl2::video::WindowBuildError;
use crate::device::keyboard::KeyboardEvent;
pub type EmulatorResult<T> = Result<T, EmulatorError>; pub type EmulatorResult<T> = Result<T, EmulatorError>;
@@ -43,3 +45,9 @@ impl<T> From<PoisonError<T>> for EmulatorError{
Self::MutexInvalidState(value.to_string()) Self::MutexInvalidState(value.to_string())
} }
} }
impl From<SendError<KeyboardEvent>> for EmulatorError{
fn from(value: SendError<KeyboardEvent>) -> Self {
Self::IOError(format!("Failed to communicate keyboard stats to main thread: {}",value))
}
}