You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

186 lines
5.0 KiB

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