diff --git a/src/configurations.rs b/src/configurations.rs index bd35e1d..5c01298 100644 --- a/src/configurations.rs +++ b/src/configurations.rs @@ -249,7 +249,7 @@ pub const MODE_640X480X16_CONFIGURATION: VgaConfiguration = VgaConfiguration { sequencer_registers: &[ (SequencerIndex::SequencerReset, 0x03), (SequencerIndex::ClockingMode, 0x01), - (SequencerIndex::PlaneMask, 0x08), + (SequencerIndex::PlaneMask, 0x0F), (SequencerIndex::CharacterFont, 0x00), (SequencerIndex::MemoryMode, 0x06), ], @@ -286,7 +286,7 @@ pub const MODE_640X480X16_CONFIGURATION: VgaConfiguration = VgaConfiguration { (GraphicsControllerIndex::ColorCompare, 0x00), (GraphicsControllerIndex::DataRotate, 0x00), (GraphicsControllerIndex::ReadPlaneSelect, 0x03), - (GraphicsControllerIndex::GraphicsMode, 0x00), + (GraphicsControllerIndex::GraphicsMode, 0x02), (GraphicsControllerIndex::Miscellaneous, 0x05), (GraphicsControllerIndex::ColorDontCare, 0x0F), (GraphicsControllerIndex::BitMask, 0xFF), diff --git a/src/drawing/device.rs b/src/drawing/device.rs index d74d1ef..ebde02d 100644 --- a/src/drawing/device.rs +++ b/src/drawing/device.rs @@ -8,12 +8,12 @@ where Color: Clone + Copy, { /// Draws a character at the given `(x, y)` coordinant to the specified `color`. - fn draw_character(&self, x: usize, y: usize, character: char, color: Color); + fn draw_character(&mut self, x: usize, y: usize, character: char, color: Color); /// Draws a line from `start` to `end` with the specified `color`. - fn draw_line(&self, start: Point, end: Point, color: Color); + fn draw_line(&mut self, start: Point, end: Point, color: Color); - fn draw_triangle(&self, v0: &Point, v1: &Point, v2: &Point, color: Color) { + fn draw_triangle(&mut self, v0: &Point, v1: &Point, v2: &Point, color: Color) { let screen_width = self.get_width() as i32; let screen_height = self.get_height() as i32; let mut min_x = min(v0.x, min(v1.x, v2.x)); @@ -39,6 +39,8 @@ where } } } + + fn present(&self); } #[inline] diff --git a/src/lib.rs b/src/lib.rs index 06b9ff5..5c486e8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,6 +7,8 @@ #![no_std] #![warn(missing_docs)] +extern crate alloc; + pub mod colors; pub mod configurations; pub mod drawing; diff --git a/src/registers/general.rs b/src/registers/general.rs index e6392c5..78da603 100644 --- a/src/registers/general.rs +++ b/src/registers/general.rs @@ -1,6 +1,7 @@ use super::{ - FCR_CGA_WRITE_ADDRESS, FCR_MDA_WRITE_ADDRESS, FCR_READ_ADDRESS, MSR_READ_ADDRESS, - MSR_WRITE_ADDRESS, ST00_READ_ADDRESS, ST01_READ_CGA_ADDRESS, ST01_READ_MDA_ADDRESS, + EmulationMode, FCR_CGA_WRITE_ADDRESS, FCR_MDA_WRITE_ADDRESS, FCR_READ_ADDRESS, + MSR_READ_ADDRESS, MSR_WRITE_ADDRESS, ST00_READ_ADDRESS, ST01_READ_CGA_ADDRESS, + ST01_READ_MDA_ADDRESS, }; use x86_64::instructions::port::{PortReadOnly, PortWriteOnly}; @@ -42,4 +43,11 @@ impl GeneralRegisters { self.msr_write.write(value); } } + + pub fn read_st01(&mut self, emulation_mode: EmulationMode) -> u8 { + match emulation_mode { + EmulationMode::Cga => unsafe { self.st01_read_cga.read() }, + EmulationMode::Mda => unsafe { self.st01_read_mda.read() }, + } + } } diff --git a/src/writers/graphics_320x200x256.rs b/src/writers/graphics_320x200x256.rs index e5b1a37..ce2444c 100644 --- a/src/writers/graphics_320x200x256.rs +++ b/src/writers/graphics_320x200x256.rs @@ -2,10 +2,11 @@ use super::{GraphicsWriter, Screen}; use crate::{ colors::DEFAULT_PALETTE, drawing::{Bresenham, Device, Point}, - vga::{Vga, VideoMode, VGA}, + vga::{VideoMode, VGA}, }; +use alloc::vec::Vec; +use core::ptr; use font8x8::UnicodeFonts; -use spinning_top::SpinlockGuard; const WIDTH: usize = 320; const HEIGHT: usize = 200; @@ -34,7 +35,9 @@ const SIZE: usize = WIDTH * HEIGHT; /// } /// ``` #[derive(Default)] -pub struct Graphics320x200x256 {} +pub struct Graphics320x200x256 { + screen_buffer: Vec, +} impl Screen for Graphics320x200x256 { #[inline] @@ -54,7 +57,7 @@ impl Screen for Graphics320x200x256 { } impl Device for Graphics320x200x256 { - fn draw_character(&self, x: usize, y: usize, character: char, color: u8) { + fn draw_character(&mut self, x: usize, y: usize, character: char, color: u8) { let character = match font8x8::BASIC_FONTS.get(character) { Some(character) => character, // Default to a filled block if the character isn't found @@ -71,28 +74,41 @@ impl Device for Graphics320x200x256 { } } - fn draw_line(&self, start: Point, end: Point, color: u8) { + fn draw_line(&mut self, start: Point, end: Point, color: u8) { for Point { x, y } in Bresenham::new(start, end) { self.set_pixel(x as usize, y as usize, color); } } + + fn present(&self) { + { + let mut vga = VGA.lock(); + let emulation_mode = vga.get_emulation_mode(); + while vga.general_registers.read_st01(emulation_mode) & 0x3 != 0 {} + } + unsafe { + ptr::copy_nonoverlapping( + self.screen_buffer.as_ptr(), + self.get_frame_buffer(), + self.screen_buffer.len(), + ); + } + } } impl GraphicsWriter for Graphics320x200x256 { - fn clear_screen(&self, color: u8) { - for x in 0..WIDTH { - for y in 0..HEIGHT { - self.set_pixel(x, y, color); - } - } - } - fn set_pixel(&self, x: usize, y: usize, color: u8) { - let frame_buffer = self.get_frame_buffer(); - let offset = (y * WIDTH) + x; + fn clear_screen(&mut self, color: u8) { unsafe { - frame_buffer.add(offset).write_volatile(color); + self.screen_buffer + .as_mut_ptr() + .write_bytes(color, self.screen_buffer.len()); } } + + fn set_pixel(&mut self, x: usize, y: usize, color: u8) { + self.screen_buffer[(y * WIDTH) + x] = color; + } + fn set_mode(&self) { let mut vga = VGA.lock(); vga.set_video_mode(VideoMode::Mode320x200x256); @@ -106,7 +122,11 @@ impl GraphicsWriter for Graphics320x200x256 { impl Graphics320x200x256 { /// Creates a new `Graphics320x200x256`. pub fn new() -> Graphics320x200x256 { - Graphics320x200x256 {} + let mut screen_buffer = Vec::with_capacity(SIZE); + for _ in 0..SIZE { + screen_buffer.push(0); + } + Graphics320x200x256 { screen_buffer } } fn get_frame_buffer(&self) -> *mut u8 { diff --git a/src/writers/graphics_640x480x16.rs b/src/writers/graphics_640x480x16.rs index 3d5a7ca..d52091d 100644 --- a/src/writers/graphics_640x480x16.rs +++ b/src/writers/graphics_640x480x16.rs @@ -2,16 +2,14 @@ use super::{GraphicsWriter, Screen}; use crate::{ colors::{Color16, DEFAULT_PALETTE}, drawing::{Bresenham, Device, Point}, - registers::{PlaneMask, WriteMode}, - vga::{Vga, VideoMode, VGA}, + vga::{VideoMode, VGA}, }; +use alloc::vec::Vec; use font8x8::UnicodeFonts; -use spinning_top::SpinlockGuard; const WIDTH: usize = 640; const HEIGHT: usize = 480; const SIZE: usize = WIDTH * HEIGHT; -const ALL_PLANES_SCREEN_SIZE: usize = (WIDTH * HEIGHT) / 8; const WIDTH_IN_BYTES: usize = WIDTH / 8; /// A basic interface for interacting with vga graphics mode 640x480x16 @@ -37,7 +35,9 @@ const WIDTH_IN_BYTES: usize = WIDTH / 8; /// } /// ``` #[derive(Default)] -pub struct Graphics640x480x16; +pub struct Graphics640x480x16 { + screen_buffer: Vec, +} impl Screen for Graphics640x480x16 { fn get_width(&self) -> usize { @@ -52,8 +52,7 @@ impl Screen for Graphics640x480x16 { } impl Device for Graphics640x480x16 { - fn draw_character(&self, x: usize, y: usize, character: char, color: Color16) { - self.set_write_mode_2(); + fn draw_character(&mut self, x: usize, y: usize, character: char, color: Color16) { let character = match font8x8::BASIC_FONTS.get(character) { Some(character) => character, // Default to a filled block if the character isn't found @@ -64,38 +63,44 @@ impl Device for Graphics640x480x16 { for bit in 0..8 { match *byte & 1 << bit { 0 => (), - _ => self._set_pixel(x + bit, y + row, color), + _ => self.set_pixel(x + bit, y + row, color), } } } } - fn draw_line(&self, start: Point, end: Point, color: Color16) { - self.set_write_mode_0(color); + fn draw_line(&mut self, start: Point, end: Point, color: Color16) { for Point { x, y } in Bresenham::new(start, end) { - self._set_pixel(x as usize, y as usize, color); + self.set_pixel(x as usize, y as usize, color); + } + } + + fn present(&self) { + { + let mut vga = VGA.lock(); + let emulation_mode = vga.get_emulation_mode(); + while vga.general_registers.read_st01(emulation_mode) & 0x3 != 0 {} + } + for x in 0..WIDTH { + for y in 0..HEIGHT { + let color = self.screen_buffer[(WIDTH * y) + x]; + self._set_pixel(x, y, color); + } } } } impl GraphicsWriter for Graphics640x480x16 { - fn clear_screen(&self, color: Color16) { - self.set_write_mode_2(); - let frame_buffer = self.get_frame_buffer(); - for offset in 0..ALL_PLANES_SCREEN_SIZE { - unsafe { - frame_buffer.add(offset).write_volatile(u8::from(color)); + fn clear_screen(&mut self, color: Color16) { + for x in 0..WIDTH { + for y in 0..HEIGHT { + self.set_pixel(x, y, color); } } } - /// **Note:** This method is provided for convenience, but has terrible - /// performance since it needs to ensure the correct `WriteMode` per pixel - /// drawn. If you need to draw more then one pixel, consider using a method - /// such as `draw_line`. - fn set_pixel(&self, x: usize, y: usize, color: Color16) { - self.set_write_mode_2(); - self._set_pixel(x, y, color); + fn set_pixel(&mut self, x: usize, y: usize, color: Color16) { + self.screen_buffer[(WIDTH * y) + x] = color as u8; } fn set_mode(&self) { @@ -111,25 +116,11 @@ impl GraphicsWriter for Graphics640x480x16 { impl Graphics640x480x16 { /// Creates a new `Graphics640x480x16`. pub fn new() -> Graphics640x480x16 { - Graphics640x480x16 {} - } - - fn set_write_mode_0(&self, color: Color16) { - let mut vga = VGA.lock(); - vga.graphics_controller_registers.write_set_reset(color); - vga.graphics_controller_registers - .write_enable_set_reset(0xF); - vga.graphics_controller_registers - .set_write_mode(WriteMode::Mode0); - } - - fn set_write_mode_2(&self) { - let mut vga = VGA.lock(); - vga.graphics_controller_registers - .set_write_mode(WriteMode::Mode2); - vga.graphics_controller_registers.set_bit_mask(0xFF); - vga.sequencer_registers - .set_plane_mask(PlaneMask::ALL_PLANES); + let mut screen_buffer = Vec::with_capacity(SIZE); + for _ in 0..SIZE { + screen_buffer.push(0); + } + Graphics640x480x16 { screen_buffer } } fn get_frame_buffer(&self) -> *mut u8 { @@ -137,7 +128,7 @@ impl Graphics640x480x16 { } #[inline] - fn _set_pixel(&self, x: usize, y: usize, color: Color16) { + fn _set_pixel(&self, x: usize, y: usize, color: u8) { let frame_buffer = self.get_frame_buffer(); let offset = x / 8 + y * WIDTH_IN_BYTES; let pixel_mask = 0x80 >> (x & 0x07); @@ -146,7 +137,7 @@ impl Graphics640x480x16 { .set_bit_mask(pixel_mask); unsafe { frame_buffer.add(offset).read_volatile(); - frame_buffer.add(offset).write_volatile(u8::from(color)); + frame_buffer.add(offset).write_volatile(color); } } } diff --git a/src/writers/mod.rs b/src/writers/mod.rs index 0dbec0d..5a707b8 100644 --- a/src/writers/mod.rs +++ b/src/writers/mod.rs @@ -7,7 +7,6 @@ mod text_80x25; use super::{ colors::{Color16, TextModeColor}, - drawing::Point, registers::CrtcControllerIndex, vga::{Vga, VGA}, }; @@ -184,9 +183,9 @@ pub trait TextWriter: Screen { /// A helper trait used to interact with various vga graphics modes. pub trait GraphicsWriter { /// Clears the screen by setting all pixels to the specified `color`. - fn clear_screen(&self, color: Color); + fn clear_screen(&mut self, color: Color); /// Sets the given pixel at `(x, y)` to the given `color`. - fn set_pixel(&self, x: usize, y: usize, color: Color); + fn set_pixel(&mut self, x: usize, y: usize, color: Color); /// Sets the graphics device to a `VideoMode`. fn set_mode(&self); }