From 22029851009333405735a3cede15eac06cb7b31a Mon Sep 17 00:00:00 2001 From: Ryan Kennedy Date: Wed, 25 Mar 2020 01:13:53 -0500 Subject: [PATCH 1/4] Initial 320x200x256 mode was way easier --- src/configurations.rs | 74 ++++++++++++++++++++++ src/vga.rs | 13 +++- src/writers/graphics_320x200x256.rs | 95 +++++++++++++++++++++++++++++ src/writers/mod.rs | 2 + 4 files changed, 182 insertions(+), 2 deletions(-) create mode 100644 src/writers/graphics_320x200x256.rs diff --git a/src/configurations.rs b/src/configurations.rs index 20d0397..bd35e1d 100644 --- a/src/configurations.rs +++ b/src/configurations.rs @@ -315,3 +315,77 @@ pub const MODE_640X480X16_CONFIGURATION: VgaConfiguration = VgaConfiguration { (AttributeControllerIndex::ColorSelect, 0x00), ], }; + +/// Register values for Vga mode 640x480x16 Graphics. +pub const MODE_320X200X256_CONFIGURATION: VgaConfiguration = VgaConfiguration { + // Configuration values acquired from https://www.singlix.com/trdos/archive/vga/Graphics%20in%20pmode.pdf + miscellaneous_output: 0x63, + sequencer_registers: &[ + (SequencerIndex::SequencerReset, 0x03), + (SequencerIndex::ClockingMode, 0x01), + (SequencerIndex::PlaneMask, 0x0F), + (SequencerIndex::CharacterFont, 0x00), + (SequencerIndex::MemoryMode, 0x0E), + ], + crtc_controller_registers: &[ + (CrtcControllerIndex::HorizontalTotal, 0x5F), + (CrtcControllerIndex::HorizontalDisplayEnableEnd, 0x4F), + (CrtcControllerIndex::HorizontalBlankingStart, 0x50), + (CrtcControllerIndex::HorizontalBlankingEnd, 0x82), + (CrtcControllerIndex::HorizontalSyncStart, 0x54), + (CrtcControllerIndex::HorizontalSyncEnd, 0x80), + (CrtcControllerIndex::VeritcalTotal, 0xBF), + (CrtcControllerIndex::Overflow, 0x1F), + (CrtcControllerIndex::PresetRowScan, 0x00), + (CrtcControllerIndex::MaximumScanLine, 0x41), + (CrtcControllerIndex::TextCursorStart, 0x00), + (CrtcControllerIndex::TextCursorEnd, 0x00), + (CrtcControllerIndex::StartAddressHigh, 0x00), + (CrtcControllerIndex::StartAddressLow, 0x00), + (CrtcControllerIndex::TextCursorLocationHigh, 0x00), + (CrtcControllerIndex::TextCursorLocationLow, 0x00), + (CrtcControllerIndex::VerticalSyncStart, 0x9C), + (CrtcControllerIndex::VerticalSyncEnd, 0x0E), + (CrtcControllerIndex::VerticalDisplayEnableEnd, 0x8F), + (CrtcControllerIndex::Offset, 0x28), + (CrtcControllerIndex::UnderlineLocation, 0x40), + (CrtcControllerIndex::VerticalBlankingStart, 0x96), + (CrtcControllerIndex::VerticalBlankingEnd, 0xB9), + (CrtcControllerIndex::ModeControl, 0xA3), + (CrtcControllerIndex::LineCompare, 0xFF), + ], + graphics_controller_registers: &[ + (GraphicsControllerIndex::SetReset, 0x00), + (GraphicsControllerIndex::EnableSetReset, 0x00), + (GraphicsControllerIndex::ColorCompare, 0x00), + (GraphicsControllerIndex::DataRotate, 0x00), + (GraphicsControllerIndex::ReadPlaneSelect, 0x00), + (GraphicsControllerIndex::GraphicsMode, 0x40), + (GraphicsControllerIndex::Miscellaneous, 0x05), + (GraphicsControllerIndex::ColorDontCare, 0x0F), + (GraphicsControllerIndex::BitMask, 0xFF), + ], + attribute_controller_registers: &[ + (AttributeControllerIndex::PaletteRegister0, 0x00), + (AttributeControllerIndex::PaletteRegister1, 0x01), + (AttributeControllerIndex::PaletteRegister2, 0x02), + (AttributeControllerIndex::PaletteRegister3, 0x03), + (AttributeControllerIndex::PaletteRegister4, 0x04), + (AttributeControllerIndex::PaletteRegister5, 0x05), + (AttributeControllerIndex::PaletteRegister6, 0x06), + (AttributeControllerIndex::PaletteRegister7, 0x07), + (AttributeControllerIndex::PaletteRegister8, 0x08), + (AttributeControllerIndex::PaletteRegister9, 0x09), + (AttributeControllerIndex::PaletteRegisterA, 0x0A), + (AttributeControllerIndex::PaletteRegisterB, 0x0B), + (AttributeControllerIndex::PaletteRegisterC, 0x0C), + (AttributeControllerIndex::PaletteRegisterD, 0x0D), + (AttributeControllerIndex::PaletteRegisterE, 0x0E), + (AttributeControllerIndex::PaletteRegisterF, 0x0F), + (AttributeControllerIndex::ModeControl, 0x41), + (AttributeControllerIndex::OverscanColor, 0x00), + (AttributeControllerIndex::MemoryPlaneEnable, 0x0F), + (AttributeControllerIndex::HorizontalPixelPanning, 0x00), + (AttributeControllerIndex::ColorSelect, 0x00), + ], +}; diff --git a/src/vga.rs b/src/vga.rs index e46ad63..79d70e0 100644 --- a/src/vga.rs +++ b/src/vga.rs @@ -2,8 +2,8 @@ use super::{ configurations::{ - VgaConfiguration, MODE_40X25_CONFIGURATION, MODE_40X50_CONFIGURATION, - MODE_640X480X16_CONFIGURATION, MODE_80X25_CONFIGURATION, + VgaConfiguration, MODE_320X200X256_CONFIGURATION, MODE_40X25_CONFIGURATION, + MODE_40X50_CONFIGURATION, MODE_640X480X16_CONFIGURATION, MODE_80X25_CONFIGURATION, }, fonts::VgaFont, registers::{ @@ -57,6 +57,8 @@ pub enum VideoMode { Mode40x50, /// Represents text mode 80x25. Mode80x25, + /// Represents graphics mode 320x200x256. + Mode320x200x256, /// Represents graphics mode 640x480x16. Mode640x480x16, } @@ -98,6 +100,7 @@ impl Vga { VideoMode::Mode40x25 => self.set_video_mode_40x25(), VideoMode::Mode40x50 => self.set_video_mode_40x50(), VideoMode::Mode80x25 => self.set_video_mode_80x25(), + VideoMode::Mode320x200x256 => self.set_video_mode_320x200x256(), VideoMode::Mode640x480x16 => self.set_video_mode_640x480x16(), } } @@ -266,6 +269,12 @@ impl Vga { self.most_recent_video_mode = Some(VideoMode::Mode80x25); } + /// Sets the video card to Mode 320x200x256. + fn set_video_mode_320x200x256(&mut self) { + self.set_registers(&MODE_320X200X256_CONFIGURATION); + self.most_recent_video_mode = Some(VideoMode::Mode320x200x256); + } + /// Sets the video card to Mode 640x480x16. fn set_video_mode_640x480x16(&mut self) { self.set_registers(&MODE_640X480X16_CONFIGURATION); diff --git a/src/writers/graphics_320x200x256.rs b/src/writers/graphics_320x200x256.rs new file mode 100644 index 0000000..30a3983 --- /dev/null +++ b/src/writers/graphics_320x200x256.rs @@ -0,0 +1,95 @@ +use super::{GraphicsWriter, Screen}; +use crate::{ + colors::DEFAULT_PALETTE, + drawing::{Bresenham, Point}, + vga::{Vga, VideoMode, VGA}, +}; +use font8x8::UnicodeFonts; +use spinning_top::SpinlockGuard; + +const WIDTH: usize = 320; +const HEIGHT: usize = 200; +const SIZE: usize = WIDTH * HEIGHT; + +/// A basic interface for interacting with vga graphics mode 320x200x256. +#[derive(Default)] +pub struct Graphics320x200x256 {} + +impl Screen for Graphics320x200x256 { + #[inline] + fn get_width(&self) -> usize { + WIDTH + } + + #[inline] + fn get_height(&self) -> usize { + HEIGHT + } + + #[inline] + fn get_size(&self) -> usize { + SIZE + } +} + +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 draw_line(&self, start: Point, end: Point, color: u8) { + for (x, y) in Bresenham::new(start, end) { + self.set_pixel(x as usize, y as usize, color); + } + } + fn set_pixel(&self, x: usize, y: usize, color: u8) { + let (_vga, frame_buffer) = self.get_frame_buffer(); + let offset = (y * WIDTH) + x; + unsafe { + frame_buffer.add(offset).write_volatile(color); + } + } + fn draw_character(&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 + None => font8x8::unicode::BLOCK_UNICODE[8].byte_array(), + }; + + for (row, byte) in character.iter().enumerate() { + for bit in 0..8 { + match *byte & 1 << bit { + 0 => (), + _ => self.set_pixel(x + bit, y + row, color), + } + } + } + } + fn set_mode(&self) { + let mut vga = VGA.lock(); + vga.set_video_mode(VideoMode::Mode320x200x256); + + // Some bios mess up the palette when switching modes, + // so explicitly set it. + vga.color_palette_registers.load_palette(&DEFAULT_PALETTE); + } +} + +impl Graphics320x200x256 { + /// Creates a new `Graphics320x200x256`. + pub fn new() -> Graphics320x200x256 { + Graphics320x200x256 {} + } + + /// 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 index b2e0b6a..ac5b14b 100644 --- a/src/writers/mod.rs +++ b/src/writers/mod.rs @@ -1,4 +1,5 @@ //! Writers for common vga modes. +mod graphics_320x200x256; mod graphics_640x480x16; mod text_40x25; mod text_40x50; @@ -12,6 +13,7 @@ use super::{ }; use spinning_top::SpinlockGuard; +pub use graphics_320x200x256::Graphics320x200x256; pub use graphics_640x480x16::Graphics640x480x16; pub use text_40x25::Text40x25; pub use text_40x50::Text40x50; From 865fecb71d187c69f5ed2e13650cf28318e07d57 Mon Sep 17 00:00:00 2001 From: Ryan Kennedy Date: Wed, 25 Mar 2020 01:16:21 -0500 Subject: [PATCH 2/4] Implemented screen for 640x480x16 --- src/writers/graphics_640x480x16.rs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/writers/graphics_640x480x16.rs b/src/writers/graphics_640x480x16.rs index e035d6f..f7edd28 100644 --- a/src/writers/graphics_640x480x16.rs +++ b/src/writers/graphics_640x480x16.rs @@ -1,4 +1,4 @@ -use super::GraphicsWriter; +use super::{GraphicsWriter, Screen}; use crate::{ colors::{Color16, DEFAULT_PALETTE}, drawing::{Bresenham, Point}, @@ -10,6 +10,7 @@ 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; @@ -38,6 +39,18 @@ const WIDTH_IN_BYTES: usize = WIDTH / 8; #[derive(Default)] pub struct Graphics640x480x16; +impl Screen for Graphics640x480x16 { + fn get_width(&self) -> usize { + WIDTH + } + fn get_height(&self) -> usize { + HEIGHT + } + fn get_size(&self) -> usize { + SIZE + } +} + impl GraphicsWriter for Graphics640x480x16 { fn clear_screen(&self, color: Color16) { self.set_write_mode_2(); From 699855669b97906e4f3477369b49d34d2ef8d4b2 Mon Sep 17 00:00:00 2001 From: Ryan Kennedy Date: Wed, 25 Mar 2020 16:50:35 -0500 Subject: [PATCH 3/4] Fixing comments --- src/writers/graphics_640x480x16.rs | 4 ++++ src/writers/mod.rs | 7 +------ 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/writers/graphics_640x480x16.rs b/src/writers/graphics_640x480x16.rs index f7edd28..8000b6b 100644 --- a/src/writers/graphics_640x480x16.rs +++ b/src/writers/graphics_640x480x16.rs @@ -87,6 +87,10 @@ impl GraphicsWriter for Graphics640x480x16 { } } + /// **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); diff --git a/src/writers/mod.rs b/src/writers/mod.rs index ac5b14b..c6c6efa 100644 --- a/src/writers/mod.rs +++ b/src/writers/mod.rs @@ -185,16 +185,11 @@ pub trait TextWriter: Screen { pub trait GraphicsWriter { /// Clears the screen by setting all pixels to the specified `color`. fn clear_screen(&self, color: Color); - /// /// Draws a line from `start` to `end` with the specified `color`. + /// Draws a line from `start` to `end` with the specified `color`. fn draw_line(&self, start: Point, end: Point, color: Color); /// 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); /// Sets the given pixel at `(x, y)` to the given `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: Color); /// Sets the graphics device to a `VideoMode`. fn set_mode(&self); From 225416353f2eb56fe55b732b8f3f82c7e2595714 Mon Sep 17 00:00:00 2001 From: Ryan Kennedy Date: Wed, 25 Mar 2020 20:01:35 -0500 Subject: [PATCH 4/4] 320x200x256 additions --- Cargo.toml | 2 +- Changelog.md | 5 +++++ src/writers/graphics_320x200x256.rs | 21 +++++++++++++++++++++ 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 16ba9b4..5d2e8ba 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "vga" -version = "0.2.0" +version = "0.2.1" authors = ["Ryan Kennedy "] edition = "2018" description = "Support for vga specific functions, data structures, and registers." diff --git a/Changelog.md b/Changelog.md index 0ed738d..52d60e3 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,8 @@ +# 0.2.1 + +- Added `Graphics320x200x256` mode. +- Implemented `Screen` for `Graphics640x480x16`. + # 0.2.0 ## Breaking diff --git a/src/writers/graphics_320x200x256.rs b/src/writers/graphics_320x200x256.rs index 30a3983..93e6f2f 100644 --- a/src/writers/graphics_320x200x256.rs +++ b/src/writers/graphics_320x200x256.rs @@ -12,6 +12,27 @@ const HEIGHT: usize = 200; const SIZE: usize = WIDTH * HEIGHT; /// A basic interface for interacting with vga graphics mode 320x200x256. +/// +/// # Examples +/// +/// Basic usage: +/// +/// ```no_run +/// use vga::colors::Color16; +/// use vga::writers::{Graphics320x200x256, GraphicsWriter}; + +/// let mode = Graphics320x200x256::new(); +/// mode.set_mode(); +/// mode.clear_screen(0); +/// mode.draw_line((60, 20), (260, 20), 255); +/// mode.draw_line((60, 20), (60, 180), 255); +/// mode.draw_line((60, 180), (260, 180), 255); +/// mode.draw_line((260, 180), (260, 20), 255); +/// mode.draw_line((60, 40), (260, 40), 255); +/// for (offset, character) in "Hello World!".chars().enumerate() { +/// mode.draw_character(118 + offset * 8, 27, character, 255); +/// } +/// ``` #[derive(Default)] pub struct Graphics320x200x256 {}