diff --git a/CHANGELOG.MD b/CHANGELOG.MD deleted file mode 100644 index b63600f..0000000 --- a/CHANGELOG.MD +++ /dev/null @@ -1,9 +0,0 @@ -# Change Log - -## v0.2.0 - -Adds HID support and basic shell, with 'mem' and 'fill' commands. - -## v0.1.0 - -First version. diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..8eaf3b3 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,19 @@ +# Change Log + +## v0.3.0 + +* Updated to Neotron Common BIOS v0.8.0 +* Use pc-keyboard for decoding HID events +* Fix Windows library build +* Added 'kbtest' command +* Added 'lshw' command +* Added 'config' command +* Uses BIOS to store/load OS configuration + +## v0.2.0 + +Adds HID support and basic shell, with 'mem' and 'fill' commands. + +## v0.1.0 + +First version. diff --git a/Cargo.toml b/Cargo.toml index c379f93..d0b1ca9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "neotron-os" -version = "0.2.0" +version = "0.3.0" authors = ["Jonathan 'theJPster' Pallant "] edition = "2018" description = "The Neotron Operating System" @@ -37,7 +37,8 @@ panic = "abort" panic = "abort" [dependencies] -neotron-common-bios = "0.7" +neotron-common-bios = "0.8" +pc-keyboard = "0.7" r0 = "1.0" postcard = "0.5" serde = { version = "1.0", default-features = false } diff --git a/README.md b/README.md index 90e7acb..5e61c56 100644 --- a/README.md +++ b/README.md @@ -51,10 +51,7 @@ $ ls ./target/debug/*.so ## Changelog -### Unreleased Changes ([Source](https://github.com/neotron-compute/Neotron-OS/tree/master)) - -* Basic `println!` to the text buffer. -* Re-arranged linker script setup +See [`CHANGELOG.md`](./CHANGELOG.md) ## Licence diff --git a/build.rs b/build.rs index aa49f39..06bdfa4 100644 --- a/build.rs +++ b/build.rs @@ -23,6 +23,11 @@ fn main() { println!("cargo:rustc-env=OS_VERSION={}", env!("CARGO_PKG_VERSION")); } - #[cfg(target_os = "macos")] - println!("cargo:rustc-link-lib=c"); + if Ok("macos") == env::var("CARGO_CFG_TARGET_OS").as_deref() { + println!("cargo:rustc-link-lib=c"); + } + + if Ok("windows") == env::var("CARGO_CFG_TARGET_OS").as_deref() { + println!("cargo:rustc-link-lib=dylib=msvcrt"); + } } diff --git a/src/charmap.rs b/src/charmap.rs deleted file mode 100644 index 9ec05dd..0000000 --- a/src/charmap.rs +++ /dev/null @@ -1,616 +0,0 @@ -//! # Handles Keyboard Character Map -//! -//! We get events from the BIOS HID API which describe keypresses. This module -//! provides a type which can convert keypresses into a stream of Unicode -//! characters. -//! -//! It only handles UK English keyboards at present. - -use crate::bios; - -use bios::hid::KeyCode; - -#[derive(Copy, Clone, PartialEq, Eq, Debug, PartialOrd, Ord)] -pub enum SpecialKey { - /// The Down cursor key - ArrowDown, - /// The Left cursor key - ArrowLeft, - /// The Right cursor key - ArrowRight, - /// The Up cursor key - ArrowUp, - /// The `Delete` (`Del`) key - Delete, - /// The `End` key - End, - /// The `F1` key - F1, - /// The `F2` key - F2, - /// The `F3` key - F3, - /// The `F4` key - F4, - /// The `F5` key - F5, - /// The `F6` key - F6, - /// The `F7` key - F7, - /// The `F8` key - F8, - /// The `F9` key - F9, - /// The `F10` key - F10, - /// The `F11` key - F11, - /// The `F12` key - F12, - /// The `Home` key - Home, - /// The `Insert` key - Insert, - /// The `Right-click Menu` key - Menus, - /// The `Num Lock` key on the Numeric Keypad - NumpadLock, - /// The `Page Down` key - PageDown, - /// The `Page Up` key - PageUp, - /// The `Pause/Break` key - PauseBreak, - /// The `Print Screen` (`PrtScr`) key - PrintScreen, - /// The `Scroll Lock` key - ScrollLock, - /// Media transport: previous track - PrevTrack, - /// Media transport: next track - NextTrack, - /// Media transport: mute audio - Mute, - /// Application key: open calculator - Calculator, - /// Media transport: play audio - Play, - /// Media transport: stop audio - Stop, - /// Media transport: turn volume down - VolumeDown, - /// Media transport: turn volume up - VolumeUp, - /// Media transport: open browser to homepage - WWWHome, - /// Keyboard Power On Test passed. - /// - /// Expect this once on start-up. - PowerOnTestOk, -} - -/// Represents a single keypress. -/// -/// Could be a Unicode character, or a special key (like Arrow Up). -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum Keypress { - /// Got a new Unicode character from the keyboard - Unicode(char), - /// Got some special keypress (e.g. an arrow key) - Special(SpecialKey), -} - -/// Decodes a 104/105-key UK English keyboard. -#[derive(Debug, Clone, Default)] -pub struct UKEnglish { - left_shift: bool, - right_shift: bool, - left_alt: bool, - right_alt: bool, - left_ctrl: bool, - right_ctrl: bool, - left_win: bool, - right_win: bool, - caps_lock: bool, -} - -impl UKEnglish { - pub fn new() -> UKEnglish { - Default::default() - } - - pub fn shift_pressed(&self) -> bool { - self.left_shift || self.right_shift - } - - pub fn want_capitals(&self) -> bool { - self.caps_lock ^ self.shift_pressed() - } - - /// Handle a new BIOS HID event. - /// - /// May generate a new keypress. - pub fn handle_event(&mut self, event: bios::hid::HidEvent) -> Option { - match event { - bios::hid::HidEvent::KeyPress(code) => self.handle_press(code), - bios::hid::HidEvent::KeyRelease(code) => { - self.handle_release(code); - None - } - _ => { - // Ignore mouse and other events - None - } - } - } - - /// Handle a key being pressed. - /// - /// This may generate a new keypress. - pub fn handle_press(&mut self, code: KeyCode) -> Option { - match code { - // Modifiers - KeyCode::AltLeft => { - self.left_alt = true; - None - } - KeyCode::AltRight => { - self.right_alt = true; - None - } - KeyCode::CapsLock => { - self.caps_lock = true; - None - } - KeyCode::ControlLeft => { - self.left_ctrl = true; - None - } - KeyCode::ControlRight => { - self.right_ctrl = true; - None - } - KeyCode::ShiftLeft => { - self.left_shift = true; - None - } - KeyCode::ShiftRight => { - self.right_shift = true; - None - } - KeyCode::WindowsLeft => { - self.left_win = true; - None - } - KeyCode::WindowsRight => { - self.right_win = true; - None - } - // Special keys - KeyCode::ArrowDown => Some(Keypress::Special(SpecialKey::ArrowDown)), - KeyCode::ArrowLeft => Some(Keypress::Special(SpecialKey::ArrowLeft)), - KeyCode::ArrowRight => Some(Keypress::Special(SpecialKey::ArrowRight)), - KeyCode::ArrowUp => Some(Keypress::Special(SpecialKey::ArrowUp)), - KeyCode::Delete => Some(Keypress::Special(SpecialKey::Delete)), - KeyCode::End => Some(Keypress::Special(SpecialKey::End)), - KeyCode::F1 => Some(Keypress::Special(SpecialKey::F1)), - KeyCode::F2 => Some(Keypress::Special(SpecialKey::F2)), - KeyCode::F3 => Some(Keypress::Special(SpecialKey::F3)), - KeyCode::F4 => Some(Keypress::Special(SpecialKey::F4)), - KeyCode::F5 => Some(Keypress::Special(SpecialKey::F5)), - KeyCode::F6 => Some(Keypress::Special(SpecialKey::F6)), - KeyCode::F7 => Some(Keypress::Special(SpecialKey::F7)), - KeyCode::F8 => Some(Keypress::Special(SpecialKey::F8)), - KeyCode::F9 => Some(Keypress::Special(SpecialKey::F9)), - KeyCode::F10 => Some(Keypress::Special(SpecialKey::F10)), - KeyCode::F11 => Some(Keypress::Special(SpecialKey::F11)), - KeyCode::F12 => Some(Keypress::Special(SpecialKey::F12)), - KeyCode::Home => Some(Keypress::Special(SpecialKey::Home)), - KeyCode::Insert => Some(Keypress::Special(SpecialKey::Insert)), - KeyCode::Menus => Some(Keypress::Special(SpecialKey::Menus)), - KeyCode::NumpadLock => Some(Keypress::Special(SpecialKey::NumpadLock)), - KeyCode::PageDown => Some(Keypress::Special(SpecialKey::PageDown)), - KeyCode::PageUp => Some(Keypress::Special(SpecialKey::PageUp)), - KeyCode::PauseBreak => Some(Keypress::Special(SpecialKey::PauseBreak)), - KeyCode::PrintScreen => Some(Keypress::Special(SpecialKey::PrintScreen)), - KeyCode::ScrollLock => Some(Keypress::Special(SpecialKey::ScrollLock)), - KeyCode::PrevTrack => Some(Keypress::Special(SpecialKey::PrevTrack)), - KeyCode::NextTrack => Some(Keypress::Special(SpecialKey::NextTrack)), - KeyCode::Mute => Some(Keypress::Special(SpecialKey::Mute)), - KeyCode::Calculator => Some(Keypress::Special(SpecialKey::Calculator)), - KeyCode::Play => Some(Keypress::Special(SpecialKey::Play)), - KeyCode::Stop => Some(Keypress::Special(SpecialKey::Stop)), - KeyCode::VolumeDown => Some(Keypress::Special(SpecialKey::VolumeDown)), - KeyCode::VolumeUp => Some(Keypress::Special(SpecialKey::VolumeUp)), - KeyCode::WWWHome => Some(Keypress::Special(SpecialKey::WWWHome)), - KeyCode::PowerOnTestOk => Some(Keypress::Special(SpecialKey::PowerOnTestOk)), - // Letter keys - KeyCode::A => { - if self.want_capitals() { - Some(Keypress::Unicode('A')) - } else { - Some(Keypress::Unicode('a')) - } - } - KeyCode::B => { - if self.want_capitals() { - Some(Keypress::Unicode('B')) - } else { - Some(Keypress::Unicode('b')) - } - } - KeyCode::C => { - if self.want_capitals() { - Some(Keypress::Unicode('C')) - } else { - Some(Keypress::Unicode('c')) - } - } - KeyCode::D => { - if self.want_capitals() { - Some(Keypress::Unicode('D')) - } else { - Some(Keypress::Unicode('d')) - } - } - KeyCode::E => { - if self.want_capitals() { - Some(Keypress::Unicode('E')) - } else { - Some(Keypress::Unicode('e')) - } - } - KeyCode::F => { - if self.want_capitals() { - Some(Keypress::Unicode('F')) - } else { - Some(Keypress::Unicode('f')) - } - } - KeyCode::G => { - if self.want_capitals() { - Some(Keypress::Unicode('G')) - } else { - Some(Keypress::Unicode('g')) - } - } - KeyCode::H => { - if self.want_capitals() { - Some(Keypress::Unicode('H')) - } else { - Some(Keypress::Unicode('h')) - } - } - KeyCode::I => { - if self.want_capitals() { - Some(Keypress::Unicode('I')) - } else { - Some(Keypress::Unicode('i')) - } - } - KeyCode::J => { - if self.want_capitals() { - Some(Keypress::Unicode('J')) - } else { - Some(Keypress::Unicode('j')) - } - } - KeyCode::K => { - if self.want_capitals() { - Some(Keypress::Unicode('K')) - } else { - Some(Keypress::Unicode('k')) - } - } - KeyCode::L => { - if self.want_capitals() { - Some(Keypress::Unicode('L')) - } else { - Some(Keypress::Unicode('l')) - } - } - KeyCode::M => { - if self.want_capitals() { - Some(Keypress::Unicode('M')) - } else { - Some(Keypress::Unicode('m')) - } - } - KeyCode::N => { - if self.want_capitals() { - Some(Keypress::Unicode('N')) - } else { - Some(Keypress::Unicode('n')) - } - } - KeyCode::O => { - if self.want_capitals() { - Some(Keypress::Unicode('O')) - } else { - Some(Keypress::Unicode('o')) - } - } - KeyCode::P => { - if self.want_capitals() { - Some(Keypress::Unicode('P')) - } else { - Some(Keypress::Unicode('p')) - } - } - KeyCode::Q => { - if self.want_capitals() { - Some(Keypress::Unicode('Q')) - } else { - Some(Keypress::Unicode('q')) - } - } - KeyCode::R => { - if self.want_capitals() { - Some(Keypress::Unicode('R')) - } else { - Some(Keypress::Unicode('r')) - } - } - KeyCode::S => { - if self.want_capitals() { - Some(Keypress::Unicode('S')) - } else { - Some(Keypress::Unicode('s')) - } - } - KeyCode::T => { - if self.want_capitals() { - Some(Keypress::Unicode('T')) - } else { - Some(Keypress::Unicode('t')) - } - } - KeyCode::U => { - if self.want_capitals() { - Some(Keypress::Unicode('U')) - } else { - Some(Keypress::Unicode('u')) - } - } - KeyCode::V => { - if self.want_capitals() { - Some(Keypress::Unicode('V')) - } else { - Some(Keypress::Unicode('v')) - } - } - KeyCode::W => { - if self.want_capitals() { - Some(Keypress::Unicode('W')) - } else { - Some(Keypress::Unicode('w')) - } - } - KeyCode::X => { - if self.want_capitals() { - Some(Keypress::Unicode('X')) - } else { - Some(Keypress::Unicode('x')) - } - } - KeyCode::Y => { - if self.want_capitals() { - Some(Keypress::Unicode('Y')) - } else { - Some(Keypress::Unicode('y')) - } - } - KeyCode::Z => { - if self.want_capitals() { - Some(Keypress::Unicode('Z')) - } else { - Some(Keypress::Unicode('z')) - } - } - // Shiftable keys - KeyCode::BackSlash => { - if self.shift_pressed() { - Some(Keypress::Unicode('|')) - } else { - Some(Keypress::Unicode('\\')) - } - } - KeyCode::BackTick => { - if self.shift_pressed() { - Some(Keypress::Unicode('¬')) - } else { - Some(Keypress::Unicode('`')) - } - } - KeyCode::BracketSquareLeft => { - if self.shift_pressed() { - Some(Keypress::Unicode('{')) - } else { - Some(Keypress::Unicode('[')) - } - } - KeyCode::BracketSquareRight => { - if self.shift_pressed() { - Some(Keypress::Unicode('}')) - } else { - Some(Keypress::Unicode(']')) - } - } - KeyCode::Comma => { - if self.shift_pressed() { - Some(Keypress::Unicode('<')) - } else { - Some(Keypress::Unicode(',')) - } - } - KeyCode::Equals => { - if self.shift_pressed() { - Some(Keypress::Unicode('_')) - } else { - Some(Keypress::Unicode('=')) - } - } - KeyCode::Key1 => { - if self.shift_pressed() { - Some(Keypress::Unicode('!')) - } else { - Some(Keypress::Unicode('1')) - } - } - KeyCode::Key2 => { - if self.shift_pressed() { - Some(Keypress::Unicode('"')) - } else { - Some(Keypress::Unicode('2')) - } - } - KeyCode::Key3 => { - if self.shift_pressed() { - Some(Keypress::Unicode('£')) - } else { - Some(Keypress::Unicode('3')) - } - } - KeyCode::Key4 => { - if self.shift_pressed() { - Some(Keypress::Unicode('$')) - } else { - Some(Keypress::Unicode('4')) - } - } - KeyCode::Key5 => { - if self.shift_pressed() { - Some(Keypress::Unicode('%')) - } else { - Some(Keypress::Unicode('5')) - } - } - KeyCode::Key6 => { - if self.shift_pressed() { - Some(Keypress::Unicode('^')) - } else { - Some(Keypress::Unicode('6')) - } - } - KeyCode::Key7 => { - if self.shift_pressed() { - Some(Keypress::Unicode('&')) - } else { - Some(Keypress::Unicode('7')) - } - } - KeyCode::Key8 => { - if self.shift_pressed() { - Some(Keypress::Unicode('*')) - } else { - Some(Keypress::Unicode('8')) - } - } - KeyCode::Key9 => { - if self.shift_pressed() { - Some(Keypress::Unicode('(')) - } else { - Some(Keypress::Unicode('9')) - } - } - KeyCode::Key0 => { - if self.shift_pressed() { - Some(Keypress::Unicode(')')) - } else { - Some(Keypress::Unicode('0')) - } - } - KeyCode::Minus => { - if self.shift_pressed() { - Some(Keypress::Unicode('_')) - } else { - Some(Keypress::Unicode('-')) - } - } - KeyCode::SemiColon => { - if self.shift_pressed() { - Some(Keypress::Unicode(':')) - } else { - Some(Keypress::Unicode(';')) - } - } - KeyCode::Slash => { - if self.shift_pressed() { - Some(Keypress::Unicode('?')) - } else { - Some(Keypress::Unicode('/')) - } - } - KeyCode::HashTilde => { - if self.shift_pressed() { - Some(Keypress::Unicode('~')) - } else { - Some(Keypress::Unicode('#')) - } - } - // Non-shiftable keys - KeyCode::Backspace => Some(Keypress::Unicode(0x08 as char)), - KeyCode::Enter => Some(Keypress::Unicode('\r')), - KeyCode::Escape => Some(Keypress::Unicode(0x1B as char)), - KeyCode::Fullstop => Some(Keypress::Unicode('.')), - KeyCode::Numpad1 => Some(Keypress::Unicode('1')), - KeyCode::Numpad2 => Some(Keypress::Unicode('2')), - KeyCode::Numpad3 => Some(Keypress::Unicode('3')), - KeyCode::Numpad4 => Some(Keypress::Unicode('4')), - KeyCode::Numpad5 => Some(Keypress::Unicode('5')), - KeyCode::Numpad6 => Some(Keypress::Unicode('6')), - KeyCode::Numpad7 => Some(Keypress::Unicode('7')), - KeyCode::Numpad8 => Some(Keypress::Unicode('8')), - KeyCode::Numpad9 => Some(Keypress::Unicode('9')), - KeyCode::Numpad0 => Some(Keypress::Unicode('0')), - KeyCode::NumpadEnter => Some(Keypress::Unicode('\r')), - KeyCode::NumpadSlash => Some(Keypress::Unicode('\\')), - KeyCode::NumpadStar => Some(Keypress::Unicode('*')), - KeyCode::NumpadMinus => Some(Keypress::Unicode('-')), - KeyCode::NumpadPeriod => Some(Keypress::Unicode('.')), - KeyCode::NumpadPlus => Some(Keypress::Unicode('+')), - KeyCode::Spacebar => Some(Keypress::Unicode(' ')), - KeyCode::Tab => Some(Keypress::Unicode('\t')), - KeyCode::Quote => Some(Keypress::Unicode('\'')), - } - } - - /// Handle a key being released. - /// - /// This never generates a new keypress. - pub fn handle_release(&mut self, code: bios::hid::KeyCode) { - match code { - KeyCode::AltLeft => { - self.left_alt = false; - } - KeyCode::AltRight => { - self.right_alt = false; - } - KeyCode::CapsLock => { - self.caps_lock = false; - } - KeyCode::ControlLeft => { - self.left_ctrl = false; - } - KeyCode::ControlRight => { - self.right_ctrl = false; - } - KeyCode::WindowsLeft => { - self.left_win = false; - } - KeyCode::WindowsRight => { - self.right_win = false; - } - KeyCode::ShiftLeft => { - self.left_shift = false; - } - KeyCode::ShiftRight => { - self.right_shift = false; - } - _ => { - // Ignore - } - } - } -} diff --git a/src/config.rs b/src/config.rs index 3126f7f..4aa3119 100644 --- a/src/config.rs +++ b/src/config.rs @@ -8,8 +8,8 @@ use serde::{Deserialize, Serialize}; /// Represents our configuration information that we ask the BIOS to serialise #[derive(Debug, Serialize, Deserialize)] pub struct Config { - vga_console_on: bool, - serial_console_on: bool, + vga_console: bool, + serial_console: bool, serial_baud: u32, } @@ -29,18 +29,26 @@ impl Config { let api = API.get(); let mut buffer = [0u8; 64]; let slice = postcard::to_slice(self, &mut buffer).map_err(|_e| "Failed to parse config")?; - (api.configuration_set)(bios::ApiByteSlice::new(slice)); - Ok(()) + match (api.configuration_set)(bios::ApiByteSlice::new(slice)) { + bios::Result::Ok(_) => Ok(()), + bios::Result::Err(bios::Error::Unimplemented) => Err("BIOS doesn't support this (yet)"), + bios::Result::Err(_) => Err("BIOS reported an error"), + } } /// Should this system use the VGA console? - pub fn has_vga_console(&self) -> bool { - self.vga_console_on + pub fn get_vga_console(&self) -> bool { + self.vga_console + } + + // Set whether this system should use the VGA console. + pub fn set_vga_console(&mut self, new_value: bool) { + self.vga_console = new_value; } /// Should this system use the UART console? - pub fn has_serial_console(&self) -> Option<(u8, bios::serial::Config)> { - if self.serial_console_on { + pub fn get_serial_console(&self) -> Option<(u8, bios::serial::Config)> { + if self.serial_console { Some(( 0, bios::serial::Config { @@ -55,13 +63,25 @@ impl Config { None } } + + /// Turn the serial console off + pub fn set_serial_console_off(&mut self) { + self.serial_console = false; + self.serial_baud = 0; + } + + /// Turn the serial console on + pub fn set_serial_console_on(&mut self, serial_baud: u32) { + self.serial_console = true; + self.serial_baud = serial_baud; + } } impl core::default::Default for Config { fn default() -> Config { Config { - vga_console_on: true, - serial_console_on: false, + vga_console: true, + serial_console: false, serial_baud: 115200, } } diff --git a/src/lib.rs b/src/lib.rs index ffad8ea..271233b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,9 +10,9 @@ // Imports use core::fmt::Write; +use core::sync::atomic::{AtomicBool, Ordering}; use neotron_common_bios as bios; -mod charmap; mod config; mod vgaconsole; @@ -32,16 +32,21 @@ static mut VGA_CONSOLE: Option = None; /// We store our VGA console here. static mut SERIAL_CONSOLE: Option = None; +/// Note if we are panicking right now. +/// +/// If so, don't panic if a serial write fails. +static IS_PANIC: AtomicBool = AtomicBool::new(false); + static OS_MENU: menu::Menu = menu::Menu { label: "root", items: &[ &menu::Item { item_type: menu::ItemType::Callback { - function: cmd_mem, + function: cmd_lshw, parameters: &[], }, - command: "mem", - help: Some("Show memory regions"), + command: "lshw", + help: Some("List all the hardware"), }, &menu::Item { item_type: menu::ItemType::Callback { @@ -59,6 +64,31 @@ static OS_MENU: menu::Menu = menu::Menu { command: "fill", help: Some("Fill the screen with characters"), }, + &menu::Item { + item_type: menu::ItemType::Callback { + function: cmd_config, + parameters: &[ + menu::Parameter::Optional { + parameter_name: "command", + help: Some("Which operation to perform (try help)"), + }, + menu::Parameter::Optional { + parameter_name: "value", + help: Some("new value for the setting"), + }, + ], + }, + command: "config", + help: Some("Handle non-volatile OS configuration"), + }, + &menu::Item { + item_type: menu::ItemType::Callback { + function: cmd_kbtest, + parameters: &[], + }, + command: "kbtest", + help: Some("Test the keyboard (press ESC to quit)"), + }, ], entry: None, exit: None, @@ -122,20 +152,26 @@ struct SerialConsole(u8); impl core::fmt::Write for SerialConsole { fn write_str(&mut self, data: &str) -> core::fmt::Result { let api = API.get(); - (api.serial_write)( + let is_panic = IS_PANIC.load(Ordering::SeqCst); + let res = (api.serial_write)( // Which port self.0, // Data bios::ApiByteSlice::new(data.as_bytes()), // No timeout bios::Option::None, - ) - .unwrap(); + ); + if !is_panic { + res.unwrap(); + } Ok(()) } } -struct Ctx; +struct Ctx { + config: config::Config, + keyboard: pc_keyboard::EventDecoder, +} impl core::fmt::Write for Ctx { fn write_str(&mut self, data: &str) -> core::fmt::Result { @@ -192,7 +228,7 @@ pub extern "C" fn main(api: *const bios::Api) -> ! { let config = config::Config::load().unwrap_or_default(); - if config.has_vga_console() { + if config.get_vga_console() { // Try and set 80x30 mode for maximum compatibility (api.video_set_mode)(bios::video::Mode::new( bios::video::Timing::T640x480, @@ -216,7 +252,7 @@ pub extern "C" fn main(api: *const bios::Api) -> ! { } } - if let Some((idx, serial_config)) = config.has_serial_console() { + if let Some((idx, serial_config)) = config.get_serial_console() { let _ignored = (api.serial_configure)(idx, serial_config); unsafe { SERIAL_CONSOLE = Some(SerialConsole(idx)) }; println!("Configured Serial console on Serial {}", idx); @@ -226,71 +262,160 @@ pub extern "C" fn main(api: *const bios::Api) -> ! { println!("Welcome to {}!", OS_VERSION); println!("Copyright © Jonathan 'theJPster' Pallant and the Neotron Developers, 2022"); - let mut found; - - println!("Serial Ports:"); - found = false; - for device_idx in 0..=255 { - if let bios::Option::Some(serial) = (api.serial_get_info)(device_idx) { - println!("Serial Device {}: {:?}", device_idx, serial); - found = true; - } else { - // Ran out of serial devices (we assume they are consecutive) - break; - } - } - if !found { - println!("None."); - } + let ctx = Ctx { + config, + keyboard: pc_keyboard::EventDecoder::new( + pc_keyboard::layouts::AnyLayout::Uk105Key(pc_keyboard::layouts::Uk105Key), + pc_keyboard::HandleControl::MapLettersToUnicode, + ), + }; - let mut keyboard = charmap::UKEnglish::new(); let mut buffer = [0u8; 256]; - let mut menu = menu::Runner::new(&OS_MENU, &mut buffer, Ctx); + let mut menu = menu::Runner::new(&OS_MENU, &mut buffer, ctx); loop { - if let neotron_common_bios::Result::Ok(neotron_common_bios::Option::Some(ev)) = - (api.hid_get_event)() - { - if let Some(charmap::Keypress::Unicode(ch)) = keyboard.handle_event(ev) { - let mut buffer = [0u8; 6]; - let s = ch.encode_utf8(&mut buffer); - for b in s.as_bytes() { - menu.input_byte(*b); + match (api.hid_get_event)() { + bios::Result::Ok(bios::Option::Some(bios::hid::HidEvent::KeyPress(code))) => { + let pckb_ev = pc_keyboard::KeyEvent { + code, + state: pc_keyboard::KeyState::Down, + }; + if let Some(pc_keyboard::DecodedKey::Unicode(mut ch)) = + menu.context.keyboard.process_keyevent(pckb_ev) + { + if ch == '\n' { + ch = '\r'; + } + let mut buffer = [0u8; 6]; + let s = ch.encode_utf8(&mut buffer); + for b in s.as_bytes() { + menu.input_byte(*b); + } } } + bios::Result::Ok(bios::Option::Some(bios::hid::HidEvent::KeyRelease(code))) => { + let pckb_ev = pc_keyboard::KeyEvent { + code, + state: pc_keyboard::KeyState::Up, + }; + if let Some(pc_keyboard::DecodedKey::Unicode(ch)) = + menu.context.keyboard.process_keyevent(pckb_ev) + { + let mut buffer = [0u8; 6]; + let s = ch.encode_utf8(&mut buffer); + for b in s.as_bytes() { + menu.input_byte(*b); + } + } + } + bios::Result::Ok(bios::Option::Some(bios::hid::HidEvent::MouseInput(_ignore))) => {} + bios::Result::Ok(bios::Option::None) => { + // Do nothing + } + bios::Result::Err(e) => { + println!("Failed to get HID events: {:?}", e); + } } (api.power_idle)(); } } -/// Called when the "mem" command is executed. -fn cmd_mem(_menu: &menu::Menu, _item: &menu::Item, _args: &[&str], _context: &mut Ctx) { - println!("Memory Regions:"); - let mut found = false; +/// Called when the "lshw" command is executed. +fn cmd_lshw(_menu: &menu::Menu, _item: &menu::Item, _args: &[&str], _ctx: &mut Ctx) { let api = API.get(); - for region_idx in 0..=255 { + let mut found = false; + + println!("Memory regions:"); + for region_idx in 0..=255u8 { if let bios::Option::Some(region) = (api.memory_get_region)(region_idx) { - println!("Region {}: {}", region_idx, region); + println!(" {}: {}", region_idx, region); + found = true; + } + } + if !found { + println!(" None"); + } + + println!(); + found = false; + + println!("Serial Devices:"); + for dev_idx in 0..=255u8 { + if let bios::Option::Some(device_info) = (api.serial_get_info)(dev_idx) { + println!(" {}: {:?}", dev_idx, device_info); + found = true; + } + } + if !found { + println!(" None"); + } + + println!(); + found = false; + + println!("Block Devices:"); + for dev_idx in 0..=255u8 { + if let bios::Option::Some(device_info) = (api.block_dev_get_info)(dev_idx) { + println!(" {}: {:?}", dev_idx, device_info); + found = true; + } + } + if !found { + println!(" None"); + } + + println!(); + found = false; + + println!("I2C Buses:"); + for dev_idx in 0..=255u8 { + if let bios::Option::Some(device_info) = (api.i2c_bus_get_info)(dev_idx) { + println!(" {}: {:?}", dev_idx, device_info); found = true; - } else { - // Ran out of regions (we assume they are consecutive) - break; } } if !found { - println!("None."); + println!(" None"); + } + + println!(); + found = false; + + println!("Neotron Bus Devices:"); + for dev_idx in 0..=255u8 { + if let bios::Option::Some(device_info) = (api.bus_get_info)(dev_idx) { + println!(" {}: {:?}", dev_idx, device_info); + found = true; + } + } + if !found { + println!(" None"); + } + + println!(); + found = false; + + println!("Audio Mixers:"); + for dev_idx in 0..=255u8 { + if let bios::Option::Some(device_info) = (api.audio_mixer_channel_get_info)(dev_idx) { + println!(" {}: {:?}", dev_idx, device_info); + found = true; + } + } + if !found { + println!(" None"); } } /// Called when the "clear" command is executed. -fn cmd_clear(_menu: &menu::Menu, _item: &menu::Item, _args: &[&str], _context: &mut Ctx) { +fn cmd_clear(_menu: &menu::Menu, _item: &menu::Item, _args: &[&str], _ctx: &mut Ctx) { if let Some(ref mut console) = unsafe { &mut VGA_CONSOLE } { console.clear(); } } /// Called when the "fill" command is executed. -fn cmd_fill(_menu: &menu::Menu, _item: &menu::Item, _args: &[&str], _context: &mut Ctx) { +fn cmd_fill(_menu: &menu::Menu, _item: &menu::Item, _args: &[&str], _ctx: &mut Ctx) { if let Some(ref mut console) = unsafe { &mut VGA_CONSOLE } { console.clear(); } @@ -310,10 +435,123 @@ fn cmd_fill(_menu: &menu::Menu, _item: &menu::Item, _args: &[&str], _c } } +/// Called when the "config" command is executed. +fn cmd_config(_menu: &menu::Menu, _item: &menu::Item, args: &[&str], ctx: &mut Ctx) { + let command = args.get(0).cloned().unwrap_or("print"); + match command { + "reset" => match config::Config::load() { + Ok(new_config) => { + ctx.config = new_config; + println!("Loaded OK."); + } + Err(e) => { + println!("Error loading; {}", e); + } + }, + "save" => match ctx.config.save() { + Ok(_) => { + println!("Saved OK."); + } + Err(e) => { + println!("Error saving: {}", e); + } + }, + "vga" => match args.get(1).cloned() { + Some("on") => { + ctx.config.set_vga_console(true); + println!("VGA now on"); + } + Some("off") => { + ctx.config.set_vga_console(false); + println!("VGA now off"); + } + _ => { + println!("Give on or off as argument"); + } + }, + "serial" => match (args.get(1).cloned(), args.get(1).map(|s| s.parse::())) { + (_, Some(Ok(baud))) => { + println!("Turning serial console on at {} bps", baud); + ctx.config.set_serial_console_on(baud); + } + (Some("off"), _) => { + println!("Turning serial console off"); + ctx.config.set_serial_console_off(); + } + _ => { + println!("Give off or an integer as argument"); + } + }, + "print" => { + println!("VGA : {}", ctx.config.get_vga_console()); + match ctx.config.get_serial_console() { + None => { + println!("Serial: off"); + } + Some((_port, config)) => { + println!("Serial: {} bps", config.data_rate_bps); + } + } + } + _ => { + println!("config print - print the config"); + println!("config help - print this help text"); + println!("config reset - load config from BIOS store"); + println!("config save - save config to BIOS store"); + println!("config vga on - turn VGA on"); + println!("config vga off - turn VGA off"); + println!("config serial off - turn serial console off"); + println!("config serial - turn serial console on with given baud rate"); + } + } +} + +/// Called when the "kbtest" command is executed. +fn cmd_kbtest(_menu: &menu::Menu, _item: &menu::Item, _args: &[&str], ctx: &mut Ctx) { + let api = API.get(); + loop { + match (api.hid_get_event)() { + bios::Result::Ok(bios::Option::Some(bios::hid::HidEvent::KeyPress(code))) => { + let pckb_ev = pc_keyboard::KeyEvent { + code, + state: pc_keyboard::KeyState::Down, + }; + if let Some(ev) = ctx.keyboard.process_keyevent(pckb_ev) { + println!("Code={code:?} State=Down Decoded={ev:?}"); + } else { + println!("Code={code:?} State=Down Decoded=None"); + } + if code == pc_keyboard::KeyCode::Escape { + break; + } + } + bios::Result::Ok(bios::Option::Some(bios::hid::HidEvent::KeyRelease(code))) => { + let pckb_ev = pc_keyboard::KeyEvent { + code, + state: pc_keyboard::KeyState::Up, + }; + if let Some(ev) = ctx.keyboard.process_keyevent(pckb_ev) { + println!("Code={code:?} State=Up Decoded={ev:?}"); + } else { + println!("Code={code:?} State=Up Decoded=None"); + } + } + bios::Result::Ok(bios::Option::Some(bios::hid::HidEvent::MouseInput(_ignore))) => {} + bios::Result::Ok(bios::Option::None) => { + // Do nothing + } + bios::Result::Err(e) => { + println!("Failed to get HID events: {:?}", e); + } + } + } +} + /// Called when we have a panic. #[inline(never)] #[panic_handler] fn panic(info: &core::panic::PanicInfo) -> ! { + IS_PANIC.store(true, Ordering::SeqCst); println!("PANIC!\n{:#?}", info); let api = API.get(); loop {