diff --git a/src/writers/mod.rs b/src/writers/mod.rs index 258ed6c..5e36f6e 100644 --- a/src/writers/mod.rs +++ b/src/writers/mod.rs @@ -4,16 +4,112 @@ mod text_40x25; mod text_40x50; mod text_80x25; -use super::colors::TextModeColor; +use super::{ + colors::{Color16Bit, TextModeColor}, + registers::CrtcControllerIndex, + vga::{Vga, VGA}, +}; +use spinning_top::SpinlockGuard; pub use graphics_640x480x16::Graphics640x480x16; pub use text_40x25::Text40x25; pub use text_40x50::Text40x50; pub use text_80x25::Text80x25; +/// Represents a `ScreenCharacter` in vga text modes. #[derive(Debug, Copy, Clone)] #[repr(C)] -struct ScreenCharacter { +pub struct ScreenCharacter { character: u8, color: TextModeColor, } + +impl ScreenCharacter { + /// Creates a new `ScreenCharacter` with the specified `character` + /// and a `TextModeColor` with the specified `foreground` and `background`. + pub fn new(character: u8, foreground: Color16Bit, background: Color16Bit) -> ScreenCharacter { + let color = TextModeColor::new(foreground, background); + ScreenCharacter { character, color } + } +} + +static BLANK_CHARACTER: ScreenCharacter = ScreenCharacter { + character: b' ', + color: TextModeColor::new(Color16Bit::Yellow, Color16Bit::Black), +}; + +/// A helper trait used to interact with various vga text modes. +pub trait TextWriter { + /// Returns the width of the `TextWriter`. + fn get_width(&self) -> usize; + + /// Returns the height of the `TextWriter`. + fn get_height(&self) -> usize; + + /// Sets the graphics device to a video mode as determined by + /// the `TextWriter` implementation. + fn set_mode(&self); + + /// Returns the start of the `FrameBuffer` as `*mut ScreenCharacter` + /// as well as a lock to the vga driver. This ensures the vga + /// driver stays locked while the frame buffer is in use. + fn get_frame_buffer(&self) -> (SpinlockGuard, *mut ScreenCharacter) { + let mut vga = VGA.lock(); + let frame_buffer = vga.get_frame_buffer(); + (vga, u32::from(frame_buffer) as *mut ScreenCharacter) + } + + /// Clears the screen by setting all cells to `b' '` with + /// a background color of `Color16Bit::Black` and a foreground + /// color of `Color16Bit::Yellow`. + fn clear_screen(&self) { + let (_vga, frame_buffer) = self.get_frame_buffer(); + let screen_size = self.get_width() * self.get_height(); + for i in 0..screen_size { + unsafe { + frame_buffer.add(i).write_volatile(BLANK_CHARACTER); + } + } + } + + /// Sets the current text cursor to the position specified by + /// `x` and `y`. + /// + /// Panics if `x >= se.lf.get_width()` or `y >= self.get_height()`. + fn set_cursor_position(&self, x: usize, y: usize) { + let width = self.get_width(); + let height = self.get_height(); + assert!(x < width, "x >= {}", width); + assert!(y < height, "y >= {}", height); + let offset = width * y + x; + let (mut vga, _frame_buffer) = self.get_frame_buffer(); + let emulation_mode = vga.get_emulation_mode(); + let cursor_start = offset & 0xFF; + let cursor_end = (offset >> 8) & 0xFF; + vga.write_crtc_controller( + emulation_mode, + CrtcControllerIndex::TextCursorLocationLow, + cursor_start as u8, + ); + vga.write_crtc_controller( + emulation_mode, + CrtcControllerIndex::TextCursorLocationHigh, + cursor_end as u8, + ); + } + + /// Prints the given `character` and `color` at `(x, y)`. + /// + /// Panics if `x >= self.get_width()` or `y >= self.get_height()`. + fn write_character(&self, x: usize, y: usize, screen_character: ScreenCharacter) { + let width = self.get_width(); + let height = self.get_height(); + assert!(x < width, "x >= {}", width); + assert!(y < height, "y >= {}", height); + let (_vga, frame_buffer) = self.get_frame_buffer(); + let offset = width * y + x; + unsafe { + frame_buffer.add(offset).write_volatile(screen_character); + } + } +} diff --git a/src/writers/text_40x25.rs b/src/writers/text_40x25.rs index 88781e4..9ff796c 100644 --- a/src/writers/text_40x25.rs +++ b/src/writers/text_40x25.rs @@ -1,20 +1,12 @@ -use super::ScreenCharacter; +use super::TextWriter; use crate::{ - colors::{Color16Bit, TextModeColor, DEFAULT_PALETTE}, + colors::DEFAULT_PALETTE, fonts::TEXT_8X16_FONT, - registers::CrtcControllerIndex, - vga::{Vga, VideoMode, VGA}, + vga::{VideoMode, VGA}, }; -use spinning_top::SpinlockGuard; const WIDTH: usize = 40; const HEIGHT: usize = 25; -const SCREEN_SIZE: usize = WIDTH * HEIGHT; - -static BLANK_CHARACTER: ScreenCharacter = ScreenCharacter { - character: b' ', - color: TextModeColor::new(Color16Bit::Yellow, Color16Bit::Black), -}; /// A basic interface for interacting with vga text mode 40x25 /// @@ -23,7 +15,7 @@ static BLANK_CHARACTER: ScreenCharacter = ScreenCharacter { /// Basic usage: /// /// ```no_run -/// use vga::writers::Text40x25; +/// use vga::writers::{TextWriter, Text40x25}; /// /// let text_mode = Text40x25::new(); /// @@ -33,41 +25,17 @@ static BLANK_CHARACTER: ScreenCharacter = ScreenCharacter { #[derive(Default)] pub struct Text40x25; -impl Text40x25 { - /// Creates a new `Text40x25`. - pub fn new() -> Text40x25 { - Text40x25 {} +impl TextWriter for Text40x25 { + fn get_width(&self) -> usize { + WIDTH } - /// Clears the screen by setting all cells to `b' '` with - /// a background color of `Color16Bit::Black` and a foreground - /// color of `Color16Bit::Yellow`. - pub fn clear_screen(&self) { - let (_vga, frame_buffer) = self.get_frame_buffer(); - for i in 0..SCREEN_SIZE { - unsafe { - frame_buffer.add(i).write_volatile(BLANK_CHARACTER); - } - } - } - - /// Prints the given `character` and `color` at `(x, y)`. - /// - /// Panics if `x >= 40` or `y >= 25`. - pub fn write_character(&self, x: usize, y: usize, character: u8, color: TextModeColor) { - assert!(x < WIDTH, "x >= {}", WIDTH); - assert!(y < HEIGHT, "y >= {}", HEIGHT); - let (_vga, frame_buffer) = self.get_frame_buffer(); - let offset = WIDTH * y + x; - unsafe { - frame_buffer - .add(offset) - .write_volatile(ScreenCharacter { character, color }); - } + fn get_height(&self) -> usize { + HEIGHT } /// Sets the graphics device to `VideoMode::Mode40x25`. - pub fn set_mode(&self) { + fn set_mode(&self) { let mut vga = VGA.lock(); vga.set_video_mode(VideoMode::Mode40x25); @@ -76,37 +44,11 @@ impl Text40x25 { vga.load_palette(&DEFAULT_PALETTE); vga.load_font(&TEXT_8X16_FONT); } +} - /// Sets the current text cursor to the position specified by - /// `x` and `y`. - /// - /// Panics if `x >= 40` or `y >= 25`. - pub fn set_cursor_position(&self, x: usize, y: usize) { - assert!(x < WIDTH, "x >= {}", WIDTH); - assert!(y < HEIGHT, "y >= {}", HEIGHT); - let offset = WIDTH * y + x; - let (mut vga, _frame_buffer) = self.get_frame_buffer(); - let emulation_mode = vga.get_emulation_mode(); - let cursor_start = offset & 0xFF; - let cursor_end = (offset >> 8) & 0xFF; - vga.write_crtc_controller( - emulation_mode, - CrtcControllerIndex::TextCursorLocationLow, - cursor_start as u8, - ); - vga.write_crtc_controller( - emulation_mode, - CrtcControllerIndex::TextCursorLocationHigh, - cursor_end as u8, - ); - } - - /// Returns the start of the `FrameBuffer` as `*mut ScreenCharacter` - /// as well as a lock to the vga driver. This ensures the vga - /// driver stays locked while the frame buffer is in use. - fn get_frame_buffer(&self) -> (SpinlockGuard, *mut ScreenCharacter) { - let mut vga = VGA.lock(); - let frame_buffer = vga.get_frame_buffer(); - (vga, u32::from(frame_buffer) as *mut ScreenCharacter) +impl Text40x25 { + /// Creates a new `Text40x25`. + pub fn new() -> Text40x25 { + Text40x25 {} } } diff --git a/src/writers/text_40x50.rs b/src/writers/text_40x50.rs index 6f62895..67078dc 100644 --- a/src/writers/text_40x50.rs +++ b/src/writers/text_40x50.rs @@ -1,20 +1,12 @@ -use super::ScreenCharacter; +use super::TextWriter; use crate::{ - colors::{Color16Bit, TextModeColor, DEFAULT_PALETTE}, + colors::DEFAULT_PALETTE, fonts::TEXT_8X8_FONT, - registers::CrtcControllerIndex, - vga::{Vga, VideoMode, VGA}, + vga::{VideoMode, VGA}, }; -use spinning_top::SpinlockGuard; const WIDTH: usize = 40; const HEIGHT: usize = 50; -const SCREEN_SIZE: usize = WIDTH * HEIGHT; - -static BLANK_CHARACTER: ScreenCharacter = ScreenCharacter { - character: b' ', - color: TextModeColor::new(Color16Bit::Yellow, Color16Bit::Black), -}; /// A basic interface for interacting with vga text mode 40x50 /// @@ -23,7 +15,7 @@ static BLANK_CHARACTER: ScreenCharacter = ScreenCharacter { /// Basic usage: /// /// ```no_run -/// use vga::writers::Text40x50; +/// use vga::writers::{TextWriter, Text40x50}; /// /// let text_mode = Text40x50::new(); /// @@ -33,41 +25,17 @@ static BLANK_CHARACTER: ScreenCharacter = ScreenCharacter { #[derive(Default)] pub struct Text40x50; -impl Text40x50 { - /// Creates a new `Text40x50`. - pub fn new() -> Text40x50 { - Text40x50 {} +impl TextWriter for Text40x50 { + fn get_width(&self) -> usize { + WIDTH } - /// Clears the screen by setting all cells to `b' '` with - /// a background color of `Color16Bit::Black` and a foreground - /// color of `Color16Bit::Yellow`. - pub fn clear_screen(&self) { - let (_vga, frame_buffer) = self.get_frame_buffer(); - for i in 0..SCREEN_SIZE { - unsafe { - frame_buffer.add(i).write_volatile(BLANK_CHARACTER); - } - } - } - - /// Prints the given `character` and `color` at `(x, y)`. - /// - /// Panics if `x >= 40` or `y >= 50`. - pub fn write_character(&self, x: usize, y: usize, character: u8, color: TextModeColor) { - assert!(x < WIDTH, "x >= {}", WIDTH); - assert!(y < HEIGHT, "y >= {}", HEIGHT); - let (_vga, frame_buffer) = self.get_frame_buffer(); - let offset = WIDTH * y + x; - unsafe { - frame_buffer - .add(offset) - .write_volatile(ScreenCharacter { character, color }); - } + fn get_height(&self) -> usize { + HEIGHT } /// Sets the graphics device to `VideoMode::Mode40x50`. - pub fn set_mode(&self) { + fn set_mode(&self) { let mut vga = VGA.lock(); vga.set_video_mode(VideoMode::Mode40x50); @@ -76,37 +44,11 @@ impl Text40x50 { vga.load_palette(&DEFAULT_PALETTE); vga.load_font(&TEXT_8X8_FONT); } +} - /// Sets the current text cursor to the position specified by - /// `x` and `y`. - /// - /// Panics if `x >= 40` or `y >= 50`. - pub fn set_cursor_position(&self, x: usize, y: usize) { - assert!(x < WIDTH, "x >= {}", WIDTH); - assert!(y < HEIGHT, "y >= {}", HEIGHT); - let offset = WIDTH * y + x; - let (mut vga, _frame_buffer) = self.get_frame_buffer(); - let emulation_mode = vga.get_emulation_mode(); - let cursor_start = offset & 0xFF; - let cursor_end = (offset >> 8) & 0xFF; - vga.write_crtc_controller( - emulation_mode, - CrtcControllerIndex::TextCursorLocationLow, - cursor_start as u8, - ); - vga.write_crtc_controller( - emulation_mode, - CrtcControllerIndex::TextCursorLocationHigh, - cursor_end as u8, - ); - } - - /// Returns the start of the `FrameBuffer` as `*mut ScreenCharacter` - /// as well as a lock to the vga driver. This ensures the vga - /// driver stays locked while the frame buffer is in use. - fn get_frame_buffer(&self) -> (SpinlockGuard, *mut ScreenCharacter) { - let mut vga = VGA.lock(); - let frame_buffer = vga.get_frame_buffer(); - (vga, u32::from(frame_buffer) as *mut ScreenCharacter) +impl Text40x50 { + /// Creates a new `Text40x50`. + pub fn new() -> Text40x50 { + Text40x50 {} } } diff --git a/src/writers/text_80x25.rs b/src/writers/text_80x25.rs index 63a533d..c8936e9 100644 --- a/src/writers/text_80x25.rs +++ b/src/writers/text_80x25.rs @@ -1,20 +1,12 @@ -use super::ScreenCharacter; +use super::TextWriter; use crate::{ - colors::{Color16Bit, TextModeColor, DEFAULT_PALETTE}, + colors::DEFAULT_PALETTE, fonts::TEXT_8X16_FONT, - registers::CrtcControllerIndex, - vga::{Vga, VideoMode, VGA}, + vga::{VideoMode, VGA}, }; -use spinning_top::SpinlockGuard; const WIDTH: usize = 80; const HEIGHT: usize = 25; -const SCREEN_SIZE: usize = WIDTH * HEIGHT; - -static BLANK_CHARACTER: ScreenCharacter = ScreenCharacter { - character: b' ', - color: TextModeColor::new(Color16Bit::Yellow, Color16Bit::Black), -}; /// A basic interface for interacting with vga text mode 80x25 /// @@ -23,7 +15,7 @@ static BLANK_CHARACTER: ScreenCharacter = ScreenCharacter { /// Basic usage: /// /// ```no_run -/// use vga::writers::Text80x25; +/// use vga::writers::{TextWriter, Text80x25}; /// /// let text_mode = Text80x25::new(); /// @@ -33,41 +25,16 @@ static BLANK_CHARACTER: ScreenCharacter = ScreenCharacter { #[derive(Default)] pub struct Text80x25; -impl Text80x25 { - /// Creates a new `Text80x25`. - pub fn new() -> Text80x25 { - Text80x25 {} +impl TextWriter for Text80x25 { + fn get_width(&self) -> usize { + WIDTH } - /// Clears the screen by setting all cells to `b' '` with - /// a background color of `Color16Bit::Black` and a foreground - /// color of `Color16Bit::Yellow`. - pub fn clear_screen(&self) { - let (_vga, frame_buffer) = self.get_frame_buffer(); - for i in 0..SCREEN_SIZE { - unsafe { - frame_buffer.add(i).write_volatile(BLANK_CHARACTER); - } - } + fn get_height(&self) -> usize { + HEIGHT } - /// Prints the given `character` and `color` at `(x, y)`. - /// - /// Panics if `x >= 80` or `y >= 25`. - pub fn write_character(&self, x: usize, y: usize, character: u8, color: TextModeColor) { - assert!(x < WIDTH, "x >= {}", WIDTH); - assert!(y < HEIGHT, "y >= {}", HEIGHT); - let (_vga, frame_buffer) = self.get_frame_buffer(); - let offset = WIDTH * y + x; - unsafe { - frame_buffer - .add(offset) - .write_volatile(ScreenCharacter { character, color }); - } - } - - /// Sets the graphics device to `VideoMode::Mode80x25`. - pub fn set_mode(&self) { + fn set_mode(&self) { let mut vga = VGA.lock(); vga.set_video_mode(VideoMode::Mode80x25); @@ -76,37 +43,11 @@ impl Text80x25 { vga.load_palette(&DEFAULT_PALETTE); vga.load_font(&TEXT_8X16_FONT); } +} - /// Sets the current text cursor to the position specified by - /// `x` and `y`. - /// - /// Panics if `x >= 80` or `y >= 25`. - pub fn set_cursor_position(&self, x: usize, y: usize) { - assert!(x < WIDTH, "x >= {}", WIDTH); - assert!(y < HEIGHT, "y >= {}", HEIGHT); - let offset = WIDTH * y + x; - let (mut vga, _frame_buffer) = self.get_frame_buffer(); - let emulation_mode = vga.get_emulation_mode(); - let cursor_start = offset & 0xFF; - let cursor_end = (offset >> 8) & 0xFF; - vga.write_crtc_controller( - emulation_mode, - CrtcControllerIndex::TextCursorLocationLow, - cursor_start as u8, - ); - vga.write_crtc_controller( - emulation_mode, - CrtcControllerIndex::TextCursorLocationHigh, - cursor_end as u8, - ); - } - - /// Returns the start of the `FrameBuffer` as `*mut ScreenCharacter` - /// as well as a lock to the vga driver. This ensures the vga - /// driver stays locked while the frame buffer is in use. - fn get_frame_buffer(&self) -> (SpinlockGuard, *mut ScreenCharacter) { - let mut vga = VGA.lock(); - let frame_buffer = vga.get_frame_buffer(); - (vga, u32::from(frame_buffer) as *mut ScreenCharacter) +impl Text80x25 { + /// Creates a new `Text80x25`. + pub fn new() -> Text80x25 { + Text80x25 {} } }