diff --git a/CHANGELOG.md b/CHANGELOG.md index 8eaf3b3..384c17e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Change Log +## v0.3.1 + +* Add `hexdump`, `load` and `run` commands. +* Set colour attributes correctly (White on Black only currently) + ## v0.3.0 * Updated to Neotron Common BIOS v0.8.0 diff --git a/Cargo.toml b/Cargo.toml index d0b1ca9..96bb39e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "neotron-os" -version = "0.3.0" +version = "0.3.1" authors = ["Jonathan 'theJPster' Pallant "] edition = "2018" description = "The Neotron Operating System" diff --git a/src/commands/config.rs b/src/commands/config.rs new file mode 100644 index 0000000..18b5f8d --- /dev/null +++ b/src/commands/config.rs @@ -0,0 +1,92 @@ +//! Configuration related commands for Neotron OS + +use crate::{config, println, Ctx}; + +pub static COMMAND_ITEM: menu::Item = menu::Item { + item_type: menu::ItemType::Callback { + function: command, + 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"), +}; + +/// Called when the "config" command is executed. +fn command(_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"); + } + } +} diff --git a/src/commands/hardware.rs b/src/commands/hardware.rs new file mode 100644 index 0000000..7068b9e --- /dev/null +++ b/src/commands/hardware.rs @@ -0,0 +1,104 @@ +//! Hardware related commands for Neotron OS + +use crate::{bios, println, Ctx, API}; + +pub static LSHW_ITEM: menu::Item = menu::Item { + item_type: menu::ItemType::Callback { + function: bioshw, + parameters: &[], + }, + command: "bioshw", + help: Some("List all the BIOS hardware"), +}; + +/// Called when the "bioshw" command is executed. +fn bioshw(_menu: &menu::Menu, _item: &menu::Item, _args: &[&str], _ctx: &mut Ctx) { + let api = API.get(); + 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_idx, region); + found = true; + } + } + if !found { + println!(" None"); + } + + 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.name, device_info.device_type + ); + found = true; + } + } + if !found { + println!(" None"); + } + + 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!( + " {}: {} {:?} bs={} size={} MiB", + dev_idx, + device_info.name, + device_info.device_type, + device_info.block_size, + (device_info.num_blocks * u64::from(device_info.block_size)) / (1024 * 1024) + ); + found = true; + } + } + if !found { + println!(" None"); + } + + 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; + } + } + if !found { + println!(" None"); + } + + 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"); + } + + 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"); + } +} diff --git a/src/commands/input.rs b/src/commands/input.rs new file mode 100644 index 0000000..36c29d5 --- /dev/null +++ b/src/commands/input.rs @@ -0,0 +1,53 @@ +//! Input related commands for Neotron OS + +use crate::{bios, println, Ctx, API}; + +pub static KBTEST_ITEM: menu::Item = menu::Item { + item_type: menu::ItemType::Callback { + function: kbtest, + parameters: &[], + }, + command: "input_kbtest", + help: Some("Test the keyboard (press ESC to quit)"), +}; + +/// Called when the "kbtest" command is executed. +fn 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); + } + } + } +} diff --git a/src/commands/mod.rs b/src/commands/mod.rs new file mode 100644 index 0000000..a7b0dd4 --- /dev/null +++ b/src/commands/mod.rs @@ -0,0 +1,28 @@ +//! Commands for Neotron OS +//! +//! Defines the top-level menu, and the commands it can call. + +pub use super::Ctx; + +mod config; +mod hardware; +mod input; +mod ram; +mod screen; + +pub static OS_MENU: menu::Menu = menu::Menu { + label: "root", + items: &[ + &config::COMMAND_ITEM, + &hardware::LSHW_ITEM, + &ram::HEXDUMP_ITEM, + &ram::LOAD_ITEM, + #[cfg(target_os = "none")] + &ram::RUN_ITEM, + &screen::CLEAR_ITEM, + &screen::FILL_ITEM, + &input::KBTEST_ITEM, + ], + entry: None, + exit: None, +}; diff --git a/src/commands/ram.rs b/src/commands/ram.rs new file mode 100644 index 0000000..42580f8 --- /dev/null +++ b/src/commands/ram.rs @@ -0,0 +1,176 @@ +//! Raw RAM read/write related commands for Neotron OS + +use crate::{print, println, Ctx}; + +pub static HEXDUMP_ITEM: menu::Item = menu::Item { + item_type: menu::ItemType::Callback { + function: hexdump, + parameters: &[ + menu::Parameter::Mandatory { + parameter_name: "address", + help: Some("Start address"), + }, + menu::Parameter::Optional { + parameter_name: "length", + help: Some("Number of bytes"), + }, + ], + }, + command: "hexdump", + help: Some("Dump the contents of RAM as hex"), +}; + +pub static LOAD_ITEM: menu::Item = menu::Item { + item_type: menu::ItemType::Callback { + function: load, + parameters: &[ + menu::Parameter::Mandatory { + parameter_name: "address", + help: Some("Start address"), + }, + menu::Parameter::Mandatory { + parameter_name: "hex", + help: Some("Bytes as hex string"), + }, + ], + }, + command: "load", + help: Some("Load hex bytes into RAM from stdin"), +}; + +#[cfg(target_os = "none")] +pub static RUN_ITEM: menu::Item = menu::Item { + item_type: menu::ItemType::Callback { + function: run, + parameters: &[], + }, + command: "run", + help: Some("Jump to start of application area"), +}; + +fn parse_usize(input: &str) -> Result { + if let Some(digits) = input.strip_prefix("0x") { + // Parse as hex + usize::from_str_radix(digits, 16) + } else { + // Parse as decimal + usize::from_str_radix(input, 10) + } +} + +/// Called when the "hexdump" command is executed. +/// +/// If you ask for an address that generates a HardFault, the OS will crash. So +/// don't. +fn hexdump(_menu: &menu::Menu, _item: &menu::Item, args: &[&str], _ctx: &mut Ctx) { + const BYTES_PER_LINE: usize = 16; + + let Some(address_str) = args.get(0) else { + println!("No address"); + return; + }; + let Ok(address) = parse_usize(address_str) else { + println!("Bad address"); + return; + }; + let len_str = args.get(1).unwrap_or(&"16"); + let Ok(len) = parse_usize(len_str) else { + println!("Bad length"); + return; + }; + + let mut ptr = address as *const u8; + + let mut this_line = 0; + print!("{:08x}: ", address); + for count in 0..len { + if this_line == BYTES_PER_LINE { + println!(); + print!("{:08x}: ", address + count); + this_line = 1; + } else { + this_line += 1; + } + + let b = unsafe { ptr.read_volatile() }; + print!("{:02x} ", b); + ptr = unsafe { ptr.offset(1) }; + } + println!(); +} + +/// Called when the "load" command is executed. +fn load(_menu: &menu::Menu, _item: &menu::Item, args: &[&str], _ctx: &mut Ctx) { + let Some(address_str) = args.get(0) else { + println!("No address"); + return; + }; + let Ok(address) = parse_usize(address_str) else { + println!("Bad address"); + return; + }; + let Some(mut hex_str) = args.get(1).cloned() else { + println!("No hex"); + return; + }; + + let mut address = address as *mut u8; + let mut count = 0; + loop { + let Some(hex_byte) = hex_str.get(0..2) else { + println!("Bad hex from {:?}", hex_str); + return; + }; + hex_str = &hex_str[2..]; + let Ok(byte) = u8::from_str_radix(hex_byte, 16) else { + println!("Bad hex {:?}", hex_byte); + return; + }; + + unsafe { + address.write_volatile(byte); + address = address.offset(1); + } + count += 1; + + println!("Loaded {} bytes", count); + } +} + +#[allow(unused)] +#[repr(C)] +pub struct Api { + pub print: extern "C" fn(data: *const u8, len: usize), +} + +static CALLBACK_TABLE: Api = Api { print: print_fn }; + +extern "C" fn print_fn(data: *const u8, len: usize) { + let slice = unsafe { core::slice::from_raw_parts(data, len) }; + if let Ok(s) = core::str::from_utf8(slice) { + print!("{}", s); + } else { + // Ignore App output - not UTF-8 + } +} + +/// Called when the "run" command is executed. +#[cfg(target_os = "none")] +fn run(_menu: &menu::Menu, _item: &menu::Item, _args: &[&str], _ctx: &mut Ctx) { + use core::convert::TryInto; + const APPLICATION_START_ADDR: usize = 0x2000_1000; + const APPLICATION_LEN: usize = 4096; + // Application space starts 4K into Cortex-M SRAM + let application_ram: &'static mut [u8] = unsafe { + core::slice::from_raw_parts_mut(APPLICATION_START_ADDR as *mut u8, APPLICATION_LEN) + }; + let start_word: [u8; 4] = (&application_ram[0..4]).try_into().unwrap(); + let start_ptr = usize::from_le_bytes(start_word) as *const (); + let result = unsafe { + let code: extern "C" fn(*const Api) -> u32 = ::core::mem::transmute(start_ptr); + code(&CALLBACK_TABLE) + }; + if result != 0 { + println!("Got error code {}", result); + } +} diff --git a/src/commands/screen.rs b/src/commands/screen.rs new file mode 100644 index 0000000..ee8f7a3 --- /dev/null +++ b/src/commands/screen.rs @@ -0,0 +1,49 @@ +//! Screen-related commands for Neotron OS + +use crate::{print, println, Ctx, API, VGA_CONSOLE}; + +pub static CLEAR_ITEM: menu::Item = menu::Item { + item_type: menu::ItemType::Callback { + function: clear, + parameters: &[], + }, + command: "screen_clear", + help: Some("Clear the screen"), +}; + +pub static FILL_ITEM: menu::Item = menu::Item { + item_type: menu::ItemType::Callback { + function: fill, + parameters: &[], + }, + command: "screen_fill", + help: Some("Fill the screen with characters"), +}; + +/// Called when the "clear" command is executed. +fn 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 fill(_menu: &menu::Menu, _item: &menu::Item, _args: &[&str], _ctx: &mut Ctx) { + if let Some(ref mut console) = unsafe { &mut VGA_CONSOLE } { + console.clear(); + } + let api = API.get(); + let mode = (api.video_get_mode)(); + let (Some(width), Some(height)) = (mode.text_width(), mode.text_height()) else { + println!("Unable to get console size"); + return; + }; + // A range of printable ASCII compatible characters + let mut char_cycle = (' '..='~').cycle(); + // Scroll two screen fulls + for _row in 0..height * 2 { + for _col in 0..width { + print!("{}", char_cycle.next().unwrap()); + } + } +} diff --git a/src/lib.rs b/src/lib.rs index 271233b..836ca5e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,10 +9,10 @@ #![no_std] // Imports -use core::fmt::Write; use core::sync::atomic::{AtomicBool, Ordering}; use neotron_common_bios as bios; +mod commands; mod config; mod vgaconsole; @@ -37,63 +37,6 @@ static mut SERIAL_CONSOLE: Option = None; /// 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_lshw, - parameters: &[], - }, - command: "lshw", - help: Some("List all the hardware"), - }, - &menu::Item { - item_type: menu::ItemType::Callback { - function: cmd_clear, - parameters: &[], - }, - command: "clear", - help: Some("Clear the screen"), - }, - &menu::Item { - item_type: menu::ItemType::Callback { - function: cmd_fill, - parameters: &[], - }, - 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, -}; - // =========================================================================== // Macros // =========================================================================== @@ -102,10 +45,14 @@ static OS_MENU: menu::Menu = menu::Menu { #[macro_export] macro_rules! print { ($($arg:tt)*) => { - if let Some(ref mut console) = unsafe { &mut VGA_CONSOLE } { + if let Some(ref mut console) = unsafe { &mut crate::VGA_CONSOLE } { + #[allow(unused)] + use core::fmt::Write as _; write!(console, $($arg)*).unwrap(); } - if let Some(ref mut console) = unsafe { &mut SERIAL_CONSOLE } { + if let Some(ref mut console) = unsafe { &mut crate::SERIAL_CONSOLE } { + #[allow(unused)] + use core::fmt::Write as _; write!(console, $($arg)*).unwrap(); } }; @@ -114,10 +61,10 @@ macro_rules! print { /// Prints to the screen and puts a new-line on the end #[macro_export] macro_rules! println { - () => (print!("\n")); + () => (crate::print!("\n")); ($($arg:tt)*) => { - print!($($arg)*); - print!("\n"); + crate::print!($($arg)*); + crate::print!("\n"); }; } @@ -168,7 +115,7 @@ impl core::fmt::Write for SerialConsole { } } -struct Ctx { +pub struct Ctx { config: config::Config, keyboard: pc_keyboard::EventDecoder, } @@ -271,7 +218,7 @@ pub extern "C" fn main(api: *const bios::Api) -> ! { }; let mut buffer = [0u8; 256]; - let mut menu = menu::Runner::new(&OS_MENU, &mut buffer, ctx); + let mut menu = menu::Runner::new(&commands::OS_MENU, &mut buffer, ctx); loop { match (api.hid_get_event)() { @@ -320,233 +267,6 @@ pub extern "C" fn main(api: *const bios::Api) -> ! { } } -/// 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(); - 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_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; - } - } - if !found { - 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], _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], _ctx: &mut Ctx) { - if let Some(ref mut console) = unsafe { &mut VGA_CONSOLE } { - console.clear(); - } - let api = API.get(); - let mode = (api.video_get_mode)(); - let (Some(width), Some(height)) = (mode.text_width(), mode.text_height()) else { - println!("Unable to get console size"); - return; - }; - // A range of printable ASCII compatible characters - let mut char_cycle = (' '..='~').cycle(); - // Scroll two screen fulls - for _row in 0..height * 2 { - for _col in 0..width { - print!("{}", char_cycle.next().unwrap()); - } - } -} - -/// 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] diff --git a/src/vgaconsole.rs b/src/vgaconsole.rs index 737e935..63e8c05 100644 --- a/src/vgaconsole.rs +++ b/src/vgaconsole.rs @@ -3,6 +3,8 @@ //! Code for dealing with a VGA-style console, where there's a buffer of 16-bit //! values, each corresponding to a glyph and some attributes. +use neotron_common_bios::video::{Attr, TextBackgroundColour, TextForegroundColour}; + #[derive(Debug)] pub struct VgaConsole { addr: *mut u8, @@ -14,7 +16,11 @@ pub struct VgaConsole { impl VgaConsole { /// White on Black - const DEFAULT_ATTR: u8 = 15 << 3; + const DEFAULT_ATTR: Attr = Attr::new( + TextForegroundColour::WHITE, + TextBackgroundColour::BLACK, + false, + ); pub fn new(addr: *mut u8, width: isize, height: isize) -> VgaConsole { VgaConsole { @@ -60,17 +66,17 @@ impl VgaConsole { self.reset_cursor(); } - fn write(&mut self, glyph: u8, attr: Option) { + fn write(&mut self, glyph: u8, attr: Option) { self.write_at(self.row, self.col, glyph, attr); } - fn write_at(&mut self, row: isize, col: isize, glyph: u8, attr: Option) { + fn write_at(&mut self, row: isize, col: isize, glyph: u8, attr: Option) { assert!(row < self.height, "{} >= {}?", row, self.height); assert!(col < self.width, "{} => {}?", col, self.width); let offset = ((row * self.width) + col) * 2; unsafe { core::ptr::write_volatile(self.addr.offset(offset), glyph) }; if let Some(a) = attr { - unsafe { core::ptr::write_volatile(self.addr.offset(offset + 1), a) }; + unsafe { core::ptr::write_volatile(self.addr.offset(offset + 1), a.0) }; } }