From 67ef7bd8a8050000c7a889a922a1cf5235ca62cb Mon Sep 17 00:00:00 2001 From: Vincent Ollivier Date: Mon, 14 Oct 2024 15:48:52 +0200 Subject: [PATCH 01/33] Add blank command --- src/api/vga/mod.rs | 18 ++++++++++++++++++ src/sys/vga.rs | 1 + src/usr/blank.rs | 28 ++++++++++++++++++++++++++++ src/usr/mod.rs | 1 + src/usr/render.rs | 44 ++++++++++++++------------------------------ src/usr/shell.rs | 1 + 6 files changed, 63 insertions(+), 30 deletions(-) create mode 100644 src/usr/blank.rs diff --git a/src/api/vga/mod.rs b/src/api/vga/mod.rs index e8902ad9d..475809645 100644 --- a/src/api/vga/mod.rs +++ b/src/api/vga/mod.rs @@ -3,3 +3,21 @@ pub mod palette; pub use color::Color; pub use palette::Palette; + +use crate::sys::vga; +use crate::usr::shell; + +pub fn graphic_mode() { + // TODO: Backup font and palette + vga::set_320x200_mode(); +} + +pub fn text_mode() { + vga::set_80x25_mode(); + + // TODO: Restore font and palette backup instead of this + shell::exec("shell /ini/palettes/gruvbox-dark.sh").ok(); + shell::exec("read /ini/fonts/zap-light-8x16.psf => /dev/vga/font").ok(); + + print!("\x1b[2J\x1b[1;1H"); // Clear screen and move to top +} diff --git a/src/sys/vga.rs b/src/sys/vga.rs index 4d5c8bae8..efa643660 100644 --- a/src/sys/vga.rs +++ b/src/sys/vga.rs @@ -514,6 +514,7 @@ fn vga_color(color: u8) -> u8 { color >> 2 } +// TODO: Remove this fn parse_palette(palette: &str) -> Result<(usize, u8, u8, u8), ParseIntError> { debug_assert!(palette.len() == 8); debug_assert!(palette.starts_with('P')); diff --git a/src/usr/blank.rs b/src/usr/blank.rs new file mode 100644 index 000000000..47b70da03 --- /dev/null +++ b/src/usr/blank.rs @@ -0,0 +1,28 @@ +use crate::api::io; +use crate::api::process::ExitCode; +use crate::api::vga; + +const FRAMEBUFFER: usize = 0xA0000; +const WIDTH: usize = 320; +const HEIGHT: usize = 200; + +fn clear() { + let ptr = FRAMEBUFFER as *mut u8; + let size = WIDTH * HEIGHT; + unsafe { + let buf = core::slice::from_raw_parts_mut(ptr, size); + buf.fill(0x00); + } +} + +pub fn main(_args: &[&str]) -> Result<(), ExitCode> { + vga::graphic_mode(); + print!("\x1b]R\x1b[1A"); // Reset palette + clear(); + while io::stdin().read_char().is_none() { + x86_64::instructions::hlt(); + } + clear(); + vga::text_mode(); + Ok(()) +} diff --git a/src/usr/mod.rs b/src/usr/mod.rs index ed98d2cbb..40c0212e6 100644 --- a/src/usr/mod.rs +++ b/src/usr/mod.rs @@ -1,4 +1,5 @@ pub mod beep; +pub mod blank; pub mod calc; pub mod chess; pub mod copy; diff --git a/src/usr/render.rs b/src/usr/render.rs index 6e1147c91..7afbfc3c6 100644 --- a/src/usr/render.rs +++ b/src/usr/render.rs @@ -2,7 +2,7 @@ use crate::api::console::Style; use crate::api::fs; use crate::api::io; use crate::api::process::ExitCode; -use crate::usr::shell; +use crate::api::vga; use crate::sys; use alloc::vec::Vec; @@ -13,6 +13,15 @@ const FRAMEBUFFER: usize = 0xA0000; const WIDTH: usize = 320; const HEIGHT: usize = 200; +fn clear() { + let ptr = FRAMEBUFFER as *mut u8; + let size = WIDTH * HEIGHT; + unsafe { + let buf = core::slice::from_raw_parts_mut(ptr, size); + buf.fill(0x00); + } +} + #[derive(Debug)] #[repr(C, packed)] struct BmpHeader { @@ -84,15 +93,6 @@ fn parse_bmp(data: &[u8]) -> Result { Ok(BmpInfo { width, height, palette, pixels }) } -fn clear() { - let ptr = FRAMEBUFFER as *mut u8; - let size = WIDTH * HEIGHT; - unsafe { - let buf = core::slice::from_raw_parts_mut(ptr, size); - buf.fill(0x00); - } -} - fn help() { let csi_option = Style::color("aqua"); let csi_title = Style::color("yellow"); @@ -127,36 +127,21 @@ impl Config { pub fn text_mode(&mut self) { if self.mode == Mode::Graphic { - text_mode(); + clear(); + vga::text_mode(); self.mode = Mode::Text; } } pub fn graphic_mode(&mut self) { if self.mode == Mode::Text { - graphic_mode(); + vga::graphic_mode(); + clear(); self.mode = Mode::Graphic; } } } -fn graphic_mode() { - // TODO: Backup font and palette - sys::vga::set_320x200_mode(); - clear(); -} - -fn text_mode() { - clear(); - sys::vga::set_80x25_mode(); - - // TODO: Restore font and palette backup instead of this - shell::exec("shell /ini/palettes/gruvbox-dark.sh").ok(); - shell::exec("read /ini/fonts/zap-light-8x16.psf => /dev/vga/font").ok(); - - print!("\x1b[2J\x1b[1;1H"); // Clear screen and move to top -} - fn render_bmp(path: &str, config: &mut Config) -> Result { if let Ok(buf) = fs::read_to_bytes(&path) { if let Ok(bmp) = parse_bmp(&buf) { @@ -261,7 +246,6 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> { return Ok(()); } let files = &args[1..]; - let mut config = Config::new(); let mut i = 0; let n = files.len(); diff --git a/src/usr/shell.rs b/src/usr/shell.rs index 043d97515..a25bfc09c 100644 --- a/src/usr/shell.rs +++ b/src/usr/shell.rs @@ -517,6 +517,7 @@ fn dispatch(args: &[&str], config: &mut Config) -> Result<(), ExitCode> { "2048" => usr::pow::main(args), "alias" => cmd_alias(args, config), "beep" => usr::beep::main(args), + "blank" => usr::blank::main(args), "calc" => usr::calc::main(args), "chess" => usr::chess::main(args), "copy" => usr::copy::main(args), From 16135309d04a8cf797026a87cf06287e419c3001 Mon Sep 17 00:00:00 2001 From: Vincent Ollivier Date: Mon, 14 Oct 2024 15:49:59 +0200 Subject: [PATCH 02/33] Move vga driver to module --- src/sys/{vga.rs => vga/mod.rs} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/sys/{vga.rs => vga/mod.rs} (100%) diff --git a/src/sys/vga.rs b/src/sys/vga/mod.rs similarity index 100% rename from src/sys/vga.rs rename to src/sys/vga/mod.rs From 97b8940c415a39af87877e9853249f5c74ce2442 Mon Sep 17 00:00:00 2001 From: Vincent Ollivier Date: Mon, 14 Oct 2024 16:25:42 +0200 Subject: [PATCH 03/33] Remove vga command --- src/usr/mod.rs | 1 - src/usr/shell.rs | 5 ++- src/usr/vga.rs | 81 ------------------------------------------------ 3 files changed, 2 insertions(+), 85 deletions(-) delete mode 100644 src/usr/vga.rs diff --git a/src/usr/mod.rs b/src/usr/mod.rs index 40c0212e6..45d02c6f5 100644 --- a/src/usr/mod.rs +++ b/src/usr/mod.rs @@ -39,6 +39,5 @@ pub mod socket; pub mod tcp; pub mod time; pub mod user; -pub mod vga; pub mod view; pub mod write; diff --git a/src/usr/shell.rs b/src/usr/shell.rs index a25bfc09c..163962686 100644 --- a/src/usr/shell.rs +++ b/src/usr/shell.rs @@ -14,12 +14,12 @@ use alloc::vec::Vec; use core::sync::atomic::{fence, Ordering}; // TODO: Scan /bin -const AUTOCOMPLETE_COMMANDS: [&str; 41] = [ +const AUTOCOMPLETE_COMMANDS: [&str; 40] = [ "2048", "calc", "chess", "copy", "date", "decode", "delete", "dhcp", "diff", "disk", "edit", "elf", "encode", "env", "goto", "hash", "help", "hex", "host", "http", "httpd", "install", "keyboard", "life", "lisp", "list", "memory", "move", "net", "pci", "quit", "read", "render", "shell", - "socket", "tcp", "time", "user", "vga", "view", "write", + "socket", "tcp", "time", "user", "view", "write", ]; struct Config { @@ -563,7 +563,6 @@ fn dispatch(args: &[&str], config: &mut Config) -> Result<(), ExitCode> { "unset" => cmd_unset(args, config), "version" => cmd_version(), "user" => usr::user::main(args), - "vga" => usr::vga::main(args), "view" => usr::view::main(args), "write" => usr::write::main(args), "panic" => panic!("{}", args[1..].join(" ")), diff --git a/src/usr/vga.rs b/src/usr/vga.rs deleted file mode 100644 index 89d21c3b1..000000000 --- a/src/usr/vga.rs +++ /dev/null @@ -1,81 +0,0 @@ -use crate::api::console::Style; -use crate::api::fs; -use crate::api::process::ExitCode; -use crate::api::font::Font; -use crate::api::vga::palette; -use crate::sys; - -use core::convert::TryFrom; - -// TODO: Remove this command in the next version of MOROS -pub fn main(args: &[&str]) -> Result<(), ExitCode> { - if args.len() == 1 { - help(); - return Err(ExitCode::UsageError); - } - - match args[1] { - "-h" | "--help" => { - help(); - Ok(()) - } - "set" => { - if args.len() == 4 && args[2] == "font" { - warning!("Use VGA font device"); - if let Ok(buf) = fs::read_to_bytes(args[3]) { - if let Ok(font) = Font::try_from(buf.as_slice()) { - sys::vga::set_font(&font); - Ok(()) - } else { - error!("Could not parse font file"); - Err(ExitCode::Failure) - } - } else { - error!("Could not read font file"); - Err(ExitCode::Failure) - } - } else if args.len() == 4 && args[2] == "palette" { - warning!("Use ANSI OSC palette sequence"); - if let Ok(csv) = fs::read_to_string(args[3]) { - if let Ok(palette) = palette::from_csv(&csv) { - sys::vga::set_palette(palette); - Ok(()) - } else { - error!("Could not parse palette file"); - Err(ExitCode::Failure) - } - } else { - error!("Could not read palette file"); - Err(ExitCode::Failure) - } - } else { - error!("Invalid command"); - Err(ExitCode::Failure) - } - } - _ => { - error!("Invalid command"); - Err(ExitCode::Failure) - } - } -} - -fn help() { - let csi_option = Style::color("aqua"); - let csi_title = Style::color("yellow"); - let csi_reset = Style::reset(); - println!( - "{}Usage:{} vga {}{1}", - csi_title, csi_reset, csi_option - ); - println!(); - println!("{}Commands:{}", csi_title, csi_reset); - println!( - " {}set font {} Set VGA font", - csi_option, csi_reset - ); - println!( - " {}set palette {} Set VGA color palette", - csi_option, csi_reset - ); -} From 4fdd5c78dac5e03cad7c5c71bd1d8b022d11b168 Mon Sep 17 00:00:00 2001 From: Vincent Ollivier Date: Mon, 14 Oct 2024 16:26:54 +0200 Subject: [PATCH 04/33] Split vga module --- src/sys/vga/font.rs | 47 +++ src/sys/vga/mod.rs | 681 +----------------------------------------- src/sys/vga/screen.rs | 134 +++++++++ src/sys/vga/writer.rs | 502 +++++++++++++++++++++++++++++++ 4 files changed, 691 insertions(+), 673 deletions(-) create mode 100644 src/sys/vga/font.rs create mode 100644 src/sys/vga/screen.rs create mode 100644 src/sys/vga/writer.rs diff --git a/src/sys/vga/font.rs b/src/sys/vga/font.rs new file mode 100644 index 000000000..2e6710deb --- /dev/null +++ b/src/sys/vga/font.rs @@ -0,0 +1,47 @@ +use super::writer::WRITER; + +use crate::api::font::Font; +use crate::api::fs::{FileIO, IO}; + +use core::convert::TryFrom; +use x86_64::instructions::interrupts; + +#[derive(Debug, Clone)] +pub struct VgaFont; + +impl VgaFont { + pub fn new() -> Self { + Self + } +} + +impl FileIO for VgaFont { + fn read(&mut self, _buf: &mut [u8]) -> Result { + Err(()) // TODO + } + + fn write(&mut self, buf: &[u8]) -> Result { + if let Ok(font) = Font::try_from(buf) { + set_font(&font); + Ok(buf.len()) // TODO: Use font.data.len() ? + } else { + Err(()) + } + } + + fn close(&mut self) {} + + fn poll(&mut self, event: IO) -> bool { + match event { + IO::Read => false, // TODO + IO::Write => true, + } + } +} + +// TODO: Remove this +pub fn set_font(font: &Font) { + interrupts::without_interrupts(|| + WRITER.lock().set_font(font) + ) +} diff --git a/src/sys/vga/mod.rs b/src/sys/vga/mod.rs index efa643660..d48998fa5 100644 --- a/src/sys/vga/mod.rs +++ b/src/sys/vga/mod.rs @@ -1,19 +1,20 @@ -use crate::api::font::Font; -use crate::api::fs::{FileIO, IO}; +mod font; +mod screen; +mod writer; + +pub use font::VgaFont; +pub use screen::{set_80x25_mode, set_320x200_mode, set_640x480_mode}; +use writer::WRITER; + use crate::api::vga::color; use crate::api::vga::{Color, Palette}; -use crate::sys; use alloc::string::String; use bit_field::BitField; -use core::convert::TryFrom; use core::cmp; use core::fmt; use core::fmt::Write; use core::num::ParseIntError; -use lazy_static::lazy_static; -use spin::Mutex; -use vte::{Params, Parser, Perform}; use x86_64::instructions::interrupts; use x86_64::instructions::port::Port; @@ -32,139 +33,6 @@ const CRTC_DATA_REG: u16 = 0x3D5; const INPUT_STATUS_REG: u16 = 0x3DA; const INSTAT_READ_REG: u16 = 0x3DA; -// Source: https://www.singlix.com/trdos/archive/vga/Graphics%20in%20pmode.pdf -const T_80_25: [u8; 61] = [ - // MISC - 0x67, - // SEQ - 0x03, 0x00, 0x03, 0x00, 0x02, - // CRTC - 0x5F, 0x4F, 0x50, 0x82, 0x55, 0x81, 0xBF, 0x1F, 0x00, 0x4F, 0x0D, 0x0E, - 0x00, 0x00, 0x00, 0x50, 0x9C, 0x0E, 0x8F, 0x28, 0x1F, 0x96, 0xB9, 0xA3, - 0xFF, - // GC - 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x0E, 0x00, 0xFF, - // AC - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x14, 0x07, 0x38, 0x39, 0x3A, 0x3B, - 0x3C, 0x3D, 0x3E, 0x3F, 0x0C, 0x00, 0x0F, 0x08, 0x00 -]; - -const G_320_200_256: [u8; 61] = [ - // MISC - 0x63, - // SEQ - 0x03, 0x01, 0x0F, 0x00, 0x0E, - // CRTC - 0x5F, 0x4F, 0x50, 0x82, 0x54, 0x80, 0xBF, 0x1F, 0x00, 0x41, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x9C, 0x0E, 0x8F, 0x28, 0x40, 0x96, 0xB9, 0xA3, - 0xFF, - // GC - 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0x0F, 0xFF, - // AC - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, - 0x0C, 0x0D, 0x0E, 0x0F, 0x41, 0x00, 0x0F, 0x00, 0x00 -]; - -const G_640_480_16: [u8; 61] = [ - // MISC - 0xE3, - // SEQ - 0x03, 0x01, 0x08, 0x00, 0x06, - // CRTC - 0x5F, 0x4F, 0x50, 0x82, 0x54, 0x80, 0x0B, 0x3E, 0x00, 0x40, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xEA, 0x0C, 0xDF, 0x28, 0x00, 0xE7, 0x04, 0xE3, - 0xFF, - // GC - 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x05, 0x0F, 0xFF, - // AC - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x14, 0x07, 0x38, 0x39, 0x3A, 0x3B, - 0x3C, 0x3D, 0x3E, 0x3F, 0x01, 0x00, 0x0F, 0x00, 0x00 -]; - -const SEQ_REGS_COUNT: usize = 5; -const CRTC_REGS_COUNT: usize = 25; -const GC_REGS_COUNT: usize = 9; -const AC_REGS_COUNT: usize = 21; - -pub fn set_80x25_mode() { - set_mode(&T_80_25); - disable_blinking(); - disable_underline(); -} - -pub fn set_320x200_mode() { - set_mode(&G_320_200_256); -} - -pub fn set_640x480_mode() { - set_mode(&G_640_480_16); -} - -// Source: https://www.singlix.com/trdos/archive/vga/Graphics%20in%20pmode.pdf -fn set_mode(regs: &[u8]) { - interrupts::without_interrupts(|| { - let mut misc_write: Port = Port::new(MISC_WRITE_REG); - let mut crtc_addr: Port = Port::new(CRTC_ADDR_REG); - let mut crtc_data: Port = Port::new(CRTC_DATA_REG); - let mut seq_addr: Port = Port::new(SEQUENCER_ADDR_REG); - let mut seq_data: Port = Port::new(SEQUENCER_DATA_REG); - let mut gc_addr: Port = Port::new(GRAPHICS_ADDR_REG); - let mut gc_data: Port = Port::new(GRAPHICS_DATA_REG); - let mut ac_addr: Port = Port::new(ATTR_ADDR_REG); - let mut ac_write: Port = Port::new(ATTR_WRITE_REG); - let mut instat_read: Port = Port::new(INSTAT_READ_REG); - - let mut regs = regs.to_vec(); - let mut i = 0; - - unsafe { - misc_write.write(regs[i]); - i += 1; - - for j in 0..SEQ_REGS_COUNT { - seq_addr.write(j as u8); - seq_data.write(regs[i]); - i += 1; - } - - // Unlock CRTC regs - crtc_addr.write(0x03); - let data = crtc_data.read(); - crtc_data.write(data | 0x80); - crtc_addr.write(0x11); - let data = crtc_data.read(); - crtc_data.write(data & !0x80); - - // Keep them unlocked - regs[0x03] |= 0x80; - regs[0x11] &= !0x80; - - for j in 0..CRTC_REGS_COUNT { - crtc_addr.write(j as u8); - crtc_data.write(regs[i]); - i += 1; - } - - for j in 0..GC_REGS_COUNT { - gc_addr.write(j as u8); - gc_data.write(regs[i]); - i += 1; - } - - for j in 0..AC_REGS_COUNT { - instat_read.read(); - ac_addr.write(j as u8); - ac_write.write(regs[i]); - i += 1; - } - - // Lock 16-color palette and unblank display - instat_read.read(); - ac_addr.write(0x20); - } - }); -} - const FG: Color = Color::DarkWhite; const BG: Color = Color::DarkBlack; const UNPRINTABLE: u8 = 0x00; // Unprintable chars will be replaced by this one @@ -204,311 +72,6 @@ struct ScreenBuffer { chars: [[ScreenChar; SCREEN_WIDTH]; SCREEN_HEIGHT], } -lazy_static! { - pub static ref PARSER: Mutex = Mutex::new(Parser::new()); - pub static ref WRITER: Mutex = Mutex::new(Writer { - cursor: [0; 2], - writer: [0; 2], - color_code: ColorCode::new(FG, BG), - screen_buffer: unsafe { &mut *(0xB8000 as *mut ScreenBuffer) }, - scroll_buffer: [[ScreenChar::new(); SCREEN_WIDTH]; SCROLL_HEIGHT], - scroll_reader: 0, - scroll_bottom: SCREEN_HEIGHT, - }); -} - -pub struct Writer { - cursor: [usize; 2], // x, y - writer: [usize; 2], // x, y - color_code: ColorCode, - screen_buffer: &'static mut ScreenBuffer, - scroll_buffer: [[ScreenChar; SCREEN_WIDTH]; SCROLL_HEIGHT], - scroll_reader: usize, // Top of the screen - scroll_bottom: usize, // Bottom of the buffer -} - -// Scroll Buffer -// +----------------------------+ -// | line 01 | -// | line 02 | -// | line 03 | -// | line 04 | -// +----------------------------+ -// | line 05 | <-- scroll_reader -// | line 06 | -// | line 07 | -// | line 08 | -// +----------------------------+ -// | line 09 | -// | line 10 | -// | line 11 | -// | line 12 | <-- scroll_bottom -// | | -// | | -// | | -// | | -// +----------------------------+ -// -// Screen Buffer -// +----------------------------+ -// | line 05 | -// | line 06 | -// | line 07 | -// | line 08 | -// +----------------------------+ - -impl Writer { - fn writer_position(&self) -> (usize, usize) { - (self.writer[0], self.writer[1]) - } - - fn set_writer_position(&mut self, x: usize, y: usize) { - self.writer = [x, y]; - } - - fn cursor_position(&self) -> (usize, usize) { - (self.cursor[0], self.cursor[1]) - } - - fn set_cursor_position(&mut self, x: usize, y: usize) { - self.cursor = [x, y]; - self.write_cursor(); - } - - fn write_cursor(&mut self) { - let pos = self.cursor[0] + self.cursor[1] * SCREEN_WIDTH; - let mut addr = Port::new(CRTC_ADDR_REG); - let mut data = Port::new(CRTC_DATA_REG); - unsafe { - addr.write(0x0F as u8); - data.write((pos & 0xFF) as u8); - addr.write(0x0E as u8); - data.write(((pos >> 8) & 0xFF) as u8); - } - } - - // Source: http://www.osdever.net/FreeVGA/vga/crtcreg.htm#0A - fn disable_cursor(&self) { - let mut addr = Port::new(CRTC_ADDR_REG); - let mut data = Port::new(CRTC_DATA_REG); - unsafe { - addr.write(0x0A as u8); - data.write(0x20 as u8); - } - } - - fn enable_cursor(&self) { - let mut addr: Port = Port::new(CRTC_ADDR_REG); - let mut data: Port = Port::new(CRTC_DATA_REG); - let cursor_start = 13; // Starting row - let cursor_end = 14; // Ending row - unsafe { - addr.write(0x0A); // Cursor Start Register - let b = data.read(); - data.write((b & 0xC0) | cursor_start); - - addr.write(0x0B); // Cursor End Register - let b = data.read(); - data.write((b & 0xE0) | cursor_end); - } - } - - fn disable_echo(&self) { - sys::console::disable_echo(); - } - - fn enable_echo(&self) { - sys::console::enable_echo(); - } - - fn write_byte(&mut self, byte: u8) { - if self.is_scrolling() { - // Scroll to the current screen - self.scroll_reader = self.scroll_bottom - SCREEN_HEIGHT; - self.scroll(); - } - - match byte { - 0x0A => { - // Newline - self.new_line(); - } - 0x0D => { // Carriage Return - } - 0x08 => { - // Backspace - if self.writer[0] > 0 { - self.writer[0] -= 1; - let c = ScreenChar { - ascii_code: b' ', - color_code: self.color_code, - }; - let x = self.writer[0]; - let y = self.writer[1]; - let ptr = &mut self.screen_buffer.chars[y][x]; - unsafe { core::ptr::write_volatile(ptr, c); } - - let dy = self.scroll_reader; - self.scroll_buffer[y + dy][x] = c; - } - } - byte => { - if self.writer[0] >= SCREEN_WIDTH { - self.new_line(); - } - - let x = self.writer[0]; - let y = self.writer[1]; - let ascii_code = if is_printable(byte) { - byte - } else { - UNPRINTABLE - }; - let color_code = self.color_code; - let c = ScreenChar { - ascii_code, - color_code, - }; - let ptr = &mut self.screen_buffer.chars[y][x]; - unsafe { core::ptr::write_volatile(ptr, c); } - self.writer[0] += 1; - - let dy = self.scroll_reader; - self.scroll_buffer[y + dy][x] = c; - } - } - } - - fn new_line(&mut self) { - if self.writer[1] < SCREEN_HEIGHT - 1 { - self.writer[1] += 1; - } else { - for y in 1..SCREEN_HEIGHT { - self.screen_buffer.chars[y - 1] = self.screen_buffer.chars[y]; - } - if self.scroll_bottom == SCROLL_HEIGHT - 1 { - for y in 1..SCROLL_HEIGHT { - self.scroll_buffer[y - 1] = self.scroll_buffer[y]; - } - } else { - self.scroll_reader += 1; - self.scroll_bottom += 1; - } - self.clear_row_after(0, SCREEN_HEIGHT - 1); - } - self.writer[0] = 0; - } - - fn clear_row_after(&mut self, x: usize, y: usize) { - let c = ScreenChar { - ascii_code: b' ', - color_code: self.color_code, - }; - self.screen_buffer.chars[y][x..SCREEN_WIDTH].fill(c); - - let dy = self.scroll_reader; - self.scroll_buffer[y + dy][x..SCREEN_WIDTH].fill(c); - } - - fn clear_screen(&mut self) { - self.scroll_reader = 0; - self.scroll_bottom = SCREEN_HEIGHT; - for y in 0..SCREEN_HEIGHT { - self.clear_row_after(0, y); - } - } - - pub fn set_color(&mut self, foreground: Color, background: Color) { - self.color_code = ColorCode::new(foreground, background); - } - - pub fn color(&self) -> (Color, Color) { - let cc = self.color_code.0; - let fg = color::from_index(cc.get_bits(0..4) as usize); - let bg = color::from_index(cc.get_bits(4..8) as usize); - (fg, bg) - } - - // Source: https://slideplayer.com/slide/3888880 - pub fn set_font(&mut self, font: &Font) { - let mut sequencer: Port = Port::new(SEQUENCER_ADDR_REG); - let mut graphics: Port = Port::new(GRAPHICS_ADDR_REG); - let buffer = 0xA0000 as *mut u8; - - unsafe { - sequencer.write(0x0100); // do a sync reset - sequencer.write(0x0402); // write plane 2 only - sequencer.write(0x0704); // sequetial access - sequencer.write(0x0300); // end the reset - graphics.write(0x0204); // read plane 2 only - graphics.write(0x0005); // disable odd/even - graphics.write(0x0006); // VRAM at 0xA0000 - - for i in 0..font.size as usize { - for j in 0..font.height as usize { - let vga_offset = j + i * 32 as usize; - let fnt_offset = j + i * font.height as usize; - let ptr = buffer.add(vga_offset); - ptr.write_volatile(font.data[fnt_offset]); - } - } - - sequencer.write(0x0100); // do a sync reset - sequencer.write(0x0302); // write plane 0 & 1 - sequencer.write(0x0304); // even/odd access - sequencer.write(0x0300); // end the reset - graphics.write(0x0004); // restore to default - graphics.write(0x1005); // resume odd/even - graphics.write(0x0E06); // VRAM at 0xB800 - } - } - - pub fn set_palette(&mut self, i: usize, r: u8, g: u8, b: u8) { - let mut addr: Port = Port::new(DAC_ADDR_WRITE_MODE_REG); - let mut data: Port = Port::new(DAC_DATA_REG); - unsafe { - addr.write(i as u8); - data.write(vga_color(r)); - data.write(vga_color(g)); - data.write(vga_color(b)); - } - } - - fn scroll_up(&mut self, n: usize) { - self.scroll_reader = self.scroll_reader.saturating_sub(n); - self.scroll(); - } - - fn scroll_down(&mut self, n: usize) { - self.scroll_reader = cmp::min( - self.scroll_reader + n, - self.scroll_bottom - SCREEN_HEIGHT - ); - self.scroll(); - } - - fn scroll(&mut self) { - let dy = self.scroll_reader; - for y in 0..SCREEN_HEIGHT { - for x in 0..SCREEN_WIDTH { - let c = self.scroll_buffer[y + dy][x]; - let ptr = &mut self.screen_buffer.chars[y][x]; - unsafe { core::ptr::write_volatile(ptr, c); } - } - } - if self.is_scrolling() { - self.disable_cursor(); - } else { - self.enable_cursor(); - } - } - - fn is_scrolling(&self) -> bool { - // If the current screen is reached we are not scrolling anymore - self.scroll_reader != self.scroll_bottom - SCREEN_HEIGHT - } -} - // Convert 8-bit to 6-bit color fn vga_color(color: u8) -> u8 { color >> 2 @@ -527,227 +90,6 @@ fn parse_palette(palette: &str) -> Result<(usize, u8, u8, u8), ParseIntError> { Ok((i, r, g, b)) } -/// Source: https://vt100.net/emu/dec_ansi_parser -impl Perform for Writer { - fn print(&mut self, c: char) { - self.write_byte(c as u8); - } - - fn execute(&mut self, byte: u8) { - self.write_byte(byte); - } - - fn csi_dispatch(&mut self, params: &Params, _: &[u8], _: bool, c: char) { - match c { - 'm' => { - let mut fg = FG; - let mut bg = BG; - for param in params.iter() { - match param[0] { - 0 => { - fg = FG; - bg = BG; - } - 30..=37 | 90..=97 => { - fg = color::from_ansi(param[0] as u8); - } - 40..=47 | 100..=107 => { - bg = color::from_ansi((param[0] as u8) - 10); - } - _ => {} - } - } - self.set_color(fg, bg); - } - 'A' => { // Cursor Up - let mut n = 1; - for param in params.iter() { - n = param[0] as usize; - } - self.writer[1] = self.writer[1].saturating_sub(n); - self.cursor[1] = self.cursor[1].saturating_sub(n); - } - 'B' => { // Cursor Down - let mut n = 1; - for param in params.iter() { - n = param[0] as usize; - } - let height = SCREEN_HEIGHT - 1; - self.writer[1] = cmp::min(self.writer[1] + n, height); - self.cursor[1] = cmp::min(self.cursor[1] + n, height); - } - 'C' => { // Cursor Forward - let mut n = 1; - for param in params.iter() { - n = param[0] as usize; - } - let width = SCREEN_WIDTH - 1; - self.writer[0] = cmp::min(self.writer[0] + n, width); - self.cursor[0] = cmp::min(self.cursor[0] + n, width); - } - 'D' => { // Cursor Backward - let mut n = 1; - for param in params.iter() { - n = param[0] as usize; - } - self.writer[0] = self.writer[0].saturating_sub(n); - self.cursor[0] = self.cursor[0].saturating_sub(n); - } - 'G' => { // Cursor Horizontal Absolute - let (_, y) = self.cursor_position(); - let mut x = 1; - for param in params.iter() { - x = param[0] as usize; // 1-indexed value - } - if x == 0 || x > SCREEN_WIDTH { - return; - } - self.set_writer_position(x - 1, y); - self.set_cursor_position(x - 1, y); - } - 'H' => { // Move cursor - let mut x = 1; - let mut y = 1; - for (i, param) in params.iter().enumerate() { - match i { - 0 => y = param[0] as usize, // 1-indexed value - 1 => x = param[0] as usize, // 1-indexed value - _ => break, - }; - } - if x == 0 || y == 0 || x > SCREEN_WIDTH || y > SCREEN_HEIGHT { - return; - } - self.set_writer_position(x - 1, y - 1); - self.set_cursor_position(x - 1, y - 1); - } - 'J' => { // Erase in Display - let mut n = 0; - for param in params.iter() { - n = param[0] as usize; - } - match n { - // TODO: 0 and 1, cursor to beginning or to end of screen - 2 => self.clear_screen(), - _ => return, - } - self.set_writer_position(0, 0); - self.set_cursor_position(0, 0); - } - 'K' => { // Erase in Line - let (x, y) = self.cursor_position(); - let mut n = 0; - for param in params.iter() { - n = param[0] as usize; - } - match n { - 0 => self.clear_row_after(x, y), - 1 => return, // TODO: self.clear_row_before(x, y), - 2 => self.clear_row_after(0, y), - _ => return, - } - self.set_writer_position(x, y); - self.set_cursor_position(x, y); - } - 'h' => { // Enable - for param in params.iter() { - match param[0] { - 12 => self.enable_echo(), - 25 => self.enable_cursor(), - _ => return, - } - } - } - 'l' => { // Disable - for param in params.iter() { - match param[0] { - 12 => self.disable_echo(), - 25 => self.disable_cursor(), - _ => return, - } - } - } - '~' => { - for param in params.iter() { - match param[0] { - 5 => self.scroll_up(SCREEN_HEIGHT), - 6 => self.scroll_down(SCREEN_HEIGHT), - _ => continue, - } - } - } - _ => {} - } - } - - fn osc_dispatch(&mut self, params: &[&[u8]], _: bool) { - if params.len() == 1 { - let s = String::from_utf8_lossy(params[0]); - match s.chars().next() { - Some('P') if s.len() == 8 => { - if let Ok((i, r, g, b)) = parse_palette(&s) { - let i = color::from_index(i).to_vga_reg() as usize; - self.set_palette(i, r, g, b); - } - } - Some('R') if s.len() == 1 => { - let palette = Palette::default(); - for (i, (r, g, b)) in palette.colors.iter().enumerate() { - let i = color::from_index(i).to_vga_reg() as usize; - self.set_palette(i, *r, *g, *b); - } - } - _ => {} - } - } - } -} - -impl fmt::Write for Writer { - fn write_str(&mut self, s: &str) -> fmt::Result { - let mut parser = PARSER.lock(); - for byte in s.bytes() { - parser.advance(self, byte); - } - let (x, y) = self.writer_position(); - self.set_cursor_position(x, y); - Ok(()) - } -} - -#[derive(Debug, Clone)] -pub struct VgaFont; - -impl VgaFont { - pub fn new() -> Self { - Self - } -} - -impl FileIO for VgaFont { - fn read(&mut self, _buf: &mut [u8]) -> Result { - Err(()) // TODO - } - - fn write(&mut self, buf: &[u8]) -> Result { - if let Ok(font) = Font::try_from(buf) { - set_font(&font); - Ok(buf.len()) // TODO: Use font.data.len() ? - } else { - Err(()) - } - } - - fn close(&mut self) {} - - fn poll(&mut self, event: IO) -> bool { - match event { - IO::Read => false, // TODO - IO::Write => true, - } - } -} - #[doc(hidden)] pub fn print_fmt(args: fmt::Arguments) { interrupts::without_interrupts(|| @@ -776,13 +118,6 @@ pub fn is_printable(c: u8) -> bool { matches!(c, 0x20..=0x7E | 0x08 | 0x0A | 0x0D | 0x80..=0xFF) } -// TODO: Remove this -pub fn set_font(font: &Font) { - interrupts::without_interrupts(|| - WRITER.lock().set_font(font) - ) -} - // TODO: Remove this pub fn set_palette(palette: Palette) { interrupts::without_interrupts(|| diff --git a/src/sys/vga/screen.rs b/src/sys/vga/screen.rs new file mode 100644 index 000000000..4334c288e --- /dev/null +++ b/src/sys/vga/screen.rs @@ -0,0 +1,134 @@ +use super::*; + +// Source: https://www.singlix.com/trdos/archive/vga/Graphics%20in%20pmode.pdf +const T_80_25: [u8; 61] = [ + // MISC + 0x67, + // SEQ + 0x03, 0x00, 0x03, 0x00, 0x02, + // CRTC + 0x5F, 0x4F, 0x50, 0x82, 0x55, 0x81, 0xBF, 0x1F, 0x00, 0x4F, 0x0D, 0x0E, + 0x00, 0x00, 0x00, 0x50, 0x9C, 0x0E, 0x8F, 0x28, 0x1F, 0x96, 0xB9, 0xA3, + 0xFF, + // GC + 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x0E, 0x00, 0xFF, + // AC + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x14, 0x07, 0x38, 0x39, 0x3A, 0x3B, + 0x3C, 0x3D, 0x3E, 0x3F, 0x0C, 0x00, 0x0F, 0x08, 0x00 +]; + +const G_320_200_256: [u8; 61] = [ + // MISC + 0x63, + // SEQ + 0x03, 0x01, 0x0F, 0x00, 0x0E, + // CRTC + 0x5F, 0x4F, 0x50, 0x82, 0x54, 0x80, 0xBF, 0x1F, 0x00, 0x41, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x9C, 0x0E, 0x8F, 0x28, 0x40, 0x96, 0xB9, 0xA3, + 0xFF, + // GC + 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x05, 0x0F, 0xFF, + // AC + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, + 0x0C, 0x0D, 0x0E, 0x0F, 0x41, 0x00, 0x0F, 0x00, 0x00 +]; + +const G_640_480_16: [u8; 61] = [ + // MISC + 0xE3, + // SEQ + 0x03, 0x01, 0x08, 0x00, 0x06, + // CRTC + 0x5F, 0x4F, 0x50, 0x82, 0x54, 0x80, 0x0B, 0x3E, 0x00, 0x40, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xEA, 0x0C, 0xDF, 0x28, 0x00, 0xE7, 0x04, 0xE3, + 0xFF, + // GC + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x05, 0x0F, 0xFF, + // AC + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x14, 0x07, 0x38, 0x39, 0x3A, 0x3B, + 0x3C, 0x3D, 0x3E, 0x3F, 0x01, 0x00, 0x0F, 0x00, 0x00 +]; + +const SEQ_REGS_COUNT: usize = 5; +const CRTC_REGS_COUNT: usize = 25; +const GC_REGS_COUNT: usize = 9; +const AC_REGS_COUNT: usize = 21; + +// Source: https://www.singlix.com/trdos/archive/vga/Graphics%20in%20pmode.pdf +fn set_mode(regs: &[u8]) { + interrupts::without_interrupts(|| { + let mut misc_write: Port = Port::new(MISC_WRITE_REG); + let mut crtc_addr: Port = Port::new(CRTC_ADDR_REG); + let mut crtc_data: Port = Port::new(CRTC_DATA_REG); + let mut seq_addr: Port = Port::new(SEQUENCER_ADDR_REG); + let mut seq_data: Port = Port::new(SEQUENCER_DATA_REG); + let mut gc_addr: Port = Port::new(GRAPHICS_ADDR_REG); + let mut gc_data: Port = Port::new(GRAPHICS_DATA_REG); + let mut ac_addr: Port = Port::new(ATTR_ADDR_REG); + let mut ac_write: Port = Port::new(ATTR_WRITE_REG); + let mut instat_read: Port = Port::new(INSTAT_READ_REG); + + let mut regs = regs.to_vec(); + let mut i = 0; + + unsafe { + misc_write.write(regs[i]); + i += 1; + + for j in 0..SEQ_REGS_COUNT { + seq_addr.write(j as u8); + seq_data.write(regs[i]); + i += 1; + } + + // Unlock CRTC regs + crtc_addr.write(0x03); + let data = crtc_data.read(); + crtc_data.write(data | 0x80); + crtc_addr.write(0x11); + let data = crtc_data.read(); + crtc_data.write(data & !0x80); + + // Keep them unlocked + regs[0x03] |= 0x80; + regs[0x11] &= !0x80; + + for j in 0..CRTC_REGS_COUNT { + crtc_addr.write(j as u8); + crtc_data.write(regs[i]); + i += 1; + } + + for j in 0..GC_REGS_COUNT { + gc_addr.write(j as u8); + gc_data.write(regs[i]); + i += 1; + } + + for j in 0..AC_REGS_COUNT { + instat_read.read(); + ac_addr.write(j as u8); + ac_write.write(regs[i]); + i += 1; + } + + // Lock 16-color palette and unblank display + instat_read.read(); + ac_addr.write(0x20); + } + }); +} + +pub fn set_80x25_mode() { + set_mode(&T_80_25); + disable_blinking(); + disable_underline(); +} + +pub fn set_320x200_mode() { + set_mode(&G_320_200_256); +} + +pub fn set_640x480_mode() { + set_mode(&G_640_480_16); +} diff --git a/src/sys/vga/writer.rs b/src/sys/vga/writer.rs new file mode 100644 index 000000000..7d836c042 --- /dev/null +++ b/src/sys/vga/writer.rs @@ -0,0 +1,502 @@ +use super::*; + +use crate::api::font::Font; +use crate::sys; + +//use core::fmt::Write; +use lazy_static::lazy_static; +use spin::Mutex; +use vte::{Params, Parser, Perform}; + +lazy_static! { + pub static ref PARSER: Mutex = Mutex::new(Parser::new()); + pub static ref WRITER: Mutex = Mutex::new(Writer { + cursor: [0; 2], + writer: [0; 2], + color_code: ColorCode::new(FG, BG), + screen_buffer: unsafe { &mut *(0xB8000 as *mut ScreenBuffer) }, + scroll_buffer: [[ScreenChar::new(); SCREEN_WIDTH]; SCROLL_HEIGHT], + scroll_reader: 0, + scroll_bottom: SCREEN_HEIGHT, + }); +} + +pub struct Writer { + cursor: [usize; 2], // x, y + writer: [usize; 2], // x, y + color_code: ColorCode, + screen_buffer: &'static mut ScreenBuffer, + scroll_buffer: [[ScreenChar; SCREEN_WIDTH]; SCROLL_HEIGHT], + scroll_reader: usize, // Top of the screen + scroll_bottom: usize, // Bottom of the buffer +} + +// Scroll Buffer +// +----------------------------+ +// | line 01 | +// | line 02 | +// | line 03 | +// | line 04 | +// +----------------------------+ +// | line 05 | <-- scroll_reader +// | line 06 | +// | line 07 | +// | line 08 | +// +----------------------------+ +// | line 09 | +// | line 10 | +// | line 11 | +// | line 12 | <-- scroll_bottom +// | | +// | | +// | | +// | | +// +----------------------------+ +// +// Screen Buffer +// +----------------------------+ +// | line 05 | +// | line 06 | +// | line 07 | +// | line 08 | +// +----------------------------+ + +impl Writer { + fn writer_position(&self) -> (usize, usize) { + (self.writer[0], self.writer[1]) + } + + fn set_writer_position(&mut self, x: usize, y: usize) { + self.writer = [x, y]; + } + + fn cursor_position(&self) -> (usize, usize) { + (self.cursor[0], self.cursor[1]) + } + + fn set_cursor_position(&mut self, x: usize, y: usize) { + self.cursor = [x, y]; + self.write_cursor(); + } + + fn write_cursor(&mut self) { + let pos = self.cursor[0] + self.cursor[1] * SCREEN_WIDTH; + let mut addr = Port::new(CRTC_ADDR_REG); + let mut data = Port::new(CRTC_DATA_REG); + unsafe { + addr.write(0x0F as u8); + data.write((pos & 0xFF) as u8); + addr.write(0x0E as u8); + data.write(((pos >> 8) & 0xFF) as u8); + } + } + + // Source: http://www.osdever.net/FreeVGA/vga/crtcreg.htm#0A + fn disable_cursor(&self) { + let mut addr = Port::new(CRTC_ADDR_REG); + let mut data = Port::new(CRTC_DATA_REG); + unsafe { + addr.write(0x0A as u8); + data.write(0x20 as u8); + } + } + + fn enable_cursor(&self) { + let mut addr: Port = Port::new(CRTC_ADDR_REG); + let mut data: Port = Port::new(CRTC_DATA_REG); + let cursor_start = 13; // Starting row + let cursor_end = 14; // Ending row + unsafe { + addr.write(0x0A); // Cursor Start Register + let b = data.read(); + data.write((b & 0xC0) | cursor_start); + + addr.write(0x0B); // Cursor End Register + let b = data.read(); + data.write((b & 0xE0) | cursor_end); + } + } + + fn disable_echo(&self) { + sys::console::disable_echo(); + } + + fn enable_echo(&self) { + sys::console::enable_echo(); + } + + fn write_byte(&mut self, byte: u8) { + if self.is_scrolling() { + // Scroll to the current screen + self.scroll_reader = self.scroll_bottom - SCREEN_HEIGHT; + self.scroll(); + } + + match byte { + 0x0A => { + // Newline + self.new_line(); + } + 0x0D => { // Carriage Return + } + 0x08 => { + // Backspace + if self.writer[0] > 0 { + self.writer[0] -= 1; + let c = ScreenChar { + ascii_code: b' ', + color_code: self.color_code, + }; + let x = self.writer[0]; + let y = self.writer[1]; + let ptr = &mut self.screen_buffer.chars[y][x]; + unsafe { core::ptr::write_volatile(ptr, c); } + + let dy = self.scroll_reader; + self.scroll_buffer[y + dy][x] = c; + } + } + byte => { + if self.writer[0] >= SCREEN_WIDTH { + self.new_line(); + } + + let x = self.writer[0]; + let y = self.writer[1]; + let ascii_code = if is_printable(byte) { + byte + } else { + UNPRINTABLE + }; + let color_code = self.color_code; + let c = ScreenChar { + ascii_code, + color_code, + }; + let ptr = &mut self.screen_buffer.chars[y][x]; + unsafe { core::ptr::write_volatile(ptr, c); } + self.writer[0] += 1; + + let dy = self.scroll_reader; + self.scroll_buffer[y + dy][x] = c; + } + } + } + + fn new_line(&mut self) { + if self.writer[1] < SCREEN_HEIGHT - 1 { + self.writer[1] += 1; + } else { + for y in 1..SCREEN_HEIGHT { + self.screen_buffer.chars[y - 1] = self.screen_buffer.chars[y]; + } + if self.scroll_bottom == SCROLL_HEIGHT - 1 { + for y in 1..SCROLL_HEIGHT { + self.scroll_buffer[y - 1] = self.scroll_buffer[y]; + } + } else { + self.scroll_reader += 1; + self.scroll_bottom += 1; + } + self.clear_row_after(0, SCREEN_HEIGHT - 1); + } + self.writer[0] = 0; + } + + fn clear_row_after(&mut self, x: usize, y: usize) { + let c = ScreenChar { + ascii_code: b' ', + color_code: self.color_code, + }; + self.screen_buffer.chars[y][x..SCREEN_WIDTH].fill(c); + + let dy = self.scroll_reader; + self.scroll_buffer[y + dy][x..SCREEN_WIDTH].fill(c); + } + + pub fn clear_screen(&mut self) { + self.scroll_reader = 0; + self.scroll_bottom = SCREEN_HEIGHT; + for y in 0..SCREEN_HEIGHT { + self.clear_row_after(0, y); + } + } + + pub fn set_color(&mut self, foreground: Color, background: Color) { + self.color_code = ColorCode::new(foreground, background); + } + + pub fn color(&self) -> (Color, Color) { + let cc = self.color_code.0; + let fg = color::from_index(cc.get_bits(0..4) as usize); + let bg = color::from_index(cc.get_bits(4..8) as usize); + (fg, bg) + } + + // Source: https://slideplayer.com/slide/3888880 + pub fn set_font(&mut self, font: &Font) { + let mut sequencer: Port = Port::new(SEQUENCER_ADDR_REG); + let mut graphics: Port = Port::new(GRAPHICS_ADDR_REG); + let buffer = 0xA0000 as *mut u8; + + unsafe { + sequencer.write(0x0100); // do a sync reset + sequencer.write(0x0402); // write plane 2 only + sequencer.write(0x0704); // sequetial access + sequencer.write(0x0300); // end the reset + graphics.write(0x0204); // read plane 2 only + graphics.write(0x0005); // disable odd/even + graphics.write(0x0006); // VRAM at 0xA0000 + + for i in 0..font.size as usize { + for j in 0..font.height as usize { + let vga_offset = j + i * 32 as usize; + let fnt_offset = j + i * font.height as usize; + let ptr = buffer.add(vga_offset); + ptr.write_volatile(font.data[fnt_offset]); + } + } + + sequencer.write(0x0100); // do a sync reset + sequencer.write(0x0302); // write plane 0 & 1 + sequencer.write(0x0304); // even/odd access + sequencer.write(0x0300); // end the reset + graphics.write(0x0004); // restore to default + graphics.write(0x1005); // resume odd/even + graphics.write(0x0E06); // VRAM at 0xB800 + } + } + + pub fn set_palette(&mut self, i: usize, r: u8, g: u8, b: u8) { + let mut addr: Port = Port::new(DAC_ADDR_WRITE_MODE_REG); + let mut data: Port = Port::new(DAC_DATA_REG); + unsafe { + addr.write(i as u8); + data.write(vga_color(r)); + data.write(vga_color(g)); + data.write(vga_color(b)); + } + } + + fn scroll_up(&mut self, n: usize) { + self.scroll_reader = self.scroll_reader.saturating_sub(n); + self.scroll(); + } + + fn scroll_down(&mut self, n: usize) { + self.scroll_reader = cmp::min( + self.scroll_reader + n, + self.scroll_bottom - SCREEN_HEIGHT + ); + self.scroll(); + } + + fn scroll(&mut self) { + let dy = self.scroll_reader; + for y in 0..SCREEN_HEIGHT { + for x in 0..SCREEN_WIDTH { + let c = self.scroll_buffer[y + dy][x]; + let ptr = &mut self.screen_buffer.chars[y][x]; + unsafe { core::ptr::write_volatile(ptr, c); } + } + } + if self.is_scrolling() { + self.disable_cursor(); + } else { + self.enable_cursor(); + } + } + + fn is_scrolling(&self) -> bool { + // If the current screen is reached we are not scrolling anymore + self.scroll_reader != self.scroll_bottom - SCREEN_HEIGHT + } +} + +/// Source: https://vt100.net/emu/dec_ansi_parser +impl Perform for Writer { + fn print(&mut self, c: char) { + self.write_byte(c as u8); + } + + fn execute(&mut self, byte: u8) { + self.write_byte(byte); + } + + fn csi_dispatch(&mut self, params: &Params, _: &[u8], _: bool, c: char) { + match c { + 'm' => { + let mut fg = FG; + let mut bg = BG; + for param in params.iter() { + match param[0] { + 0 => { + fg = FG; + bg = BG; + } + 30..=37 | 90..=97 => { + fg = color::from_ansi(param[0] as u8); + } + 40..=47 | 100..=107 => { + bg = color::from_ansi((param[0] as u8) - 10); + } + _ => {} + } + } + self.set_color(fg, bg); + } + 'A' => { // Cursor Up + let mut n = 1; + for param in params.iter() { + n = param[0] as usize; + } + self.writer[1] = self.writer[1].saturating_sub(n); + self.cursor[1] = self.cursor[1].saturating_sub(n); + } + 'B' => { // Cursor Down + let mut n = 1; + for param in params.iter() { + n = param[0] as usize; + } + let height = SCREEN_HEIGHT - 1; + self.writer[1] = cmp::min(self.writer[1] + n, height); + self.cursor[1] = cmp::min(self.cursor[1] + n, height); + } + 'C' => { // Cursor Forward + let mut n = 1; + for param in params.iter() { + n = param[0] as usize; + } + let width = SCREEN_WIDTH - 1; + self.writer[0] = cmp::min(self.writer[0] + n, width); + self.cursor[0] = cmp::min(self.cursor[0] + n, width); + } + 'D' => { // Cursor Backward + let mut n = 1; + for param in params.iter() { + n = param[0] as usize; + } + self.writer[0] = self.writer[0].saturating_sub(n); + self.cursor[0] = self.cursor[0].saturating_sub(n); + } + 'G' => { // Cursor Horizontal Absolute + let (_, y) = self.cursor_position(); + let mut x = 1; + for param in params.iter() { + x = param[0] as usize; // 1-indexed value + } + if x == 0 || x > SCREEN_WIDTH { + return; + } + self.set_writer_position(x - 1, y); + self.set_cursor_position(x - 1, y); + } + 'H' => { // Move cursor + let mut x = 1; + let mut y = 1; + for (i, param) in params.iter().enumerate() { + match i { + 0 => y = param[0] as usize, // 1-indexed value + 1 => x = param[0] as usize, // 1-indexed value + _ => break, + }; + } + if x == 0 || y == 0 || x > SCREEN_WIDTH || y > SCREEN_HEIGHT { + return; + } + self.set_writer_position(x - 1, y - 1); + self.set_cursor_position(x - 1, y - 1); + } + 'J' => { // Erase in Display + let mut n = 0; + for param in params.iter() { + n = param[0] as usize; + } + match n { + // TODO: 0 and 1, cursor to beginning or to end of screen + 2 => self.clear_screen(), + _ => return, + } + self.set_writer_position(0, 0); + self.set_cursor_position(0, 0); + } + 'K' => { // Erase in Line + let (x, y) = self.cursor_position(); + let mut n = 0; + for param in params.iter() { + n = param[0] as usize; + } + match n { + 0 => self.clear_row_after(x, y), + 1 => return, // TODO: self.clear_row_before(x, y), + 2 => self.clear_row_after(0, y), + _ => return, + } + self.set_writer_position(x, y); + self.set_cursor_position(x, y); + } + 'h' => { // Enable + for param in params.iter() { + match param[0] { + 12 => self.enable_echo(), + 25 => self.enable_cursor(), + _ => return, + } + } + } + 'l' => { // Disable + for param in params.iter() { + match param[0] { + 12 => self.disable_echo(), + 25 => self.disable_cursor(), + _ => return, + } + } + } + '~' => { + for param in params.iter() { + match param[0] { + 5 => self.scroll_up(SCREEN_HEIGHT), + 6 => self.scroll_down(SCREEN_HEIGHT), + _ => continue, + } + } + } + _ => {} + } + } + + fn osc_dispatch(&mut self, params: &[&[u8]], _: bool) { + if params.len() == 1 { + let s = String::from_utf8_lossy(params[0]); + match s.chars().next() { + Some('P') if s.len() == 8 => { + if let Ok((i, r, g, b)) = parse_palette(&s) { + let i = color::from_index(i).to_vga_reg() as usize; + self.set_palette(i, r, g, b); + } + } + Some('R') if s.len() == 1 => { + let palette = Palette::default(); + for (i, (r, g, b)) in palette.colors.iter().enumerate() { + let i = color::from_index(i).to_vga_reg() as usize; + self.set_palette(i, *r, *g, *b); + } + } + _ => {} + } + } + } +} + +impl fmt::Write for Writer { + fn write_str(&mut self, s: &str) -> fmt::Result { + let mut parser = PARSER.lock(); + for byte in s.bytes() { + parser.advance(self, byte); + } + let (x, y) = self.writer_position(); + self.set_cursor_position(x, y); + Ok(()) + } +} From d655ee064c290e5f70da05a6ae4bfc92be3b0966 Mon Sep 17 00:00:00 2001 From: Vincent Ollivier Date: Mon, 14 Oct 2024 16:50:57 +0200 Subject: [PATCH 05/33] Rename vga device type --- src/api/fs.rs | 2 +- src/usr/install.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/api/fs.rs b/src/api/fs.rs index 196fc230e..d29fd266d 100644 --- a/src/api/fs.rs +++ b/src/api/fs.rs @@ -165,7 +165,7 @@ fn device_type(name: &str) -> Result { "rtc" => Ok(DeviceType::RTC), "tcp" => Ok(DeviceType::TcpSocket), "udp" => Ok(DeviceType::UdpSocket), - "font" => Ok(DeviceType::VgaFont), + "vga-font" => Ok(DeviceType::VgaFont), "ata" => Ok(DeviceType::Drive), _ => Err(()), } diff --git a/src/usr/install.rs b/src/usr/install.rs index 3b3555a4c..bc8322789 100644 --- a/src/usr/install.rs +++ b/src/usr/install.rs @@ -55,7 +55,7 @@ pub fn copy_files(verbose: bool) { create_dev("/dev/console", "console", verbose); create_dev("/dev/net/tcp", "tcp", verbose); create_dev("/dev/net/udp", "udp", verbose); - create_dev("/dev/vga/font", "font", verbose); + create_dev("/dev/vga/font", "vga-font", verbose); copy_file!("/ini/banner.txt", verbose); copy_file!("/ini/boot.sh", verbose); From 6f7096ddc8404b9f57ad750887aa4082cea8a7aa Mon Sep 17 00:00:00 2001 From: Vincent Ollivier Date: Mon, 14 Oct 2024 17:18:36 +0200 Subject: [PATCH 06/33] Add /dev/vga/mode device file --- src/api/fs.rs | 1 + src/api/vga/mod.rs | 7 ++++--- src/sys/fs/device.rs | 10 +++++++++- src/sys/vga/mod.rs | 2 +- src/sys/vga/screen.rs | 38 ++++++++++++++++++++++++++++++++++++++ src/usr/install.rs | 1 + 6 files changed, 54 insertions(+), 5 deletions(-) diff --git a/src/api/fs.rs b/src/api/fs.rs index d29fd266d..8ab778904 100644 --- a/src/api/fs.rs +++ b/src/api/fs.rs @@ -166,6 +166,7 @@ fn device_type(name: &str) -> Result { "tcp" => Ok(DeviceType::TcpSocket), "udp" => Ok(DeviceType::UdpSocket), "vga-font" => Ok(DeviceType::VgaFont), + "vga-mode" => Ok(DeviceType::VgaMode), "ata" => Ok(DeviceType::Drive), _ => Err(()), } diff --git a/src/api/vga/mod.rs b/src/api/vga/mod.rs index 475809645..7c5754825 100644 --- a/src/api/vga/mod.rs +++ b/src/api/vga/mod.rs @@ -4,16 +4,17 @@ pub mod palette; pub use color::Color; pub use palette::Palette; -use crate::sys::vga; +use crate::api::fs; use crate::usr::shell; pub fn graphic_mode() { + fs::write("/dev/vga/mode", b"320x200").ok(); + // TODO: Backup font and palette - vga::set_320x200_mode(); } pub fn text_mode() { - vga::set_80x25_mode(); + fs::write("/dev/vga/mode", b"80x25").ok(); // TODO: Restore font and palette backup instead of this shell::exec("shell /ini/palettes/gruvbox-dark.sh").ok(); diff --git a/src/sys/fs/device.rs b/src/sys/fs/device.rs index 9a70a18f4..e88b70561 100644 --- a/src/sys/fs/device.rs +++ b/src/sys/fs/device.rs @@ -10,7 +10,7 @@ use crate::sys::console::Console; use crate::sys::net::socket::tcp::TcpSocket; use crate::sys::net::socket::udp::UdpSocket; use crate::sys::rng::Random; -use crate::sys::vga::VgaFont; +use crate::sys::vga::{VgaFont, VgaMode}; use alloc::vec; use alloc::vec::Vec; @@ -31,6 +31,7 @@ pub enum DeviceType { UdpSocket = 8, Drive = 9, VgaFont = 10, + VgaMode = 11, } impl TryFrom<&[u8]> for DeviceType { @@ -49,6 +50,7 @@ impl TryFrom<&[u8]> for DeviceType { 8 => Ok(DeviceType::UdpSocket), 9 => Ok(DeviceType::Drive), 10 => Ok(DeviceType::VgaFont), + 11 => Ok(DeviceType::VgaMode), _ => Err(()), } } @@ -87,6 +89,7 @@ pub enum Device { TcpSocket(TcpSocket), UdpSocket(UdpSocket), VgaFont(VgaFont), + VgaMode(VgaMode), Drive(Drive), } @@ -105,6 +108,7 @@ impl TryFrom<&[u8]> for Device { DeviceType::TcpSocket => Ok(Device::TcpSocket(TcpSocket::new())), DeviceType::UdpSocket => Ok(Device::UdpSocket(UdpSocket::new())), DeviceType::VgaFont => Ok(Device::VgaFont(VgaFont::new())), + DeviceType::VgaMode => Ok(Device::VgaMode(VgaMode::new())), DeviceType::Drive if buf.len() > 2 => { let bus = buf[1]; let dsk = buf[2]; @@ -164,6 +168,7 @@ impl FileIO for Device { Device::TcpSocket(io) => io.read(buf), Device::UdpSocket(io) => io.read(buf), Device::VgaFont(io) => io.read(buf), + Device::VgaMode(io) => io.read(buf), Device::Drive(io) => io.read(buf), } } @@ -180,6 +185,7 @@ impl FileIO for Device { Device::TcpSocket(io) => io.write(buf), Device::UdpSocket(io) => io.write(buf), Device::VgaFont(io) => io.write(buf), + Device::VgaMode(io) => io.write(buf), Device::Drive(io) => io.write(buf), } } @@ -196,6 +202,7 @@ impl FileIO for Device { Device::TcpSocket(io) => io.close(), Device::UdpSocket(io) => io.close(), Device::VgaFont(io) => io.close(), + Device::VgaMode(io) => io.close(), Device::Drive(io) => io.close(), } } @@ -212,6 +219,7 @@ impl FileIO for Device { Device::TcpSocket(io) => io.poll(event), Device::UdpSocket(io) => io.poll(event), Device::VgaFont(io) => io.poll(event), + Device::VgaMode(io) => io.poll(event), Device::Drive(io) => io.poll(event), } } diff --git a/src/sys/vga/mod.rs b/src/sys/vga/mod.rs index d48998fa5..fc625ff95 100644 --- a/src/sys/vga/mod.rs +++ b/src/sys/vga/mod.rs @@ -3,7 +3,7 @@ mod screen; mod writer; pub use font::VgaFont; -pub use screen::{set_80x25_mode, set_320x200_mode, set_640x480_mode}; +pub use screen::VgaMode; use writer::WRITER; use crate::api::vga::color; diff --git a/src/sys/vga/screen.rs b/src/sys/vga/screen.rs index 4334c288e..f09ef8230 100644 --- a/src/sys/vga/screen.rs +++ b/src/sys/vga/screen.rs @@ -1,5 +1,7 @@ use super::*; +use crate::api::fs::{FileIO, IO}; + // Source: https://www.singlix.com/trdos/archive/vga/Graphics%20in%20pmode.pdf const T_80_25: [u8; 61] = [ // MISC @@ -127,8 +129,44 @@ pub fn set_80x25_mode() { pub fn set_320x200_mode() { set_mode(&G_320_200_256); + // TODO: Clear screen } pub fn set_640x480_mode() { set_mode(&G_640_480_16); + // TODO: Clear screen +} + +#[derive(Debug, Clone)] +pub struct VgaMode; + +impl VgaMode { + pub fn new() -> Self { + Self + } +} + +impl FileIO for VgaMode { + fn read(&mut self, _buf: &mut [u8]) -> Result { + Err(()) // TODO + } + + fn write(&mut self, buf: &[u8]) -> Result { + match buf { + b"80x25" => set_80x25_mode(), + b"320x200" => set_320x200_mode(), + b"640x480" => set_640x480_mode(), + _ => return Err(()), + } + Ok(buf.len()) + } + + fn close(&mut self) {} + + fn poll(&mut self, event: IO) -> bool { + match event { + IO::Read => false, // TODO + IO::Write => true, + } + } } diff --git a/src/usr/install.rs b/src/usr/install.rs index bc8322789..57d1af6a4 100644 --- a/src/usr/install.rs +++ b/src/usr/install.rs @@ -56,6 +56,7 @@ pub fn copy_files(verbose: bool) { create_dev("/dev/net/tcp", "tcp", verbose); create_dev("/dev/net/udp", "udp", verbose); create_dev("/dev/vga/font", "vga-font", verbose); + create_dev("/dev/vga/mode", "vga-mode", verbose); copy_file!("/ini/banner.txt", verbose); copy_file!("/ini/boot.sh", verbose); From c5294e87ad35f47ba649614f05f158f2bde1a273 Mon Sep 17 00:00:00 2001 From: Vincent Ollivier Date: Mon, 14 Oct 2024 18:50:25 +0200 Subject: [PATCH 07/33] Restore font after mode change --- src/api/vga/mod.rs | 5 ++--- src/sys/vga/font.rs | 11 ++++++++++- src/sys/vga/screen.rs | 1 + 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/api/vga/mod.rs b/src/api/vga/mod.rs index 7c5754825..da184154d 100644 --- a/src/api/vga/mod.rs +++ b/src/api/vga/mod.rs @@ -10,15 +10,14 @@ use crate::usr::shell; pub fn graphic_mode() { fs::write("/dev/vga/mode", b"320x200").ok(); - // TODO: Backup font and palette + // TODO: Backup palette } pub fn text_mode() { fs::write("/dev/vga/mode", b"80x25").ok(); - // TODO: Restore font and palette backup instead of this + // TODO: Restore and palette backup instead of this shell::exec("shell /ini/palettes/gruvbox-dark.sh").ok(); - shell::exec("read /ini/fonts/zap-light-8x16.psf => /dev/vga/font").ok(); print!("\x1b[2J\x1b[1;1H"); // Clear screen and move to top } diff --git a/src/sys/vga/font.rs b/src/sys/vga/font.rs index 2e6710deb..f35c6eb10 100644 --- a/src/sys/vga/font.rs +++ b/src/sys/vga/font.rs @@ -4,6 +4,7 @@ use crate::api::font::Font; use crate::api::fs::{FileIO, IO}; use core::convert::TryFrom; +use spin::Mutex; use x86_64::instructions::interrupts; #[derive(Debug, Clone)] @@ -22,6 +23,7 @@ impl FileIO for VgaFont { fn write(&mut self, buf: &[u8]) -> Result { if let Ok(font) = Font::try_from(buf) { + *FONT.lock() = Some(font.clone()); set_font(&font); Ok(buf.len()) // TODO: Use font.data.len() ? } else { @@ -39,9 +41,16 @@ impl FileIO for VgaFont { } } -// TODO: Remove this +static FONT: Mutex> = Mutex::new(None); + pub fn set_font(font: &Font) { interrupts::without_interrupts(|| WRITER.lock().set_font(font) ) } + +pub fn restore_font() { + if let Some(ref font) = *FONT.lock() { + set_font(font); + } +} diff --git a/src/sys/vga/screen.rs b/src/sys/vga/screen.rs index f09ef8230..88e2f3714 100644 --- a/src/sys/vga/screen.rs +++ b/src/sys/vga/screen.rs @@ -125,6 +125,7 @@ pub fn set_80x25_mode() { set_mode(&T_80_25); disable_blinking(); disable_underline(); + font::restore_font(); } pub fn set_320x200_mode() { From d24ab74e6609d0d24ddabd1d7bddb1b376147982 Mon Sep 17 00:00:00 2001 From: Vincent Ollivier Date: Mon, 14 Oct 2024 19:52:51 +0200 Subject: [PATCH 08/33] Remove cvs support for palette --- dsk/ini/palettes/gruvbox-dark.csv | 17 -------------- dsk/ini/palettes/gruvbox-light.csv | 17 -------------- src/api/vga/palette.rs | 36 ------------------------------ 3 files changed, 70 deletions(-) delete mode 100644 dsk/ini/palettes/gruvbox-dark.csv delete mode 100644 dsk/ini/palettes/gruvbox-light.csv diff --git a/dsk/ini/palettes/gruvbox-dark.csv b/dsk/ini/palettes/gruvbox-dark.csv deleted file mode 100644 index 1414de891..000000000 --- a/dsk/ini/palettes/gruvbox-dark.csv +++ /dev/null @@ -1,17 +0,0 @@ -# VGA Palette File (Red, Green, Blue) -0x28, 0x28, 0x28 # Black -0x45, 0x85, 0x88 # Navy -0x98, 0x97, 0x1A # Green -0x68, 0x9D, 0x6A # Teal -0xCC, 0x24, 0x1D # Red -0xB1, 0x62, 0x86 # Purple -0xD7, 0x99, 0x21 # Maroon -0xEB, 0xDB, 0xB2 # Silver -0xA8, 0x99, 0x84 # Gray -0x83, 0xa5, 0x98 # Blue -0xB8, 0xBB, 0x26 # Lime -0x8E, 0xC0, 0x7C # Aqua -0xFB, 0x49, 0x34 # Red -0xD3, 0x86, 0x9B # Fushia -0xFA, 0xBD, 0x2F # Yellow -0xFB, 0xF1, 0xC7 # White diff --git a/dsk/ini/palettes/gruvbox-light.csv b/dsk/ini/palettes/gruvbox-light.csv deleted file mode 100644 index 13e5085ba..000000000 --- a/dsk/ini/palettes/gruvbox-light.csv +++ /dev/null @@ -1,17 +0,0 @@ -# VGA Palette File (Red, Green, Blue) -0xFB, 0xF1, 0xC7 # Black -0x45, 0x85, 0x88 # Navy -0x98, 0x97, 0x1A # Green -0x68, 0x9D, 0x6A # Teal -0xCC, 0x24, 0x1D # Red -0xB1, 0x62, 0x86 # Purple -0xD7, 0x99, 0x21 # Maroon -0x3C, 0x38, 0x36 # Silver -0x7C, 0x6F, 0x64 # Gray -0x07, 0x66, 0x78 # Blue -0x79, 0x74, 0x0E # Lime -0x42, 0x7B, 0x58 # Aqua -0x9D, 0x00, 0x06 # Red -0x8F, 0x3F, 0x71 # Fushia -0xB5, 0x76, 0x14 # Yellow -0x28, 0x28, 0x28 # White diff --git a/src/api/vga/palette.rs b/src/api/vga/palette.rs index bfc494afc..fe64c43d2 100644 --- a/src/api/vga/palette.rs +++ b/src/api/vga/palette.rs @@ -1,6 +1,3 @@ -use alloc::vec::Vec; -use core::convert::TryInto; - // TODO: Move this to kernel after removing the `vga set palette` command pub struct Palette { pub colors: [(u8, u8, u8); 16], @@ -30,36 +27,3 @@ impl Palette { } } } - -// TODO: Remove this -pub fn from_csv(s: &str) -> Result { - let colors: Vec<_> = s.lines().filter_map(|line| { - let line = line.split('#').next().unwrap(); // Remove comments - let color: Vec = line.split(',').filter_map(|value| { - let radix = if value.contains("0x") { 16 } else { 10 }; - let value = value.trim().trim_start_matches("0x"); - u8::from_str_radix(value, radix).ok() - }).collect(); - if color.len() == 3 { // RGB values - Some((color[0], color[1], color[2])) - } else { - None - } - }).collect(); - if let Ok(colors) = colors.try_into() { // Array of 16 colors - Ok(Palette { colors }) - } else { - Err(()) - } -} - -#[test_case] -fn test_from_csv() { - assert!(from_csv("").is_err()); - assert!(from_csv("0,0,0,0").is_err()); - - let s = include_str!("../../../dsk/ini/palettes/gruvbox-dark.csv"); - let palette = from_csv(s).unwrap(); - assert_eq!(palette.colors[0x03].0, 0x68); - assert_eq!(palette.colors[0x0D].1, 0x86); -} From 479632f08ed4f8d9a83097a45fe45186ba2bf1c9 Mon Sep 17 00:00:00 2001 From: Vincent Ollivier Date: Mon, 14 Oct 2024 20:08:55 +0200 Subject: [PATCH 09/33] Refactor api::vga::color --- src/api/vga/color.rs | 46 +++++++++++++++++++------------------------- 1 file changed, 20 insertions(+), 26 deletions(-) diff --git a/src/api/vga/color.rs b/src/api/vga/color.rs index 2ca1d0263..73cd8fa19 100644 --- a/src/api/vga/color.rs +++ b/src/api/vga/color.rs @@ -1,5 +1,4 @@ /// The standard color palette in VGA text mode -#[allow(dead_code)] #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[repr(u8)] pub enum Color { @@ -22,31 +21,26 @@ pub enum Color { BrightWhite = 0xF, } -const COLORS: [Color; 16] = [ - Color::DarkBlack, - Color::DarkBlue, - Color::DarkGreen, - Color::DarkCyan, - Color::DarkRed, - Color::DarkMagenta, - Color::DarkYellow, - Color::DarkWhite, - Color::BrightBlack, - Color::BrightBlue, - Color::BrightGreen, - Color::BrightCyan, - Color::BrightRed, - Color::BrightMagenta, - Color::BrightYellow, - Color::BrightWhite, -]; - -pub fn colors() -> [Color; 16] { - COLORS -} - -pub fn from_index(index: usize) -> Color { - COLORS[index] +pub fn from_index(code: usize) -> Color { + match code { + 0x0 => Color::DarkBlack, + 0x1 => Color::DarkBlue, + 0x2 => Color::DarkGreen, + 0x3 => Color::DarkCyan, + 0x4 => Color::DarkRed, + 0x5 => Color::DarkMagenta, + 0x6 => Color::DarkYellow, + 0x7 => Color::DarkWhite, + 0x8 => Color::BrightBlack, + 0x9 => Color::BrightBlue, + 0xA => Color::BrightGreen, + 0xB => Color::BrightCyan, + 0xC => Color::BrightRed, + 0xD => Color::BrightMagenta, + 0xE => Color::BrightYellow, + 0xF => Color::BrightWhite, + _ => Color::DarkBlack, // TODO: Error + } } pub fn from_ansi(code: u8) -> Color { From f94ae91bf1abd7a17b0cc5dad742c62ae8632021 Mon Sep 17 00:00:00 2001 From: Vincent Ollivier Date: Mon, 14 Oct 2024 20:10:47 +0200 Subject: [PATCH 10/33] Move api::vga::color to sys::vga::color --- src/api/vga/mod.rs | 2 -- src/{api => sys}/vga/color.rs | 0 src/sys/vga/mod.rs | 5 +++-- 3 files changed, 3 insertions(+), 4 deletions(-) rename src/{api => sys}/vga/color.rs (100%) diff --git a/src/api/vga/mod.rs b/src/api/vga/mod.rs index da184154d..5bf8a81f4 100644 --- a/src/api/vga/mod.rs +++ b/src/api/vga/mod.rs @@ -1,7 +1,5 @@ -pub mod color; pub mod palette; -pub use color::Color; pub use palette::Palette; use crate::api::fs; diff --git a/src/api/vga/color.rs b/src/sys/vga/color.rs similarity index 100% rename from src/api/vga/color.rs rename to src/sys/vga/color.rs diff --git a/src/sys/vga/mod.rs b/src/sys/vga/mod.rs index fc625ff95..9171ca7e8 100644 --- a/src/sys/vga/mod.rs +++ b/src/sys/vga/mod.rs @@ -1,3 +1,4 @@ +mod color; mod font; mod screen; mod writer; @@ -5,9 +6,9 @@ mod writer; pub use font::VgaFont; pub use screen::VgaMode; use writer::WRITER; +use color::Color; -use crate::api::vga::color; -use crate::api::vga::{Color, Palette}; +use crate::api::vga::Palette; use alloc::string::String; use bit_field::BitField; From b106cf484f73f03e1421056d168f77aee36938f0 Mon Sep 17 00:00:00 2001 From: Vincent Ollivier Date: Mon, 14 Oct 2024 20:14:50 +0200 Subject: [PATCH 11/33] Move api::vga::palette to sys::vga::palette --- src/api/vga/mod.rs | 4 ---- src/sys/vga/mod.rs | 7 ++++--- src/{api => sys}/vga/palette.rs | 1 - 3 files changed, 4 insertions(+), 8 deletions(-) rename src/{api => sys}/vga/palette.rs (92%) diff --git a/src/api/vga/mod.rs b/src/api/vga/mod.rs index 5bf8a81f4..8b15be4cf 100644 --- a/src/api/vga/mod.rs +++ b/src/api/vga/mod.rs @@ -1,7 +1,3 @@ -pub mod palette; - -pub use palette::Palette; - use crate::api::fs; use crate::usr::shell; diff --git a/src/sys/vga/mod.rs b/src/sys/vga/mod.rs index 9171ca7e8..2a53e259f 100644 --- a/src/sys/vga/mod.rs +++ b/src/sys/vga/mod.rs @@ -1,14 +1,15 @@ mod color; mod font; +mod palette; mod screen; mod writer; pub use font::VgaFont; pub use screen::VgaMode; -use writer::WRITER; -use color::Color; -use crate::api::vga::Palette; +use color::Color; +use palette::Palette; +use writer::WRITER; use alloc::string::String; use bit_field::BitField; diff --git a/src/api/vga/palette.rs b/src/sys/vga/palette.rs similarity index 92% rename from src/api/vga/palette.rs rename to src/sys/vga/palette.rs index fe64c43d2..79dc6abc7 100644 --- a/src/api/vga/palette.rs +++ b/src/sys/vga/palette.rs @@ -1,4 +1,3 @@ -// TODO: Move this to kernel after removing the `vga set palette` command pub struct Palette { pub colors: [(u8, u8, u8); 16], } From ad02e9bc9e979a085647be5ba1974863d9569472 Mon Sep 17 00:00:00 2001 From: Vincent Ollivier Date: Mon, 14 Oct 2024 20:50:38 +0200 Subject: [PATCH 12/33] Use 256 colors palette --- src/sys/vga/mod.rs | 1 - src/sys/vga/palette.rs | 47 ++++++++++++++++++++++++------------------ src/sys/vga/writer.rs | 1 - 3 files changed, 27 insertions(+), 22 deletions(-) diff --git a/src/sys/vga/mod.rs b/src/sys/vga/mod.rs index 2a53e259f..7448839cd 100644 --- a/src/sys/vga/mod.rs +++ b/src/sys/vga/mod.rs @@ -124,7 +124,6 @@ pub fn is_printable(c: u8) -> bool { pub fn set_palette(palette: Palette) { interrupts::without_interrupts(|| for (i, (r, g, b)) in palette.colors.iter().enumerate() { - let i = color::from_index(i).to_vga_reg() as usize; WRITER.lock().set_palette(i, *r, *g, *b) } ) diff --git a/src/sys/vga/palette.rs b/src/sys/vga/palette.rs index 79dc6abc7..f742efed9 100644 --- a/src/sys/vga/palette.rs +++ b/src/sys/vga/palette.rs @@ -1,28 +1,35 @@ +use super::*; + +const DEFAULT_COLORS: [(u8, u8, u8); 16] = [ + (0x00, 0x00, 0x00), // DarkBlack + (0x00, 0x00, 0x80), // DarkBlue + (0x00, 0x80, 0x00), // DarkGreen + (0x00, 0x80, 0x80), // DarkCyan + (0x80, 0x00, 0x00), // DarkRed + (0x80, 0x00, 0x80), // DarkMagenta + (0x80, 0x80, 0x00), // DarkYellow + (0xC0, 0xC0, 0xC0), // DarkWhite + (0x80, 0x80, 0x80), // BrightBlack + (0x00, 0x00, 0xFF), // BrightBlue + (0x00, 0xFF, 0x00), // BrightGreen + (0x00, 0xFF, 0xFF), // BrightCyan + (0xFF, 0x00, 0x00), // BrightRed + (0xFF, 0x00, 0xFF), // BrightMagenta + (0xFF, 0xFF, 0x00), // BrightYellow + (0xFF, 0xFF, 0xFF), // BrightWhite +]; + pub struct Palette { - pub colors: [(u8, u8, u8); 16], + pub colors: [(u8, u8, u8); 256], } impl Palette { pub fn default() -> Palette { - Palette { - colors: [ - (0x00, 0x00, 0x00), // Black - (0x00, 0x00, 0x80), // Blue - (0x00, 0x80, 0x00), // Green - (0x00, 0x80, 0x80), // Cyan - (0x80, 0x00, 0x00), // Red - (0x80, 0x00, 0x80), // Magenta - (0x80, 0x80, 0x00), // Brown (Dark Yellow) - (0xC0, 0xC0, 0xC0), // Light Gray - (0x80, 0x80, 0x80), // Dark Gray (Gray) - (0x00, 0x00, 0xFF), // Light Blue - (0x00, 0xFF, 0x00), // Light Green - (0x00, 0xFF, 0xFF), // Light Cyan - (0xFF, 0x00, 0x00), // Light Red - (0xFF, 0x00, 0xFF), // Pink (Light Magenta) - (0xFF, 0xFF, 0x00), // Yellow (Light Yellow) - (0xFF, 0xFF, 0xFF), // White - ], + let mut colors = [(0, 0, 0); 256]; + for (i, (r, g, b)) in DEFAULT_COLORS.iter().enumerate() { + let i = color::from_index(i).to_vga_reg() as usize; + colors[i] = (*r, *g, *b); } + Palette { colors } } } diff --git a/src/sys/vga/writer.rs b/src/sys/vga/writer.rs index 7d836c042..aae15b315 100644 --- a/src/sys/vga/writer.rs +++ b/src/sys/vga/writer.rs @@ -479,7 +479,6 @@ impl Perform for Writer { Some('R') if s.len() == 1 => { let palette = Palette::default(); for (i, (r, g, b)) in palette.colors.iter().enumerate() { - let i = color::from_index(i).to_vga_reg() as usize; self.set_palette(i, *r, *g, *b); } } From 1f765d6884a7fb37261d2353475bf01c5b07a77b Mon Sep 17 00:00:00 2001 From: Vincent Ollivier Date: Mon, 14 Oct 2024 21:02:23 +0200 Subject: [PATCH 13/33] Refactor Color --- src/sys/vga/color.rs | 82 +++++++++++++++++++++--------------------- src/sys/vga/palette.rs | 2 +- src/sys/vga/writer.rs | 10 +++--- 3 files changed, 47 insertions(+), 47 deletions(-) diff --git a/src/sys/vga/color.rs b/src/sys/vga/color.rs index 73cd8fa19..f63661fbb 100644 --- a/src/sys/vga/color.rs +++ b/src/sys/vga/color.rs @@ -21,51 +21,51 @@ pub enum Color { BrightWhite = 0xF, } -pub fn from_index(code: usize) -> Color { - match code { - 0x0 => Color::DarkBlack, - 0x1 => Color::DarkBlue, - 0x2 => Color::DarkGreen, - 0x3 => Color::DarkCyan, - 0x4 => Color::DarkRed, - 0x5 => Color::DarkMagenta, - 0x6 => Color::DarkYellow, - 0x7 => Color::DarkWhite, - 0x8 => Color::BrightBlack, - 0x9 => Color::BrightBlue, - 0xA => Color::BrightGreen, - 0xB => Color::BrightCyan, - 0xC => Color::BrightRed, - 0xD => Color::BrightMagenta, - 0xE => Color::BrightYellow, - 0xF => Color::BrightWhite, - _ => Color::DarkBlack, // TODO: Error +impl Color { + pub fn from_index(code: usize) -> Color { + match code { + 0x0 => Color::DarkBlack, + 0x1 => Color::DarkBlue, + 0x2 => Color::DarkGreen, + 0x3 => Color::DarkCyan, + 0x4 => Color::DarkRed, + 0x5 => Color::DarkMagenta, + 0x6 => Color::DarkYellow, + 0x7 => Color::DarkWhite, + 0x8 => Color::BrightBlack, + 0x9 => Color::BrightBlue, + 0xA => Color::BrightGreen, + 0xB => Color::BrightCyan, + 0xC => Color::BrightRed, + 0xD => Color::BrightMagenta, + 0xE => Color::BrightYellow, + 0xF => Color::BrightWhite, + _ => Color::DarkBlack, // TODO: Error + } } -} -pub fn from_ansi(code: u8) -> Color { - match code { - 30 => Color::DarkBlack, - 31 => Color::DarkRed, - 32 => Color::DarkGreen, - 33 => Color::DarkYellow, - 34 => Color::DarkBlue, - 35 => Color::DarkMagenta, - 36 => Color::DarkCyan, - 37 => Color::DarkWhite, - 90 => Color::BrightBlack, - 91 => Color::BrightRed, - 92 => Color::BrightGreen, - 93 => Color::BrightYellow, - 94 => Color::BrightBlue, - 95 => Color::BrightMagenta, - 96 => Color::BrightCyan, - 97 => Color::BrightWhite, - _ => Color::DarkBlack, // TODO: Error + pub fn from_ansi(code: u8) -> Color { + match code { + 30 => Color::DarkBlack, + 31 => Color::DarkRed, + 32 => Color::DarkGreen, + 33 => Color::DarkYellow, + 34 => Color::DarkBlue, + 35 => Color::DarkMagenta, + 36 => Color::DarkCyan, + 37 => Color::DarkWhite, + 90 => Color::BrightBlack, + 91 => Color::BrightRed, + 92 => Color::BrightGreen, + 93 => Color::BrightYellow, + 94 => Color::BrightBlue, + 95 => Color::BrightMagenta, + 96 => Color::BrightCyan, + 97 => Color::BrightWhite, + _ => Color::DarkBlack, // TODO: Error + } } -} -impl Color { pub fn to_vga_reg(&self) -> u8 { match self { Color::DarkBlack => 0x00, diff --git a/src/sys/vga/palette.rs b/src/sys/vga/palette.rs index f742efed9..aaa77dcba 100644 --- a/src/sys/vga/palette.rs +++ b/src/sys/vga/palette.rs @@ -27,7 +27,7 @@ impl Palette { pub fn default() -> Palette { let mut colors = [(0, 0, 0); 256]; for (i, (r, g, b)) in DEFAULT_COLORS.iter().enumerate() { - let i = color::from_index(i).to_vga_reg() as usize; + let i = Color::from_index(i).to_vga_reg() as usize; colors[i] = (*r, *g, *b); } Palette { colors } diff --git a/src/sys/vga/writer.rs b/src/sys/vga/writer.rs index aae15b315..c669db6a0 100644 --- a/src/sys/vga/writer.rs +++ b/src/sys/vga/writer.rs @@ -228,8 +228,8 @@ impl Writer { pub fn color(&self) -> (Color, Color) { let cc = self.color_code.0; - let fg = color::from_index(cc.get_bits(0..4) as usize); - let bg = color::from_index(cc.get_bits(4..8) as usize); + let fg = Color::from_index(cc.get_bits(0..4) as usize); + let bg = Color::from_index(cc.get_bits(4..8) as usize); (fg, bg) } @@ -335,10 +335,10 @@ impl Perform for Writer { bg = BG; } 30..=37 | 90..=97 => { - fg = color::from_ansi(param[0] as u8); + fg = Color::from_ansi(param[0] as u8); } 40..=47 | 100..=107 => { - bg = color::from_ansi((param[0] as u8) - 10); + bg = Color::from_ansi((param[0] as u8) - 10); } _ => {} } @@ -472,7 +472,7 @@ impl Perform for Writer { match s.chars().next() { Some('P') if s.len() == 8 => { if let Ok((i, r, g, b)) = parse_palette(&s) { - let i = color::from_index(i).to_vga_reg() as usize; + let i = Color::from_index(i).to_vga_reg() as usize; self.set_palette(i, r, g, b); } } From fcc555a04225e79646b7c3df65ecb51d813d12ec Mon Sep 17 00:00:00 2001 From: Vincent Ollivier Date: Mon, 14 Oct 2024 21:07:45 +0200 Subject: [PATCH 14/33] Rename Color#to_vga_reg to Color#register --- src/sys/vga/color.rs | 2 +- src/sys/vga/palette.rs | 2 +- src/sys/vga/writer.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sys/vga/color.rs b/src/sys/vga/color.rs index f63661fbb..f4b43a51d 100644 --- a/src/sys/vga/color.rs +++ b/src/sys/vga/color.rs @@ -66,7 +66,7 @@ impl Color { } } - pub fn to_vga_reg(&self) -> u8 { + pub fn register(&self) -> usize { match self { Color::DarkBlack => 0x00, Color::DarkBlue => 0x01, diff --git a/src/sys/vga/palette.rs b/src/sys/vga/palette.rs index aaa77dcba..42f3f216c 100644 --- a/src/sys/vga/palette.rs +++ b/src/sys/vga/palette.rs @@ -27,7 +27,7 @@ impl Palette { pub fn default() -> Palette { let mut colors = [(0, 0, 0); 256]; for (i, (r, g, b)) in DEFAULT_COLORS.iter().enumerate() { - let i = Color::from_index(i).to_vga_reg() as usize; + let i = Color::from_index(i).register(); colors[i] = (*r, *g, *b); } Palette { colors } diff --git a/src/sys/vga/writer.rs b/src/sys/vga/writer.rs index c669db6a0..a1cbc5d23 100644 --- a/src/sys/vga/writer.rs +++ b/src/sys/vga/writer.rs @@ -472,7 +472,7 @@ impl Perform for Writer { match s.chars().next() { Some('P') if s.len() == 8 => { if let Ok((i, r, g, b)) = parse_palette(&s) { - let i = Color::from_index(i).to_vga_reg() as usize; + let i = Color::from_index(i).register(); self.set_palette(i, r, g, b); } } From 0f1777d25a5e1aac9df0e8b7a9c7bb1783b18014 Mon Sep 17 00:00:00 2001 From: Vincent Ollivier Date: Mon, 14 Oct 2024 21:17:34 +0200 Subject: [PATCH 15/33] Add Palette#set --- src/sys/vga/mod.rs | 18 +++--------------- src/sys/vga/palette.rs | 12 ++++++++++++ src/usr/render.rs | 2 +- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/sys/vga/mod.rs b/src/sys/vga/mod.rs index 7448839cd..00912aa19 100644 --- a/src/sys/vga/mod.rs +++ b/src/sys/vga/mod.rs @@ -7,6 +7,8 @@ mod writer; pub use font::VgaFont; pub use screen::VgaMode; +pub use palette::set_palette; // TODO: Remove this + use color::Color; use palette::Palette; use writer::WRITER; @@ -120,20 +122,6 @@ pub fn is_printable(c: u8) -> bool { matches!(c, 0x20..=0x7E | 0x08 | 0x0A | 0x0D | 0x80..=0xFF) } -// TODO: Remove this -pub fn set_palette(palette: Palette) { - interrupts::without_interrupts(|| - for (i, (r, g, b)) in palette.colors.iter().enumerate() { - WRITER.lock().set_palette(i, *r, *g, *b) - } - ) -} -pub fn set_palette_color(i: usize, r: u8, g: u8, b: u8) { - interrupts::without_interrupts(|| - WRITER.lock().set_palette(i, r, g, b) - ) -} - // 0x00 -> top // 0x0F -> bottom // 0x1F -> max (invisible) @@ -211,7 +199,7 @@ pub fn init() { set_attr_ctrl_reg(0xE, 0x3E); set_attr_ctrl_reg(0xF, 0x3F); - set_palette(Palette::default()); + Palette::default().set(); disable_blinking(); disable_underline(); diff --git a/src/sys/vga/palette.rs b/src/sys/vga/palette.rs index 42f3f216c..ee26e3f5a 100644 --- a/src/sys/vga/palette.rs +++ b/src/sys/vga/palette.rs @@ -32,4 +32,16 @@ impl Palette { } Palette { colors } } + + pub fn set(&self) { + for (i, (r, g, b)) in self.colors.iter().enumerate() { + set_palette(i, *r, *g, *b); + } + } +} + +pub fn set_palette(i: usize, r: u8, g: u8, b: u8) { + interrupts::without_interrupts(|| + WRITER.lock().set_palette(i, r, g, b) + ) } diff --git a/src/usr/render.rs b/src/usr/render.rs index 7afbfc3c6..b771592ef 100644 --- a/src/usr/render.rs +++ b/src/usr/render.rs @@ -172,7 +172,7 @@ fn render_bmp(path: &str, config: &mut Config) -> Result { // Load palette for (i, (r, g, b)) in bmp.palette.iter().enumerate() { - sys::vga::set_palette_color(i, *r, *g, *b); + sys::vga::set_palette(i, *r, *g, *b); } // Display image From 72643c612c7db7102f3a99d96ed9b158435acc5a Mon Sep 17 00:00:00 2001 From: Vincent Ollivier Date: Mon, 14 Oct 2024 21:53:32 +0200 Subject: [PATCH 16/33] Add write-only /dev/vga/palette --- src/api/fs.rs | 28 +++---- src/sys/fs/device.rs | 169 ++++++++++++++++++++++------------------- src/sys/vga/mod.rs | 1 + src/sys/vga/palette.rs | 58 +++++++++++++- src/usr/install.rs | 1 + src/usr/render.rs | 10 ++- 6 files changed, 168 insertions(+), 99 deletions(-) diff --git a/src/api/fs.rs b/src/api/fs.rs index 8ab778904..8bcacd7e6 100644 --- a/src/api/fs.rs +++ b/src/api/fs.rs @@ -154,21 +154,23 @@ fn device_buffer(name: &str) -> Result, ()> { Ok(buf) } +// TODO: Move this to sys::fs::device fn device_type(name: &str) -> Result { match name { - "null" => Ok(DeviceType::Null), - "file" => Ok(DeviceType::File), - "console" => Ok(DeviceType::Console), - "random" => Ok(DeviceType::Random), - "uptime" => Ok(DeviceType::Uptime), - "realtime" => Ok(DeviceType::Realtime), - "rtc" => Ok(DeviceType::RTC), - "tcp" => Ok(DeviceType::TcpSocket), - "udp" => Ok(DeviceType::UdpSocket), - "vga-font" => Ok(DeviceType::VgaFont), - "vga-mode" => Ok(DeviceType::VgaMode), - "ata" => Ok(DeviceType::Drive), - _ => Err(()), + "null" => Ok(DeviceType::Null), + "file" => Ok(DeviceType::File), + "console" => Ok(DeviceType::Console), + "random" => Ok(DeviceType::Random), + "uptime" => Ok(DeviceType::Uptime), + "realtime" => Ok(DeviceType::Realtime), + "rtc" => Ok(DeviceType::RTC), + "tcp" => Ok(DeviceType::TcpSocket), + "udp" => Ok(DeviceType::UdpSocket), + "vga-font" => Ok(DeviceType::VgaFont), + "vga-mode" => Ok(DeviceType::VgaMode), + "vga-palette" => Ok(DeviceType::VgaPalette), + "ata" => Ok(DeviceType::Drive), + _ => Err(()), } } diff --git a/src/sys/fs/device.rs b/src/sys/fs/device.rs index e88b70561..1f8f0b8dd 100644 --- a/src/sys/fs/device.rs +++ b/src/sys/fs/device.rs @@ -10,7 +10,7 @@ use crate::sys::console::Console; use crate::sys::net::socket::tcp::TcpSocket; use crate::sys::net::socket::udp::UdpSocket; use crate::sys::rng::Random; -use crate::sys::vga::{VgaFont, VgaMode}; +use crate::sys::vga::{VgaFont, VgaMode, VgaPalette}; use alloc::vec; use alloc::vec::Vec; @@ -20,18 +20,19 @@ use core::convert::TryInto; #[derive(PartialEq, Eq, Clone, Copy)] #[repr(u8)] pub enum DeviceType { - Null = 0, - File = 1, - Console = 2, - Random = 3, - Uptime = 4, - Realtime = 5, - RTC = 6, - TcpSocket = 7, - UdpSocket = 8, - Drive = 9, - VgaFont = 10, - VgaMode = 11, + Null = 0, + File = 1, + Console = 2, + Random = 3, + Uptime = 4, + Realtime = 5, + RTC = 6, + TcpSocket = 7, + UdpSocket = 8, + Drive = 9, + VgaFont = 10, + VgaMode = 11, + VgaPalette = 12, } impl TryFrom<&[u8]> for DeviceType { @@ -51,6 +52,7 @@ impl TryFrom<&[u8]> for DeviceType { 9 => Ok(DeviceType::Drive), 10 => Ok(DeviceType::VgaFont), 11 => Ok(DeviceType::VgaMode), + 12 => Ok(DeviceType::VgaPalette), _ => Err(()), } } @@ -62,14 +64,15 @@ impl DeviceType { // store specific device informations. pub fn buf(self) -> Vec { let len = match self { - DeviceType::RTC => RTC::size(), - DeviceType::Uptime => Uptime::size(), - DeviceType::Realtime => Realtime::size(), - DeviceType::Console => Console::size(), - DeviceType::TcpSocket => TcpSocket::size(), - DeviceType::UdpSocket => UdpSocket::size(), - DeviceType::Drive => Drive::size(), - _ => 1, + DeviceType::RTC => RTC::size(), + DeviceType::Uptime => Uptime::size(), + DeviceType::Realtime => Realtime::size(), + DeviceType::Console => Console::size(), + DeviceType::TcpSocket => TcpSocket::size(), + DeviceType::UdpSocket => UdpSocket::size(), + DeviceType::Drive => Drive::size(), + DeviceType::VgaPalette => VgaPalette::size(), + _ => 1, }; let mut res = vec![0; len]; res[0] = self as u8; // Device type @@ -90,6 +93,7 @@ pub enum Device { UdpSocket(UdpSocket), VgaFont(VgaFont), VgaMode(VgaMode), + VgaPalette(VgaPalette), Drive(Drive), } @@ -98,17 +102,18 @@ impl TryFrom<&[u8]> for Device { fn try_from(buf: &[u8]) -> Result { match buf.try_into()? { - DeviceType::Null => Ok(Device::Null), - DeviceType::File => Ok(Device::File(File::new())), - DeviceType::Console => Ok(Device::Console(Console::new())), - DeviceType::Random => Ok(Device::Random(Random::new())), - DeviceType::Uptime => Ok(Device::Uptime(Uptime::new())), - DeviceType::Realtime => Ok(Device::Realtime(Realtime::new())), - DeviceType::RTC => Ok(Device::RTC(RTC::new())), - DeviceType::TcpSocket => Ok(Device::TcpSocket(TcpSocket::new())), - DeviceType::UdpSocket => Ok(Device::UdpSocket(UdpSocket::new())), - DeviceType::VgaFont => Ok(Device::VgaFont(VgaFont::new())), - DeviceType::VgaMode => Ok(Device::VgaMode(VgaMode::new())), + DeviceType::Null => Ok(Device::Null), + DeviceType::File => Ok(Device::File(File::new())), + DeviceType::Console => Ok(Device::Console(Console::new())), + DeviceType::Random => Ok(Device::Random(Random::new())), + DeviceType::Uptime => Ok(Device::Uptime(Uptime::new())), + DeviceType::Realtime => Ok(Device::Realtime(Realtime::new())), + DeviceType::RTC => Ok(Device::RTC(RTC::new())), + DeviceType::TcpSocket => Ok(Device::TcpSocket(TcpSocket::new())), + DeviceType::UdpSocket => Ok(Device::UdpSocket(UdpSocket::new())), + DeviceType::VgaFont => Ok(Device::VgaFont(VgaFont::new())), + DeviceType::VgaMode => Ok(Device::VgaMode(VgaMode::new())), + DeviceType::VgaPalette => Ok(Device::VgaPalette(VgaPalette::new())), DeviceType::Drive if buf.len() > 2 => { let bus = buf[1]; let dsk = buf[2]; @@ -158,69 +163,73 @@ impl Device { impl FileIO for Device { fn read(&mut self, buf: &mut [u8]) -> Result { match self { - Device::Null => Err(()), - Device::File(io) => io.read(buf), - Device::Console(io) => io.read(buf), - Device::Random(io) => io.read(buf), - Device::Uptime(io) => io.read(buf), - Device::Realtime(io) => io.read(buf), - Device::RTC(io) => io.read(buf), - Device::TcpSocket(io) => io.read(buf), - Device::UdpSocket(io) => io.read(buf), - Device::VgaFont(io) => io.read(buf), - Device::VgaMode(io) => io.read(buf), - Device::Drive(io) => io.read(buf), + Device::Null => Err(()), + Device::File(io) => io.read(buf), + Device::Console(io) => io.read(buf), + Device::Random(io) => io.read(buf), + Device::Uptime(io) => io.read(buf), + Device::Realtime(io) => io.read(buf), + Device::RTC(io) => io.read(buf), + Device::TcpSocket(io) => io.read(buf), + Device::UdpSocket(io) => io.read(buf), + Device::VgaFont(io) => io.read(buf), + Device::VgaMode(io) => io.read(buf), + Device::VgaPalette(io) => io.read(buf), + Device::Drive(io) => io.read(buf), } } fn write(&mut self, buf: &[u8]) -> Result { match self { - Device::Null => Ok(0), - Device::File(io) => io.write(buf), - Device::Console(io) => io.write(buf), - Device::Random(io) => io.write(buf), - Device::Uptime(io) => io.write(buf), - Device::Realtime(io) => io.write(buf), - Device::RTC(io) => io.write(buf), - Device::TcpSocket(io) => io.write(buf), - Device::UdpSocket(io) => io.write(buf), - Device::VgaFont(io) => io.write(buf), - Device::VgaMode(io) => io.write(buf), - Device::Drive(io) => io.write(buf), + Device::Null => Ok(0), + Device::File(io) => io.write(buf), + Device::Console(io) => io.write(buf), + Device::Random(io) => io.write(buf), + Device::Uptime(io) => io.write(buf), + Device::Realtime(io) => io.write(buf), + Device::RTC(io) => io.write(buf), + Device::TcpSocket(io) => io.write(buf), + Device::UdpSocket(io) => io.write(buf), + Device::VgaFont(io) => io.write(buf), + Device::VgaMode(io) => io.write(buf), + Device::VgaPalette(io) => io.write(buf), + Device::Drive(io) => io.write(buf), } } fn close(&mut self) { match self { - Device::Null => {} - Device::File(io) => io.close(), - Device::Console(io) => io.close(), - Device::Random(io) => io.close(), - Device::Uptime(io) => io.close(), - Device::Realtime(io) => io.close(), - Device::RTC(io) => io.close(), - Device::TcpSocket(io) => io.close(), - Device::UdpSocket(io) => io.close(), - Device::VgaFont(io) => io.close(), - Device::VgaMode(io) => io.close(), - Device::Drive(io) => io.close(), + Device::Null => {} + Device::File(io) => io.close(), + Device::Console(io) => io.close(), + Device::Random(io) => io.close(), + Device::Uptime(io) => io.close(), + Device::Realtime(io) => io.close(), + Device::RTC(io) => io.close(), + Device::TcpSocket(io) => io.close(), + Device::UdpSocket(io) => io.close(), + Device::VgaFont(io) => io.close(), + Device::VgaMode(io) => io.close(), + Device::VgaPalette(io) => io.close(), + Device::Drive(io) => io.close(), } } fn poll(&mut self, event: IO) -> bool { match self { - Device::Null => false, - Device::File(io) => io.poll(event), - Device::Console(io) => io.poll(event), - Device::Random(io) => io.poll(event), - Device::Uptime(io) => io.poll(event), - Device::Realtime(io) => io.poll(event), - Device::RTC(io) => io.poll(event), - Device::TcpSocket(io) => io.poll(event), - Device::UdpSocket(io) => io.poll(event), - Device::VgaFont(io) => io.poll(event), - Device::VgaMode(io) => io.poll(event), - Device::Drive(io) => io.poll(event), + Device::Null => false, + Device::File(io) => io.poll(event), + Device::Console(io) => io.poll(event), + Device::Random(io) => io.poll(event), + Device::Uptime(io) => io.poll(event), + Device::Realtime(io) => io.poll(event), + Device::RTC(io) => io.poll(event), + Device::TcpSocket(io) => io.poll(event), + Device::UdpSocket(io) => io.poll(event), + Device::VgaFont(io) => io.poll(event), + Device::VgaMode(io) => io.poll(event), + Device::VgaPalette(io) => io.poll(event), + Device::Drive(io) => io.poll(event), } } } diff --git a/src/sys/vga/mod.rs b/src/sys/vga/mod.rs index 00912aa19..d7eef1ef4 100644 --- a/src/sys/vga/mod.rs +++ b/src/sys/vga/mod.rs @@ -6,6 +6,7 @@ mod writer; pub use font::VgaFont; pub use screen::VgaMode; +pub use palette::Palette as VgaPalette; pub use palette::set_palette; // TODO: Remove this diff --git a/src/sys/vga/palette.rs b/src/sys/vga/palette.rs index ee26e3f5a..5ccb73ce1 100644 --- a/src/sys/vga/palette.rs +++ b/src/sys/vga/palette.rs @@ -1,5 +1,9 @@ use super::*; +use crate::api::fs::{FileIO, IO}; + +use core::convert::TryFrom; + const DEFAULT_COLORS: [(u8, u8, u8); 16] = [ (0x00, 0x00, 0x00), // DarkBlack (0x00, 0x00, 0x80), // DarkBlue @@ -19,18 +23,23 @@ const DEFAULT_COLORS: [(u8, u8, u8); 16] = [ (0xFF, 0xFF, 0xFF), // BrightWhite ]; +#[derive(Debug, Clone)] pub struct Palette { pub colors: [(u8, u8, u8); 256], } impl Palette { - pub fn default() -> Palette { - let mut colors = [(0, 0, 0); 256]; + pub fn new() -> Self { + Self { colors: [(0, 0, 0); 256] } + } + + pub fn default() -> Self { + let mut palette = Palette::new(); for (i, (r, g, b)) in DEFAULT_COLORS.iter().enumerate() { let i = Color::from_index(i).register(); - colors[i] = (*r, *g, *b); + palette.colors[i] = (*r, *g, *b); } - Palette { colors } + palette } pub fn set(&self) { @@ -38,6 +47,47 @@ impl Palette { set_palette(i, *r, *g, *b); } } + + pub fn size() -> usize { + 256 * 3 + } +} + +impl TryFrom<&[u8]> for Palette { + type Error = (); + + fn try_from(buf: &[u8]) -> Result { + if buf.len() != Palette::size() { + return Err(()); + } + let mut colors = [(0, 0, 0); 256]; + for (i, rgb) in buf.chunks(3).enumerate() { + colors[i] = (rgb[0], rgb[1], rgb[2]) + } + + Ok(Palette { colors }) + } +} + +impl FileIO for VgaPalette { + fn read(&mut self, _buf: &mut [u8]) -> Result { + Err(()) // TODO + } + + fn write(&mut self, buf: &[u8]) -> Result { + let palette = Palette::try_from(buf)?; + palette.set(); + Ok(buf.len()) + } + + fn close(&mut self) {} + + fn poll(&mut self, event: IO) -> bool { + match event { + IO::Read => false, // TODO + IO::Write => true, // TODO + } + } } pub fn set_palette(i: usize, r: u8, g: u8, b: u8) { diff --git a/src/usr/install.rs b/src/usr/install.rs index 57d1af6a4..a072da772 100644 --- a/src/usr/install.rs +++ b/src/usr/install.rs @@ -57,6 +57,7 @@ pub fn copy_files(verbose: bool) { create_dev("/dev/net/udp", "udp", verbose); create_dev("/dev/vga/font", "vga-font", verbose); create_dev("/dev/vga/mode", "vga-mode", verbose); + create_dev("/dev/vga/palette", "vga-palette", verbose); copy_file!("/ini/banner.txt", verbose); copy_file!("/ini/boot.sh", verbose); diff --git a/src/usr/render.rs b/src/usr/render.rs index b771592ef..8784a0bfb 100644 --- a/src/usr/render.rs +++ b/src/usr/render.rs @@ -3,7 +3,6 @@ use crate::api::fs; use crate::api::io; use crate::api::process::ExitCode; use crate::api::vga; -use crate::sys; use alloc::vec::Vec; use alloc::string::{String, ToString}; @@ -171,8 +170,15 @@ fn render_bmp(path: &str, config: &mut Config) -> Result { config.graphic_mode(); // Load palette + let mut palette = [0; 256 * 3]; for (i, (r, g, b)) in bmp.palette.iter().enumerate() { - sys::vga::set_palette(i, *r, *g, *b); + palette[i * 3 + 0] = *r; + palette[i * 3 + 1] = *g; + palette[i * 3 + 2] = *b; + } + if fs::write("/dev/vga/palette", &palette).is_err() { + error!("Could not set palette"); + return Err(ExitCode::Failure); } // Display image From e194a77d35c24dd4e8b9a42aeea839143c12d0fc Mon Sep 17 00:00:00 2001 From: Vincent Ollivier Date: Mon, 14 Oct 2024 21:54:16 +0200 Subject: [PATCH 17/33] Remove unused set_palette --- src/sys/vga/mod.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/sys/vga/mod.rs b/src/sys/vga/mod.rs index d7eef1ef4..f354cdec7 100644 --- a/src/sys/vga/mod.rs +++ b/src/sys/vga/mod.rs @@ -8,8 +8,6 @@ pub use font::VgaFont; pub use screen::VgaMode; pub use palette::Palette as VgaPalette; -pub use palette::set_palette; // TODO: Remove this - use color::Color; use palette::Palette; use writer::WRITER; From 734fee76280dae7733092918bb066f39da2c19c1 Mon Sep 17 00:00:00 2001 From: Vincent Ollivier Date: Mon, 14 Oct 2024 22:14:40 +0200 Subject: [PATCH 18/33] Add read operation to /dev/vga/palette --- src/sys/vga/mod.rs | 6 +----- src/sys/vga/palette.rs | 41 ++++++++++++++++++++++++++++++++++++----- src/sys/vga/writer.rs | 18 +++++++++++++++--- 3 files changed, 52 insertions(+), 13 deletions(-) diff --git a/src/sys/vga/mod.rs b/src/sys/vga/mod.rs index f354cdec7..da5a6033e 100644 --- a/src/sys/vga/mod.rs +++ b/src/sys/vga/mod.rs @@ -27,6 +27,7 @@ const ATTR_READ_REG: u16 = 0x3C1; const MISC_WRITE_REG: u16 = 0x3C2; const SEQUENCER_ADDR_REG: u16 = 0x3C4; const SEQUENCER_DATA_REG: u16 = 0x3C5; +const DAC_ADDR_READ_MODE_REG: u16 = 0x3C7; const DAC_ADDR_WRITE_MODE_REG: u16 = 0x3C8; const DAC_DATA_REG: u16 = 0x3C9; const GRAPHICS_ADDR_REG: u16 = 0x3CE; @@ -75,11 +76,6 @@ struct ScreenBuffer { chars: [[ScreenChar; SCREEN_WIDTH]; SCREEN_HEIGHT], } -// Convert 8-bit to 6-bit color -fn vga_color(color: u8) -> u8 { - color >> 2 -} - // TODO: Remove this fn parse_palette(palette: &str) -> Result<(usize, u8, u8, u8), ParseIntError> { debug_assert!(palette.len() == 8); diff --git a/src/sys/vga/palette.rs b/src/sys/vga/palette.rs index 5ccb73ce1..a92f610c6 100644 --- a/src/sys/vga/palette.rs +++ b/src/sys/vga/palette.rs @@ -42,12 +42,30 @@ impl Palette { palette } + pub fn get() -> Self { + let mut palette = Palette::new(); + for i in 0..256 { + palette.colors[i] = get_palette(i); + } + palette + } + pub fn set(&self) { for (i, (r, g, b)) in self.colors.iter().enumerate() { set_palette(i, *r, *g, *b); } } + pub fn to_bytes(&self) -> [u8; 256 * 3] { + let mut buf = [0; 256 * 3]; + for (i, (r, g, b)) in self.colors.iter().enumerate() { + buf[i * 3 + 0] = *r; + buf[i * 3 + 1] = *g; + buf[i * 3 + 2] = *b; + } + buf + } + pub fn size() -> usize { 256 * 3 } @@ -70,8 +88,13 @@ impl TryFrom<&[u8]> for Palette { } impl FileIO for VgaPalette { - fn read(&mut self, _buf: &mut [u8]) -> Result { - Err(()) // TODO + fn read(&mut self, buf: &mut [u8]) -> Result { + let res = Palette::get().to_bytes(); + if buf.len() < res.len() { + return Err(()); + } + buf.clone_from_slice(&res); + Ok(res.len()) } fn write(&mut self, buf: &[u8]) -> Result { @@ -84,14 +107,22 @@ impl FileIO for VgaPalette { fn poll(&mut self, event: IO) -> bool { match event { - IO::Read => false, // TODO - IO::Write => true, // TODO + IO::Read => true, + IO::Write => true, } } } -pub fn set_palette(i: usize, r: u8, g: u8, b: u8) { +// TODO: Rename to `write_palette_index` +fn set_palette(i: usize, r: u8, g: u8, b: u8) { interrupts::without_interrupts(|| WRITER.lock().set_palette(i, r, g, b) ) } + +// TODO: Rename to `read_palette_index` +fn get_palette(i: usize) -> (u8, u8, u8) { + interrupts::without_interrupts(|| + WRITER.lock().palette(i) + ) +} diff --git a/src/sys/vga/writer.rs b/src/sys/vga/writer.rs index a1cbc5d23..3140cf9fb 100644 --- a/src/sys/vga/writer.rs +++ b/src/sys/vga/writer.rs @@ -272,9 +272,21 @@ impl Writer { let mut data: Port = Port::new(DAC_DATA_REG); unsafe { addr.write(i as u8); - data.write(vga_color(r)); - data.write(vga_color(g)); - data.write(vga_color(b)); + data.write(r >> 2); // Convert 8-bit to 6-bit color + data.write(g >> 2); + data.write(b >> 2); + } + } + + pub fn palette(&mut self, i: usize) -> (u8, u8, u8) { + let mut addr: Port = Port::new(DAC_ADDR_READ_MODE_REG); + let mut data: Port = Port::new(DAC_DATA_REG); + unsafe { + addr.write(i as u8); + let r = data.read() << 2; // Convert 6-bit to 8-bit color + let g = data.read() << 2; + let b = data.read() << 2; + (r, g, b) } } From bdff7ab6a51f19c9c1f5c33fff579b2d7e51b81f Mon Sep 17 00:00:00 2001 From: Vincent Ollivier Date: Mon, 14 Oct 2024 22:41:54 +0200 Subject: [PATCH 19/33] Add palette backup and restore --- src/api/vga/mod.rs | 7 ------- src/sys/vga/palette.rs | 13 +++++++++++++ src/sys/vga/screen.rs | 5 +++++ 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/src/api/vga/mod.rs b/src/api/vga/mod.rs index 8b15be4cf..245d2503e 100644 --- a/src/api/vga/mod.rs +++ b/src/api/vga/mod.rs @@ -1,17 +1,10 @@ use crate::api::fs; -use crate::usr::shell; pub fn graphic_mode() { fs::write("/dev/vga/mode", b"320x200").ok(); - - // TODO: Backup palette } pub fn text_mode() { fs::write("/dev/vga/mode", b"80x25").ok(); - - // TODO: Restore and palette backup instead of this - shell::exec("shell /ini/palettes/gruvbox-dark.sh").ok(); - print!("\x1b[2J\x1b[1;1H"); // Clear screen and move to top } diff --git a/src/sys/vga/palette.rs b/src/sys/vga/palette.rs index a92f610c6..0ff71ae60 100644 --- a/src/sys/vga/palette.rs +++ b/src/sys/vga/palette.rs @@ -3,6 +3,9 @@ use super::*; use crate::api::fs::{FileIO, IO}; use core::convert::TryFrom; +use spin::Mutex; + +static PALETTE: Mutex> = Mutex::new(None); const DEFAULT_COLORS: [(u8, u8, u8); 16] = [ (0x00, 0x00, 0x00), // DarkBlack @@ -126,3 +129,13 @@ fn get_palette(i: usize) -> (u8, u8, u8) { WRITER.lock().palette(i) ) } + +pub fn restore_palette() { + if let Some(ref palette) = *PALETTE.lock() { + palette.set(); + } +} + +pub fn backup_palette() { + *PALETTE.lock() = Some(Palette::get()) +} diff --git a/src/sys/vga/screen.rs b/src/sys/vga/screen.rs index 88e2f3714..2898030b8 100644 --- a/src/sys/vga/screen.rs +++ b/src/sys/vga/screen.rs @@ -126,14 +126,19 @@ pub fn set_80x25_mode() { disable_blinking(); disable_underline(); font::restore_font(); + palette::restore_palette(); } pub fn set_320x200_mode() { + // NOTE: Setting a graphic mode twice without going back to text mode will + // overwrite the backup palette. + palette::backup_palette(); set_mode(&G_320_200_256); // TODO: Clear screen } pub fn set_640x480_mode() { + palette::backup_palette(); set_mode(&G_640_480_16); // TODO: Clear screen } From 6249b09bd2788945cc85567b9e68362590a65f6d Mon Sep 17 00:00:00 2001 From: Vincent Ollivier Date: Mon, 14 Oct 2024 22:47:26 +0200 Subject: [PATCH 20/33] Check for device presence --- src/api/vga/mod.rs | 12 +++++++++--- src/usr/render.rs | 3 ++- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/api/vga/mod.rs b/src/api/vga/mod.rs index 245d2503e..768b586ea 100644 --- a/src/api/vga/mod.rs +++ b/src/api/vga/mod.rs @@ -1,10 +1,16 @@ use crate::api::fs; pub fn graphic_mode() { - fs::write("/dev/vga/mode", b"320x200").ok(); + let dev = "/dev/vga/mode"; + if fs::is_device(dev) { + fs::write(dev, b"320x200").ok(); + } } pub fn text_mode() { - fs::write("/dev/vga/mode", b"80x25").ok(); - print!("\x1b[2J\x1b[1;1H"); // Clear screen and move to top + let dev = "/dev/vga/mode"; + if fs::is_device(dev) { + fs::write(dev, b"80x25").ok(); + print!("\x1b[2J\x1b[1;1H"); // Clear screen and move to top + } } diff --git a/src/usr/render.rs b/src/usr/render.rs index 8784a0bfb..bb7324ac4 100644 --- a/src/usr/render.rs +++ b/src/usr/render.rs @@ -176,7 +176,8 @@ fn render_bmp(path: &str, config: &mut Config) -> Result { palette[i * 3 + 1] = *g; palette[i * 3 + 2] = *b; } - if fs::write("/dev/vga/palette", &palette).is_err() { + let dev = "/dev/vga/palette"; + if !fs::is_device(dev) || fs::write(dev, &palette).is_err() { error!("Could not set palette"); return Err(ExitCode::Failure); } From 6c8cc764d9b355030ab5a4e7347cb36814fd9966 Mon Sep 17 00:00:00 2001 From: Vincent Ollivier Date: Tue, 15 Oct 2024 11:23:59 +0200 Subject: [PATCH 21/33] Backup palette only in 80x25 mode --- src/sys/vga/font.rs | 4 ++-- src/sys/vga/screen.rs | 52 ++++++++++++++++++++++++++++++++----------- 2 files changed, 41 insertions(+), 15 deletions(-) diff --git a/src/sys/vga/font.rs b/src/sys/vga/font.rs index f35c6eb10..6fcb35240 100644 --- a/src/sys/vga/font.rs +++ b/src/sys/vga/font.rs @@ -7,6 +7,8 @@ use core::convert::TryFrom; use spin::Mutex; use x86_64::instructions::interrupts; +static FONT: Mutex> = Mutex::new(None); + #[derive(Debug, Clone)] pub struct VgaFont; @@ -41,8 +43,6 @@ impl FileIO for VgaFont { } } -static FONT: Mutex> = Mutex::new(None); - pub fn set_font(font: &Font) { interrupts::without_interrupts(|| WRITER.lock().set_font(font) diff --git a/src/sys/vga/screen.rs b/src/sys/vga/screen.rs index 2898030b8..99408007b 100644 --- a/src/sys/vga/screen.rs +++ b/src/sys/vga/screen.rs @@ -2,6 +2,17 @@ use super::*; use crate::api::fs::{FileIO, IO}; +use spin::Mutex; + +#[derive(Copy, Clone)] +enum ModeName { + T80x25, + G320x200x256, + G640x480x16, +} + +static MODE: Mutex> = Mutex::new(None); + // Source: https://www.singlix.com/trdos/archive/vga/Graphics%20in%20pmode.pdf const T_80_25: [u8; 61] = [ // MISC @@ -57,7 +68,14 @@ const GC_REGS_COUNT: usize = 9; const AC_REGS_COUNT: usize = 21; // Source: https://www.singlix.com/trdos/archive/vga/Graphics%20in%20pmode.pdf -fn set_mode(regs: &[u8]) { +fn set_mode(mode: ModeName) { + *MODE.lock() = Some(mode); + let mut regs = match mode { + ModeName::T80x25 => T_80_25, + ModeName::G320x200x256 => G_320_200_256, + ModeName::G640x480x16 => G_640_480_16, + }.to_vec(); + interrupts::without_interrupts(|| { let mut misc_write: Port = Port::new(MISC_WRITE_REG); let mut crtc_addr: Port = Port::new(CRTC_ADDR_REG); @@ -70,7 +88,6 @@ fn set_mode(regs: &[u8]) { let mut ac_write: Port = Port::new(ATTR_WRITE_REG); let mut instat_read: Port = Port::new(INSTAT_READ_REG); - let mut regs = regs.to_vec(); let mut i = 0; unsafe { @@ -121,25 +138,34 @@ fn set_mode(regs: &[u8]) { }); } -pub fn set_80x25_mode() { - set_mode(&T_80_25); +fn is_80x25_mode() -> bool { + match *MODE.lock() { + Some(ModeName::T80x25) | None => true, + _ => false + } +} + +fn set_80x25_mode() { + set_mode(ModeName::T80x25); disable_blinking(); disable_underline(); - font::restore_font(); palette::restore_palette(); + font::restore_font(); } -pub fn set_320x200_mode() { - // NOTE: Setting a graphic mode twice without going back to text mode will - // overwrite the backup palette. - palette::backup_palette(); - set_mode(&G_320_200_256); +fn set_320x200_mode() { + if is_80x25_mode() { + palette::backup_palette(); + } + set_mode(ModeName::G320x200x256); // TODO: Clear screen } -pub fn set_640x480_mode() { - palette::backup_palette(); - set_mode(&G_640_480_16); +fn set_640x480_mode() { + if is_80x25_mode() { + palette::backup_palette(); + } + set_mode(ModeName::G640x480x16); // TODO: Clear screen } From a9fd0fc71d3b0d7bb846e8a3960893fd542908db Mon Sep 17 00:00:00 2001 From: Vincent Ollivier Date: Tue, 15 Oct 2024 11:40:29 +0200 Subject: [PATCH 22/33] Clear screen on mode change --- src/sys/vga/screen.rs | 22 ++++++++++++++++++++-- src/usr/blank.rs | 15 --------------- src/usr/render.rs | 11 ----------- 3 files changed, 20 insertions(+), 28 deletions(-) diff --git a/src/sys/vga/screen.rs b/src/sys/vga/screen.rs index 99408007b..0a0a4ee00 100644 --- a/src/sys/vga/screen.rs +++ b/src/sys/vga/screen.rs @@ -11,6 +11,8 @@ enum ModeName { G640x480x16, } +const FRAMEBUFFER: usize = 0xA0000; + static MODE: Mutex> = Mutex::new(None); // Source: https://www.singlix.com/trdos/archive/vga/Graphics%20in%20pmode.pdf @@ -146,6 +148,7 @@ fn is_80x25_mode() -> bool { } fn set_80x25_mode() { + clear_screen(); set_mode(ModeName::T80x25); disable_blinking(); disable_underline(); @@ -158,7 +161,7 @@ fn set_320x200_mode() { palette::backup_palette(); } set_mode(ModeName::G320x200x256); - // TODO: Clear screen + clear_screen(); } fn set_640x480_mode() { @@ -166,7 +169,22 @@ fn set_640x480_mode() { palette::backup_palette(); } set_mode(ModeName::G640x480x16); - // TODO: Clear screen + clear_screen(); +} + +fn clear_screen() { + // Clear screen + let screen = [0; 640 * 480]; + let size = match *MODE.lock() { + Some(ModeName::G320x200x256) => 320 * 200, + Some(ModeName::G640x480x16) => 640 * 480, + _ => return, + }; + let src = screen.as_ptr(); + let dst = FRAMEBUFFER as *mut u8; + unsafe { + core::ptr::copy_nonoverlapping(src, dst, size); + } } #[derive(Debug, Clone)] diff --git a/src/usr/blank.rs b/src/usr/blank.rs index 47b70da03..a91c15af1 100644 --- a/src/usr/blank.rs +++ b/src/usr/blank.rs @@ -2,27 +2,12 @@ use crate::api::io; use crate::api::process::ExitCode; use crate::api::vga; -const FRAMEBUFFER: usize = 0xA0000; -const WIDTH: usize = 320; -const HEIGHT: usize = 200; - -fn clear() { - let ptr = FRAMEBUFFER as *mut u8; - let size = WIDTH * HEIGHT; - unsafe { - let buf = core::slice::from_raw_parts_mut(ptr, size); - buf.fill(0x00); - } -} - pub fn main(_args: &[&str]) -> Result<(), ExitCode> { vga::graphic_mode(); print!("\x1b]R\x1b[1A"); // Reset palette - clear(); while io::stdin().read_char().is_none() { x86_64::instructions::hlt(); } - clear(); vga::text_mode(); Ok(()) } diff --git a/src/usr/render.rs b/src/usr/render.rs index bb7324ac4..4c13c6ac8 100644 --- a/src/usr/render.rs +++ b/src/usr/render.rs @@ -12,15 +12,6 @@ const FRAMEBUFFER: usize = 0xA0000; const WIDTH: usize = 320; const HEIGHT: usize = 200; -fn clear() { - let ptr = FRAMEBUFFER as *mut u8; - let size = WIDTH * HEIGHT; - unsafe { - let buf = core::slice::from_raw_parts_mut(ptr, size); - buf.fill(0x00); - } -} - #[derive(Debug)] #[repr(C, packed)] struct BmpHeader { @@ -126,7 +117,6 @@ impl Config { pub fn text_mode(&mut self) { if self.mode == Mode::Graphic { - clear(); vga::text_mode(); self.mode = Mode::Text; } @@ -135,7 +125,6 @@ impl Config { pub fn graphic_mode(&mut self) { if self.mode == Mode::Text { vga::graphic_mode(); - clear(); self.mode = Mode::Graphic; } } From 8fab113a75c6511c3a9b9357ae6c573417d05dec Mon Sep 17 00:00:00 2001 From: Vincent Ollivier Date: Tue, 15 Oct 2024 12:07:56 +0200 Subject: [PATCH 23/33] Make /dev/vga/mode readable --- src/sys/fs/device.rs | 1 + src/sys/vga/screen.rs | 24 +++++++++++++++++++++--- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/src/sys/fs/device.rs b/src/sys/fs/device.rs index 1f8f0b8dd..1097264de 100644 --- a/src/sys/fs/device.rs +++ b/src/sys/fs/device.rs @@ -71,6 +71,7 @@ impl DeviceType { DeviceType::TcpSocket => TcpSocket::size(), DeviceType::UdpSocket => UdpSocket::size(), DeviceType::Drive => Drive::size(), + DeviceType::VgaMode => VgaMode::size(), DeviceType::VgaPalette => VgaPalette::size(), _ => 1, }; diff --git a/src/sys/vga/screen.rs b/src/sys/vga/screen.rs index 0a0a4ee00..658b7a3da 100644 --- a/src/sys/vga/screen.rs +++ b/src/sys/vga/screen.rs @@ -194,11 +194,19 @@ impl VgaMode { pub fn new() -> Self { Self } + + pub fn size() -> usize { + 16 // Must be larger than 8 bytes to be readable as a block device + } } impl FileIO for VgaMode { - fn read(&mut self, _buf: &mut [u8]) -> Result { - Err(()) // TODO + fn read(&mut self, buf: &mut [u8]) -> Result { + match *MODE.lock() { + Some(ModeName::T80x25) | None => write_mode(buf, b"80x25"), + Some(ModeName::G320x200x256) => write_mode(buf, b"320x200"), + Some(ModeName::G640x480x16) => write_mode(buf, b"640x480"), + } } fn write(&mut self, buf: &[u8]) -> Result { @@ -215,8 +223,18 @@ impl FileIO for VgaMode { fn poll(&mut self, event: IO) -> bool { match event { - IO::Read => false, // TODO + IO::Read => true, IO::Write => true, } } } + +fn write_mode(buf: &mut [u8], mode: &[u8]) -> Result { + let n = mode.len(); + if buf.len() < n { + Err(()) + } else { + buf[0..n].clone_from_slice(mode); + Ok(n) + } +} From 3182e7f96d4b0ba31a4046967151575f8bd87905 Mon Sep 17 00:00:00 2001 From: Vincent Ollivier Date: Tue, 15 Oct 2024 12:38:14 +0200 Subject: [PATCH 24/33] Fix double buffer allocation --- src/sys/vga/screen.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sys/vga/screen.rs b/src/sys/vga/screen.rs index 658b7a3da..47dea19e9 100644 --- a/src/sys/vga/screen.rs +++ b/src/sys/vga/screen.rs @@ -11,7 +11,8 @@ enum ModeName { G640x480x16, } -const FRAMEBUFFER: usize = 0xA0000; +const FRAME_BUFFER_ADDR: usize = 0xA0000; +const DOUBLE_BUFFER: [u8; 640 * 480] = [0; 640 * 480]; static MODE: Mutex> = Mutex::new(None); @@ -174,14 +175,13 @@ fn set_640x480_mode() { fn clear_screen() { // Clear screen - let screen = [0; 640 * 480]; let size = match *MODE.lock() { Some(ModeName::G320x200x256) => 320 * 200, Some(ModeName::G640x480x16) => 640 * 480, _ => return, }; - let src = screen.as_ptr(); - let dst = FRAMEBUFFER as *mut u8; + let src = DOUBLE_BUFFER.as_ptr(); + let dst = FRAME_BUFFER_ADDR as *mut u8; unsafe { core::ptr::copy_nonoverlapping(src, dst, size); } From 465abaa86a1f1d7076f0dd840e3ca33ea9ef089e Mon Sep 17 00:00:00 2001 From: Vincent Ollivier Date: Tue, 15 Oct 2024 12:54:14 +0200 Subject: [PATCH 25/33] Add framebuffer:addr() --- src/sys/vga/framebuffer.rs | 3 +++ src/sys/vga/mod.rs | 1 + src/sys/vga/screen.rs | 7 +++---- src/sys/vga/writer.rs | 2 +- 4 files changed, 8 insertions(+), 5 deletions(-) create mode 100644 src/sys/vga/framebuffer.rs diff --git a/src/sys/vga/framebuffer.rs b/src/sys/vga/framebuffer.rs new file mode 100644 index 000000000..a0022e7e0 --- /dev/null +++ b/src/sys/vga/framebuffer.rs @@ -0,0 +1,3 @@ +pub fn addr() -> u64 { + 0xA0000 +} diff --git a/src/sys/vga/mod.rs b/src/sys/vga/mod.rs index da5a6033e..7c4aae7e5 100644 --- a/src/sys/vga/mod.rs +++ b/src/sys/vga/mod.rs @@ -1,5 +1,6 @@ mod color; mod font; +mod framebuffer; mod palette; mod screen; mod writer; diff --git a/src/sys/vga/screen.rs b/src/sys/vga/screen.rs index 47dea19e9..0ed4dfb32 100644 --- a/src/sys/vga/screen.rs +++ b/src/sys/vga/screen.rs @@ -11,8 +11,7 @@ enum ModeName { G640x480x16, } -const FRAME_BUFFER_ADDR: usize = 0xA0000; -const DOUBLE_BUFFER: [u8; 640 * 480] = [0; 640 * 480]; +const BUFFER: [u8; 640 * 480] = [0; 640 * 480]; static MODE: Mutex> = Mutex::new(None); @@ -180,8 +179,8 @@ fn clear_screen() { Some(ModeName::G640x480x16) => 640 * 480, _ => return, }; - let src = DOUBLE_BUFFER.as_ptr(); - let dst = FRAME_BUFFER_ADDR as *mut u8; + let src = BUFFER.as_ptr(); + let dst = framebuffer::addr() as *mut u8; unsafe { core::ptr::copy_nonoverlapping(src, dst, size); } diff --git a/src/sys/vga/writer.rs b/src/sys/vga/writer.rs index 3140cf9fb..e74581eb6 100644 --- a/src/sys/vga/writer.rs +++ b/src/sys/vga/writer.rs @@ -237,7 +237,7 @@ impl Writer { pub fn set_font(&mut self, font: &Font) { let mut sequencer: Port = Port::new(SEQUENCER_ADDR_REG); let mut graphics: Port = Port::new(GRAPHICS_ADDR_REG); - let buffer = 0xA0000 as *mut u8; + let buffer = framebuffer::addr() as *mut u8; unsafe { sequencer.write(0x0100); // do a sync reset From 674bac14a88753eb6aa75eb7f417ea4daf8d5ec2 Mon Sep 17 00:00:00 2001 From: Vincent Ollivier Date: Tue, 15 Oct 2024 14:39:39 +0200 Subject: [PATCH 26/33] Add /dev/vga/buffer device --- src/api/fs.rs | 1 + src/sys/fs/device.rs | 11 ++++++++- src/sys/vga/buffer.rs | 46 ++++++++++++++++++++++++++++++++++++++ src/sys/vga/framebuffer.rs | 3 --- src/sys/vga/mod.rs | 3 ++- src/sys/vga/screen.rs | 4 +++- src/sys/vga/writer.rs | 4 +++- src/usr/install.rs | 1 + src/usr/render.rs | 13 ++++++----- 9 files changed, 73 insertions(+), 13 deletions(-) create mode 100644 src/sys/vga/buffer.rs delete mode 100644 src/sys/vga/framebuffer.rs diff --git a/src/api/fs.rs b/src/api/fs.rs index 8bcacd7e6..5af169a19 100644 --- a/src/api/fs.rs +++ b/src/api/fs.rs @@ -169,6 +169,7 @@ fn device_type(name: &str) -> Result { "vga-font" => Ok(DeviceType::VgaFont), "vga-mode" => Ok(DeviceType::VgaMode), "vga-palette" => Ok(DeviceType::VgaPalette), + "vga-buffer" => Ok(DeviceType::VgaBuffer), "ata" => Ok(DeviceType::Drive), _ => Err(()), } diff --git a/src/sys/fs/device.rs b/src/sys/fs/device.rs index 1097264de..6e2403b7c 100644 --- a/src/sys/fs/device.rs +++ b/src/sys/fs/device.rs @@ -10,7 +10,7 @@ use crate::sys::console::Console; use crate::sys::net::socket::tcp::TcpSocket; use crate::sys::net::socket::udp::UdpSocket; use crate::sys::rng::Random; -use crate::sys::vga::{VgaFont, VgaMode, VgaPalette}; +use crate::sys::vga::{VgaFont, VgaMode, VgaPalette, VgaBuffer}; use alloc::vec; use alloc::vec::Vec; @@ -33,6 +33,7 @@ pub enum DeviceType { VgaFont = 10, VgaMode = 11, VgaPalette = 12, + VgaBuffer = 13, } impl TryFrom<&[u8]> for DeviceType { @@ -53,6 +54,7 @@ impl TryFrom<&[u8]> for DeviceType { 10 => Ok(DeviceType::VgaFont), 11 => Ok(DeviceType::VgaMode), 12 => Ok(DeviceType::VgaPalette), + 13 => Ok(DeviceType::VgaBuffer), _ => Err(()), } } @@ -73,6 +75,7 @@ impl DeviceType { DeviceType::Drive => Drive::size(), DeviceType::VgaMode => VgaMode::size(), DeviceType::VgaPalette => VgaPalette::size(), + DeviceType::VgaBuffer => VgaBuffer::size(), _ => 1, }; let mut res = vec![0; len]; @@ -95,6 +98,7 @@ pub enum Device { VgaFont(VgaFont), VgaMode(VgaMode), VgaPalette(VgaPalette), + VgaBuffer(VgaBuffer), Drive(Drive), } @@ -115,6 +119,7 @@ impl TryFrom<&[u8]> for Device { DeviceType::VgaFont => Ok(Device::VgaFont(VgaFont::new())), DeviceType::VgaMode => Ok(Device::VgaMode(VgaMode::new())), DeviceType::VgaPalette => Ok(Device::VgaPalette(VgaPalette::new())), + DeviceType::VgaBuffer => Ok(Device::VgaBuffer(VgaBuffer::new())), DeviceType::Drive if buf.len() > 2 => { let bus = buf[1]; let dsk = buf[2]; @@ -176,6 +181,7 @@ impl FileIO for Device { Device::VgaFont(io) => io.read(buf), Device::VgaMode(io) => io.read(buf), Device::VgaPalette(io) => io.read(buf), + Device::VgaBuffer(io) => io.read(buf), Device::Drive(io) => io.read(buf), } } @@ -194,6 +200,7 @@ impl FileIO for Device { Device::VgaFont(io) => io.write(buf), Device::VgaMode(io) => io.write(buf), Device::VgaPalette(io) => io.write(buf), + Device::VgaBuffer(io) => io.write(buf), Device::Drive(io) => io.write(buf), } } @@ -212,6 +219,7 @@ impl FileIO for Device { Device::VgaFont(io) => io.close(), Device::VgaMode(io) => io.close(), Device::VgaPalette(io) => io.close(), + Device::VgaBuffer(io) => io.close(), Device::Drive(io) => io.close(), } } @@ -230,6 +238,7 @@ impl FileIO for Device { Device::VgaFont(io) => io.poll(event), Device::VgaMode(io) => io.poll(event), Device::VgaPalette(io) => io.poll(event), + Device::VgaBuffer(io) => io.poll(event), Device::Drive(io) => io.poll(event), } } diff --git a/src/sys/vga/buffer.rs b/src/sys/vga/buffer.rs new file mode 100644 index 000000000..4531d3ebd --- /dev/null +++ b/src/sys/vga/buffer.rs @@ -0,0 +1,46 @@ +use crate::api::fs::{FileIO, IO}; + +#[derive(Debug, Clone)] +pub struct Buffer; + +impl Buffer { + pub fn new() -> Self { + Self + } + + pub fn addr() -> u64 { + 0xA0000 + } + + pub fn size() -> usize { + 320 * 200 // FIXME + } +} + +impl FileIO for Buffer { + fn read(&mut self, _buf: &mut [u8]) -> Result { + Err(()) // TODO + } + + fn write(&mut self, buf: &[u8]) -> Result { + let len = buf.len(); + let src = buf.as_ptr(); + let dst = Self::addr() as *mut u8; + if Self::size() < len { + return Err(()); + } + unsafe { + core::ptr::copy_nonoverlapping(src, dst, len); + } + Ok(len) + } + + fn close(&mut self) {} + + fn poll(&mut self, event: IO) -> bool { + match event { + IO::Read => false, // TODO + IO::Write => false, // TODO + } + } +} diff --git a/src/sys/vga/framebuffer.rs b/src/sys/vga/framebuffer.rs deleted file mode 100644 index a0022e7e0..000000000 --- a/src/sys/vga/framebuffer.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub fn addr() -> u64 { - 0xA0000 -} diff --git a/src/sys/vga/mod.rs b/src/sys/vga/mod.rs index 7c4aae7e5..2e61a4707 100644 --- a/src/sys/vga/mod.rs +++ b/src/sys/vga/mod.rs @@ -1,6 +1,6 @@ mod color; mod font; -mod framebuffer; +mod buffer; mod palette; mod screen; mod writer; @@ -8,6 +8,7 @@ mod writer; pub use font::VgaFont; pub use screen::VgaMode; pub use palette::Palette as VgaPalette; +pub use buffer::Buffer as VgaBuffer; use color::Color; use palette::Palette; diff --git a/src/sys/vga/screen.rs b/src/sys/vga/screen.rs index 0ed4dfb32..023eed090 100644 --- a/src/sys/vga/screen.rs +++ b/src/sys/vga/screen.rs @@ -1,5 +1,7 @@ use super::*; +use buffer::Buffer; + use crate::api::fs::{FileIO, IO}; use spin::Mutex; @@ -180,7 +182,7 @@ fn clear_screen() { _ => return, }; let src = BUFFER.as_ptr(); - let dst = framebuffer::addr() as *mut u8; + let dst = Buffer::addr() as *mut u8; unsafe { core::ptr::copy_nonoverlapping(src, dst, size); } diff --git a/src/sys/vga/writer.rs b/src/sys/vga/writer.rs index e74581eb6..6b2add60e 100644 --- a/src/sys/vga/writer.rs +++ b/src/sys/vga/writer.rs @@ -1,5 +1,7 @@ use super::*; +use buffer::Buffer; + use crate::api::font::Font; use crate::sys; @@ -237,7 +239,7 @@ impl Writer { pub fn set_font(&mut self, font: &Font) { let mut sequencer: Port = Port::new(SEQUENCER_ADDR_REG); let mut graphics: Port = Port::new(GRAPHICS_ADDR_REG); - let buffer = framebuffer::addr() as *mut u8; + let buffer = Buffer::addr() as *mut u8; unsafe { sequencer.write(0x0100); // do a sync reset diff --git a/src/usr/install.rs b/src/usr/install.rs index a072da772..93a25d7d2 100644 --- a/src/usr/install.rs +++ b/src/usr/install.rs @@ -58,6 +58,7 @@ pub fn copy_files(verbose: bool) { create_dev("/dev/vga/font", "vga-font", verbose); create_dev("/dev/vga/mode", "vga-mode", verbose); create_dev("/dev/vga/palette", "vga-palette", verbose); + create_dev("/dev/vga/buffer", "vga-buffer", verbose); copy_file!("/ini/banner.txt", verbose); copy_file!("/ini/boot.sh", verbose); diff --git a/src/usr/render.rs b/src/usr/render.rs index 4c13c6ac8..46e3389b0 100644 --- a/src/usr/render.rs +++ b/src/usr/render.rs @@ -8,7 +8,6 @@ use alloc::vec::Vec; use alloc::string::{String, ToString}; use core::mem::size_of; -const FRAMEBUFFER: usize = 0xA0000; const WIDTH: usize = 320; const HEIGHT: usize = 200; @@ -167,15 +166,17 @@ fn render_bmp(path: &str, config: &mut Config) -> Result { } let dev = "/dev/vga/palette"; if !fs::is_device(dev) || fs::write(dev, &palette).is_err() { - error!("Could not set palette"); + config.text_mode(); + error!("Could not write to '{}'", dev); return Err(ExitCode::Failure); } // Display image - let src = img.as_ptr(); - let dst = FRAMEBUFFER as *mut u8; - unsafe { - core::ptr::copy_nonoverlapping(src, dst, size); + let dev = "/dev/vga/buffer"; + if !fs::is_device(dev) || fs::write(dev, &img).is_err() { + config.text_mode(); + error!("Could not write to '{}'", dev); + return Err(ExitCode::Failure); } Ok(read_command()) From 7660af09f7d01b95a395bc3f5abd3c14fcd74626 Mon Sep 17 00:00:00 2001 From: Vincent Ollivier Date: Tue, 15 Oct 2024 14:48:41 +0200 Subject: [PATCH 27/33] Reorder device files --- src/api/fs.rs | 2 +- src/sys/fs/device.rs | 30 +++++++++++++++--------------- src/sys/vga/buffer.rs | 4 ++-- src/usr/install.rs | 2 +- 4 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/api/fs.rs b/src/api/fs.rs index 5af169a19..0841f9002 100644 --- a/src/api/fs.rs +++ b/src/api/fs.rs @@ -166,10 +166,10 @@ fn device_type(name: &str) -> Result { "rtc" => Ok(DeviceType::RTC), "tcp" => Ok(DeviceType::TcpSocket), "udp" => Ok(DeviceType::UdpSocket), + "vga-buffer" => Ok(DeviceType::VgaBuffer), "vga-font" => Ok(DeviceType::VgaFont), "vga-mode" => Ok(DeviceType::VgaMode), "vga-palette" => Ok(DeviceType::VgaPalette), - "vga-buffer" => Ok(DeviceType::VgaBuffer), "ata" => Ok(DeviceType::Drive), _ => Err(()), } diff --git a/src/sys/fs/device.rs b/src/sys/fs/device.rs index 6e2403b7c..ee4197bf5 100644 --- a/src/sys/fs/device.rs +++ b/src/sys/fs/device.rs @@ -30,10 +30,10 @@ pub enum DeviceType { TcpSocket = 7, UdpSocket = 8, Drive = 9, - VgaFont = 10, - VgaMode = 11, - VgaPalette = 12, - VgaBuffer = 13, + VgaBuffer = 10, + VgaFont = 11, + VgaMode = 12, + VgaPalette = 13, } impl TryFrom<&[u8]> for DeviceType { @@ -51,10 +51,10 @@ impl TryFrom<&[u8]> for DeviceType { 7 => Ok(DeviceType::TcpSocket), 8 => Ok(DeviceType::UdpSocket), 9 => Ok(DeviceType::Drive), - 10 => Ok(DeviceType::VgaFont), - 11 => Ok(DeviceType::VgaMode), - 12 => Ok(DeviceType::VgaPalette), - 13 => Ok(DeviceType::VgaBuffer), + 10 => Ok(DeviceType::VgaBuffer), + 11 => Ok(DeviceType::VgaFont), + 12 => Ok(DeviceType::VgaMode), + 13 => Ok(DeviceType::VgaPalette), _ => Err(()), } } @@ -73,9 +73,9 @@ impl DeviceType { DeviceType::TcpSocket => TcpSocket::size(), DeviceType::UdpSocket => UdpSocket::size(), DeviceType::Drive => Drive::size(), + DeviceType::VgaBuffer => VgaBuffer::size(), DeviceType::VgaMode => VgaMode::size(), DeviceType::VgaPalette => VgaPalette::size(), - DeviceType::VgaBuffer => VgaBuffer::size(), _ => 1, }; let mut res = vec![0; len]; @@ -95,10 +95,10 @@ pub enum Device { RTC(RTC), TcpSocket(TcpSocket), UdpSocket(UdpSocket), + VgaBuffer(VgaBuffer), VgaFont(VgaFont), VgaMode(VgaMode), VgaPalette(VgaPalette), - VgaBuffer(VgaBuffer), Drive(Drive), } @@ -116,10 +116,10 @@ impl TryFrom<&[u8]> for Device { DeviceType::RTC => Ok(Device::RTC(RTC::new())), DeviceType::TcpSocket => Ok(Device::TcpSocket(TcpSocket::new())), DeviceType::UdpSocket => Ok(Device::UdpSocket(UdpSocket::new())), + DeviceType::VgaBuffer => Ok(Device::VgaBuffer(VgaBuffer::new())), DeviceType::VgaFont => Ok(Device::VgaFont(VgaFont::new())), DeviceType::VgaMode => Ok(Device::VgaMode(VgaMode::new())), DeviceType::VgaPalette => Ok(Device::VgaPalette(VgaPalette::new())), - DeviceType::VgaBuffer => Ok(Device::VgaBuffer(VgaBuffer::new())), DeviceType::Drive if buf.len() > 2 => { let bus = buf[1]; let dsk = buf[2]; @@ -178,10 +178,10 @@ impl FileIO for Device { Device::RTC(io) => io.read(buf), Device::TcpSocket(io) => io.read(buf), Device::UdpSocket(io) => io.read(buf), + Device::VgaBuffer(io) => io.read(buf), Device::VgaFont(io) => io.read(buf), Device::VgaMode(io) => io.read(buf), Device::VgaPalette(io) => io.read(buf), - Device::VgaBuffer(io) => io.read(buf), Device::Drive(io) => io.read(buf), } } @@ -197,10 +197,10 @@ impl FileIO for Device { Device::RTC(io) => io.write(buf), Device::TcpSocket(io) => io.write(buf), Device::UdpSocket(io) => io.write(buf), + Device::VgaBuffer(io) => io.write(buf), Device::VgaFont(io) => io.write(buf), Device::VgaMode(io) => io.write(buf), Device::VgaPalette(io) => io.write(buf), - Device::VgaBuffer(io) => io.write(buf), Device::Drive(io) => io.write(buf), } } @@ -216,10 +216,10 @@ impl FileIO for Device { Device::RTC(io) => io.close(), Device::TcpSocket(io) => io.close(), Device::UdpSocket(io) => io.close(), + Device::VgaBuffer(io) => io.close(), Device::VgaFont(io) => io.close(), Device::VgaMode(io) => io.close(), Device::VgaPalette(io) => io.close(), - Device::VgaBuffer(io) => io.close(), Device::Drive(io) => io.close(), } } @@ -235,10 +235,10 @@ impl FileIO for Device { Device::RTC(io) => io.poll(event), Device::TcpSocket(io) => io.poll(event), Device::UdpSocket(io) => io.poll(event), + Device::VgaBuffer(io) => io.poll(event), Device::VgaFont(io) => io.poll(event), Device::VgaMode(io) => io.poll(event), Device::VgaPalette(io) => io.poll(event), - Device::VgaBuffer(io) => io.poll(event), Device::Drive(io) => io.poll(event), } } diff --git a/src/sys/vga/buffer.rs b/src/sys/vga/buffer.rs index 4531d3ebd..25b20d03e 100644 --- a/src/sys/vga/buffer.rs +++ b/src/sys/vga/buffer.rs @@ -13,7 +13,7 @@ impl Buffer { } pub fn size() -> usize { - 320 * 200 // FIXME + 320 * 200 } } @@ -40,7 +40,7 @@ impl FileIO for Buffer { fn poll(&mut self, event: IO) -> bool { match event { IO::Read => false, // TODO - IO::Write => false, // TODO + IO::Write => true, } } } diff --git a/src/usr/install.rs b/src/usr/install.rs index 93a25d7d2..47c92ce8d 100644 --- a/src/usr/install.rs +++ b/src/usr/install.rs @@ -55,10 +55,10 @@ pub fn copy_files(verbose: bool) { create_dev("/dev/console", "console", verbose); create_dev("/dev/net/tcp", "tcp", verbose); create_dev("/dev/net/udp", "udp", verbose); + create_dev("/dev/vga/buffer", "vga-buffer", verbose); create_dev("/dev/vga/font", "vga-font", verbose); create_dev("/dev/vga/mode", "vga-mode", verbose); create_dev("/dev/vga/palette", "vga-palette", verbose); - create_dev("/dev/vga/buffer", "vga-buffer", verbose); copy_file!("/ini/banner.txt", verbose); copy_file!("/ini/boot.sh", verbose); From a34cfa2178b4a3d6969a47bcc409f003dc84d4cf Mon Sep 17 00:00:00 2001 From: Vincent Ollivier Date: Tue, 15 Oct 2024 15:04:57 +0200 Subject: [PATCH 28/33] Rename some functions --- src/sys/vga/font.rs | 6 +++--- src/sys/vga/mod.rs | 2 +- src/sys/vga/palette.rs | 22 ++++++++++------------ 3 files changed, 14 insertions(+), 16 deletions(-) diff --git a/src/sys/vga/font.rs b/src/sys/vga/font.rs index 6fcb35240..64895878d 100644 --- a/src/sys/vga/font.rs +++ b/src/sys/vga/font.rs @@ -26,7 +26,7 @@ impl FileIO for VgaFont { fn write(&mut self, buf: &[u8]) -> Result { if let Ok(font) = Font::try_from(buf) { *FONT.lock() = Some(font.clone()); - set_font(&font); + write_font(&font); Ok(buf.len()) // TODO: Use font.data.len() ? } else { Err(()) @@ -43,7 +43,7 @@ impl FileIO for VgaFont { } } -pub fn set_font(font: &Font) { +fn write_font(font: &Font) { interrupts::without_interrupts(|| WRITER.lock().set_font(font) ) @@ -51,6 +51,6 @@ pub fn set_font(font: &Font) { pub fn restore_font() { if let Some(ref font) = *FONT.lock() { - set_font(font); + write_font(font); } } diff --git a/src/sys/vga/mod.rs b/src/sys/vga/mod.rs index 2e61a4707..f47c069df 100644 --- a/src/sys/vga/mod.rs +++ b/src/sys/vga/mod.rs @@ -196,7 +196,7 @@ pub fn init() { set_attr_ctrl_reg(0xE, 0x3E); set_attr_ctrl_reg(0xF, 0x3F); - Palette::default().set(); + Palette::default().write(); disable_blinking(); disable_underline(); diff --git a/src/sys/vga/palette.rs b/src/sys/vga/palette.rs index 0ff71ae60..1df106d1e 100644 --- a/src/sys/vga/palette.rs +++ b/src/sys/vga/palette.rs @@ -45,17 +45,17 @@ impl Palette { palette } - pub fn get() -> Self { + pub fn read() -> Self { let mut palette = Palette::new(); for i in 0..256 { - palette.colors[i] = get_palette(i); + palette.colors[i] = read_palette(i); } palette } - pub fn set(&self) { + pub fn write(&self) { for (i, (r, g, b)) in self.colors.iter().enumerate() { - set_palette(i, *r, *g, *b); + write_palette(i, *r, *g, *b); } } @@ -92,7 +92,7 @@ impl TryFrom<&[u8]> for Palette { impl FileIO for VgaPalette { fn read(&mut self, buf: &mut [u8]) -> Result { - let res = Palette::get().to_bytes(); + let res = Palette::read().to_bytes(); if buf.len() < res.len() { return Err(()); } @@ -102,7 +102,7 @@ impl FileIO for VgaPalette { fn write(&mut self, buf: &[u8]) -> Result { let palette = Palette::try_from(buf)?; - palette.set(); + palette.write(); Ok(buf.len()) } @@ -116,15 +116,13 @@ impl FileIO for VgaPalette { } } -// TODO: Rename to `write_palette_index` -fn set_palette(i: usize, r: u8, g: u8, b: u8) { +fn write_palette(i: usize, r: u8, g: u8, b: u8) { interrupts::without_interrupts(|| WRITER.lock().set_palette(i, r, g, b) ) } -// TODO: Rename to `read_palette_index` -fn get_palette(i: usize) -> (u8, u8, u8) { +fn read_palette(i: usize) -> (u8, u8, u8) { interrupts::without_interrupts(|| WRITER.lock().palette(i) ) @@ -132,10 +130,10 @@ fn get_palette(i: usize) -> (u8, u8, u8) { pub fn restore_palette() { if let Some(ref palette) = *PALETTE.lock() { - palette.set(); + palette.write(); } } pub fn backup_palette() { - *PALETTE.lock() = Some(Palette::get()) + *PALETTE.lock() = Some(Palette::read()) } From bd9ed9250e214733228a6cdafe4023033eb151c3 Mon Sep 17 00:00:00 2001 From: Vincent Ollivier Date: Tue, 15 Oct 2024 22:16:07 +0200 Subject: [PATCH 29/33] Move test --- src/sys/vga/mod.rs | 8 -------- src/sys/vga/writer.rs | 8 ++++++++ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/sys/vga/mod.rs b/src/sys/vga/mod.rs index f47c069df..bcb204af5 100644 --- a/src/sys/vga/mod.rs +++ b/src/sys/vga/mod.rs @@ -203,11 +203,3 @@ pub fn init() { WRITER.lock().clear_screen(); } - -#[test_case] -fn test_parse_palette() { - assert_eq!(parse_palette("P0282828"), Ok((0, 0x28, 0x28, 0x28))); - assert_eq!(parse_palette("P4CC241D"), Ok((4, 0xCC, 0x24, 0x1D))); - assert!(parse_palette("BAAAAAAD").is_ok()); - assert!(parse_palette("GOOOOOOD").is_err()); -} diff --git a/src/sys/vga/writer.rs b/src/sys/vga/writer.rs index 6b2add60e..562939e8a 100644 --- a/src/sys/vga/writer.rs +++ b/src/sys/vga/writer.rs @@ -513,3 +513,11 @@ impl fmt::Write for Writer { Ok(()) } } + +#[test_case] +fn test_parse_palette() { + assert_eq!(parse_palette("P0282828"), Ok((0, 0x28, 0x28, 0x28))); + assert_eq!(parse_palette("P4CC241D"), Ok((4, 0xCC, 0x24, 0x1D))); + assert!(parse_palette("BAAAAAAD").is_ok()); + assert!(parse_palette("GOOOOOOD").is_err()); +} From e429639f9f1ac74271be6415a7fa379051aa547d Mon Sep 17 00:00:00 2001 From: Vincent Ollivier Date: Tue, 15 Oct 2024 22:16:16 +0200 Subject: [PATCH 30/33] Run clippy --- src/usr/render.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/usr/render.rs b/src/usr/render.rs index 46e3389b0..cc295afe4 100644 --- a/src/usr/render.rs +++ b/src/usr/render.rs @@ -74,7 +74,7 @@ fn parse_bmp(data: &[u8]) -> Result { let pixels = data[pixels_offset..].to_vec(); let width = dib_header.width as u32; - let height = dib_header.height.abs() as u32; + let height = dib_header.height.unsigned_abs(); if pixels.len() != (width * height) as usize { return Err("Invalid BMP file: wrong pixels count".to_string()); } @@ -130,7 +130,7 @@ impl Config { } fn render_bmp(path: &str, config: &mut Config) -> Result { - if let Ok(buf) = fs::read_to_bytes(&path) { + if let Ok(buf) = fs::read_to_bytes(path) { if let Ok(bmp) = parse_bmp(&buf) { let width = bmp.width as usize; let height = bmp.height as usize; @@ -150,7 +150,7 @@ fn render_bmp(path: &str, config: &mut Config) -> Result { // BMP stores images bottom-up let bmp_y = height - 1 - y; - let i = (bmp_y * (width + row_padding) + x) as usize; + let i = bmp_y * (width + row_padding) + x; img.push(bmp.pixels[i]); } } From 43ed03da7e7794e7e7e99bbdb96e6a3b6261b1ab Mon Sep 17 00:00:00 2001 From: Vincent Ollivier Date: Tue, 15 Oct 2024 22:21:13 +0200 Subject: [PATCH 31/33] Add color test --- src/sys/vga/color.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/sys/vga/color.rs b/src/sys/vga/color.rs index f4b43a51d..48cbc02e9 100644 --- a/src/sys/vga/color.rs +++ b/src/sys/vga/color.rs @@ -87,3 +87,10 @@ impl Color { } } } + +#[test_case] +fn test_color() { + assert_eq!(Color::from_index(6), Color::DarkYellow); + assert_eq!(Color::from_ansi(33), Color::DarkYellow); + assert_eq!(Color::DarkYellow.register(), 0x14); +} From 5971a69dacb8241737cd82a898bcefc9a6a19093 Mon Sep 17 00:00:00 2001 From: Vincent Ollivier Date: Tue, 15 Oct 2024 22:29:54 +0200 Subject: [PATCH 32/33] Refactor code --- src/sys/vga/mod.rs | 64 ------------------------------------------- src/sys/vga/writer.rs | 60 ++++++++++++++++++++++++++++++++++------ 2 files changed, 52 insertions(+), 72 deletions(-) diff --git a/src/sys/vga/mod.rs b/src/sys/vga/mod.rs index bcb204af5..59d3dae39 100644 --- a/src/sys/vga/mod.rs +++ b/src/sys/vga/mod.rs @@ -39,58 +39,6 @@ const CRTC_DATA_REG: u16 = 0x3D5; const INPUT_STATUS_REG: u16 = 0x3DA; const INSTAT_READ_REG: u16 = 0x3DA; -const FG: Color = Color::DarkWhite; -const BG: Color = Color::DarkBlack; -const UNPRINTABLE: u8 = 0x00; // Unprintable chars will be replaced by this one - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -#[repr(transparent)] -struct ColorCode(u8); - -impl ColorCode { - fn new(foreground: Color, background: Color) -> ColorCode { - ColorCode((background as u8) << 4 | (foreground as u8)) - } -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -#[repr(C)] -struct ScreenChar { - ascii_code: u8, - color_code: ColorCode, -} - -impl ScreenChar { - fn new() -> Self { - Self { - ascii_code: b' ', - color_code: ColorCode::new(FG, BG), - } - } -} - -const SCREEN_WIDTH: usize = 80; -const SCREEN_HEIGHT: usize = 25; -const SCROLL_HEIGHT: usize = 250; - -#[repr(transparent)] -struct ScreenBuffer { - chars: [[ScreenChar; SCREEN_WIDTH]; SCREEN_HEIGHT], -} - -// TODO: Remove this -fn parse_palette(palette: &str) -> Result<(usize, u8, u8, u8), ParseIntError> { - debug_assert!(palette.len() == 8); - debug_assert!(palette.starts_with('P')); - - let i = usize::from_str_radix(&palette[1..2], 16)?; - let r = u8::from_str_radix(&palette[2..4], 16)?; - let g = u8::from_str_radix(&palette[4..6], 16)?; - let b = u8::from_str_radix(&palette[6..8], 16)?; - - Ok((i, r, g, b)) -} - #[doc(hidden)] pub fn print_fmt(args: fmt::Arguments) { interrupts::without_interrupts(|| @@ -98,18 +46,6 @@ pub fn print_fmt(args: fmt::Arguments) { ) } -pub fn color() -> (Color, Color) { - interrupts::without_interrupts(|| - WRITER.lock().color() - ) -} - -pub fn set_color(foreground: Color, background: Color) { - interrupts::without_interrupts(|| - WRITER.lock().set_color(foreground, background) - ) -} - // ASCII Printable // Backspace // New Line diff --git a/src/sys/vga/writer.rs b/src/sys/vga/writer.rs index 562939e8a..727b6518c 100644 --- a/src/sys/vga/writer.rs +++ b/src/sys/vga/writer.rs @@ -10,6 +10,45 @@ use lazy_static::lazy_static; use spin::Mutex; use vte::{Params, Parser, Perform}; +const FG: Color = Color::DarkWhite; +const BG: Color = Color::DarkBlack; +const UNPRINTABLE: u8 = 0x00; // Unprintable chars will be replaced by this one + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(transparent)] +struct ColorCode(u8); + +impl ColorCode { + fn new(foreground: Color, background: Color) -> ColorCode { + ColorCode((background as u8) << 4 | (foreground as u8)) + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(C)] +struct ScreenChar { + ascii_code: u8, + color_code: ColorCode, +} + +impl ScreenChar { + fn new() -> Self { + Self { + ascii_code: b' ', + color_code: ColorCode::new(FG, BG), + } + } +} + +const SCREEN_WIDTH: usize = 80; +const SCREEN_HEIGHT: usize = 25; +const SCROLL_HEIGHT: usize = 250; + +#[repr(transparent)] +struct ScreenBuffer { + chars: [[ScreenChar; SCREEN_WIDTH]; SCREEN_HEIGHT], +} + lazy_static! { pub static ref PARSER: Mutex = Mutex::new(Parser::new()); pub static ref WRITER: Mutex = Mutex::new(Writer { @@ -224,17 +263,10 @@ impl Writer { } } - pub fn set_color(&mut self, foreground: Color, background: Color) { + fn set_color(&mut self, foreground: Color, background: Color) { self.color_code = ColorCode::new(foreground, background); } - pub fn color(&self) -> (Color, Color) { - let cc = self.color_code.0; - let fg = Color::from_index(cc.get_bits(0..4) as usize); - let bg = Color::from_index(cc.get_bits(4..8) as usize); - (fg, bg) - } - // Source: https://slideplayer.com/slide/3888880 pub fn set_font(&mut self, font: &Font) { let mut sequencer: Port = Port::new(SEQUENCER_ADDR_REG); @@ -514,6 +546,18 @@ impl fmt::Write for Writer { } } +fn parse_palette(palette: &str) -> Result<(usize, u8, u8, u8), ParseIntError> { + debug_assert!(palette.len() == 8); + debug_assert!(palette.starts_with('P')); + + let i = usize::from_str_radix(&palette[1..2], 16)?; + let r = u8::from_str_radix(&palette[2..4], 16)?; + let g = u8::from_str_radix(&palette[4..6], 16)?; + let b = u8::from_str_radix(&palette[6..8], 16)?; + + Ok((i, r, g, b)) +} + #[test_case] fn test_parse_palette() { assert_eq!(parse_palette("P0282828"), Ok((0, 0x28, 0x28, 0x28))); From 07bbab0292d4e61b94557797fda830952a54b4d4 Mon Sep 17 00:00:00 2001 From: Vincent Ollivier Date: Tue, 15 Oct 2024 23:21:04 +0200 Subject: [PATCH 33/33] Move blank to userspace --- Makefile | 1 + dsk/var/pkg/blank | 1 + dsk/var/pkg/index.html | 1 + src/{usr => bin}/blank.rs | 17 ++++++++++++----- src/usr/mod.rs | 1 - src/usr/shell.rs | 1 - 6 files changed, 15 insertions(+), 7 deletions(-) create mode 100644 dsk/var/pkg/blank rename src/{usr => bin}/blank.rs (51%) diff --git a/Makefile b/Makefile index 9fc4f9f12..41bb07a97 100644 --- a/Makefile +++ b/Makefile @@ -49,6 +49,7 @@ user-rust: basename -s .rs src/bin/*.rs | xargs -I {} \ cargo rustc $(user-cargo-opts) --bin {} cargo rustc $(user-cargo-opts) --bin exec -- $(linker-opts) + cargo rustc $(user-cargo-opts) --bin blank -- $(linker-opts) cargo rustc $(user-cargo-opts) --bin hello -- $(linker-opts) cargo rustc $(user-cargo-opts) --bin geocal -- $(linker-opts) cargo rustc $(user-cargo-opts) --bin geodate -- $(linker-opts) diff --git a/dsk/var/pkg/blank b/dsk/var/pkg/blank new file mode 100644 index 000000000..8e36ec5c1 --- /dev/null +++ b/dsk/var/pkg/blank @@ -0,0 +1 @@ +/bin/blank diff --git a/dsk/var/pkg/index.html b/dsk/var/pkg/index.html index 12159e471..17d5d57b2 100644 --- a/dsk/var/pkg/index.html +++ b/dsk/var/pkg/index.html @@ -1,4 +1,5 @@ beep +blank chess exec fonts diff --git a/src/usr/blank.rs b/src/bin/blank.rs similarity index 51% rename from src/usr/blank.rs rename to src/bin/blank.rs index a91c15af1..1f452c321 100644 --- a/src/usr/blank.rs +++ b/src/bin/blank.rs @@ -1,13 +1,20 @@ -use crate::api::io; -use crate::api::process::ExitCode; -use crate::api::vga; +#![no_std] +#![no_main] -pub fn main(_args: &[&str]) -> Result<(), ExitCode> { +extern crate alloc; + +use moros::print; +use moros::api::io; +use moros::api::vga; +use moros::entry_point; + +entry_point!(main); + +fn main(_args: &[&str]) { vga::graphic_mode(); print!("\x1b]R\x1b[1A"); // Reset palette while io::stdin().read_char().is_none() { x86_64::instructions::hlt(); } vga::text_mode(); - Ok(()) } diff --git a/src/usr/mod.rs b/src/usr/mod.rs index 45d02c6f5..cba745ec6 100644 --- a/src/usr/mod.rs +++ b/src/usr/mod.rs @@ -1,5 +1,4 @@ pub mod beep; -pub mod blank; pub mod calc; pub mod chess; pub mod copy; diff --git a/src/usr/shell.rs b/src/usr/shell.rs index 163962686..9bf27023b 100644 --- a/src/usr/shell.rs +++ b/src/usr/shell.rs @@ -517,7 +517,6 @@ fn dispatch(args: &[&str], config: &mut Config) -> Result<(), ExitCode> { "2048" => usr::pow::main(args), "alias" => cmd_alias(args, config), "beep" => usr::beep::main(args), - "blank" => usr::blank::main(args), "calc" => usr::calc::main(args), "chess" => usr::chess::main(args), "copy" => usr::copy::main(args),