diff options
Diffstat (limited to 'src/ps2.rs')
| -rw-r--r-- | src/ps2.rs | 364 |
1 files changed, 0 insertions, 364 deletions
diff --git a/src/ps2.rs b/src/ps2.rs deleted file mode 100644 index 4ea8b7a..0000000 --- a/src/ps2.rs +++ /dev/null | |||
| @@ -1,364 +0,0 @@ | |||
| 1 | /// PS/2 keyboard driver — polling, scancode set 1, US QWERTY layout. | ||
| 2 | // Port addresses | ||
| 3 | const DATA_PORT: u16 = 0x60; | ||
| 4 | const STATUS_PORT: u16 = 0x64; | ||
| 5 | |||
| 6 | // Status register bits | ||
| 7 | const STAT_OUTPUT_FULL: u8 = 0x01; | ||
| 8 | |||
| 9 | // Modifier bit constants | ||
| 10 | const MOD_LSHIFT: u8 = 0x01; | ||
| 11 | const MOD_RSHIFT: u8 = 0x02; | ||
| 12 | const MOD_LCTRL: u8 = 0x04; | ||
| 13 | const MOD_RCTRL: u8 = 0x08; | ||
| 14 | const MOD_LALT: u8 = 0x10; | ||
| 15 | const MOD_RALT: u8 = 0x20; | ||
| 16 | const MOD_CAPS: u8 = 0x40; | ||
| 17 | |||
| 18 | /// A decoded key event. | ||
| 19 | pub enum KeyEvent { | ||
| 20 | Char(char), | ||
| 21 | Enter, | ||
| 22 | Backspace, | ||
| 23 | Tab, | ||
| 24 | Escape, | ||
| 25 | ArrowUp, | ||
| 26 | ArrowDown, | ||
| 27 | ArrowLeft, | ||
| 28 | ArrowRight, | ||
| 29 | Home, | ||
| 30 | End, | ||
| 31 | PageUp, | ||
| 32 | PageDown, | ||
| 33 | Delete, | ||
| 34 | F(u8), | ||
| 35 | Unknown(u8), | ||
| 36 | } | ||
| 37 | |||
| 38 | // ----- scancode-set-1 translation tables (US QWERTY) ----- | ||
| 39 | |||
| 40 | /// Normal (unshifted) scancode → ASCII mapping. 0 = unmapped. | ||
| 41 | static SCAN_NORMAL: [u8; 128] = { | ||
| 42 | let mut t = [0u8; 128]; | ||
| 43 | t[0x02] = b'1'; | ||
| 44 | t[0x03] = b'2'; | ||
| 45 | t[0x04] = b'3'; | ||
| 46 | t[0x05] = b'4'; | ||
| 47 | t[0x06] = b'5'; | ||
| 48 | t[0x07] = b'6'; | ||
| 49 | t[0x08] = b'7'; | ||
| 50 | t[0x09] = b'8'; | ||
| 51 | t[0x0A] = b'9'; | ||
| 52 | t[0x0B] = b'0'; | ||
| 53 | t[0x0C] = b'-'; | ||
| 54 | t[0x0D] = b'='; | ||
| 55 | t[0x10] = b'q'; | ||
| 56 | t[0x11] = b'w'; | ||
| 57 | t[0x12] = b'e'; | ||
| 58 | t[0x13] = b'r'; | ||
| 59 | t[0x14] = b't'; | ||
| 60 | t[0x15] = b'y'; | ||
| 61 | t[0x16] = b'u'; | ||
| 62 | t[0x17] = b'i'; | ||
| 63 | t[0x18] = b'o'; | ||
| 64 | t[0x19] = b'p'; | ||
| 65 | t[0x1A] = b'['; | ||
| 66 | t[0x1B] = b']'; | ||
| 67 | t[0x1E] = b'a'; | ||
| 68 | t[0x1F] = b's'; | ||
| 69 | t[0x20] = b'd'; | ||
| 70 | t[0x21] = b'f'; | ||
| 71 | t[0x22] = b'g'; | ||
| 72 | t[0x23] = b'h'; | ||
| 73 | t[0x24] = b'j'; | ||
| 74 | t[0x25] = b'k'; | ||
| 75 | t[0x26] = b'l'; | ||
| 76 | t[0x27] = b';'; | ||
| 77 | t[0x28] = b'\''; | ||
| 78 | t[0x29] = b'`'; | ||
| 79 | t[0x2B] = b'\\'; | ||
| 80 | t[0x2C] = b'z'; | ||
| 81 | t[0x2D] = b'x'; | ||
| 82 | t[0x2E] = b'c'; | ||
| 83 | t[0x2F] = b'v'; | ||
| 84 | t[0x30] = b'b'; | ||
| 85 | t[0x31] = b'n'; | ||
| 86 | t[0x32] = b'm'; | ||
| 87 | t[0x33] = b','; | ||
| 88 | t[0x34] = b'.'; | ||
| 89 | t[0x35] = b'/'; | ||
| 90 | t[0x37] = b'*'; | ||
| 91 | t[0x39] = b' '; | ||
| 92 | t | ||
| 93 | }; | ||
| 94 | |||
| 95 | /// Shifted scancode → ASCII. | ||
| 96 | static SCAN_SHIFT: [u8; 128] = { | ||
| 97 | let mut t = [0u8; 128]; | ||
| 98 | t[0x02] = b'!'; | ||
| 99 | t[0x03] = b'@'; | ||
| 100 | t[0x04] = b'#'; | ||
| 101 | t[0x05] = b'$'; | ||
| 102 | t[0x06] = b'%'; | ||
| 103 | t[0x07] = b'^'; | ||
| 104 | t[0x08] = b'&'; | ||
| 105 | t[0x09] = b'*'; | ||
| 106 | t[0x0A] = b'('; | ||
| 107 | t[0x0B] = b')'; | ||
| 108 | t[0x0C] = b'_'; | ||
| 109 | t[0x0D] = b'+'; | ||
| 110 | t[0x10] = b'Q'; | ||
| 111 | t[0x11] = b'W'; | ||
| 112 | t[0x12] = b'E'; | ||
| 113 | t[0x13] = b'R'; | ||
| 114 | t[0x14] = b'T'; | ||
| 115 | t[0x15] = b'Y'; | ||
| 116 | t[0x16] = b'U'; | ||
| 117 | t[0x17] = b'I'; | ||
| 118 | t[0x18] = b'O'; | ||
| 119 | t[0x19] = b'P'; | ||
| 120 | t[0x1A] = b'{'; | ||
| 121 | t[0x1B] = b'}'; | ||
| 122 | t[0x1E] = b'A'; | ||
| 123 | t[0x1F] = b'S'; | ||
| 124 | t[0x20] = b'D'; | ||
| 125 | t[0x21] = b'F'; | ||
| 126 | t[0x22] = b'G'; | ||
| 127 | t[0x23] = b'H'; | ||
| 128 | t[0x24] = b'J'; | ||
| 129 | t[0x25] = b'K'; | ||
| 130 | t[0x26] = b'L'; | ||
| 131 | t[0x27] = b':'; | ||
| 132 | t[0x28] = b'"'; | ||
| 133 | t[0x29] = b'~'; | ||
| 134 | t[0x2B] = b'|'; | ||
| 135 | t[0x2C] = b'Z'; | ||
| 136 | t[0x2D] = b'X'; | ||
| 137 | t[0x2E] = b'C'; | ||
| 138 | t[0x2F] = b'V'; | ||
| 139 | t[0x30] = b'B'; | ||
| 140 | t[0x31] = b'N'; | ||
| 141 | t[0x32] = b'M'; | ||
| 142 | t[0x33] = b'<'; | ||
| 143 | t[0x34] = b'>'; | ||
| 144 | t[0x35] = b'?'; | ||
| 145 | t[0x37] = b'*'; | ||
| 146 | t[0x39] = b' '; | ||
| 147 | t | ||
| 148 | }; | ||
| 149 | |||
| 150 | /// Internal keyboard state | ||
| 151 | struct KbdState { | ||
| 152 | modifiers: u8, | ||
| 153 | got_e0: bool, | ||
| 154 | } | ||
| 155 | |||
| 156 | static mut KBD: KbdState = KbdState { | ||
| 157 | modifiers: 0, | ||
| 158 | got_e0: false, | ||
| 159 | }; | ||
| 160 | |||
| 161 | /// Initialise the PS/2 port: drain stale bytes + enable keyboard interrupt. | ||
| 162 | pub fn init() { | ||
| 163 | drain_input(); | ||
| 164 | |||
| 165 | // Enable the keyboard interrupt on the PS/2 controller. | ||
| 166 | // Read Configuration Byte (command 0x20), set bit 0 (IRQ1 enable), | ||
| 167 | // write back (command 0x60). | ||
| 168 | unsafe { | ||
| 169 | // Tell the keyboard controller we want to read the config byte. | ||
| 170 | // Need to wait for the controller to be ready first. | ||
| 171 | wait_write(); | ||
| 172 | core::arch::asm!("out dx, al", in("dx") 0x64u16, in("al") 0x20u8, | ||
| 173 | options(nostack, preserves_flags)); | ||
| 174 | wait_read(); | ||
| 175 | let mut cfg: u8; | ||
| 176 | core::arch::asm!("in al, dx", out("al") cfg, in("dx") 0x60u16, | ||
| 177 | options(nostack, preserves_flags)); | ||
| 178 | |||
| 179 | cfg |= 0x01; // enable keyboard interrupt | ||
| 180 | |||
| 181 | // Write config byte back | ||
| 182 | wait_write(); | ||
| 183 | core::arch::asm!("out dx, al", in("dx") 0x64u16, in("al") 0x60u8, | ||
| 184 | options(nostack, preserves_flags)); | ||
| 185 | wait_write(); | ||
| 186 | core::arch::asm!("out dx, al", in("dx") 0x60u16, in("al") cfg, | ||
| 187 | options(nostack, preserves_flags)); | ||
| 188 | } | ||
| 189 | } | ||
| 190 | |||
| 191 | /// Wait until the PS/2 controller is ready to receive a command. | ||
| 192 | fn wait_write() { | ||
| 193 | unsafe { | ||
| 194 | loop { | ||
| 195 | let status: u8; | ||
| 196 | core::arch::asm!("in al, dx", out("al") status, in("dx") 0x64u16, | ||
| 197 | options(nostack, preserves_flags)); | ||
| 198 | if status & 0x02 == 0 { | ||
| 199 | // Input buffer empty — controller ready to receive | ||
| 200 | break; | ||
| 201 | } | ||
| 202 | } | ||
| 203 | } | ||
| 204 | } | ||
| 205 | |||
| 206 | /// Wait until the PS/2 controller has data available to read. | ||
| 207 | fn wait_read() { | ||
| 208 | unsafe { | ||
| 209 | loop { | ||
| 210 | let status: u8; | ||
| 211 | core::arch::asm!("in al, dx", out("al") status, in("dx") 0x64u16, | ||
| 212 | options(nostack, preserves_flags)); | ||
| 213 | if status & 0x01 != 0 { | ||
| 214 | // Output buffer full — data ready to read | ||
| 215 | break; | ||
| 216 | } | ||
| 217 | } | ||
| 218 | } | ||
| 219 | } | ||
| 220 | |||
| 221 | fn drain_input() { | ||
| 222 | unsafe { | ||
| 223 | loop { | ||
| 224 | let status: u8; | ||
| 225 | core::arch::asm!("in al, dx", out("al") status, in("dx") STATUS_PORT, | ||
| 226 | options(nostack, preserves_flags)); | ||
| 227 | if status & STAT_OUTPUT_FULL == 0 { | ||
| 228 | break; | ||
| 229 | } | ||
| 230 | let _data: u8; | ||
| 231 | core::arch::asm!("in al, dx", out("al") _data, in("dx") DATA_PORT, | ||
| 232 | options(nostack, preserves_flags)); | ||
| 233 | } | ||
| 234 | } | ||
| 235 | } | ||
| 236 | |||
| 237 | /// Poll for a key event. Blocks via `hlt` until a key is pressed. | ||
| 238 | /// The keyboard interrupt handler pushes raw scancodes to a ring buffer; | ||
| 239 | /// this function pops and decodes them. | ||
| 240 | pub fn poll_key() -> KeyEvent { | ||
| 241 | loop { | ||
| 242 | if let Some(byte) = crate::interrupts::read_scancode() { | ||
| 243 | if let Some(ev) = process_byte(byte) { | ||
| 244 | return ev; | ||
| 245 | } | ||
| 246 | } else { | ||
| 247 | unsafe { | ||
| 248 | core::arch::asm!("sti; hlt", options(nostack, preserves_flags)); | ||
| 249 | } | ||
| 250 | } | ||
| 251 | } | ||
| 252 | } | ||
| 253 | |||
| 254 | /// Non-blocking poll — read from the interrupt ring buffer if available. | ||
| 255 | pub fn try_poll_key() -> Option<KeyEvent> { | ||
| 256 | let byte = crate::interrupts::read_scancode()?; | ||
| 257 | process_byte(byte) | ||
| 258 | } | ||
| 259 | |||
| 260 | /// Process a single scancode byte. Returns Some(KeyEvent) when a visible | ||
| 261 | /// event happens; returns None for modifier changes, releases, and prefixes. | ||
| 262 | fn process_byte(byte: u8) -> Option<KeyEvent> { | ||
| 263 | let state = unsafe { &mut KBD }; | ||
| 264 | |||
| 265 | if byte == 0xE0 { | ||
| 266 | state.got_e0 = true; | ||
| 267 | return None; | ||
| 268 | } | ||
| 269 | |||
| 270 | if state.got_e0 { | ||
| 271 | state.got_e0 = false; | ||
| 272 | return process_e0(byte); | ||
| 273 | } | ||
| 274 | |||
| 275 | // Break code (key release) — bit 7 set | ||
| 276 | if byte & 0x80 != 0 { | ||
| 277 | update_modifiers(byte & 0x7F, false, false); | ||
| 278 | return None; | ||
| 279 | } | ||
| 280 | |||
| 281 | // Make code (key press) | ||
| 282 | update_modifiers(byte, true, false); | ||
| 283 | |||
| 284 | match byte { | ||
| 285 | 0x01 => Some(KeyEvent::Escape), | ||
| 286 | 0x0E => Some(KeyEvent::Backspace), | ||
| 287 | 0x0F => Some(KeyEvent::Tab), | ||
| 288 | 0x1C => Some(KeyEvent::Enter), | ||
| 289 | 0x37 => Some(KeyEvent::Char('*')), | ||
| 290 | 0x3B..=0x44 => Some(KeyEvent::F(byte - 0x3B + 1)), | ||
| 291 | _ => { | ||
| 292 | let shifted = (state.modifiers & (MOD_LSHIFT | MOD_RSHIFT)) != 0; | ||
| 293 | // CapsLock inverts case for letters | ||
| 294 | let caps = state.modifiers & MOD_CAPS != 0; | ||
| 295 | let table = if shifted { SCAN_SHIFT } else { SCAN_NORMAL }; | ||
| 296 | let mut ch = table[byte as usize]; | ||
| 297 | // Handle CapsLock for letters | ||
| 298 | if caps && ch.is_ascii_alphabetic() { | ||
| 299 | // Swap case: if table gives lowercase, make uppercase; etc. | ||
| 300 | // Since normal table has lowercase, shift table has uppercase: | ||
| 301 | ch = if shifted { | ||
| 302 | ch.to_ascii_lowercase() | ||
| 303 | } else { | ||
| 304 | ch.to_ascii_uppercase() | ||
| 305 | }; | ||
| 306 | } | ||
| 307 | if ch != 0 { | ||
| 308 | Some(KeyEvent::Char(ch as char)) | ||
| 309 | } else { | ||
| 310 | Some(KeyEvent::Unknown(byte)) | ||
| 311 | } | ||
| 312 | } | ||
| 313 | } | ||
| 314 | } | ||
| 315 | |||
| 316 | /// Process an E0-prefixed scancode. | ||
| 317 | fn process_e0(byte: u8) -> Option<KeyEvent> { | ||
| 318 | if byte & 0x80 != 0 { | ||
| 319 | update_modifiers(byte & 0x7F, false, true); | ||
| 320 | return None; | ||
| 321 | } | ||
| 322 | |||
| 323 | update_modifiers(byte, true, true); | ||
| 324 | |||
| 325 | match byte { | ||
| 326 | 0x48 => Some(KeyEvent::ArrowUp), | ||
| 327 | 0x50 => Some(KeyEvent::ArrowDown), | ||
| 328 | 0x4B => Some(KeyEvent::ArrowLeft), | ||
| 329 | 0x4D => Some(KeyEvent::ArrowRight), | ||
| 330 | 0x47 => Some(KeyEvent::Home), | ||
| 331 | 0x4F => Some(KeyEvent::End), | ||
| 332 | 0x49 => Some(KeyEvent::PageUp), | ||
| 333 | 0x51 => Some(KeyEvent::PageDown), | ||
| 334 | 0x53 => Some(KeyEvent::Delete), | ||
| 335 | 0x35 => Some(KeyEvent::Char('/')), | ||
| 336 | _ => Some(KeyEvent::Unknown(byte)), | ||
| 337 | } | ||
| 338 | } | ||
| 339 | |||
| 340 | fn update_modifiers(scancode: u8, pressed: bool, e0: bool) { | ||
| 341 | let state = unsafe { &mut KBD }; | ||
| 342 | match (scancode, e0) { | ||
| 343 | (0x2A, false) => set_mod(state, MOD_LSHIFT, pressed), | ||
| 344 | (0x36, false) => set_mod(state, MOD_RSHIFT, pressed), | ||
| 345 | (0x1D, false) => set_mod(state, MOD_LCTRL, pressed), | ||
| 346 | (0x1D, true) => set_mod(state, MOD_RCTRL, pressed), | ||
| 347 | (0x38, false) => set_mod(state, MOD_LALT, pressed), | ||
| 348 | (0x38, true) => set_mod(state, MOD_RALT, pressed), | ||
| 349 | (0x3A, false) => { | ||
| 350 | if pressed { | ||
| 351 | state.modifiers ^= MOD_CAPS; | ||
| 352 | } | ||
| 353 | } | ||
| 354 | _ => {} | ||
| 355 | } | ||
| 356 | } | ||
| 357 | |||
| 358 | fn set_mod(state: &mut KbdState, bit: u8, pressed: bool) { | ||
| 359 | if pressed { | ||
| 360 | state.modifiers |= bit; | ||
| 361 | } else { | ||
| 362 | state.modifiers &= !bit; | ||
| 363 | } | ||
| 364 | } | ||
