|
|
- //! A basic console driver, using the VGA text-mode buffer.
-
- use core::fmt;
- use core::ptr::Unique;
- use spin::Mutex;
- use volatile::Volatile;
- use x86::shared::io;
-
- pub static WRITER: Mutex<Writer> = Mutex::new(Writer {
- column_pos: 0, row_pos: 0,
- style: CharStyle::new(Color::Cyan, Color::DarkGray),
- buffer: unsafe {Unique::new_unchecked(0xb8000 as *mut _)},
- });
-
- #[allow(dead_code)]
- #[derive(Clone, Copy, Debug)]
- #[repr(u8)]
- pub enum Color {
- Black = 0,
- Blue = 1,
- Green = 2,
- Cyan = 3,
- Red = 4,
- Magenta = 5,
- Brown = 6,
- LightGray = 7,
- DarkGray = 8,
- LightBlue = 9,
- LightGreen = 10,
- LightCyan = 11,
- LightRed = 12,
- Pink = 13,
- Yellow = 14,
- White = 15,
- }
-
- /// Foreground + background color pair for a VGA console cell.
- #[derive(Clone, Copy, Debug)]
- pub struct CharStyle(u8);
- impl CharStyle {
- pub const fn new(foreground: Color, background: Color) -> CharStyle {
- CharStyle((background as u8) << 4 | (foreground as u8))
- }
- }
-
- /// Character/style pair.
- #[derive(Clone, Copy, Debug)]
- #[repr(C)]
- struct VgaChar {
- ascii_char: u8,
- style: CharStyle,
- }
-
- const BUFFER_HEIGHT: usize = 25;
- const BUFFER_WIDTH: usize = 80;
-
- /// Shadows the actual VGA text-mode buffer at `0xb8000`.
- struct Buffer {
- chars: [[Volatile<VgaChar>; BUFFER_WIDTH]; BUFFER_HEIGHT],
- }
-
- /// Console-oriented abstraction layer used to write into the VGA text buffer.
- /// Maintains a cursor and current style.
- pub struct Writer {
- /// Current position of the 'write cursor'
- row_pos: usize, column_pos: usize,
- /// Current style we're writing with
- style: CharStyle,
- /// This is set up on initialization to shadow `0xb8000`, the VGA text-mode buffer.
- buffer: Unique<Buffer>,
- }
-
- #[allow(dead_code)]
- impl Writer {
- /// Write a single byte into the VGA buffer at the cursor location.
- /// Increments the cursor location and wraps to the next line if necessary.
- pub fn write_byte(&mut self, byte: u8) {
- match byte {
- b'\n' => self.new_line(),
- byte => {
- if self.column_pos >= BUFFER_WIDTH {self.new_line();}
-
- let row = self.row_pos;
- let col = self.column_pos;
- let style = self.style;
-
- self.buffer().chars[row][col].write(VgaChar {
- ascii_char: byte,
- style: style,
- });
-
- self.column_pos += 1;
- }
- }
- }
-
- /// Write a single byte at (row, col).
- pub fn write_byte_at(&mut self, byte: u8, row: usize, col: usize) {
- self.row_pos = row; self.column_pos = col;
- let style = self.style;
-
- self.buffer().chars[row][col].write(VgaChar {ascii_char: byte, style: style});
- }
-
- /// Clear the VGA buffer.
- pub fn clear_screen(&mut self) {
- let clear_style = VgaChar {ascii_char: b' ', style: self.style};
- for row in 0..BUFFER_HEIGHT {
- for col in 0..BUFFER_WIDTH {
- self.buffer().chars[row][col].write(clear_style);
- }
- }
- }
-
- pub fn set_style(&mut self, style: CharStyle) {
- self.style = style;
- }
-
- pub fn get_style(&self) -> CharStyle {
- self.style
- }
-
- /// Move the _console cursor_ (blinky bar) to (row, col).
- fn move_cursor(&mut self, row: usize, col: usize) {
- let pos: u16 = ((row * 80) + col) as u16;
- // Lovingly ripped off from wiki.osdev.org/Text_Mode_Cursor
- unsafe {
- io::outb(0x3d4, 0x0F);
- io::outb(0x3d5, (pos & 0xff) as u8);
-
- io::outb(0x3d4, 0x0e);
- io::outb(0x3d5, ((pos>>8) & 0xff) as u8);
- }
- }
-
- /// Move the internal cursor to a new line, scrolling if necessary.
- fn new_line(&mut self) {
- self.column_pos = 0;
- self.row_pos += 1;
-
- if self.row_pos >= BUFFER_HEIGHT {
- self.scroll();
- }
- }
-
- /// Clear a `row` of the VGA buffer.
- fn clear_row(&mut self, row: usize) {
- let clear_style = VgaChar {ascii_char: b' ', style: self.style};
- for col in 0..BUFFER_WIDTH {
- self.buffer().chars[row][col].write(clear_style);
- }
- }
-
- /// Scroll the buffer up by one row.
- fn scroll(&mut self) {
- for row in 0..(BUFFER_HEIGHT - 1) {
- for col in 0..BUFFER_WIDTH {
- let c = self.buffer().chars[row + 1][col].read();
- self.buffer().chars[row][col].write(c);
- }
- }
-
- self.clear_row(BUFFER_HEIGHT - 1);
- self.column_pos = 0;
- self.row_pos = BUFFER_HEIGHT - 1;
- }
-
- /// Get the underlying buffer.
- fn buffer(&mut self) -> &mut Buffer {
- unsafe { self.buffer.as_mut() }
- }
- }
-
- impl fmt::Write for Writer {
- fn write_str(&mut self, s: &str) -> fmt::Result {
- for byte in s.bytes() {
- self.write_byte(byte);
- }
-
- let row = self.row_pos;
- let col = self.column_pos;
- self.move_cursor(row, col);
-
- Ok(())
- }
- }
|