summaryrefslogtreecommitdiffstats
path: root/src/ps2.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/ps2.rs')
-rw-r--r--src/ps2.rs329
1 files changed, 329 insertions, 0 deletions
diff --git a/src/ps2.rs b/src/ps2.rs
new file mode 100644
index 0000000..e972658
--- /dev/null
+++ b/src/ps2.rs
@@ -0,0 +1,329 @@
1/// PS/2 keyboard driver — polling, scancode set 1, US QWERTY layout.
2// Port addresses
3const DATA_PORT: u16 = 0x60;
4const STATUS_PORT: u16 = 0x64;
5
6// Status register bits
7const STAT_OUTPUT_FULL: u8 = 0x01;
8
9// Modifier bit constants
10const MOD_LSHIFT: u8 = 0x01;
11const MOD_RSHIFT: u8 = 0x02;
12const MOD_LCTRL: u8 = 0x04;
13const MOD_RCTRL: u8 = 0x08;
14const MOD_LALT: u8 = 0x10;
15const MOD_RALT: u8 = 0x20;
16const MOD_CAPS: u8 = 0x40;
17
18/// A decoded key event.
19pub 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.
41static 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.
96static 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
151struct KbdState {
152 modifiers: u8,
153 got_e0: bool,
154}
155
156static mut KBD: KbdState = KbdState {
157 modifiers: 0,
158 got_e0: false,
159};
160
161/// Initialise the PS/2 port for the keyboard — just drain stale bytes.
162pub fn init() {
163 drain_input();
164}
165
166fn drain_input() {
167 unsafe {
168 loop {
169 let status: u8;
170 core::arch::asm!("in al, dx", out("al") status, in("dx") STATUS_PORT,
171 options(nostack, preserves_flags));
172 if status & STAT_OUTPUT_FULL == 0 {
173 break;
174 }
175 let _data: u8;
176 core::arch::asm!("in al, dx", out("al") _data, in("dx") DATA_PORT,
177 options(nostack, preserves_flags));
178 }
179 }
180}
181
182/// Poll for a key event. Blocks until a key is pressed.
183pub fn poll_key() -> KeyEvent {
184 loop {
185 let byte = read_data();
186 if let Some(ev) = process_byte(byte) {
187 return ev;
188 }
189 }
190}
191
192/// Non-blocking poll.
193pub fn try_poll_key() -> Option<KeyEvent> {
194 unsafe {
195 let status: u8;
196 core::arch::asm!("in al, dx", out("al") status, in("dx") STATUS_PORT,
197 options(nostack, preserves_flags));
198 if status & STAT_OUTPUT_FULL == 0 {
199 return None;
200 }
201 let data: u8;
202 core::arch::asm!("in al, dx", out("al") data, in("dx") DATA_PORT,
203 options(nostack, preserves_flags));
204 process_byte(data)
205 }
206}
207
208/// Read a byte from the keyboard data port (blocking).
209fn read_data() -> u8 {
210 unsafe {
211 loop {
212 let status: u8;
213 core::arch::asm!("in al, dx", out("al") status, in("dx") STATUS_PORT,
214 options(nostack, preserves_flags));
215 if status & STAT_OUTPUT_FULL != 0 {
216 let data: u8;
217 core::arch::asm!("in al, dx", out("al") data, in("dx") DATA_PORT,
218 options(nostack, preserves_flags));
219 return data;
220 }
221 }
222 }
223}
224
225/// Process a single scancode byte. Returns Some(KeyEvent) when a visible
226/// event happens; returns None for modifier changes, releases, and prefixes.
227fn process_byte(byte: u8) -> Option<KeyEvent> {
228 let state = unsafe { &mut KBD };
229
230 if byte == 0xE0 {
231 state.got_e0 = true;
232 return None;
233 }
234
235 if state.got_e0 {
236 state.got_e0 = false;
237 return process_e0(byte);
238 }
239
240 // Break code (key release) — bit 7 set
241 if byte & 0x80 != 0 {
242 update_modifiers(byte & 0x7F, false, false);
243 return None;
244 }
245
246 // Make code (key press)
247 update_modifiers(byte, true, false);
248
249 match byte {
250 0x01 => Some(KeyEvent::Escape),
251 0x0E => Some(KeyEvent::Backspace),
252 0x0F => Some(KeyEvent::Tab),
253 0x1C => Some(KeyEvent::Enter),
254 0x37 => Some(KeyEvent::Char('*')),
255 0x3B..=0x44 => Some(KeyEvent::F(byte - 0x3B + 1)),
256 _ => {
257 let shifted = (state.modifiers & (MOD_LSHIFT | MOD_RSHIFT)) != 0;
258 // CapsLock inverts case for letters
259 let caps = state.modifiers & MOD_CAPS != 0;
260 let table = if shifted { SCAN_SHIFT } else { SCAN_NORMAL };
261 let mut ch = table[byte as usize];
262 // Handle CapsLock for letters
263 if caps && ch.is_ascii_alphabetic() {
264 // Swap case: if table gives lowercase, make uppercase; etc.
265 // Since normal table has lowercase, shift table has uppercase:
266 ch = if shifted {
267 ch.to_ascii_lowercase()
268 } else {
269 ch.to_ascii_uppercase()
270 };
271 }
272 if ch != 0 {
273 Some(KeyEvent::Char(ch as char))
274 } else {
275 Some(KeyEvent::Unknown(byte))
276 }
277 }
278 }
279}
280
281/// Process an E0-prefixed scancode.
282fn process_e0(byte: u8) -> Option<KeyEvent> {
283 if byte & 0x80 != 0 {
284 update_modifiers(byte & 0x7F, false, true);
285 return None;
286 }
287
288 update_modifiers(byte, true, true);
289
290 match byte {
291 0x48 => Some(KeyEvent::ArrowUp),
292 0x50 => Some(KeyEvent::ArrowDown),
293 0x4B => Some(KeyEvent::ArrowLeft),
294 0x4D => Some(KeyEvent::ArrowRight),
295 0x47 => Some(KeyEvent::Home),
296 0x4F => Some(KeyEvent::End),
297 0x49 => Some(KeyEvent::PageUp),
298 0x51 => Some(KeyEvent::PageDown),
299 0x53 => Some(KeyEvent::Delete),
300 0x35 => Some(KeyEvent::Char('/')),
301 _ => Some(KeyEvent::Unknown(byte)),
302 }
303}
304
305fn update_modifiers(scancode: u8, pressed: bool, e0: bool) {
306 let state = unsafe { &mut KBD };
307 match (scancode, e0) {
308 (0x2A, false) => set_mod(state, MOD_LSHIFT, pressed),
309 (0x36, false) => set_mod(state, MOD_RSHIFT, pressed),
310 (0x1D, false) => set_mod(state, MOD_LCTRL, pressed),
311 (0x1D, true) => set_mod(state, MOD_RCTRL, pressed),
312 (0x38, false) => set_mod(state, MOD_LALT, pressed),
313 (0x38, true) => set_mod(state, MOD_RALT, pressed),
314 (0x3A, false) => {
315 if pressed {
316 state.modifiers ^= MOD_CAPS;
317 }
318 }
319 _ => {}
320 }
321}
322
323fn set_mod(state: &mut KbdState, bit: u8, pressed: bool) {
324 if pressed {
325 state.modifiers |= bit;
326 } else {
327 state.modifiers &= !bit;
328 }
329}