aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/lib.rs20
-rw-r--r--src/machine/key.rs51
-rw-r--r--src/machine/keyctrl.rs321
3 files changed, 251 insertions, 141 deletions
diff --git a/src/lib.rs b/src/lib.rs
index 80d0146..95a9455 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -5,11 +5,11 @@
mod arch;
mod io;
mod machine;
+use arch::x86_64::interrupt::pic_8259;
+use arch::x86_64::interrupt::pic_8259::PicDeviceInt;
use core::panic::PanicInfo;
use machine::cgascr::CGAScreen;
use machine::interrupt;
-use arch::x86_64::interrupt::pic_8259;
-use arch::x86_64::interrupt::pic_8259::PicDeviceInt;
#[cfg(not(test))]
#[panic_handler]
@@ -28,9 +28,17 @@ pub extern "C" fn _entry() -> ! {
println!(" `-.-' \\ )-`( , o o)");
println!(" `- \\`_`\"'-");
println!("it works!");
-
+
// testing interrupt/PIC
- pic_8259::allow(PicDeviceInt::KEYBOARD);
- interrupt::interrupt_enable();
- loop {}
+ // pic_8259::allow(PicDeviceInt::KEYBOARD);
+ // interrupt::interrupt_enable();
+ //
+ // busy loop query keyboard
+ loop {
+ // let code = io::KBCTL_GLOBAL.lock().simple_read();
+ io::KBCTL_GLOBAL.lock().fetch_key();
+ if let Some(k) = io::KBCTL_GLOBAL.lock().consume_key() {
+ println! {"caught key: {:?}", k}
+ }
+ }
}
diff --git a/src/machine/key.rs b/src/machine/key.rs
index 04220a6..8d4e5f9 100644
--- a/src/machine/key.rs
+++ b/src/machine/key.rs
@@ -1,17 +1,14 @@
use bitflags::bitflags;
use core::convert;
+use core::ffi::c_uchar;
+#[derive(Copy, Clone, Debug)]
pub struct Key {
- asc: u8,
- scan: u8,
- modi: Modifiers,
- rawcode: u8, // this field not necessary, remove after testing
+ pub asc: c_uchar,
+ pub scan: u8,
+ pub modi: Modifiers,
}
-// Not implementing
-// +operator char()
-//
-
impl convert::Into<char> for Key {
fn into(self) -> char {
self.asc as char
@@ -25,6 +22,7 @@ impl convert::Into<u8> for Key {
}
bitflags! {
+ #[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub struct Modifiers:u8 {
const NONE = 0;
const SHIFT = 1 << 0;
@@ -42,10 +40,9 @@ bitflags! {
impl Key {
pub fn new() -> Self {
Self {
- asc: 0,
- scan: 0,
+ asc: 0, // logically scan + modi.shift => asc
+ scan: 0, // scancode, "raw"
modi: Modifiers::NONE,
- rawcode: 0,
}
}
pub fn decode(&mut self) {
@@ -59,14 +56,6 @@ impl Key {
self.scan = 0;
}
- pub fn set_raw(&mut self, code: u8) {
- self.rawcode = code;
- }
-
- pub fn get_raw(self) -> u8 {
- self.rawcode
- }
-
// setter and getter for ascii and scancode
pub fn set_ascii(&mut self, ascii: u8) {
self.asc = ascii;
@@ -98,20 +87,6 @@ impl Key {
}
}
-use core::ffi::c_uchar;
-
-// bit masks for modifier keys
-pub enum MbitDefs {
- Shift = 0b00000001,
- AltLeft = 0b00000010,
- AltRight = 0b00000100,
- CtrlLeft = 0b00001000,
- CtrlRight = 0b00010000,
- CapsLock = 0b00100000,
- NumLock = 0b01000000,
- ScrollLock = 0b10000000,
-}
-
// scan codes of a few specific keys
pub enum Scan {
F1 = 0x3b,
@@ -126,25 +101,25 @@ pub enum Scan {
// Decoding tables ... this shit is so ugly, thanks to rust's strong typing system!!!
// Also, this is a german layout keyboard
// oh btw, the code translation is done by ChatGPT if it's wrong complain to the AI!
-const NORMAL_TAB: [c_uchar; 89] = [
+pub const NORMAL_TAB: [c_uchar; 89] = [
0, 0, 49, 50, 51, 52, 53, 54, 55, 56, 57, 48, 225, 39, 8, 0, 113, 119, 101, 114, 116, 122, 117,
105, 111, 112, 129, 43, 10, 0, 97, 115, 100, 102, 103, 104, 106, 107, 108, 148, 132, 94, 0, 35,
121, 120, 99, 118, 98, 110, 109, 44, 46, 45, 0, 42, 0, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 45, 0, 0, 0, 43, 0, 0, 0, 0, 0, 0, 0, 60, 0, 0,
];
-const SHIFT_TAB: [c_uchar; 89] = [
+pub const SHIFT_TAB: [c_uchar; 89] = [
0, 0, 33, 34, 21, 36, 37, 38, 47, 40, 41, 61, 63, 96, 0, 0, 81, 87, 69, 82, 84, 90, 85, 73, 79,
80, 154, 42, 0, 0, 65, 83, 68, 70, 71, 72, 74, 75, 76, 153, 142, 248, 0, 39, 89, 88, 67, 86,
66, 78, 77, 59, 58, 95, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 62, 0, 0,
];
-const ALT_TAB: [c_uchar; 89] = [
+pub const ALT_TAB: [c_uchar; 89] = [
0, 0, 0, 253, 0, 0, 0, 0, 123, 91, 93, 125, 92, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 126,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 230, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 124, 0, 0,
];
-const ASC_NUM_TAB: [c_uchar; 13] = [55, 56, 57, 45, 52, 53, 54, 43, 49, 50, 51, 48, 44];
-const SCAN_NUM_TAB: [c_uchar; 13] = [8, 9, 10, 53, 5, 6, 7, 27, 2, 3, 4, 11, 51];
+pub const ASC_NUM_TAB: [c_uchar; 13] = [55, 56, 57, 45, 52, 53, 54, 43, 49, 50, 51, 48, 44];
+pub const SCAN_NUM_TAB: [c_uchar; 13] = [8, 9, 10, 53, 5, 6, 7, 27, 2, 3, 4, 11, 51];
diff --git a/src/machine/keyctrl.rs b/src/machine/keyctrl.rs
index 246ac02..40385a2 100644
--- a/src/machine/keyctrl.rs
+++ b/src/machine/keyctrl.rs
@@ -3,151 +3,238 @@ use crate::machine::device_io::*;
use bitflags::bitflags;
use core::cmp;
use core::cmp::{Eq, PartialEq};
+use core::ffi::c_uchar;
use num_enum::{IntoPrimitive, TryFromPrimitive};
// Driver for the PS/2 keybard/mouse controller
-// beyound OOStuBS: I'm gonna write full driver for it.
-
+// beyound OOStuBS:
+// The 'gather' field should NEVER have imcomplete state: it should either be a
+// valid key, or nothing.
+// TODO what if IO ops fails or timeout?
// TODO figure out how to abstract the "interrupt control" layer.
// Keyboard controller should not be arch dependent
#[cfg(target_arch = "x86_64")]
use crate::arch::x86_64::interrupt::{pic_8259, pic_8259::PicDeviceInt as PD};
-// this is the driver for keyboard controller not to confuse with the keyboard module.
-// The later is an abstraction
-// This one serves a the HW driver
+use super::key::Modifiers;
// TODO
-// [functions]
-// Keyboard_Controller()
-// get_ascii_code()
-// key_decoded()
-// key_hit()
// reboot()
// set_led(char led,bool on)
// set_repeat_rate(int speed,int delay)
pub struct KeyboardController {
- code: u8,
- prefix: u8,
leds: Led,
- current: Option<Key>,
- last: Option<Key>,
- gather: Option<Key>,
+ keystate: KeyState,
+ gather: Option<Key>, // if not collected timely it will be overwritten
cport: IOPort,
dport: IOPort,
}
-pub enum KeyDelay {
- L0 = 0,
- L1 = 1,
- L2 = 2,
- L3 = 3,
+struct KeyState {
+ modi: Modifiers, // active modifiers
+ prefix: Prefix, // prefix state for some certain modifiers
+ scan: Option<u8>,
}
-// x86 and arm has different interrupt controller
-#[cfg(target_arch = "x86_64")]
-impl KeyboardController {
- #[inline(always)]
- fn disable_keyboard_int() {
- pic_8259::forbid(PD::KEYBOARD);
+#[derive(Clone, Copy, Eq, PartialEq)]
+enum Prefix {
+ PREFIX1,
+ PREFIX2,
+ NONE, // don't confuse Option None with Prefix enum NONE
+}
+
+impl Prefix {
+ pub fn try_from_u8(val: u8) -> Option<Prefix> {
+ match val {
+ Defs::C_PREFIX1 => Some(Self::PREFIX1),
+ Defs::C_PREFIX2 => Some(Self::PREFIX2),
+ _ => None,
+ }
}
+}
- #[inline(always)]
- fn enable_keyboard_int() {
- pic_8259::allow(PD::KEYBOARD);
+impl KeyState {
+ pub fn new() -> Self {
+ return Self {
+ modi: Modifiers::NONE,
+ prefix: Prefix::NONE,
+ scan: None,
+ };
}
- #[inline(always)]
- fn toggle_keyboard_int(enable: bool) {
- if enable {
- Self::enable_keyboard_int()
- } else {
- Self::disable_keyboard_int()
- }
+ pub fn toggle_capslock(&mut self) {
+ self.modi.toggle(Modifiers::CAPSLOCK);
}
- #[inline(always)]
- fn is_int_masked() -> bool {
- pic_8259::is_masked(PD::KEYBOARD)
+ pub fn toggle_numlock(&mut self) {
+ self.modi.toggle(Modifiers::NUMLOCK);
+ }
+
+ pub fn toggle_scroll_lock(&mut self) {
+ self.modi.toggle(Modifiers::SCROLL_LOCK);
}
}
impl KeyboardController {
pub fn new() -> Self {
Self {
- code: 0,
- prefix: 0,
leds: Led::NONE,
- last: None,
- current: None,
- gather: None,
+ keystate: KeyState::new(),
cport: IOPort::new(Defs::CTRL),
dport: IOPort::new(Defs::DATA),
+ gather: None,
}
}
- pub fn fetch_key(&mut self) -> Key {
- todo!();
- // this should be called by the interrupt handler.
- // 1. read raw keycode from data port
- // 2. try to decode and put the result into self.gather.
- // TODO consider move the decoding into the epilogue.
- }
-
- // key decoding is stateful, this can't be implemented into Key struct.
- pub fn decode_key(&mut self) -> bool {
- // try to decode the raw 'code' into self.gather
- // return true upon success
- // this is a transcription of the C code.... Improve later.
- // OOStuBS: The keys that were added in MF II keyboards -- compared to the older AT
- // keyboard -- always send one of two possible prefix bytes first.
- let done = false;
- if self.code == Defs::PREFIX1 || self.code == Defs::PREFIX2 {
- self.prefix = self.code;
- return false;
+ // Led and lock state are toggled together, to prevent out-of-sync
+ // TODO: rollback lock state if setting led fails
+ fn toggle_lock(&mut self, lock: Modifiers) {
+ let led = match lock {
+ Modifiers::SCROLL_LOCK => Some(Led::SCROLL_LOCK),
+ Modifiers::CAPSLOCK => Some(Led::CAPS_LOCK),
+ Modifiers::NUMLOCK => Some(Led::NUM_LOCk),
+ _ => None,
+ };
+ if let Some(led) = led {
+ self.keystate.modi.toggle(lock);
+ self.toggle_led(led);
}
+ }
- // OOStuBS: Releasing a key is actually only interesting in this implementation
- // for the "modifier" keys SHIFT, CTRL and ALT. For the other keys, we
- // can ignore the break code.
- // ; A Key's break code is identical to its make code with break_bit set
-
- false
+ fn toggle_led(&mut self, led: Led) {
+ self.leds.toggle(led);
+ todo!("toggle keyboard led: ");
}
- // block until status.outb becomes 1
- // return false on invalid SR.
- // these wait_ functions are unsafe. If used inproperly this becomes a deadloop
- // TODO support software timeout
- unsafe fn wait_write(&self) {
- loop {
- let sr = StatusReg::from_bits_truncate(self.cport.inb());
- if sr.contains(StatusReg::OUTB) {
- break;
+ pub fn update_state(&mut self, code: u8) {
+ // TODO investigate this code pattern: is there much runtime cost??
+ self.keystate.scan = Some(code);
+ if let Some(p) = Prefix::try_from_u8(code) {
+ self.keystate.prefix = p;
+ return;
+ }
+ if code & Defs::BREAK_BIT == 0 {
+ if self.press_event() {
+ self.decode_key();
}
+ } else {
+ self.release_event();
}
+ // the prefix should have been comsumed at this point so clear it
+ self.keystate.prefix = Prefix::NONE;
}
- // block until status.inb becomes 0
- unsafe fn wait_read(&self) {
- loop {
- let sr = StatusReg::from_bits_truncate(self.cport.inb());
- if !sr.contains(StatusReg::INB) {
- break;
+ fn press_event(&mut self) -> bool {
+ let mut should_decode_ascii = false;
+ let code = self.keystate.scan.unwrap();
+ match code {
+ Defs::C_SHIFT_L | Defs::C_SHIFT_R => self.keystate.modi.insert(Modifiers::SHIFT),
+ Defs::C_ALT => match self.keystate.prefix {
+ Prefix::PREFIX1 => self.keystate.modi.insert(Modifiers::ALT_RIGHT),
+ _ => self.keystate.modi.insert(Modifiers::ALT_LEFT),
+ },
+ Defs::C_CTRL => match self.keystate.prefix {
+ Prefix::PREFIX1 => self.keystate.modi.insert(Modifiers::CTRL_RIGHT),
+ _ => self.keystate.modi.insert(Modifiers::CTRL_LEFT),
+ },
+ Defs::C_CAPSLOCK => self.toggle_lock(Modifiers::CAPSLOCK),
+ Defs::C_NUM_P => {
+ if !self.keystate.modi.contains(Modifiers::CTRL_LEFT) {
+ self.toggle_lock(Modifiers::NUMLOCK);
+ }
+ }
+ Defs::C_SCRLOCK => self.toggle_lock(Modifiers::SCROLL_LOCK),
+ _ => {
+ should_decode_ascii = true;
}
}
+ should_decode_ascii
}
- pub fn set_repeat_rate_delay(rate: u8, delay: KeyDelay) {
- let rate = cmp::min(rate, 31);
- let delay = delay as u8;
- let is_masked = Self::is_int_masked();
- // idsable keyboard interrupt
- // TODO should have a timeout
- Self::disable_keyboard_int();
+ fn release_event(&mut self) {
+ // we only care about release events for shift/alt/ctrl
+ let code = self.keystate.scan.unwrap() & !Defs::BREAK_BIT;
+ match code {
+ Defs::C_SHIFT_L | Defs::C_SHIFT_R => self.keystate.modi.remove(Modifiers::SHIFT),
+ Defs::C_ALT => match self.keystate.prefix {
+ Prefix::PREFIX1 => self.keystate.modi.remove(Modifiers::ALT_RIGHT),
+ _ => self.keystate.modi.remove(Modifiers::ALT_LEFT),
+ },
+ Defs::C_CTRL => match self.keystate.prefix {
+ Prefix::PREFIX1 => self.keystate.modi.remove(Modifiers::CTRL_RIGHT),
+ _ => self.keystate.modi.remove(Modifiers::CTRL_LEFT),
+ },
+ _ => {}
+ }
+ }
- Self::toggle_keyboard_int(is_masked);
+ #[inline(always)]
+ pub fn read_status(&self) -> Option<StatusReg> {
+ // TODO maybe there is a path which leads to invalid SR, e.g. timeout?
+ Some(StatusReg::from_bits_truncate(self.cport.inb()))
+ }
+
+ // this should be called by the interrupt handler prologue
+ pub fn fetch_key(&mut self) {
+ // I'd like to see if this panics....
+ let sr = self.read_status().unwrap();
+ // ignore mouse events
+ if !sr.contains(StatusReg::OUTB) || sr.contains(StatusReg::AUXB) {
+ return;
+ }
+ self.update_state(self.dport.inb());
+ }
+
+ // this should be called by the "epilogue"
+ pub fn consume_key(&mut self) -> Option<Key>{
+ let res = self.gather.clone();
+ self.gather = None;
+ return res
+ }
+
+ pub fn decode_key(&mut self) {
+ // the decode_key should not be called when there is no scancode.
+ // mask the breakbit
+ let s = self.keystate.scan.unwrap();
+ let c = s & !Defs::BREAK_BIT;
+ let m = self.keystate.modi;
+ let p = self.keystate.prefix;
+
+ if c == 53 && p == Prefix::PREFIX1 {
+ self.gather = Some(Key {
+ asc: b'/',
+ modi: m,
+ scan: s,
+ });
+ return;
+ }
+
+ let asc = if m.contains(Modifiers::NUMLOCK) && p == Prefix::NONE && c >= 71 && c <= 83 {
+ ASC_NUM_TAB[c as usize - 71]
+ } else if m.contains(Modifiers::ALT_RIGHT) {
+ ALT_TAB[c as usize]
+ } else if m.contains(Modifiers::SHIFT) {
+ SHIFT_TAB[c as usize]
+ } else if m.contains(Modifiers::CAPSLOCK) {
+ if (c >= 16 && c <= 26) || (c >= 30 && c <= 40) || (c >= 44 && c <= 50) {
+ SHIFT_TAB[c as usize]
+ } else {
+ NORMAL_TAB[c as usize]
+ }
+ } else {
+ NORMAL_TAB[c as usize]
+ };
+
+ self.gather = Some(Key {
+ asc,
+ modi: m,
+ scan: s,
+ });
+ }
+
+ pub fn set_repeat_rate_delay() {
+ todo!();
}
pub fn reboot(&mut self) {
@@ -155,8 +242,33 @@ impl KeyboardController {
}
}
-// I think constants are more handy than enum for these...
-// Keyboard controller commands
+// x86 and arm has different interrupt controller
+#[cfg(target_arch = "x86_64")]
+impl KeyboardController {
+ #[inline(always)]
+ fn disable_keyboard_int() {
+ pic_8259::forbid(PD::KEYBOARD);
+ }
+
+ #[inline(always)]
+ fn enable_keyboard_int() {
+ pic_8259::allow(PD::KEYBOARD);
+ }
+
+ #[inline(always)]
+ fn toggle_keyboard_int(enable: bool) {
+ if enable {
+ Self::enable_keyboard_int()
+ } else {
+ Self::disable_keyboard_int()
+ }
+ }
+
+ #[inline(always)]
+ fn is_int_masked() -> bool {
+ pic_8259::is_masked(PD::KEYBOARD)
+ }
+}
enum Cmd {
// these commands are sent through DATA port
@@ -200,7 +312,22 @@ impl Defs {
pub const CTRL: u16 = 0x64;
pub const DATA: u16 = 0x60;
pub const CPU_RESET: u8 = 0xfe;
- pub const BREAK_BIT: u8 = 0x80;
- pub const PREFIX1: u8 = 0xe0;
- pub const PREFIX2: u8 = 0xe1;
+ pub const BREAK_BIT: u8 = 1 << 7;
+ // defs of special scan codes.
+ pub const C_PREFIX1: u8 = 0xe0;
+ pub const C_PREFIX2: u8 = 0xe1;
+ pub const C_SHIFT_L: u8 = 0x2a;
+ pub const C_SHIFT_R: u8 = 0x36;
+ pub const C_ALT: u8 = 0x38;
+ pub const C_CTRL: u8 = 0x1d;
+ pub const C_CAPSLOCK: u8 = 0x3a;
+ pub const C_SCRLOCK: u8 = 0x46;
+ pub const C_NUM_P: u8 = 0x45; // NumLock or Pause
+ pub const C_F1: u8 = 0x3b;
+ pub const C_DEL: u8 = 0x53;
+ pub const C_UP: u8 = 0x48;
+ pub const C_DOWN: u8 = 0x50;
+ pub const C_LEFT: u8 = 0x4b;
+ pub const C_RIGHT: u8 = 0x4d;
+ pub const C_DIV: u8 = 0x8;
}