From 5ea088b659d6d824b9629abdc1b312b0d1ec0e9b Mon Sep 17 00:00:00 2001 From: Ryan Kennedy Date: Thu, 12 Mar 2020 12:34:13 -0500 Subject: [PATCH] Added writers and cleanup --- src/{vga_colors.rs => colors.rs} | 0 ...ga_configurations.rs => configurations.rs} | 2 +- src/{vga_fonts.rs => fonts.rs} | 0 src/lib.rs | 10 ++- src/{vga_registers.rs => registers.rs} | 0 src/vga.rs | 6 +- src/writers/graphics_640x480x16.rs | 82 +++++++++++++++++++ src/writers/mod.rs | 18 ++++ src/writers/text_40x25.rs | 78 ++++++++++++++++++ src/writers/text_40x50.rs | 78 ++++++++++++++++++ src/writers/text_80x25.rs | 78 ++++++++++++++++++ 11 files changed, 344 insertions(+), 8 deletions(-) rename src/{vga_colors.rs => colors.rs} (100%) rename src/{vga_configurations.rs => configurations.rs} (99%) rename src/{vga_fonts.rs => fonts.rs} (100%) rename src/{vga_registers.rs => registers.rs} (100%) create mode 100644 src/writers/graphics_640x480x16.rs create mode 100644 src/writers/mod.rs create mode 100644 src/writers/text_40x25.rs create mode 100644 src/writers/text_40x50.rs create mode 100644 src/writers/text_80x25.rs diff --git a/src/vga_colors.rs b/src/colors.rs similarity index 100% rename from src/vga_colors.rs rename to src/colors.rs diff --git a/src/vga_configurations.rs b/src/configurations.rs similarity index 99% rename from src/vga_configurations.rs rename to src/configurations.rs index fbefcd8..0ad2c70 100644 --- a/src/vga_configurations.rs +++ b/src/configurations.rs @@ -1,4 +1,4 @@ -use super::vga_registers::{ +use super::registers::{ AttributeControllerIndex, CrtcControllerIndex, GraphicsControllerIndex, SequencerIndex, }; diff --git a/src/vga_fonts.rs b/src/fonts.rs similarity index 100% rename from src/vga_fonts.rs rename to src/fonts.rs diff --git a/src/lib.rs b/src/lib.rs index 06db024..6fbba27 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,13 +4,15 @@ #![no_std] #![warn(missing_docs)] +pub mod colors; +mod configurations; +mod fonts; +mod registers; pub mod vga; -pub mod vga_colors; -mod vga_configurations; -mod vga_fonts; -mod vga_registers; +mod writers; pub use self::vga::VGA; +pub use self::writers::{Graphics640x480x16, Text40x25, Text40x50, Text80x25}; #[cfg(test)] mod tests { diff --git a/src/vga_registers.rs b/src/registers.rs similarity index 100% rename from src/vga_registers.rs rename to src/registers.rs diff --git a/src/vga.rs b/src/vga.rs index 9ba4b70..2ff6126 100644 --- a/src/vga.rs +++ b/src/vga.rs @@ -1,12 +1,12 @@ //! Provides access to the vga graphics card. use super::{ - vga_configurations::{ + configurations::{ VgaConfiguration, MODE_40X25_CONFIGURATION, MODE_40X50_CONFIGURATION, MODE_640X480X16_CONFIGURATION, MODE_80X25_CONFIGURATION, }, - vga_fonts::{VgaFont, TEXT_8X16_FONT, TEXT_8X8_FONT}, - vga_registers::{ + fonts::{VgaFont, TEXT_8X16_FONT, TEXT_8X8_FONT}, + registers::{ AttributeControllerRegisters, CrtcControllerIndex, CrtcControllerRegisters, EmulationMode, GeneralRegisters, GraphicsControllerIndex, GraphicsControllerRegisters, SequencerIndex, SequencerRegisters, diff --git a/src/writers/graphics_640x480x16.rs b/src/writers/graphics_640x480x16.rs new file mode 100644 index 0000000..5a200f3 --- /dev/null +++ b/src/writers/graphics_640x480x16.rs @@ -0,0 +1,82 @@ +use crate::{ + colors::Color16Bit, + vga::{Plane, Vga, VideoMode, VGA}, +}; +use spinning_top::SpinlockGuard; + +const WIDTH: usize = 640; +const HEIGHT: usize = 480; + +static PLANES: &'static [Plane] = &[Plane::Plane0, Plane::Plane1, Plane::Plane2, Plane::Plane3]; + +/// A basic interface for interacting with vga graphics mode 640x480x16 +/// +/// # Examples +/// +/// Basic usage: +/// +/// ``` +/// let graphics_mode = Graphics640x480x16::new(); +/// graphics_mode.set_mode(); +/// graphics_mode.clear_screen(); +/// ``` +pub struct Graphics640x480x16; + +impl Graphics640x480x16 { + /// Creates a new `Graphics640x480x16`. + pub fn new() -> Graphics640x480x16 { + Graphics640x480x16 {} + } + + /// Clears the screen by setting all pixels to `Color16Bit::Black`. + pub fn clear_screen(&self) { + for x in 0..WIDTH { + for y in 0..HEIGHT { + self.set_pixel(x, y, Color16Bit::Yellow); + } + } + } + + /// Sets the given pixel at `(x, y)` to the given `color`. + /// + /// Panics if `x >= 640` or `y >= 480`. + pub fn set_pixel(&self, x: usize, y: usize, color: Color16Bit) { + assert!(x < WIDTH, "x >= {}", WIDTH); + assert!(y < HEIGHT, "y >= {}", HEIGHT); + let (mut vga, frame_buffer) = self.get_frame_buffer(); + let offset = (x / 8 + (WIDTH / 8) * y) as isize; + + // Store the current value for masking. + let x = x & 7; + let mask = 0x80 >> (x & 7); + let mut plane_mask = 0x01; + + for plane in PLANES { + vga.set_plane(*plane); + let current_value = unsafe { frame_buffer.offset(offset).read_volatile() }; + let new_value = if plane_mask & color as u8 != 0 { + current_value | mask + } else { + current_value & !mask + }; + unsafe { + frame_buffer.offset(offset).write_volatile(new_value); + } + plane_mask <<= 1; + } + } + + /// Sets the graphics device to `VideoMode::Mode640x480x16`. + pub fn set_mode(&self) { + VGA.lock().set_video_mode(VideoMode::Mode640x480x16); + } + + /// Returns the start of the `FrameBuffer` as `*mut u8` 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 u8) { + let mut vga = VGA.lock(); + let frame_buffer = vga.get_frame_buffer(); + (vga, u32::from(frame_buffer) as *mut u8) + } +} diff --git a/src/writers/mod.rs b/src/writers/mod.rs new file mode 100644 index 0000000..5a319df --- /dev/null +++ b/src/writers/mod.rs @@ -0,0 +1,18 @@ +mod graphics_640x480x16; +mod text_40x25; +mod text_40x50; +mod text_80x25; + +use super::colors::TextModeColor; + +pub use graphics_640x480x16::Graphics640x480x16; +pub use text_40x25::Text40x25; +pub use text_40x50::Text40x50; +pub use text_80x25::Text80x25; + +#[derive(Debug, Copy, Clone)] +#[repr(C)] +struct ScreenCharacter { + character: u8, + color: TextModeColor, +} diff --git a/src/writers/text_40x25.rs b/src/writers/text_40x25.rs new file mode 100644 index 0000000..703d328 --- /dev/null +++ b/src/writers/text_40x25.rs @@ -0,0 +1,78 @@ +use super::ScreenCharacter; +use crate::{ + colors::{Color16Bit, TextModeColor}, + 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 +/// +/// # Examples +/// +/// Basic usage: +/// +/// ``` +/// let text_mode = Text40x25::new(); +/// text_mode.set_mode(); +/// text_mode.clear_screen(); +/// ``` +pub struct Text40x25; + +impl Text40x25 { + /// Creates a new `Text40x25`. + pub fn new() -> Text40x25 { + Text40x25 {} + } + + /// 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 + .offset(i as isize) + .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) as isize; + unsafe { + frame_buffer + .offset(offset) + .write_volatile(ScreenCharacter { character, color }); + } + } + + /// Sets the graphics device to `VideoMode::Mode40x25`. + pub fn set_mode(&self) { + VGA.lock().set_video_mode(VideoMode::Mode40x25); + } + + /// 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) + } +} diff --git a/src/writers/text_40x50.rs b/src/writers/text_40x50.rs new file mode 100644 index 0000000..c60ae58 --- /dev/null +++ b/src/writers/text_40x50.rs @@ -0,0 +1,78 @@ +use super::ScreenCharacter; +use crate::{ + colors::{Color16Bit, TextModeColor}, + 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 +/// +/// # Examples +/// +/// Basic usage: +/// +/// ``` +/// let text_mode = Text40x50::new(); +/// text_mode.set_mode(); +/// text_mode.clear_screen(); +/// ``` +pub struct Text40x50; + +impl Text40x50 { + /// Creates a new `Text40x50`. + pub fn new() -> Text40x50 { + Text40x50 {} + } + + /// 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 + .offset(i as isize) + .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) as isize; + unsafe { + frame_buffer + .offset(offset) + .write_volatile(ScreenCharacter { character, color }); + } + } + + /// Sets the graphics device to `VideoMode::Mode40x50`. + pub fn set_mode(&self) { + VGA.lock().set_video_mode(VideoMode::Mode40x50); + } + + /// 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) + } +} diff --git a/src/writers/text_80x25.rs b/src/writers/text_80x25.rs new file mode 100644 index 0000000..b3edf61 --- /dev/null +++ b/src/writers/text_80x25.rs @@ -0,0 +1,78 @@ +use super::ScreenCharacter; +use crate::{ + colors::{Color16Bit, TextModeColor}, + 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 +/// +/// # Examples +/// +/// Basic usage: +/// +/// ``` +/// let text_mode = Text80x25::new(); +/// text_mode.set_mode(); +/// text_mode.clear_screen(); +/// ``` +pub struct Text80x25; + +impl Text80x25 { + /// Creates a new `Text80x25`. + pub fn new() -> Text80x25 { + Text80x25 {} + } + + /// 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 + .offset(i as isize) + .write_volatile(BLANK_CHARACTER); + } + } + } + + /// 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) as isize; + unsafe { + frame_buffer + .offset(offset) + .write_volatile(ScreenCharacter { character, color }); + } + } + + /// Sets the graphics device to `VideoMode::Mode80x25`. + pub fn set_mode(&self) { + VGA.lock().set_video_mode(VideoMode::Mode80x25); + } + + /// 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) + } +}