//! 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(())
|
|
}
|
|
}
|