diff --git a/Cargo.lock b/Cargo.lock index 6f5f524..1e2787d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -44,6 +44,60 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" +[[package]] +name = "conquer-once" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96eb12fb69466716fbae9009d389e6a30830ae8975e170eff2d2cff579f9efa3" +dependencies = [ + "conquer-util", +] + +[[package]] +name = "conquer-util" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "654fb2472cc369d311c547103a1fa81d467bef370ae7a0680f65939895b1182a" + +[[package]] +name = "crossbeam-queue" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "pin-utils", +] + [[package]] name = "generic-array" version = "0.12.4" @@ -98,6 +152,9 @@ name = "infinity_os" version = "0.1.0" dependencies = [ "bootloader", + "conquer-once", + "crossbeam-queue", + "futures-util", "heapless", "lazy_static", "linked_list_allocator", @@ -138,9 +195,9 @@ dependencies = [ [[package]] name = "pc-keyboard" -version = "0.5.1" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c6f2d937e3b8d63449b01401e2bae4041bc9dd1129c2e3e0d239407cf6635ac" +checksum = "ed089a1fbffe3337a1a345501c981f1eb1e47e69de5a40e852433e12953c3174" [[package]] name = "pic8259" @@ -151,6 +208,18 @@ dependencies = [ "x86_64", ] +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + [[package]] name = "rustversion" version = "1.0.19" diff --git a/Cargo.toml b/Cargo.toml index e0ff6f1..52502ec 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,7 +6,7 @@ edition = "2018" [dependencies] volatile = "0.2.6" spin = "0.5.2" -pc-keyboard = "0.5.0" +pc-keyboard = "0.7.0" heapless = "0.6.1" linked_list_allocator = "0.9.0" @@ -19,6 +19,20 @@ pic8259 = "0.10.1" version = "1.0" features = ["spin_no_std"] +[dependencies.crossbeam-queue] +version = "0.3.11" +default-features = false +features = ["alloc"] + +[dependencies.conquer-once] +version = "0.2.0" +default-features = false + +[dependencies.futures-util] +version = "0.3.4" +default-features = false +features = ["alloc"] + [package.metadata.bootimage] test-args = [ "-device", diff --git a/src/kernel/interrupts.rs b/src/kernel/interrupts.rs index 23bf7cc..bc3c42f 100644 --- a/src/kernel/interrupts.rs +++ b/src/kernel/interrupts.rs @@ -1,7 +1,6 @@ use crate::hlt_loop; use crate::kernel::{clock, gdt}; use crate::print; -use crate::user; use lazy_static::lazy_static; use pic8259::ChainedPics; use spin; @@ -83,34 +82,14 @@ extern "x86-interrupt" fn timer_interrupt_handler(_stack_frame: InterruptStackFr } } -extern "x86-interrupt" fn keyboard_interrupt_handler(_stack_frame: InterruptStackFrame) { - use crate::kernel::layouts; - use pc_keyboard::{DecodedKey, HandleControl, Keyboard, ScancodeSet1}; - use spin::Mutex; +extern "x86-interrupt" fn keyboard_interrupt_handler( + _stack_frame: InterruptStackFrame +) { use x86_64::instructions::port::Port; - lazy_static! { - static ref KEYBOARD: Mutex> = - Mutex::new(Keyboard::new( - layouts::Qwerty104Key, - ScancodeSet1, - HandleControl::MapLettersToUnicode - )); - } - - let mut keyboard = KEYBOARD.lock(); let mut port = Port::new(0x60); - let scancode: u8 = unsafe { port.read() }; - if let Ok(Some(key_event)) = keyboard.add_byte(scancode) { - if let Some(key) = keyboard.process_keyevent(key_event) { - //user::shell::key_handle(key); - match key { - DecodedKey::Unicode(c) => user::shell::key_handle(c), - DecodedKey::RawKey(_) => {} - } - } - } + crate::kernel::task::keyboard::add_scancode(scancode); unsafe { PICS.lock() diff --git a/src/kernel/layouts.rs b/src/kernel/layouts.rs deleted file mode 100644 index 66e1e88..0000000 --- a/src/kernel/layouts.rs +++ /dev/null @@ -1,195 +0,0 @@ -//! A standard United States 101-key (or 104-key including Windows keys) QWERTY keyboard. -//! Has a 1-row high Enter key, with Backslash above. - -use pc_keyboard::{DecodedKey, HandleControl, KeyCode, KeyboardLayout, Modifiers}; - -pub struct Qwerty104Key; - -impl KeyboardLayout for Qwerty104Key { - fn map_keycode( - keycode: KeyCode, - modifiers: &Modifiers, - _handle_ctrl: HandleControl, - ) -> DecodedKey { - match keycode { - KeyCode::BackTick => { - if modifiers.is_shifted() { - DecodedKey::Unicode('~') - } else { - DecodedKey::Unicode('`') - } - } - KeyCode::Key1 => { - if modifiers.is_shifted() { - DecodedKey::Unicode('!') - } else { - DecodedKey::Unicode('1') - } - } - KeyCode::Key2 => { - if modifiers.is_shifted() { - DecodedKey::Unicode('@') - } else { - DecodedKey::Unicode('2') - } - } - KeyCode::Key3 => { - if modifiers.is_shifted() { - DecodedKey::Unicode('#') - } else { - DecodedKey::Unicode('3') - } - } - KeyCode::Key4 => { - if modifiers.is_shifted() { - DecodedKey::Unicode('$') - } else { - DecodedKey::Unicode('4') - } - } - KeyCode::Key5 => { - if modifiers.is_shifted() { - DecodedKey::Unicode('%') - } else { - DecodedKey::Unicode('5') - } - } - KeyCode::Key6 => { - if modifiers.is_shifted() { - DecodedKey::Unicode('^') - } else { - DecodedKey::Unicode('6') - } - } - KeyCode::Key7 => { - if modifiers.is_shifted() { - DecodedKey::Unicode('&') - } else { - DecodedKey::Unicode('7') - } - } - KeyCode::Key8 => { - if modifiers.is_shifted() { - DecodedKey::Unicode('*') - } else { - DecodedKey::Unicode('8') - } - } - KeyCode::Key9 => { - if modifiers.is_shifted() { - DecodedKey::Unicode('(') - } else { - DecodedKey::Unicode('9') - } - } - KeyCode::Key0 => { - if modifiers.is_shifted() { - DecodedKey::Unicode(')') - } else { - DecodedKey::Unicode('0') - } - } - KeyCode::Minus => { - if modifiers.is_shifted() { - DecodedKey::Unicode('_') - } else { - DecodedKey::Unicode('-') - } - } - KeyCode::Equals => { - if modifiers.is_shifted() { - DecodedKey::Unicode('+') - } else { - DecodedKey::Unicode('=') - } - } - KeyCode::Backspace => DecodedKey::Unicode(0x08.into()), - KeyCode::Tab => DecodedKey::Unicode(0x09.into()), - KeyCode::Q => DecodedKey::Unicode(if modifiers.is_shifted() { 'Q' } else { 'q' }), - KeyCode::W => DecodedKey::Unicode(if modifiers.is_shifted() { 'W' } else { 'w' }), - KeyCode::E => DecodedKey::Unicode(if modifiers.is_shifted() { 'E' } else { 'e' }), - KeyCode::R => DecodedKey::Unicode(if modifiers.is_shifted() { 'R' } else { 'r' }), - KeyCode::T => DecodedKey::Unicode(if modifiers.is_shifted() { 'T' } else { 't' }), - KeyCode::Y => DecodedKey::Unicode(if modifiers.is_shifted() { 'Y' } else { 'y' }), - KeyCode::U => DecodedKey::Unicode(if modifiers.is_shifted() { 'U' } else { 'u' }), - KeyCode::I => DecodedKey::Unicode(if modifiers.is_shifted() { 'I' } else { 'i' }), - KeyCode::O => DecodedKey::Unicode(if modifiers.is_shifted() { 'O' } else { 'o' }), - KeyCode::P => DecodedKey::Unicode(if modifiers.is_shifted() { 'P' } else { 'p' }), - KeyCode::BracketSquareLeft => { - if modifiers.is_shifted() { - DecodedKey::Unicode('{') - } else { - DecodedKey::Unicode('[') - } - } - KeyCode::BracketSquareRight => { - if modifiers.is_shifted() { - DecodedKey::Unicode('}') - } else { - DecodedKey::Unicode(']') - } - } - KeyCode::BackSlash => { - if modifiers.is_shifted() { - DecodedKey::Unicode('|') - } else { - DecodedKey::Unicode('\\') - } - } - KeyCode::A => DecodedKey::Unicode(if modifiers.is_shifted() { 'A' } else { 'a' }), - KeyCode::S => DecodedKey::Unicode(if modifiers.is_shifted() { 'S' } else { 's' }), - KeyCode::D => DecodedKey::Unicode(if modifiers.is_shifted() { 'D' } else { 'd' }), - KeyCode::F => DecodedKey::Unicode(if modifiers.is_shifted() { 'F' } else { 'f' }), - KeyCode::G => DecodedKey::Unicode(if modifiers.is_shifted() { 'G' } else { 'g' }), - KeyCode::H => DecodedKey::Unicode(if modifiers.is_shifted() { 'H' } else { 'h' }), - KeyCode::J => DecodedKey::Unicode(if modifiers.is_shifted() { 'J' } else { 'j' }), - KeyCode::K => DecodedKey::Unicode(if modifiers.is_shifted() { 'K' } else { 'k' }), - KeyCode::L => DecodedKey::Unicode(if modifiers.is_shifted() { 'L' } else { 'l' }), - KeyCode::SemiColon => { - if modifiers.is_shifted() { - DecodedKey::Unicode(':') - } else { - DecodedKey::Unicode(';') - } - } - KeyCode::Quote => { - if modifiers.is_shifted() { - DecodedKey::Unicode('"') - } else { - DecodedKey::Unicode('\'') - } - } - KeyCode::Enter => DecodedKey::Unicode(10.into()), - KeyCode::Z => DecodedKey::Unicode(if modifiers.is_shifted() { 'Z' } else { 'z' }), - KeyCode::X => DecodedKey::Unicode(if modifiers.is_shifted() { 'X' } else { 'x' }), - KeyCode::C => DecodedKey::Unicode(if modifiers.is_shifted() { 'C' } else { 'c' }), - KeyCode::V => DecodedKey::Unicode(if modifiers.is_shifted() { 'V' } else { 'v' }), - KeyCode::B => DecodedKey::Unicode(if modifiers.is_shifted() { 'B' } else { 'b' }), - KeyCode::N => DecodedKey::Unicode(if modifiers.is_shifted() { 'N' } else { 'n' }), - KeyCode::M => DecodedKey::Unicode(if modifiers.is_shifted() { 'M' } else { 'm' }), - KeyCode::Comma => { - if modifiers.is_shifted() { - DecodedKey::Unicode('<') - } else { - DecodedKey::Unicode(',') - } - } - KeyCode::Fullstop => { - if modifiers.is_shifted() { - DecodedKey::Unicode('>') - } else { - DecodedKey::Unicode('.') - } - } - KeyCode::Slash => { - if modifiers.is_shifted() { - DecodedKey::Unicode('?') - } else { - DecodedKey::Unicode('/') - } - } - KeyCode::Spacebar => DecodedKey::Unicode(' '), - _ => DecodedKey::RawKey(keycode), - } - } -} diff --git a/src/kernel/mod.rs b/src/kernel/mod.rs index 98c3c03..06447cf 100644 --- a/src/kernel/mod.rs +++ b/src/kernel/mod.rs @@ -2,7 +2,7 @@ pub mod allocator; pub mod clock; pub mod gdt; pub mod interrupts; -pub mod layouts; pub mod memory; pub mod string; +pub mod task; pub mod vga; diff --git a/src/kernel/task/executor.rs b/src/kernel/task/executor.rs new file mode 100644 index 0000000..6b3247a --- /dev/null +++ b/src/kernel/task/executor.rs @@ -0,0 +1,104 @@ +use super::{Task, TaskId}; +use alloc::task::Wake; +use alloc::{collections::BTreeMap, sync::Arc}; +use core::task::Waker; +use core::task::{Context, Poll}; +use crossbeam_queue::ArrayQueue; + +pub struct Executor { + tasks: BTreeMap, + task_queue: Arc>, + waker_cache: BTreeMap, +} + +impl Executor { + pub fn new() -> Self { + Executor { + tasks: BTreeMap::new(), + task_queue: Arc::new(ArrayQueue::new(100)), + waker_cache: BTreeMap::new(), + } + } + + pub fn spawn(&mut self, task: Task) { + let task_id = task.id; + if self.tasks.insert(task.id, task).is_some() { + panic!("task with same ID already in tasks"); + } + self.task_queue.push(task_id).expect("queue full"); + } + + pub fn run(&mut self) -> ! { + loop { + self.run_ready_tasks(); + self.sleep_if_idle(); + } + } + + fn sleep_if_idle(&self) { + use x86_64::instructions::interrupts::{self, enable_and_hlt}; + + interrupts::disable(); + if self.task_queue.is_empty() { + enable_and_hlt(); + } else { + interrupts::enable(); + } + } + + fn run_ready_tasks(&mut self) { + // destructure `self` to avoid borrow checker errors + let Self { + tasks, + task_queue, + waker_cache, + } = self; + + while let Some(task_id) = task_queue.pop() { + let task = match tasks.get_mut(&task_id) { + Some(task) => task, + None => continue, // task no longer exists + }; + let waker = waker_cache + .entry(task_id) + .or_insert_with(|| TaskWaker::new(task_id, task_queue.clone())); + let mut context = Context::from_waker(waker); + match task.poll(&mut context) { + Poll::Ready(()) => { + // task done -> remove it and its cached waker + tasks.remove(&task_id); + waker_cache.remove(&task_id); + } + Poll::Pending => {} + } + } + } +} + +struct TaskWaker { + task_id: TaskId, + task_queue: Arc>, +} + +impl TaskWaker { + fn wake_task(&self) { + self.task_queue.push(self.task_id).expect("task_queue full"); + } + + fn new(task_id: TaskId, task_queue: Arc>) -> Waker { + Waker::from(Arc::new(TaskWaker { + task_id, + task_queue, + })) + } +} + +impl Wake for TaskWaker { + fn wake(self: Arc) { + self.wake_task(); + } + + fn wake_by_ref(self: &Arc) { + self.wake_task(); + } +} diff --git a/src/kernel/task/keyboard.rs b/src/kernel/task/keyboard.rs new file mode 100644 index 0000000..3047e81 --- /dev/null +++ b/src/kernel/task/keyboard.rs @@ -0,0 +1,84 @@ +use crate::print; +use conquer_once::spin::OnceCell; +use core::{ + pin::Pin, + task::{Context, Poll}, +}; +use crossbeam_queue::ArrayQueue; +use futures_util::{stream::Stream, StreamExt}; +use futures_util::task::AtomicWaker; +use pc_keyboard::{layouts, DecodedKey, HandleControl, Keyboard, ScancodeSet1}; + +static SCANCODE_QUEUE: OnceCell> = OnceCell::uninit(); +static WAKER: AtomicWaker = AtomicWaker::new(); + +/// Called by the keyboard interrupt handler +/// +/// Must not block or allocate. +pub(crate) fn add_scancode(scancode: u8) { + if let Ok(queue) = SCANCODE_QUEUE.try_get() { + if let Err(_) = queue.push(scancode) { + print!("WARNING: scancode queue full; dropping keyboard input\n"); + } else { + WAKER.wake(); // new + } + } else { + print!("WARNING: scancode queue uninitialized\n"); + } +} + +pub struct ScancodeStream { + _private: (), +} + +impl ScancodeStream { + pub fn new() -> Self { + SCANCODE_QUEUE + .try_init_once(|| ArrayQueue::new(100)) + .expect("ScancodeStream::new should only be called once"); + ScancodeStream { _private: () } + } +} + +impl Stream for ScancodeStream { + type Item = u8; + + fn poll_next(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + let queue = SCANCODE_QUEUE + .try_get() + .expect("scancode queue not initialized"); + + if let Some(scancode) = queue.pop() { + return Poll::Ready(Some(scancode)); + } + + WAKER.register(&cx.waker()); + match queue.pop() { + Some(scancode) => { + WAKER.take(); + Poll::Ready(Some(scancode)) + } + None => Poll::Pending, + } + } +} + +pub async fn keyboard_task() { + let mut scancodes = ScancodeStream::new(); + let mut keyboard = Keyboard::new( + ScancodeSet1::new(), + layouts::Us104Key, + HandleControl::Ignore, + ); + + while let Some(scancode) = scancodes.next().await { + if let Ok(Some(key_event)) = keyboard.add_byte(scancode) { + if let Some(key) = keyboard.process_keyevent(key_event) { + match key { + DecodedKey::Unicode(character) => print!("{}", character), + DecodedKey::RawKey(key) => print!("{:?}", key), + } + } + } + } +} diff --git a/src/kernel/task/mod.rs b/src/kernel/task/mod.rs new file mode 100644 index 0000000..4a6a425 --- /dev/null +++ b/src/kernel/task/mod.rs @@ -0,0 +1,36 @@ +use alloc::boxed::Box; +use core::sync::atomic::{AtomicU64, Ordering}; +use core::task::{Context, Poll}; +use core::{future::Future, pin::Pin}; + +pub mod executor; +pub mod keyboard; +pub mod simple_executor; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +struct TaskId(u64); + +impl TaskId { + fn new() -> Self { + static NEXT_ID: AtomicU64 = AtomicU64::new(0); + TaskId(NEXT_ID.fetch_add(1, Ordering::Relaxed)) + } +} + +pub struct Task { + id: TaskId, + future: Pin>>, +} + +impl Task { + pub fn new(future: impl Future + 'static) -> Task { + Task { + id: TaskId::new(), + future: Box::pin(future), + } + } + + fn poll(&mut self, context: &mut Context) -> Poll<()> { + self.future.as_mut().poll(context) + } +} diff --git a/src/kernel/task/simple_executor.rs b/src/kernel/task/simple_executor.rs new file mode 100644 index 0000000..d3ca30a --- /dev/null +++ b/src/kernel/task/simple_executor.rs @@ -0,0 +1,47 @@ +use super::Task; +use alloc::collections::VecDeque; +use core::task::RawWakerVTable; +use core::task::{Context, Poll}; + +pub struct SimpleExecutor { + task_queue: VecDeque, +} + +impl SimpleExecutor { + pub fn new() -> SimpleExecutor { + SimpleExecutor { + task_queue: VecDeque::new(), + } + } + + pub fn spawn(&mut self, task: Task) { + self.task_queue.push_back(task) + } + + pub fn run(&mut self) { + while let Some(mut task) = self.task_queue.pop_front() { + let waker = dummy_waker(); + let mut context = Context::from_waker(&waker); + match task.poll(&mut context) { + Poll::Ready(()) => {} // task done + Poll::Pending => self.task_queue.push_back(task), + } + } + } +} + +use core::task::{RawWaker, Waker}; + +fn dummy_raw_waker() -> RawWaker { + fn no_op(_: *const ()) {} + fn clone(_: *const ()) -> RawWaker { + dummy_raw_waker() + } + + let vtable = &RawWakerVTable::new(clone, no_op, no_op, no_op); + RawWaker::new(0 as *const (), vtable) +} + +fn dummy_waker() -> Waker { + unsafe { Waker::from_raw(dummy_raw_waker()) } +} diff --git a/src/lib.rs b/src/lib.rs index c69fc0b..50f89b0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,7 +10,6 @@ extern crate alloc; use core::panic::PanicInfo; pub mod kernel; -pub mod user; #[cfg(test)] use bootloader::{entry_point, BootInfo}; diff --git a/src/main.rs b/src/main.rs index e045aae..63170fd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -9,8 +9,9 @@ extern crate alloc; use bootloader::{entry_point, BootInfo}; use core::panic::PanicInfo; use infinity_os::kernel; +use infinity_os::kernel::task::keyboard; +use infinity_os::kernel::task::{executor::Executor, Task}; use infinity_os::print; -use infinity_os::user::shell; use x86_64::VirtAddr; entry_point!(kernel_main); @@ -18,8 +19,6 @@ entry_point!(kernel_main); pub fn kernel_main(boot_info: &'static BootInfo) -> ! { infinity_os::init(); - shell::print_banner(); - /* Memory Initialization */ let phys_mem_offset = VirtAddr::new(boot_info.physical_memory_offset); let mut mapper = unsafe { kernel::memory::init(phys_mem_offset) }; @@ -28,9 +27,9 @@ pub fn kernel_main(boot_info: &'static BootInfo) -> ! { kernel::allocator::init_heap(&mut mapper, &mut frame_allocator) .expect("heap initialization failed"); - shell::print_prompt(); - - infinity_os::hlt_loop(); + let mut executor = Executor::new(); + executor.spawn(Task::new(keyboard::keyboard_task())); + executor.run(); } #[panic_handler] diff --git a/src/user/mod.rs b/src/user/mod.rs deleted file mode 100644 index 327cf1b..0000000 --- a/src/user/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod shell; diff --git a/src/user/shell.rs b/src/user/shell.rs deleted file mode 100644 index e10c668..0000000 --- a/src/user/shell.rs +++ /dev/null @@ -1,60 +0,0 @@ -use crate::print; -use crate::kernel::clock; -use crate::kernel::string::String; -use lazy_static::lazy_static; -use spin::Mutex; - -lazy_static! { - pub static ref STDIN: Mutex = Mutex::new(String::new()); -} - -pub fn print_banner() { - print!(" ____\n"); - print!(" /|o | ()()\n"); - print!(" /o| o| (o.o )\n"); - print!("+-----------------/o_|_o_|------------> ^ <------------------------------------+\n"); - print!("| |\n"); - print!("| Infinity OS |\n"); - print!("| |\n"); - print!("| Lightweight Operating System |\n"); - print!("| |\n"); - print!("+------------------------------------------------------------------------------+\n"); -} - -pub fn print_prompt() { - print!("\n> "); -} - -pub fn key_handle(c: char) { - let mut stdin = STDIN.lock(); - if c == '\n' { - print!("\n"); - match stdin.as_str() { - "help" => { - print!("Lightweight easy to use operating system made to limit e-waste"); - }, - "version" => { - print!("Infinity OS v{}", env!("CARGO_PKG_VERSION")); - }, - "uptime" => { - print!("{:.0} seconds", clock::uptime()); - }, - _ => { - print!("Unknown command: {}", stdin.as_str()); - } - } - stdin.clear(); - print!("\n"); - print_prompt(); - } else { - if c == 0x08 as char { - if stdin.len() > 0 { - stdin.pop(); - print!("{}", c); - } - } else { - stdin.push(c as u8); - print!("{}", c); - } - } -}