diff --git a/nucleus/src/devices/console.rs b/nucleus/src/devices/console.rs new file mode 100644 index 0000000..1f58d0d --- /dev/null +++ b/nucleus/src/devices/console.rs @@ -0,0 +1,136 @@ +/* + * SPDX-License-Identifier: BlueOak-1.0.0 + */ + +#![allow(dead_code)] + +use core::fmt; + +/// A trait that must be implemented by devices that are candidates for the +/// global console. +#[allow(unused_variables)] +pub trait ConsoleOps { + fn putc(&self, c: char) {} + fn puts(&self, string: &str) {} + fn getc(&self) -> char { + ' ' + } + fn flush(&self) {} +} + +/// A dummy console that just ignores its inputs. +pub struct NullConsole; +impl Drop for NullConsole { + fn drop(&mut self) {} +} +impl ConsoleOps for NullConsole {} + +/// Possible outputs which the console can store. +pub enum Output { + None(NullConsole), +} + +/// Generate boilerplate for converting into one of Output enum values +macro output_from($name:ty, $optname:ident) { + impl From<$name> for Output { + fn from(instance: $name) -> Self { + Output::$optname(instance) + } + } +} + +output_from!(NullConsole, None); +pub struct Console { + output: Output, +} + +impl Default for Console { + fn default() -> Self { + Console { + output: (NullConsole {}).into(), + } + } +} + +impl Console { + pub const fn new() -> Console { + Console { + output: Output::None(NullConsole {}), + } + } + + #[inline(always)] + fn current_ptr(&self) -> &dyn ConsoleOps { + match &self.output { + Output::None(i) => i, + } + } + + /// Overwrite the current output. The old output will go out of scope and + /// it's Drop function will be called. + pub fn replace_with(&mut self, x: Output) { + self.current_ptr().flush(); + + self.output = x; + } + + /// A command prompt. + pub fn command_prompt<'a>(&self, buf: &'a mut [u8]) -> &'a [u8] { + self.puts("\n$> "); + + let mut i = 0; + let mut input; + loop { + input = self.getc(); + + if input == '\n' { + self.puts("\n"); // do \r\n output + return &buf[..i]; + } else { + if i < buf.len() { + buf[i] = input as u8; + i += 1; + } else { + return &buf[..i]; + } + + self.putc(input); + } + } + } +} + +impl Drop for Console { + fn drop(&mut self) {} +} + +/// Dispatch the respective function to the currently stored output device. +impl ConsoleOps for Console { + fn putc(&self, c: char) { + self.current_ptr().putc(c); + } + + fn puts(&self, string: &str) { + self.current_ptr().puts(string); + } + + fn getc(&self) -> char { + self.current_ptr().getc() + } + + fn flush(&self) { + self.current_ptr().flush() + } +} + +/// Implementing this trait enables usage of the format_args! macros, which in +/// turn are used to implement the kernel's print! and println! macros. +/// +/// See src/macros.rs. +impl fmt::Write for Console { + fn write_str(&mut self, s: &str) -> fmt::Result { + self.current_ptr().puts(s); + + Ok(()) + } +} diff --git a/nucleus/src/devices/mod.rs b/nucleus/src/devices/mod.rs new file mode 100644 index 0000000..a361cc4 --- /dev/null +++ b/nucleus/src/devices/mod.rs @@ -0,0 +1,6 @@ +/* + * SPDX-License-Identifier: BlueOak-1.0.0 + */ +pub mod console; + +pub use console::{Console, ConsoleOps}; diff --git a/nucleus/src/main.rs b/nucleus/src/main.rs index de2e245..158c04d 100644 --- a/nucleus/src/main.rs +++ b/nucleus/src/main.rs @@ -31,6 +31,7 @@ extern crate rlibc; // To enable linking memory intrinsics. #[macro_use] pub mod arch; pub use arch::*; +mod devices; mod macros; mod mm; mod panic;