diff --git a/src/ps2/controller.rs b/src/ps2/controller.rs index 71eaf94..e2e5b12 100755 --- a/src/ps2/controller.rs +++ b/src/ps2/controller.rs @@ -1,15 +1,11 @@ /// https://wiki.osdev.org/%228042%22_PS/2_Controller -use core::arch::asm; - -use super::{Key, scancodes::SCANCODE_TO_KEY}; - -const DATA_PORT: u16 = 0x60; -const STATUS_PORT: u16 = 0x64; -const COMMAND_PORT: u16 = 0x64; -const OUTPUT_BUFFER_STATUS_BIT: u8 = 1; +use crate::ps2::{ + DATA_PORT, + io::{flush_output_buffer, read, send_command, send_data, wait_for_data}, +}; #[repr(u8)] -enum Command { +pub enum Command { DisableFirstPort = 0xAD, DisableSecondPort = 0xA7, EnableFirstPort = 0xAE, @@ -22,7 +18,7 @@ enum Command { } #[repr(u8)] -enum Status { +pub enum Status { OutputFull = 0x01, InputFull = 0x02, } @@ -293,110 +289,3 @@ pub fn init() -> Result<(), &'static str> { Ok(()) } - -fn send_command(cmd: Command) { - while unsafe { read(STATUS_PORT) } & Status::InputFull as u8 != 0 {} - - unsafe { write(COMMAND_PORT, cmd as u8) }; -} - -fn send_data(data: u8) { - while unsafe { read(STATUS_PORT) } & Status::InputFull as u8 != 0 {} - - unsafe { write(DATA_PORT, data) }; -} - -fn wait_for_data() -> u8 { - while unsafe { read(STATUS_PORT) } & Status::OutputFull as u8 == 0 {} - - unsafe { read(DATA_PORT) } -} - -/// Reads all data from the output buffer, flushing it. Note that this will -/// go into an endless loop if called without disabling the ports first. -fn flush_output_buffer() { - while unsafe { read(STATUS_PORT) } & Status::OutputFull as u8 != 0 { - unsafe { read(DATA_PORT) }; - } -} - -static mut LAST_KEY: Option = None; - -/// Reads from the PS2 data port if the PS2 status port is ready. Returns `Some(KeyScanCode)` -/// if the converted scancode is a supported character. -/// -/// /// ### Example Usage: -/// ``` -/// let mut v = Vga::new(); -/// -/// if let Some(c) = read_if_ready() == KeyScanCode::A { -/// v.write_char(b'a'); -/// } -pub fn read_if_ready() -> Option { - if !is_ps2_data_available() { - return None; - } - - let code = unsafe { read(DATA_PORT) }; - - if code == 0xF0 { - while !is_ps2_data_available() {} - let _ = unsafe { read(DATA_PORT) }; - unsafe { LAST_KEY = None }; - return None; - } - - if code == 0xE0 { - while !is_ps2_data_available() {} - let extended_code = unsafe { read(DATA_PORT) }; - unsafe { LAST_KEY = Some(extended_code) }; - return SCANCODE_TO_KEY[extended_code as usize].1; - } - - unsafe { LAST_KEY = Some(code) }; - SCANCODE_TO_KEY[code as usize].1 -} - -/// Returns `true` if the PS2 input buffer has data ready to be read, -/// meaning the least significant bit of the PS2 status port is set. -fn is_ps2_data_available() -> bool { - status() & OUTPUT_BUFFER_STATUS_BIT != 0 -} - -/// Reads from `STATUS_PORT` and returns the extracted value. -fn status() -> u8 { - let res: u8; - - unsafe { - res = read(STATUS_PORT); - } - - res -} - -/// Reads from `port` and returns the extracted value. -unsafe fn read(port: u16) -> u8 { - assert!(port == DATA_PORT || port == STATUS_PORT); - - let res: u8; - - unsafe { - asm!( - "in al, dx", - in("dx") port, - out("al") res, - ); - } - - res -} - -unsafe fn write(port: u16, val: u8) { - unsafe { - asm!( - "out dx, al", - in("dx") port, - in("al") val, - ); - } -} diff --git a/src/ps2/io.rs b/src/ps2/io.rs new file mode 100644 index 0000000..31b1ef1 --- /dev/null +++ b/src/ps2/io.rs @@ -0,0 +1,114 @@ +use core::arch::asm; + +use super::{ + COMMAND_PORT, DATA_PORT, Key, OUTPUT_BUFFER_STATUS_BIT, STATUS_PORT, + controller::{Command, Status}, + scancodes::SCANCODE_TO_KEY, +}; + +pub fn send_command(cmd: Command) { + while unsafe { read(STATUS_PORT) } & Status::InputFull as u8 != 0 {} + + unsafe { write(COMMAND_PORT, cmd as u8) }; +} + +pub fn send_data(data: u8) { + while unsafe { read(STATUS_PORT) } & Status::InputFull as u8 != 0 {} + + unsafe { write(DATA_PORT, data) }; +} + +pub fn wait_for_data() -> u8 { + while unsafe { read(STATUS_PORT) } & Status::OutputFull as u8 == 0 {} + + unsafe { read(DATA_PORT) } +} + +/// Reads all data from the output buffer, flushing it. Note that this will +/// go into an endless loop if called without disabling the ports first. +pub fn flush_output_buffer() { + while unsafe { read(STATUS_PORT) } & Status::OutputFull as u8 != 0 { + unsafe { read(DATA_PORT) }; + } +} + +static mut LAST_KEY: Option = None; + +/// Reads from the PS2 data port if the PS2 status port is ready. Returns `Some(KeyScanCode)` +/// if the converted scancode is a supported character. +/// +/// /// ### Example Usage: +/// ``` +/// let mut v = Vga::new(); +/// +/// if let Some(c) = read_if_ready() == KeyScanCode::A { +/// v.write_char(b'a'); +/// } +pub fn read_if_ready() -> Option { + if !is_ps2_data_available() { + return None; + } + + let code = unsafe { read(DATA_PORT) }; + + if code == 0xF0 { + while !is_ps2_data_available() {} + let _ = unsafe { read(DATA_PORT) }; + unsafe { LAST_KEY = None }; + return None; + } + + if code == 0xE0 { + while !is_ps2_data_available() {} + let extended_code = unsafe { read(DATA_PORT) }; + unsafe { LAST_KEY = Some(extended_code) }; + return SCANCODE_TO_KEY[extended_code as usize].1; + } + + unsafe { LAST_KEY = Some(code) }; + SCANCODE_TO_KEY[code as usize].1 +} + +/// Returns `true` if the PS2 input buffer has data ready to be read, +/// meaning the least significant bit of the PS2 status port is set. +fn is_ps2_data_available() -> bool { + status() & OUTPUT_BUFFER_STATUS_BIT != 0 +} + +/// Reads from `STATUS_PORT` and returns the extracted value. +fn status() -> u8 { + let res: u8; + + unsafe { + res = read(STATUS_PORT); + } + + res +} + +/// Reads from `port` and returns the extracted value. +pub unsafe fn read(port: u16) -> u8 { + assert!(port == DATA_PORT || port == STATUS_PORT); + + let res: u8; + + unsafe { + asm!( + "in al, dx", + in("dx") port, + out("al") res, + ); + } + + res +} + +unsafe fn write(port: u16, val: u8) { + unsafe { + asm!( + "out dx, al", + in("dx") port, + in("al") val, + ); + } +} diff --git a/src/ps2/mod.rs b/src/ps2/mod.rs index 63dcda8..8f1da67 100644 --- a/src/ps2/mod.rs +++ b/src/ps2/mod.rs @@ -1,5 +1,12 @@ mod controller; +mod io; mod scancodes; -pub use controller::{init, read_if_ready}; +pub const DATA_PORT: u16 = 0x60; +pub const STATUS_PORT: u16 = 0x64; +pub const COMMAND_PORT: u16 = 0x64; +pub const OUTPUT_BUFFER_STATUS_BIT: u8 = 1; + +pub use controller::init; +pub use io::read_if_ready; pub use scancodes::Key;