[kbd] Implement keyboard and adapter
This commit is contained in:
@@ -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
68
src/device/keyboard.rs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
@@ -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::*;
|
||||||
|
26
src/main.rs
26
src/main.rs
@@ -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;
|
||||||
|
34
src/sdl_keyboard_adapter.rs
Normal file
34
src/sdl_keyboard_adapter.rs
Normal 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)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -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))
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user