From 86c575e7bc35b01358e44744d509c24a1ab52174 Mon Sep 17 00:00:00 2001 From: Ryan Kennedy Date: Sat, 21 Mar 2020 22:05:53 -0500 Subject: [PATCH 01/25] Plane refactor --- Cargo.toml | 1 + src/registers.rs | 2 +- src/vga.rs | 79 ++++++++++++++++++++++++------ src/writers/graphics_640x480x16.rs | 12 ++--- 4 files changed, 73 insertions(+), 21 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 91d4fe4..e931d8d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,6 +19,7 @@ repository = "https://github.com/rust-osdev/vga" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +bitflags = "1.2.1" conquer-once = { version = "0.2.0", default-features = false } spinning_top = { version = "0.1.0", features = ["nightly"] } x86_64 = "0.9.6" diff --git a/src/registers.rs b/src/registers.rs index 0d3ef39..3589278 100644 --- a/src/registers.rs +++ b/src/registers.rs @@ -58,7 +58,7 @@ pub enum SequencerIndex { SequencerReset = 0x0, /// Represents the `Clocking Mode` register index. ClockingMode = 0x1, - /// Represents the Plane/Map mask register index. + /// Represents the `Plane/Map` mask register index. PlaneMask = 0x2, /// Represents the `Character Font` register index. CharacterFont = 0x3, diff --git a/src/vga.rs b/src/vga.rs index c717f81..44e6420 100644 --- a/src/vga.rs +++ b/src/vga.rs @@ -13,7 +13,9 @@ use super::{ GraphicsControllerIndex, GraphicsControllerRegisters, SequencerIndex, SequencerRegisters, }, }; +use bitflags::bitflags; use conquer_once::spin::Lazy; +use core::convert::TryFrom; use spinning_top::Spinlock; /// Provides mutable access to the vga graphics card. @@ -49,11 +51,11 @@ impl From for u32 { } } -/// Represents a plane for reading and writing vga data. +/// Represents a plane for the `GraphicsControllerIndex::ReadPlaneSelect` register. #[allow(dead_code)] #[derive(Debug, Copy, Clone)] #[repr(u8)] -pub enum Plane { +pub enum ReadPlane { /// Represents `Plane 0 (0x0)`. Plane0 = 0x0, /// Represents `Plane 1 (0x1)`. @@ -64,12 +66,60 @@ pub enum Plane { Plane3 = 0x3, } -impl From for u8 { - fn from(value: Plane) -> u8 { +impl TryFrom for ReadPlane { + type Error = &'static str; + + fn try_from(value: u8) -> Result { + match value { + 0 => Ok(ReadPlane::Plane0), + 1 => Ok(ReadPlane::Plane1), + 2 => Ok(ReadPlane::Plane2), + 3 => Ok(ReadPlane::Plane3), + _ => Err("ReadPlane only accepts values between 0-3!"), + } + } +} + +impl From for u8 { + fn from(value: ReadPlane) -> u8 { value as u8 } } +bitflags! { + /// Represents the plane masks of the `SequencerIndex::PlaneMask` register. + pub struct PlaneMask: u8 { + /// Represents `Plane0` of vga memory. + const PLANE0 = 0b00000001; + /// Represents `Plane1` of vga memory. + const PLANE1 = 0b00000010; + /// Represents `Plane2` of vga memory. + const PLANE2 = 0b00000100; + /// Represents `Plane3` of vga memory. + const PLANE3 = 0b00001000; + } +} + +impl TryFrom for PlaneMask { + type Error = &'static str; + + fn try_from(value: u8) -> Result { + match value { + 0 => Ok(PlaneMask::PLANE0), + 1 => Ok(PlaneMask::PLANE1), + 2 => Ok(PlaneMask::PLANE2), + 3 => Ok(PlaneMask::PLANE3), + _ => Err("PlaneMask only accepts values between 0-3!"), + } + } +} + +impl From for u8 { + fn from(value: PlaneMask) -> u8 { + value.bits() + } +} + /// Represents a specified vga video mode. #[derive(Debug, Clone, Copy)] pub enum VideoMode { @@ -226,7 +276,7 @@ impl Vga { ); // Write font to plane - self.set_plane(Plane::Plane2); + self.set_plane_mask(PlaneMask::PLANE2); let frame_buffer = u32::from(self.get_frame_buffer()) as *mut u8; @@ -286,16 +336,17 @@ impl Vga { ) } - /// Turns on the given `Plane` in the vga graphics card. - pub fn set_plane(&mut self, plane: Plane) { - let mut plane = u8::from(plane); - - plane &= 0x3; - - self.graphics_controller_registers - .write(GraphicsControllerIndex::ReadPlaneSelect, plane); + /// Sets the plane mask of the sequencer controller, as specified by `plane_mask`. + pub fn set_plane_mask(&mut self, plane_mask: PlaneMask) { self.sequencer_registers - .write(SequencerIndex::PlaneMask, 0x1 << plane); + .write(SequencerIndex::PlaneMask, u8::from(plane_mask)); + } + + /// Sets the read plane of the graphics controller, as specified by `read_plane`. + pub fn set_read_plane(&mut self, read_plane: ReadPlane) { + let read_plane = u8::from(read_plane) & 0x3; + self.graphics_controller_registers + .write(GraphicsControllerIndex::ReadPlaneSelect, read_plane); } fn set_registers(&mut self, configuration: &VgaConfiguration) { diff --git a/src/writers/graphics_640x480x16.rs b/src/writers/graphics_640x480x16.rs index 98eb549..8914011 100644 --- a/src/writers/graphics_640x480x16.rs +++ b/src/writers/graphics_640x480x16.rs @@ -1,14 +1,13 @@ use crate::{ colors::{Color16Bit, DEFAULT_PALETTE}, - vga::{Plane, Vga, VideoMode, VGA}, + vga::{Vga, VideoMode, VGA}, }; +use core::convert::TryInto; use spinning_top::SpinlockGuard; const WIDTH: usize = 640; const HEIGHT: usize = 480; -static PLANES: &[Plane] = &[Plane::Plane0, Plane::Plane1, Plane::Plane2, Plane::Plane3]; - /// A basic interface for interacting with vga graphics mode 640x480x16 /// /// # Examples @@ -37,7 +36,7 @@ impl Graphics640x480x16 { // TODO: Clear the screen by using 4-plane mode instead of slow `set_pixel`. for x in 0..WIDTH { for y in 0..HEIGHT { - self.set_pixel(x, y, Color16Bit::Yellow); + self.set_pixel(x, y, Color16Bit::Black); } } } @@ -56,8 +55,9 @@ impl Graphics640x480x16 { let mask = 0x80 >> (x & 7); let mut plane_mask = 0x01; - for plane in PLANES { - vga.set_plane(*plane); + for plane in 0u8..4u8 { + vga.set_read_plane(plane.try_into().unwrap()); + vga.set_plane_mask(plane.try_into().unwrap()); let current_value = unsafe { frame_buffer.add(offset).read_volatile() }; let new_value = if plane_mask & color as u8 != 0 { current_value | mask From 700d657e25ea7d1ef7fa3a9af89368ff97904abf Mon Sep 17 00:00:00 2001 From: Ryan Kennedy Date: Sat, 21 Mar 2020 22:26:59 -0500 Subject: [PATCH 02/25] Faster clear screen --- src/vga.rs | 10 ++++++---- src/writers/graphics_640x480x16.rs | 18 +++++++++--------- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/src/vga.rs b/src/vga.rs index 44e6420..6e47adc 100644 --- a/src/vga.rs +++ b/src/vga.rs @@ -90,13 +90,15 @@ bitflags! { /// Represents the plane masks of the `SequencerIndex::PlaneMask` register. pub struct PlaneMask: u8 { /// Represents `Plane0` of vga memory. - const PLANE0 = 0b00000001; + const PLANE0 = 0b0000_0001; /// Represents `Plane1` of vga memory. - const PLANE1 = 0b00000010; + const PLANE1 = 0b0000_0010; /// Represents `Plane2` of vga memory. - const PLANE2 = 0b00000100; + const PLANE2 = 0b0000_0100; /// Represents `Plane3` of vga memory. - const PLANE3 = 0b00001000; + const PLANE3 = 0b0000_1000; + /// Represents a combination of all the plane masks. + const ALL_PLANES = Self::PLANE0.bits() | Self::PLANE1.bits() | Self::PLANE2.bits() | Self::PLANE3.bits(); } } diff --git a/src/writers/graphics_640x480x16.rs b/src/writers/graphics_640x480x16.rs index 8914011..59c9a1c 100644 --- a/src/writers/graphics_640x480x16.rs +++ b/src/writers/graphics_640x480x16.rs @@ -1,12 +1,13 @@ use crate::{ colors::{Color16Bit, DEFAULT_PALETTE}, - vga::{Vga, VideoMode, VGA}, + vga::{PlaneMask, Vga, VideoMode, VGA}, }; use core::convert::TryInto; use spinning_top::SpinlockGuard; const WIDTH: usize = 640; const HEIGHT: usize = 480; +const ALL_PLANES_SCREEN_SIZE: usize = (WIDTH * HEIGHT) / 4; /// A basic interface for interacting with vga graphics mode 640x480x16 /// @@ -33,20 +34,19 @@ impl Graphics640x480x16 { /// Clears the screen by setting all pixels to `Color16Bit::Black`. pub fn clear_screen(&self) { - // TODO: Clear the screen by using 4-plane mode instead of slow `set_pixel`. - for x in 0..WIDTH { - for y in 0..HEIGHT { - self.set_pixel(x, y, Color16Bit::Black); + let (mut vga, frame_buffer) = self.get_frame_buffer(); + vga.set_plane_mask(PlaneMask::ALL_PLANES); + for offset in 0..ALL_PLANES_SCREEN_SIZE { + unsafe { + frame_buffer + .add(offset) + .write_volatile(Color16Bit::Black as u8); } } } /// 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; From 01d75b971ac1ff8e48db0ea85a6efeb8d977056f Mon Sep 17 00:00:00 2001 From: Ryan Kennedy Date: Sat, 21 Mar 2020 23:40:40 -0500 Subject: [PATCH 03/25] Testing performance with different write modes --- src/colors.rs | 6 ++++++ src/vga.rs | 32 ++++++++++++++++++++++++++++-- src/writers/graphics_640x480x16.rs | 32 +++++++++++++----------------- 3 files changed, 50 insertions(+), 20 deletions(-) diff --git a/src/colors.rs b/src/colors.rs index 236c6dc..8bc44de 100644 --- a/src/colors.rs +++ b/src/colors.rs @@ -41,6 +41,12 @@ pub enum Color16Bit { White = 0xF, } +impl From for u8 { + fn from(value: Color16Bit) -> u8 { + value as u8 + } +} + /// Represents a color for vga text modes. #[derive(Debug, Copy, Clone)] #[repr(transparent)] diff --git a/src/vga.rs b/src/vga.rs index 6e47adc..5e32437 100644 --- a/src/vga.rs +++ b/src/vga.rs @@ -1,7 +1,7 @@ //! Provides access to the vga graphics card. use super::{ - colors::PALETTE_SIZE, + colors::{Color16Bit, PALETTE_SIZE}, configurations::{ VgaConfiguration, MODE_40X25_CONFIGURATION, MODE_40X50_CONFIGURATION, MODE_640X480X16_CONFIGURATION, MODE_80X25_CONFIGURATION, @@ -89,6 +89,8 @@ impl From for u8 { bitflags! { /// Represents the plane masks of the `SequencerIndex::PlaneMask` register. pub struct PlaneMask: u8 { + /// Represents none of the plane masks of vga memory. + const NONE = 0b0000_0000; /// Represents `Plane0` of vga memory. const PLANE0 = 0b0000_0001; /// Represents `Plane1` of vga memory. @@ -97,7 +99,7 @@ bitflags! { const PLANE2 = 0b0000_0100; /// Represents `Plane3` of vga memory. const PLANE3 = 0b0000_1000; - /// Represents a combination of all the plane masks. + /// Represents all of the plane masks of vga memory. const ALL_PLANES = Self::PLANE0.bits() | Self::PLANE1.bits() | Self::PLANE2.bits() | Self::PLANE3.bits(); } } @@ -351,6 +353,32 @@ impl Vga { .write(GraphicsControllerIndex::ReadPlaneSelect, read_plane); } + /// Sets the value to use for `GraphicsControllerIndex::SetReset`, + /// as spcified by `color`. + pub fn set_graphics_set_reset(&mut self, color: Color16Bit) { + let original_value = self + .graphics_controller_registers + .read(GraphicsControllerIndex::SetReset) + & 0xF0; + self.graphics_controller_registers.write( + GraphicsControllerIndex::SetReset, + original_value | u8::from(color), + ); + } + + /// Sets which planes are effected by `GraphicsControllerIndex::SetReset`, + /// as specified by `plane_mask`. + pub fn set_graphics_enable_set_reset(&mut self, plane_mask: PlaneMask) { + let original_value = self + .graphics_controller_registers + .read(GraphicsControllerIndex::EnableSetReset) + & 0xF0; + self.graphics_controller_registers.write( + GraphicsControllerIndex::EnableSetReset, + original_value | u8::from(plane_mask), + ); + } + fn set_registers(&mut self, configuration: &VgaConfiguration) { let emulation_mode = self.get_emulation_mode(); diff --git a/src/writers/graphics_640x480x16.rs b/src/writers/graphics_640x480x16.rs index 59c9a1c..e2f57ec 100644 --- a/src/writers/graphics_640x480x16.rs +++ b/src/writers/graphics_640x480x16.rs @@ -2,7 +2,6 @@ use crate::{ colors::{Color16Bit, DEFAULT_PALETTE}, vga::{PlaneMask, Vga, VideoMode, VGA}, }; -use core::convert::TryInto; use spinning_top::SpinlockGuard; const WIDTH: usize = 640; @@ -36,6 +35,7 @@ impl Graphics640x480x16 { pub fn clear_screen(&self) { let (mut vga, frame_buffer) = self.get_frame_buffer(); vga.set_plane_mask(PlaneMask::ALL_PLANES); + vga.set_graphics_enable_set_reset(PlaneMask::NONE); for offset in 0..ALL_PLANES_SCREEN_SIZE { unsafe { frame_buffer @@ -50,24 +50,20 @@ impl Graphics640x480x16 { let (mut vga, frame_buffer) = self.get_frame_buffer(); let offset = x / 8 + (WIDTH / 8) * y; - // Store the current value for masking. - let x = x & 7; - let mask = 0x80 >> (x & 7); - let mut plane_mask = 0x01; + // Write to all 4 planes + vga.set_plane_mask(PlaneMask::ALL_PLANES); - for plane in 0u8..4u8 { - vga.set_read_plane(plane.try_into().unwrap()); - vga.set_plane_mask(plane.try_into().unwrap()); - let current_value = unsafe { frame_buffer.add(offset).read_volatile() }; - let new_value = if plane_mask & color as u8 != 0 { - current_value | mask - } else { - current_value & !mask - }; - unsafe { - frame_buffer.add(offset).write_volatile(new_value); - } - plane_mask <<= 1; + // Set the bits we want set/reset to the color + vga.set_graphics_set_reset(color); + + // Enable set/reset for all planes + vga.set_graphics_enable_set_reset(PlaneMask::ALL_PLANES); + unsafe { + // In write mode 0, when enable set/reset is turned on, cpu data + // is replaced with the data from the set/reset register. Since + // we're using set/reset for all 4 planes, it doesn't matter what value + // we write to the memory address. + frame_buffer.add(offset).write(0x0); } } From 9be03f11900136e041ec58424a5aa575c25e7abb Mon Sep 17 00:00:00 2001 From: Ryan Kennedy Date: Sun, 22 Mar 2020 14:11:35 -0500 Subject: [PATCH 04/25] Revert set_pixel --- src/writers/graphics_640x480x16.rs | 31 +++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/src/writers/graphics_640x480x16.rs b/src/writers/graphics_640x480x16.rs index e2f57ec..e471cd4 100644 --- a/src/writers/graphics_640x480x16.rs +++ b/src/writers/graphics_640x480x16.rs @@ -2,6 +2,7 @@ use crate::{ colors::{Color16Bit, DEFAULT_PALETTE}, vga::{PlaneMask, Vga, VideoMode, VGA}, }; +use core::convert::TryInto; use spinning_top::SpinlockGuard; const WIDTH: usize = 640; @@ -50,20 +51,24 @@ impl Graphics640x480x16 { let (mut vga, frame_buffer) = self.get_frame_buffer(); let offset = x / 8 + (WIDTH / 8) * y; - // Write to all 4 planes - vga.set_plane_mask(PlaneMask::ALL_PLANES); + // Store the current value for masking. + let x = x & 7; + let mask = 0x80 >> (x & 7); + let mut plane_mask = 0x01; - // Set the bits we want set/reset to the color - vga.set_graphics_set_reset(color); - - // Enable set/reset for all planes - vga.set_graphics_enable_set_reset(PlaneMask::ALL_PLANES); - unsafe { - // In write mode 0, when enable set/reset is turned on, cpu data - // is replaced with the data from the set/reset register. Since - // we're using set/reset for all 4 planes, it doesn't matter what value - // we write to the memory address. - frame_buffer.add(offset).write(0x0); + for plane in 0u8..4u8 { + vga.set_read_plane(plane.try_into().unwrap()); + vga.set_plane_mask(plane.try_into().unwrap()); + let current_value = unsafe { frame_buffer.add(offset).read_volatile() }; + let new_value = if plane_mask & color as u8 != 0 { + current_value | mask + } else { + current_value & !mask + }; + unsafe { + frame_buffer.add(offset).write_volatile(new_value); + } + plane_mask <<= 1; } } From 3eae6fb32d876cd64bf3171b0c70a54906243589 Mon Sep 17 00:00:00 2001 From: Ryan Kennedy Date: Sun, 22 Mar 2020 18:41:14 -0500 Subject: [PATCH 05/25] Initial refactor of registers --- src/registers.rs | 510 -------------------------- src/registers/attribute_controller.rs | 145 ++++++++ src/registers/color_palette.rs | 44 +++ src/registers/crtc_controller.rs | 122 ++++++ src/registers/general.rs | 42 +++ src/registers/graphics_controller.rs | 71 ++++ src/registers/mod.rs | 64 ++++ src/registers/sequencer.rs | 59 +++ 8 files changed, 547 insertions(+), 510 deletions(-) delete mode 100644 src/registers.rs create mode 100644 src/registers/attribute_controller.rs create mode 100644 src/registers/color_palette.rs create mode 100644 src/registers/crtc_controller.rs create mode 100644 src/registers/general.rs create mode 100644 src/registers/graphics_controller.rs create mode 100644 src/registers/mod.rs create mode 100644 src/registers/sequencer.rs diff --git a/src/registers.rs b/src/registers.rs deleted file mode 100644 index 3589278..0000000 --- a/src/registers.rs +++ /dev/null @@ -1,510 +0,0 @@ -//! Common registers used in vga programming. - -use crate::colors::PALETTE_SIZE; -use x86_64::instructions::port::{Port, PortReadOnly, PortWriteOnly}; - -const ST00_READ_ADDRESS: u16 = 0x3C2; -const ST01_READ_CGA_ADDRESS: u16 = 0x3DA; -const ST01_READ_MDA_ADDRESS: u16 = 0x3BA; -const FCR_READ_ADDRESS: u16 = 0x3CA; -const FCR_CGA_WRITE_ADDRESS: u16 = 0x3DA; -const FCR_MDA_WRITE_ADDRESS: u16 = 0x3BA; -const MSR_READ_ADDRESS: u16 = 0x3CC; -const MSR_WRITE_ADDRESS: u16 = 0x3C2; - -const SRX_INDEX_ADDRESS: u16 = 0x3C4; -const SRX_DATA_ADDRESS: u16 = 0x3C5; - -const GRX_INDEX_ADDRESS: u16 = 0x3CE; -const GRX_DATA_ADDRESS: u16 = 0x3CF; - -const ARX_INDEX_ADDRESS: u16 = 0x3C0; -const ARX_DATA_ADDRESS: u16 = 0x3C1; - -const CRX_INDEX_CGA_ADDRESS: u16 = 0x3D4; -const CRX_INDEX_MDA_ADDRESS: u16 = 0x3B4; -const CRX_DATA_CGA_ADDRESS: u16 = 0x3D5; -const CRX_DATA_MDA_ADDRESS: u16 = 0x3B5; - -const COLOR_PALETTE_DATA_ADDRESS: u16 = 0x3C9; -const COLOR_PALETTE_INDEX_READ_ADDRESS: u16 = 0x3C7; -const COLOR_PALETTE_INDEX_WRITE_ADDRESSS: u16 = 0x3C8; - -/// Represents a vga emulation mode. -#[derive(Debug, Copy, Clone)] -#[repr(u8)] -pub enum EmulationMode { - /// Represents a monochrome emulation mode. - Mda = 0x0, - /// Respresents a color emulation mode. - Cga = 0x1, -} - -impl From for EmulationMode { - fn from(value: u8) -> EmulationMode { - match value { - 0x0 => EmulationMode::Mda, - 0x1 => EmulationMode::Cga, - _ => panic!("{} is an invalid emulation mode", value), - } - } -} - -/// Represents an index for the seqeuncer registers. -#[derive(Debug, Clone, Copy)] -#[repr(u8)] -pub enum SequencerIndex { - /// Represents the `Sequencer Reset` register index. - SequencerReset = 0x0, - /// Represents the `Clocking Mode` register index. - ClockingMode = 0x1, - /// Represents the `Plane/Map` mask register index. - PlaneMask = 0x2, - /// Represents the `Character Font` register index. - CharacterFont = 0x3, - /// Represents the `Memory Mode` register index. - MemoryMode = 0x4, - /// Represents the `Horizontal Character Counter Reset` register index. - CounterReset = 0x7, -} - -impl From for u8 { - fn from(value: SequencerIndex) -> u8 { - value as u8 - } -} - -/// Represents an index for the graphics controller registers. -#[derive(Debug, Copy, Clone)] -#[repr(u8)] -pub enum GraphicsControllerIndex { - /// Represents the `Set/Reset` register index. - SetReset = 0x0, - /// Represents the `Enable Set/Reset` register index. - EnableSetReset = 0x1, - /// Represents the `Color Compare` register index. - ColorCompare = 0x2, - /// Represents the `Data Rotate` register index. - DataRotate = 0x3, - /// Represents the `Read Plane Select` register index. - ReadPlaneSelect = 0x4, - /// Represents the `Graphics Mode` register index. - GraphicsMode = 0x5, - /// Represents the `Miscellaneous` register index. - Miscellaneous = 0x6, - /// Represents the `Color Don't Care` register index. - ColorDontCare = 0x7, - /// Represents the `Bit Mask` register index. - BitMask = 0x8, - /// Represents the `Address Mapping` register index. - AddressMapping = 0x10, - /// Represents the `Page Selector` register index. - PageSelector = 0x11, - /// Represents the `Software Flags` register index. - SoftwareFlags = 0x18, -} - -impl From for u8 { - fn from(value: GraphicsControllerIndex) -> u8 { - value as u8 - } -} - -/// Represents an index for the attribute controller registers. -#[derive(Debug, Copy, Clone)] -#[repr(u8)] -pub enum AttributeControllerIndex { - /// Represents the `Palette 0` register index. - PaletteRegister0 = 0x00, - /// Represents the `Palette 1` register index. - PaletteRegister1 = 0x01, - /// Represents the `Palette 2` register index. - PaletteRegister2 = 0x02, - /// Represents the `Palette 3` register index. - PaletteRegister3 = 0x03, - /// Represents the `Palette 4` register index. - PaletteRegister4 = 0x04, - /// Represents the `Palette 5` register index. - PaletteRegister5 = 0x05, - /// Represents the `Palette 6` register index. - PaletteRegister6 = 0x06, - /// Represents the `Palette 7` register index. - PaletteRegister7 = 0x07, - /// Represents the `Palette 8` register index. - PaletteRegister8 = 0x08, - /// Represents the `Palette 9` register index. - PaletteRegister9 = 0x09, - /// Represents the `Palette A` register index. - PaletteRegisterA = 0x0A, - /// Represents the `Palette B` register index. - PaletteRegisterB = 0x0B, - /// Represents the `Palette C` register index. - PaletteRegisterC = 0x0C, - /// Represents the `Palette D` register index. - PaletteRegisterD = 0x0D, - /// Represents the `Palette E` register index. - PaletteRegisterE = 0x0E, - /// Represents the `Palette F` register index. - PaletteRegisterF = 0x0F, - /// Represents the `Mode Control` register index. - ModeControl = 0x10, - /// Represents the `Overscan Color` register index. - OverscanColor = 0x11, - /// Represents the `Memory Plane Enable` register index. - MemoryPlaneEnable = 0x12, - /// Represents the `Horizontal Pixel Panning` register index. - HorizontalPixelPanning = 0x13, - /// Represents the `Color Select` register index. - ColorSelect = 0x14, -} - -impl From for u8 { - fn from(value: AttributeControllerIndex) -> u8 { - value as u8 - } -} - -/// Represents an index for the crtc controller registers. -#[derive(Debug, Copy, Clone)] -#[repr(u8)] -pub enum CrtcControllerIndex { - /// Represents the `Horizontal Total` register index. - HorizontalTotal = 0x00, - /// Represents the `Horizontal Display Enable End` register index. - HorizontalDisplayEnableEnd = 0x01, - /// Represents the `Horizontal Blanking Start` register index. - HorizontalBlankingStart = 0x02, - /// Represents the `Horizontal Blanking End` register index. - HorizontalBlankingEnd = 0x03, - /// Represents the `Horizontal Sync Start` register index. - HorizontalSyncStart = 0x04, - /// Represents the `Horizontal Sync End` register index. - HorizontalSyncEnd = 0x05, - /// Represents the `Vertical Total` register index. - VeritcalTotal = 0x06, - /// Represents the `Overflow` register index. - Overflow = 0x07, - /// Represents the `Preset Row Scan` register index. - PresetRowScan = 0x08, - /// Represents the `Maximum Scan Line` register index. - MaximumScanLine = 0x09, - /// Represents the `Text Cursor Start` register index. - TextCursorStart = 0x0A, - /// Represents the `Text Cursor End` register index. - TextCursorEnd = 0x0B, - /// Represents the `Start Address High` register index. - StartAddressHigh = 0x0C, - /// Represents the `Start Address Low` register index. - StartAddressLow = 0x0D, - /// Represents the `Text Cursor Location High` register index. - TextCursorLocationHigh = 0x0E, - /// Represents the `Text Cursor Location Low` register index. - TextCursorLocationLow = 0x0F, - /// Represents the `Vertical Sync Start` register index. - VerticalSyncStart = 0x10, - /// Represents the `Vertical Sync End` register index. - VerticalSyncEnd = 0x11, - /// Represents the `Vertical Display Enable End` register index - VerticalDisplayEnableEnd = 0x12, - /// Represents the `Offset` register index. - Offset = 0x13, - /// Represents the `Underline Location` register index. - UnderlineLocation = 0x14, - /// Represents the `Vertical Blanking Start` register index. - VerticalBlankingStart = 0x15, - /// Represents the `Vertical Blanking End` register index. - VerticalBlankingEnd = 0x16, - /// Represents the `Mode Control` register index. - ModeControl = 0x17, - /// Represents the `Line Compare` register index. - LineCompare = 0x18, - /// Represents the `Memory Read Latch Data` register index. - MemoryReadLatchData = 0x22, - /// Represents the `Toggle State Of Attribute Controller` register index. - ToggleStateOfAttributeController = 0x24, -} - -impl From for u8 { - fn from(value: CrtcControllerIndex) -> u8 { - value as u8 - } -} - -#[derive(Debug)] -pub(crate) struct GeneralRegisters { - st00_read: PortReadOnly, - st01_read_cga: PortReadOnly, - st01_read_mda: PortReadOnly, - fcr_read: PortReadOnly, - fcr_write_cga: PortWriteOnly, - fcr_write_mda: PortWriteOnly, - msr_read: PortReadOnly, - msr_write: PortWriteOnly, -} - -impl GeneralRegisters { - pub fn new() -> GeneralRegisters { - GeneralRegisters { - st00_read: PortReadOnly::new(ST00_READ_ADDRESS), - st01_read_cga: PortReadOnly::new(ST01_READ_CGA_ADDRESS), - st01_read_mda: PortReadOnly::new(ST01_READ_MDA_ADDRESS), - fcr_read: PortReadOnly::new(FCR_READ_ADDRESS), - fcr_write_cga: PortWriteOnly::new(FCR_CGA_WRITE_ADDRESS), - fcr_write_mda: PortWriteOnly::new(FCR_MDA_WRITE_ADDRESS), - msr_read: PortReadOnly::new(MSR_READ_ADDRESS), - msr_write: PortWriteOnly::new(MSR_WRITE_ADDRESS), - } - } - - pub fn read_msr(&mut self) -> u8 { - unsafe { self.msr_read.read() } - } - - pub fn write_msr(&mut self, value: u8) { - unsafe { - self.msr_write.write(value); - } - } -} - -#[derive(Debug)] -pub(crate) struct SequencerRegisters { - srx_index: Port, - srx_data: Port, -} - -impl SequencerRegisters { - pub fn new() -> SequencerRegisters { - SequencerRegisters { - srx_index: Port::new(SRX_INDEX_ADDRESS), - srx_data: Port::new(SRX_DATA_ADDRESS), - } - } - - pub fn read(&mut self, index: SequencerIndex) -> u8 { - self.set_index(index); - unsafe { self.srx_data.read() } - } - - pub fn write(&mut self, index: SequencerIndex, value: u8) { - self.set_index(index); - unsafe { - self.srx_data.write(value); - } - } - - fn set_index(&mut self, index: SequencerIndex) { - unsafe { - self.srx_index.write(u8::from(index)); - } - } -} - -#[derive(Debug)] -pub(crate) struct GraphicsControllerRegisters { - grx_index: Port, - grx_data: Port, -} - -impl GraphicsControllerRegisters { - pub fn new() -> GraphicsControllerRegisters { - GraphicsControllerRegisters { - grx_index: Port::new(GRX_INDEX_ADDRESS), - grx_data: Port::new(GRX_DATA_ADDRESS), - } - } - - pub fn read(&mut self, index: GraphicsControllerIndex) -> u8 { - self.set_index(index); - unsafe { self.grx_data.read() } - } - - pub fn write(&mut self, index: GraphicsControllerIndex, value: u8) { - self.set_index(index); - unsafe { - self.grx_data.write(value); - } - } - - fn set_index(&mut self, index: GraphicsControllerIndex) { - unsafe { - self.grx_index.write(u8::from(index)); - } - } -} - -#[derive(Debug)] -pub(crate) struct AttributeControllerRegisters { - arx_index: Port, - arx_data: Port, - st01_read_cga: Port, - st01_read_mda: Port, -} - -impl AttributeControllerRegisters { - pub fn new() -> AttributeControllerRegisters { - AttributeControllerRegisters { - arx_index: Port::new(ARX_INDEX_ADDRESS), - arx_data: Port::new(ARX_DATA_ADDRESS), - st01_read_cga: Port::new(ST01_READ_CGA_ADDRESS), - st01_read_mda: Port::new(ST01_READ_MDA_ADDRESS), - } - } - - pub fn read(&mut self, emulation_mode: EmulationMode, index: AttributeControllerIndex) -> u8 { - self.toggle_index(emulation_mode); - self.set_index(index); - unsafe { self.arx_data.read() } - } - - pub fn write( - &mut self, - emulation_mode: EmulationMode, - index: AttributeControllerIndex, - value: u8, - ) { - self.toggle_index(emulation_mode); - self.set_index(index); - unsafe { - self.arx_index.write(value); - } - } - - fn set_index(&mut self, index: AttributeControllerIndex) { - unsafe { - self.arx_index.write(u8::from(index)); - } - } - - fn toggle_index(&mut self, emulation_mode: EmulationMode) { - let st01_read = match emulation_mode { - EmulationMode::Cga => &mut self.st01_read_cga, - EmulationMode::Mda => &mut self.st01_read_mda, - }; - unsafe { - st01_read.read(); - } - } - - /// Video Enable. Note that In the VGA standard, this is called the "Palette Address Source" bit. - /// Clearing this bit will cause the VGA display data to become all 00 index values. For the default - /// palette, this will cause a black screen. The video timing signals continue. Another control bit will - /// turn video off and stop the data fetches. - /// - /// 0 = Disable. Attribute controller color registers (AR[00:0F]) can be accessed by the CPU. - /// - /// 1 = Enable. Attribute controller color registers (AR[00:0F]) are inaccessible by the CPU. - pub fn blank_screen(&mut self, emulation_mode: EmulationMode) { - self.toggle_index(emulation_mode); - let arx_index_value = unsafe { self.arx_index.read() }; - unsafe { - self.arx_index.write(arx_index_value & 0xDF); - } - } - - /// Video Enable. Note that In the VGA standard, this is called the "Palette Address Source" bit. - /// Clearing this bit will cause the VGA display data to become all 00 index values. For the default - /// palette, this will cause a black screen. The video timing signals continue. Another control bit will - /// turn video off and stop the data fetches. - /// - /// 0 = Disable. Attribute controller color registers (AR[00:0F]) can be accessed by the CPU. - /// - /// 1 = Enable. Attribute controller color registers (AR[00:0F]) are inaccessible by the CPU. - pub fn unblank_screen(&mut self, emulation_mode: EmulationMode) { - self.toggle_index(emulation_mode); - let arx_index_value = unsafe { self.arx_index.read() }; - unsafe { - self.arx_index.write(arx_index_value | 0x20); - } - } -} - -#[derive(Debug)] -pub(crate) struct CrtcControllerRegisters { - crx_index_cga: Port, - crx_index_mda: Port, - crx_data_cga: Port, - crx_data_mda: Port, -} - -impl CrtcControllerRegisters { - pub fn new() -> CrtcControllerRegisters { - CrtcControllerRegisters { - crx_index_cga: Port::new(CRX_INDEX_CGA_ADDRESS), - crx_index_mda: Port::new(CRX_INDEX_MDA_ADDRESS), - crx_data_cga: Port::new(CRX_DATA_CGA_ADDRESS), - crx_data_mda: Port::new(CRX_DATA_MDA_ADDRESS), - } - } - - pub fn read(&mut self, emulation_mode: EmulationMode, index: CrtcControllerIndex) -> u8 { - self.set_index(emulation_mode, index); - unsafe { self.get_data_port(emulation_mode).read() } - } - - pub fn write(&mut self, emulation_mode: EmulationMode, index: CrtcControllerIndex, value: u8) { - self.set_index(emulation_mode, index); - unsafe { - self.get_data_port(emulation_mode).write(value); - } - } - - fn set_index(&mut self, emulation_mode: EmulationMode, index: CrtcControllerIndex) { - unsafe { - self.get_index_port(emulation_mode).write(u8::from(index)); - } - } - - fn get_data_port(&mut self, emulation_mode: EmulationMode) -> &mut Port { - match emulation_mode { - EmulationMode::Cga => &mut self.crx_data_cga, - EmulationMode::Mda => &mut self.crx_data_mda, - } - } - - fn get_index_port(&mut self, emulation_mode: EmulationMode) -> &mut Port { - match emulation_mode { - EmulationMode::Cga => &mut self.crx_index_cga, - EmulationMode::Mda => &mut self.crx_index_mda, - } - } -} - -#[derive(Debug)] -pub(crate) struct ColorPaletteRegisters { - data_port: Port, - index_read_port: Port, - index_write_port: Port, -} - -impl ColorPaletteRegisters { - pub fn new() -> ColorPaletteRegisters { - ColorPaletteRegisters { - data_port: Port::new(COLOR_PALETTE_DATA_ADDRESS), - index_read_port: Port::new(COLOR_PALETTE_INDEX_READ_ADDRESS), - index_write_port: Port::new(COLOR_PALETTE_INDEX_WRITE_ADDRESSS), - } - } - - pub fn load_palette(&mut self, palette: &[u8; PALETTE_SIZE]) { - unsafe { - self.index_write_port.write(0); - } - for i in palette.iter() { - unsafe { - self.data_port.write(*i); - } - } - } - - pub fn read_palette(&mut self, palette: &mut [u8; PALETTE_SIZE]) { - unsafe { - self.index_read_port.write(0); - } - for byte in palette.iter_mut().take(PALETTE_SIZE) { - unsafe { - *byte = self.data_port.read(); - } - } - } -} diff --git a/src/registers/attribute_controller.rs b/src/registers/attribute_controller.rs new file mode 100644 index 0000000..63554e9 --- /dev/null +++ b/src/registers/attribute_controller.rs @@ -0,0 +1,145 @@ +use super::{ + EmulationMode, ARX_DATA_ADDRESS, ARX_INDEX_ADDRESS, ST01_READ_CGA_ADDRESS, + ST01_READ_MDA_ADDRESS, +}; +use x86_64::instructions::port::Port; + +/// Represents an index for the attribute controller registers. +#[derive(Debug, Copy, Clone)] +#[repr(u8)] +pub enum AttributeControllerIndex { + /// Represents the `Palette 0` register index. + PaletteRegister0 = 0x00, + /// Represents the `Palette 1` register index. + PaletteRegister1 = 0x01, + /// Represents the `Palette 2` register index. + PaletteRegister2 = 0x02, + /// Represents the `Palette 3` register index. + PaletteRegister3 = 0x03, + /// Represents the `Palette 4` register index. + PaletteRegister4 = 0x04, + /// Represents the `Palette 5` register index. + PaletteRegister5 = 0x05, + /// Represents the `Palette 6` register index. + PaletteRegister6 = 0x06, + /// Represents the `Palette 7` register index. + PaletteRegister7 = 0x07, + /// Represents the `Palette 8` register index. + PaletteRegister8 = 0x08, + /// Represents the `Palette 9` register index. + PaletteRegister9 = 0x09, + /// Represents the `Palette A` register index. + PaletteRegisterA = 0x0A, + /// Represents the `Palette B` register index. + PaletteRegisterB = 0x0B, + /// Represents the `Palette C` register index. + PaletteRegisterC = 0x0C, + /// Represents the `Palette D` register index. + PaletteRegisterD = 0x0D, + /// Represents the `Palette E` register index. + PaletteRegisterE = 0x0E, + /// Represents the `Palette F` register index. + PaletteRegisterF = 0x0F, + /// Represents the `Mode Control` register index. + ModeControl = 0x10, + /// Represents the `Overscan Color` register index. + OverscanColor = 0x11, + /// Represents the `Memory Plane Enable` register index. + MemoryPlaneEnable = 0x12, + /// Represents the `Horizontal Pixel Panning` register index. + HorizontalPixelPanning = 0x13, + /// Represents the `Color Select` register index. + ColorSelect = 0x14, +} + +impl From for u8 { + fn from(value: AttributeControllerIndex) -> u8 { + value as u8 + } +} + +#[derive(Debug)] +pub struct AttributeControllerRegisters { + arx_index: Port, + arx_data: Port, + st01_read_cga: Port, + st01_read_mda: Port, +} + +impl AttributeControllerRegisters { + pub fn new() -> AttributeControllerRegisters { + AttributeControllerRegisters { + arx_index: Port::new(ARX_INDEX_ADDRESS), + arx_data: Port::new(ARX_DATA_ADDRESS), + st01_read_cga: Port::new(ST01_READ_CGA_ADDRESS), + st01_read_mda: Port::new(ST01_READ_MDA_ADDRESS), + } + } + + pub fn read(&mut self, emulation_mode: EmulationMode, index: AttributeControllerIndex) -> u8 { + self.toggle_index(emulation_mode); + self.set_index(index); + unsafe { self.arx_data.read() } + } + + pub fn write( + &mut self, + emulation_mode: EmulationMode, + index: AttributeControllerIndex, + value: u8, + ) { + self.toggle_index(emulation_mode); + self.set_index(index); + unsafe { + self.arx_index.write(value); + } + } + + fn set_index(&mut self, index: AttributeControllerIndex) { + unsafe { + self.arx_index.write(u8::from(index)); + } + } + + fn toggle_index(&mut self, emulation_mode: EmulationMode) { + let st01_read = match emulation_mode { + EmulationMode::Cga => &mut self.st01_read_cga, + EmulationMode::Mda => &mut self.st01_read_mda, + }; + unsafe { + st01_read.read(); + } + } + + /// Video Enable. Note that In the VGA standard, this is called the "Palette Address Source" bit. + /// Clearing this bit will cause the VGA display data to become all 00 index values. For the default + /// palette, this will cause a black screen. The video timing signals continue. Another control bit will + /// turn video off and stop the data fetches. + /// + /// 0 = Disable. Attribute controller color registers (AR[00:0F]) can be accessed by the CPU. + /// + /// 1 = Enable. Attribute controller color registers (AR[00:0F]) are inaccessible by the CPU. + pub fn blank_screen(&mut self, emulation_mode: EmulationMode) { + self.toggle_index(emulation_mode); + let arx_index_value = unsafe { self.arx_index.read() }; + unsafe { + self.arx_index.write(arx_index_value & 0xDF); + } + } + + /// Video Enable. Note that In the VGA standard, this is called the "Palette Address Source" bit. + /// Clearing this bit will cause the VGA display data to become all 00 index values. For the default + /// palette, this will cause a black screen. The video timing signals continue. Another control bit will + /// turn video off and stop the data fetches. + /// + /// 0 = Disable. Attribute controller color registers (AR[00:0F]) can be accessed by the CPU. + /// + /// 1 = Enable. Attribute controller color registers (AR[00:0F]) are inaccessible by the CPU. + pub fn unblank_screen(&mut self, emulation_mode: EmulationMode) { + self.toggle_index(emulation_mode); + let arx_index_value = unsafe { self.arx_index.read() }; + unsafe { + self.arx_index.write(arx_index_value | 0x20); + } + } +} diff --git a/src/registers/color_palette.rs b/src/registers/color_palette.rs new file mode 100644 index 0000000..8f49f2f --- /dev/null +++ b/src/registers/color_palette.rs @@ -0,0 +1,44 @@ +use super::{ + COLOR_PALETTE_DATA_ADDRESS, COLOR_PALETTE_INDEX_READ_ADDRESS, + COLOR_PALETTE_INDEX_WRITE_ADDRESSS, PALETTE_SIZE, +}; +use x86_64::instructions::port::Port; + +#[derive(Debug)] +pub struct ColorPaletteRegisters { + data_port: Port, + index_read_port: Port, + index_write_port: Port, +} + +impl ColorPaletteRegisters { + pub fn new() -> ColorPaletteRegisters { + ColorPaletteRegisters { + data_port: Port::new(COLOR_PALETTE_DATA_ADDRESS), + index_read_port: Port::new(COLOR_PALETTE_INDEX_READ_ADDRESS), + index_write_port: Port::new(COLOR_PALETTE_INDEX_WRITE_ADDRESSS), + } + } + + pub fn load_palette(&mut self, palette: &[u8; PALETTE_SIZE]) { + unsafe { + self.index_write_port.write(0); + } + for i in palette.iter() { + unsafe { + self.data_port.write(*i); + } + } + } + + pub fn read_palette(&mut self, palette: &mut [u8; PALETTE_SIZE]) { + unsafe { + self.index_read_port.write(0); + } + for byte in palette.iter_mut().take(PALETTE_SIZE) { + unsafe { + *byte = self.data_port.read(); + } + } + } +} diff --git a/src/registers/crtc_controller.rs b/src/registers/crtc_controller.rs new file mode 100644 index 0000000..8c16388 --- /dev/null +++ b/src/registers/crtc_controller.rs @@ -0,0 +1,122 @@ +use super::{ + EmulationMode, CRX_DATA_CGA_ADDRESS, CRX_DATA_MDA_ADDRESS, CRX_INDEX_CGA_ADDRESS, + CRX_INDEX_MDA_ADDRESS, +}; +use x86_64::instructions::port::Port; + +/// Represents an index for the crtc controller registers. +#[derive(Debug, Copy, Clone)] +#[repr(u8)] +pub enum CrtcControllerIndex { + /// Represents the `Horizontal Total` register index. + HorizontalTotal = 0x00, + /// Represents the `Horizontal Display Enable End` register index. + HorizontalDisplayEnableEnd = 0x01, + /// Represents the `Horizontal Blanking Start` register index. + HorizontalBlankingStart = 0x02, + /// Represents the `Horizontal Blanking End` register index. + HorizontalBlankingEnd = 0x03, + /// Represents the `Horizontal Sync Start` register index. + HorizontalSyncStart = 0x04, + /// Represents the `Horizontal Sync End` register index. + HorizontalSyncEnd = 0x05, + /// Represents the `Vertical Total` register index. + VeritcalTotal = 0x06, + /// Represents the `Overflow` register index. + Overflow = 0x07, + /// Represents the `Preset Row Scan` register index. + PresetRowScan = 0x08, + /// Represents the `Maximum Scan Line` register index. + MaximumScanLine = 0x09, + /// Represents the `Text Cursor Start` register index. + TextCursorStart = 0x0A, + /// Represents the `Text Cursor End` register index. + TextCursorEnd = 0x0B, + /// Represents the `Start Address High` register index. + StartAddressHigh = 0x0C, + /// Represents the `Start Address Low` register index. + StartAddressLow = 0x0D, + /// Represents the `Text Cursor Location High` register index. + TextCursorLocationHigh = 0x0E, + /// Represents the `Text Cursor Location Low` register index. + TextCursorLocationLow = 0x0F, + /// Represents the `Vertical Sync Start` register index. + VerticalSyncStart = 0x10, + /// Represents the `Vertical Sync End` register index. + VerticalSyncEnd = 0x11, + /// Represents the `Vertical Display Enable End` register index + VerticalDisplayEnableEnd = 0x12, + /// Represents the `Offset` register index. + Offset = 0x13, + /// Represents the `Underline Location` register index. + UnderlineLocation = 0x14, + /// Represents the `Vertical Blanking Start` register index. + VerticalBlankingStart = 0x15, + /// Represents the `Vertical Blanking End` register index. + VerticalBlankingEnd = 0x16, + /// Represents the `Mode Control` register index. + ModeControl = 0x17, + /// Represents the `Line Compare` register index. + LineCompare = 0x18, + /// Represents the `Memory Read Latch Data` register index. + MemoryReadLatchData = 0x22, + /// Represents the `Toggle State Of Attribute Controller` register index. + ToggleStateOfAttributeController = 0x24, +} + +impl From for u8 { + fn from(value: CrtcControllerIndex) -> u8 { + value as u8 + } +} + +#[derive(Debug)] +pub struct CrtcControllerRegisters { + crx_index_cga: Port, + crx_index_mda: Port, + crx_data_cga: Port, + crx_data_mda: Port, +} + +impl CrtcControllerRegisters { + pub fn new() -> CrtcControllerRegisters { + CrtcControllerRegisters { + crx_index_cga: Port::new(CRX_INDEX_CGA_ADDRESS), + crx_index_mda: Port::new(CRX_INDEX_MDA_ADDRESS), + crx_data_cga: Port::new(CRX_DATA_CGA_ADDRESS), + crx_data_mda: Port::new(CRX_DATA_MDA_ADDRESS), + } + } + + pub fn read(&mut self, emulation_mode: EmulationMode, index: CrtcControllerIndex) -> u8 { + self.set_index(emulation_mode, index); + unsafe { self.get_data_port(emulation_mode).read() } + } + + pub fn write(&mut self, emulation_mode: EmulationMode, index: CrtcControllerIndex, value: u8) { + self.set_index(emulation_mode, index); + unsafe { + self.get_data_port(emulation_mode).write(value); + } + } + + fn set_index(&mut self, emulation_mode: EmulationMode, index: CrtcControllerIndex) { + unsafe { + self.get_index_port(emulation_mode).write(u8::from(index)); + } + } + + fn get_data_port(&mut self, emulation_mode: EmulationMode) -> &mut Port { + match emulation_mode { + EmulationMode::Cga => &mut self.crx_data_cga, + EmulationMode::Mda => &mut self.crx_data_mda, + } + } + + fn get_index_port(&mut self, emulation_mode: EmulationMode) -> &mut Port { + match emulation_mode { + EmulationMode::Cga => &mut self.crx_index_cga, + EmulationMode::Mda => &mut self.crx_index_mda, + } + } +} diff --git a/src/registers/general.rs b/src/registers/general.rs new file mode 100644 index 0000000..323f5f5 --- /dev/null +++ b/src/registers/general.rs @@ -0,0 +1,42 @@ +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, +}; +use x86_64::instructions::port::{PortReadOnly, PortWriteOnly}; + +#[derive(Debug)] +pub struct GeneralRegisters { + st00_read: PortReadOnly, + st01_read_cga: PortReadOnly, + st01_read_mda: PortReadOnly, + fcr_read: PortReadOnly, + fcr_write_cga: PortWriteOnly, + fcr_write_mda: PortWriteOnly, + msr_read: PortReadOnly, + msr_write: PortWriteOnly, +} + +impl GeneralRegisters { + pub fn new() -> GeneralRegisters { + GeneralRegisters { + st00_read: PortReadOnly::new(ST00_READ_ADDRESS), + st01_read_cga: PortReadOnly::new(ST01_READ_CGA_ADDRESS), + st01_read_mda: PortReadOnly::new(ST01_READ_MDA_ADDRESS), + fcr_read: PortReadOnly::new(FCR_READ_ADDRESS), + fcr_write_cga: PortWriteOnly::new(FCR_CGA_WRITE_ADDRESS), + fcr_write_mda: PortWriteOnly::new(FCR_MDA_WRITE_ADDRESS), + msr_read: PortReadOnly::new(MSR_READ_ADDRESS), + msr_write: PortWriteOnly::new(MSR_WRITE_ADDRESS), + } + } + + pub fn read_msr(&mut self) -> u8 { + unsafe { self.msr_read.read() } + } + + pub fn write_msr(&mut self, value: u8) { + unsafe { + self.msr_write.write(value); + } + } +} diff --git a/src/registers/graphics_controller.rs b/src/registers/graphics_controller.rs new file mode 100644 index 0000000..289e508 --- /dev/null +++ b/src/registers/graphics_controller.rs @@ -0,0 +1,71 @@ +use super::{GRX_DATA_ADDRESS, GRX_INDEX_ADDRESS}; +use x86_64::instructions::port::Port; + +/// Represents an index for the graphics controller registers. +#[derive(Debug, Copy, Clone)] +#[repr(u8)] +pub enum GraphicsControllerIndex { + /// Represents the `Set/Reset` register index. + SetReset = 0x0, + /// Represents the `Enable Set/Reset` register index. + EnableSetReset = 0x1, + /// Represents the `Color Compare` register index. + ColorCompare = 0x2, + /// Represents the `Data Rotate` register index. + DataRotate = 0x3, + /// Represents the `Read Plane Select` register index. + ReadPlaneSelect = 0x4, + /// Represents the `Graphics Mode` register index. + GraphicsMode = 0x5, + /// Represents the `Miscellaneous` register index. + Miscellaneous = 0x6, + /// Represents the `Color Don't Care` register index. + ColorDontCare = 0x7, + /// Represents the `Bit Mask` register index. + BitMask = 0x8, + /// Represents the `Address Mapping` register index. + AddressMapping = 0x10, + /// Represents the `Page Selector` register index. + PageSelector = 0x11, + /// Represents the `Software Flags` register index. + SoftwareFlags = 0x18, +} + +impl From for u8 { + fn from(value: GraphicsControllerIndex) -> u8 { + value as u8 + } +} + +#[derive(Debug)] +pub struct GraphicsControllerRegisters { + grx_index: Port, + grx_data: Port, +} + +impl GraphicsControllerRegisters { + pub fn new() -> GraphicsControllerRegisters { + GraphicsControllerRegisters { + grx_index: Port::new(GRX_INDEX_ADDRESS), + grx_data: Port::new(GRX_DATA_ADDRESS), + } + } + + pub fn read(&mut self, index: GraphicsControllerIndex) -> u8 { + self.set_index(index); + unsafe { self.grx_data.read() } + } + + pub fn write(&mut self, index: GraphicsControllerIndex, value: u8) { + self.set_index(index); + unsafe { + self.grx_data.write(value); + } + } + + fn set_index(&mut self, index: GraphicsControllerIndex) { + unsafe { + self.grx_index.write(u8::from(index)); + } + } +} diff --git a/src/registers/mod.rs b/src/registers/mod.rs new file mode 100644 index 0000000..a51901c --- /dev/null +++ b/src/registers/mod.rs @@ -0,0 +1,64 @@ +//! Common registers used in vga programming. + +mod attribute_controller; +mod color_palette; +mod crtc_controller; +mod general; +mod graphics_controller; +mod sequencer; + +use crate::colors::PALETTE_SIZE; + +pub use attribute_controller::{AttributeControllerIndex, AttributeControllerRegisters}; +pub use color_palette::ColorPaletteRegisters; +pub use crtc_controller::{CrtcControllerIndex, CrtcControllerRegisters}; +pub use general::GeneralRegisters; +pub use graphics_controller::{GraphicsControllerIndex, GraphicsControllerRegisters}; +pub use sequencer::{SequencerIndex, SequencerRegisters}; + +const ST00_READ_ADDRESS: u16 = 0x3C2; +const ST01_READ_CGA_ADDRESS: u16 = 0x3DA; +const ST01_READ_MDA_ADDRESS: u16 = 0x3BA; +const FCR_READ_ADDRESS: u16 = 0x3CA; +const FCR_CGA_WRITE_ADDRESS: u16 = 0x3DA; +const FCR_MDA_WRITE_ADDRESS: u16 = 0x3BA; +const MSR_READ_ADDRESS: u16 = 0x3CC; +const MSR_WRITE_ADDRESS: u16 = 0x3C2; + +const SRX_INDEX_ADDRESS: u16 = 0x3C4; +const SRX_DATA_ADDRESS: u16 = 0x3C5; + +const GRX_INDEX_ADDRESS: u16 = 0x3CE; +const GRX_DATA_ADDRESS: u16 = 0x3CF; + +const ARX_INDEX_ADDRESS: u16 = 0x3C0; +const ARX_DATA_ADDRESS: u16 = 0x3C1; + +const CRX_INDEX_CGA_ADDRESS: u16 = 0x3D4; +const CRX_INDEX_MDA_ADDRESS: u16 = 0x3B4; +const CRX_DATA_CGA_ADDRESS: u16 = 0x3D5; +const CRX_DATA_MDA_ADDRESS: u16 = 0x3B5; + +const COLOR_PALETTE_DATA_ADDRESS: u16 = 0x3C9; +const COLOR_PALETTE_INDEX_READ_ADDRESS: u16 = 0x3C7; +const COLOR_PALETTE_INDEX_WRITE_ADDRESSS: u16 = 0x3C8; + +/// Represents a vga emulation mode. +#[derive(Debug, Copy, Clone)] +#[repr(u8)] +pub enum EmulationMode { + /// Represents a monochrome emulation mode. + Mda = 0x0, + /// Respresents a color emulation mode. + Cga = 0x1, +} + +impl From for EmulationMode { + fn from(value: u8) -> EmulationMode { + match value { + 0x0 => EmulationMode::Mda, + 0x1 => EmulationMode::Cga, + _ => panic!("{} is an invalid emulation mode", value), + } + } +} diff --git a/src/registers/sequencer.rs b/src/registers/sequencer.rs new file mode 100644 index 0000000..29938c1 --- /dev/null +++ b/src/registers/sequencer.rs @@ -0,0 +1,59 @@ +use super::{SRX_DATA_ADDRESS, SRX_INDEX_ADDRESS}; +use x86_64::instructions::port::Port; + +/// Represents an index for the seqeuncer registers. +#[derive(Debug, Clone, Copy)] +#[repr(u8)] +pub enum SequencerIndex { + /// Represents the `Sequencer Reset` register index. + SequencerReset = 0x0, + /// Represents the `Clocking Mode` register index. + ClockingMode = 0x1, + /// Represents the `Plane/Map` mask register index. + PlaneMask = 0x2, + /// Represents the `Character Font` register index. + CharacterFont = 0x3, + /// Represents the `Memory Mode` register index. + MemoryMode = 0x4, + /// Represents the `Horizontal Character Counter Reset` register index. + CounterReset = 0x7, +} + +impl From for u8 { + fn from(value: SequencerIndex) -> u8 { + value as u8 + } +} + +#[derive(Debug)] +pub struct SequencerRegisters { + srx_index: Port, + srx_data: Port, +} + +impl SequencerRegisters { + pub fn new() -> SequencerRegisters { + SequencerRegisters { + srx_index: Port::new(SRX_INDEX_ADDRESS), + srx_data: Port::new(SRX_DATA_ADDRESS), + } + } + + pub fn read(&mut self, index: SequencerIndex) -> u8 { + self.set_index(index); + unsafe { self.srx_data.read() } + } + + pub fn write(&mut self, index: SequencerIndex, value: u8) { + self.set_index(index); + unsafe { + self.srx_data.write(value); + } + } + + fn set_index(&mut self, index: SequencerIndex) { + unsafe { + self.srx_index.write(u8::from(index)); + } + } +} From a05f2cc8261ea3923df7208c2811fe8e9c7db7d1 Mon Sep 17 00:00:00 2001 From: Ryan Kennedy Date: Sun, 22 Mar 2020 18:49:30 -0500 Subject: [PATCH 06/25] public registers and plane mask --- src/registers/attribute_controller.rs | 2 +- src/registers/color_palette.rs | 2 +- src/registers/crtc_controller.rs | 2 +- src/registers/general.rs | 2 +- src/registers/graphics_controller.rs | 2 +- src/registers/mod.rs | 2 +- src/registers/sequencer.rs | 47 +++++++++++++++++++- src/vga.rs | 62 ++++----------------------- src/writers/graphics_640x480x16.rs | 9 ++-- 9 files changed, 67 insertions(+), 63 deletions(-) diff --git a/src/registers/attribute_controller.rs b/src/registers/attribute_controller.rs index 63554e9..7fdd0b3 100644 --- a/src/registers/attribute_controller.rs +++ b/src/registers/attribute_controller.rs @@ -67,7 +67,7 @@ pub struct AttributeControllerRegisters { } impl AttributeControllerRegisters { - pub fn new() -> AttributeControllerRegisters { + pub(crate) fn new() -> AttributeControllerRegisters { AttributeControllerRegisters { arx_index: Port::new(ARX_INDEX_ADDRESS), arx_data: Port::new(ARX_DATA_ADDRESS), diff --git a/src/registers/color_palette.rs b/src/registers/color_palette.rs index 8f49f2f..83a595d 100644 --- a/src/registers/color_palette.rs +++ b/src/registers/color_palette.rs @@ -12,7 +12,7 @@ pub struct ColorPaletteRegisters { } impl ColorPaletteRegisters { - pub fn new() -> ColorPaletteRegisters { + pub(crate) fn new() -> ColorPaletteRegisters { ColorPaletteRegisters { data_port: Port::new(COLOR_PALETTE_DATA_ADDRESS), index_read_port: Port::new(COLOR_PALETTE_INDEX_READ_ADDRESS), diff --git a/src/registers/crtc_controller.rs b/src/registers/crtc_controller.rs index 8c16388..d9df5d4 100644 --- a/src/registers/crtc_controller.rs +++ b/src/registers/crtc_controller.rs @@ -79,7 +79,7 @@ pub struct CrtcControllerRegisters { } impl CrtcControllerRegisters { - pub fn new() -> CrtcControllerRegisters { + pub(crate) fn new() -> CrtcControllerRegisters { CrtcControllerRegisters { crx_index_cga: Port::new(CRX_INDEX_CGA_ADDRESS), crx_index_mda: Port::new(CRX_INDEX_MDA_ADDRESS), diff --git a/src/registers/general.rs b/src/registers/general.rs index 323f5f5..6f4ddca 100644 --- a/src/registers/general.rs +++ b/src/registers/general.rs @@ -17,7 +17,7 @@ pub struct GeneralRegisters { } impl GeneralRegisters { - pub fn new() -> GeneralRegisters { + pub(crate) fn new() -> GeneralRegisters { GeneralRegisters { st00_read: PortReadOnly::new(ST00_READ_ADDRESS), st01_read_cga: PortReadOnly::new(ST01_READ_CGA_ADDRESS), diff --git a/src/registers/graphics_controller.rs b/src/registers/graphics_controller.rs index 289e508..1aca93b 100644 --- a/src/registers/graphics_controller.rs +++ b/src/registers/graphics_controller.rs @@ -44,7 +44,7 @@ pub struct GraphicsControllerRegisters { } impl GraphicsControllerRegisters { - pub fn new() -> GraphicsControllerRegisters { + pub(crate) fn new() -> GraphicsControllerRegisters { GraphicsControllerRegisters { grx_index: Port::new(GRX_INDEX_ADDRESS), grx_data: Port::new(GRX_DATA_ADDRESS), diff --git a/src/registers/mod.rs b/src/registers/mod.rs index a51901c..5c582c6 100644 --- a/src/registers/mod.rs +++ b/src/registers/mod.rs @@ -14,7 +14,7 @@ pub use color_palette::ColorPaletteRegisters; pub use crtc_controller::{CrtcControllerIndex, CrtcControllerRegisters}; pub use general::GeneralRegisters; pub use graphics_controller::{GraphicsControllerIndex, GraphicsControllerRegisters}; -pub use sequencer::{SequencerIndex, SequencerRegisters}; +pub use sequencer::{PlaneMask, SequencerIndex, SequencerRegisters}; const ST00_READ_ADDRESS: u16 = 0x3C2; const ST01_READ_CGA_ADDRESS: u16 = 0x3DA; diff --git a/src/registers/sequencer.rs b/src/registers/sequencer.rs index 29938c1..097cdeb 100644 --- a/src/registers/sequencer.rs +++ b/src/registers/sequencer.rs @@ -1,6 +1,46 @@ use super::{SRX_DATA_ADDRESS, SRX_INDEX_ADDRESS}; +use bitflags::bitflags; +use core::convert::TryFrom; use x86_64::instructions::port::Port; +bitflags! { + /// Represents the plane masks of the `SequencerIndex::PlaneMask` register. + pub struct PlaneMask: u8 { + /// Represents none of the plane masks of vga memory. + const NONE = 0b0000_0000; + /// Represents `Plane0` of vga memory. + const PLANE0 = 0b0000_0001; + /// Represents `Plane1` of vga memory. + const PLANE1 = 0b0000_0010; + /// Represents `Plane2` of vga memory. + const PLANE2 = 0b0000_0100; + /// Represents `Plane3` of vga memory. + const PLANE3 = 0b0000_1000; + /// Represents all of the plane masks of vga memory. + const ALL_PLANES = Self::PLANE0.bits() | Self::PLANE1.bits() | Self::PLANE2.bits() | Self::PLANE3.bits(); + } +} + +impl TryFrom for PlaneMask { + type Error = &'static str; + + fn try_from(value: u8) -> Result { + match value { + 0 => Ok(PlaneMask::PLANE0), + 1 => Ok(PlaneMask::PLANE1), + 2 => Ok(PlaneMask::PLANE2), + 3 => Ok(PlaneMask::PLANE3), + _ => Err("PlaneMask only accepts values between 0-3!"), + } + } +} + +impl From for u8 { + fn from(value: PlaneMask) -> u8 { + value.bits() + } +} + /// Represents an index for the seqeuncer registers. #[derive(Debug, Clone, Copy)] #[repr(u8)] @@ -32,7 +72,7 @@ pub struct SequencerRegisters { } impl SequencerRegisters { - pub fn new() -> SequencerRegisters { + pub(crate) fn new() -> SequencerRegisters { SequencerRegisters { srx_index: Port::new(SRX_INDEX_ADDRESS), srx_data: Port::new(SRX_DATA_ADDRESS), @@ -51,6 +91,11 @@ impl SequencerRegisters { } } + /// Sets the plane mask of the sequencer controller, as specified by `plane_mask`. + pub fn set_plane_mask(&mut self, plane_mask: PlaneMask) { + self.write(SequencerIndex::PlaneMask, u8::from(plane_mask)); + } + fn set_index(&mut self, index: SequencerIndex) { unsafe { self.srx_index.write(u8::from(index)); diff --git a/src/vga.rs b/src/vga.rs index 5e32437..6f8955c 100644 --- a/src/vga.rs +++ b/src/vga.rs @@ -10,10 +10,10 @@ use super::{ registers::{ AttributeControllerIndex, AttributeControllerRegisters, ColorPaletteRegisters, CrtcControllerIndex, CrtcControllerRegisters, EmulationMode, GeneralRegisters, - GraphicsControllerIndex, GraphicsControllerRegisters, SequencerIndex, SequencerRegisters, + GraphicsControllerIndex, GraphicsControllerRegisters, PlaneMask, SequencerIndex, + SequencerRegisters, }, }; -use bitflags::bitflags; use conquer_once::spin::Lazy; use core::convert::TryFrom; use spinning_top::Spinlock; @@ -86,44 +86,6 @@ impl From for u8 { } } -bitflags! { - /// Represents the plane masks of the `SequencerIndex::PlaneMask` register. - pub struct PlaneMask: u8 { - /// Represents none of the plane masks of vga memory. - const NONE = 0b0000_0000; - /// Represents `Plane0` of vga memory. - const PLANE0 = 0b0000_0001; - /// Represents `Plane1` of vga memory. - const PLANE1 = 0b0000_0010; - /// Represents `Plane2` of vga memory. - const PLANE2 = 0b0000_0100; - /// Represents `Plane3` of vga memory. - const PLANE3 = 0b0000_1000; - /// Represents all of the plane masks of vga memory. - const ALL_PLANES = Self::PLANE0.bits() | Self::PLANE1.bits() | Self::PLANE2.bits() | Self::PLANE3.bits(); - } -} - -impl TryFrom for PlaneMask { - type Error = &'static str; - - fn try_from(value: u8) -> Result { - match value { - 0 => Ok(PlaneMask::PLANE0), - 1 => Ok(PlaneMask::PLANE1), - 2 => Ok(PlaneMask::PLANE2), - 3 => Ok(PlaneMask::PLANE3), - _ => Err("PlaneMask only accepts values between 0-3!"), - } - } -} - -impl From for u8 { - fn from(value: PlaneMask) -> u8 { - value.bits() - } -} - /// Represents a specified vga video mode. #[derive(Debug, Clone, Copy)] pub enum VideoMode { @@ -140,12 +102,12 @@ pub enum VideoMode { /// Represents a vga graphics card with it's common registers, /// as well as the most recent video mode. pub struct Vga { - general_registers: GeneralRegisters, - sequencer_registers: SequencerRegisters, - graphics_controller_registers: GraphicsControllerRegisters, - attribute_controller_registers: AttributeControllerRegisters, - crtc_controller_registers: CrtcControllerRegisters, - color_palette_registers: ColorPaletteRegisters, + pub general_registers: GeneralRegisters, + pub sequencer_registers: SequencerRegisters, + pub graphics_controller_registers: GraphicsControllerRegisters, + pub attribute_controller_registers: AttributeControllerRegisters, + pub crtc_controller_registers: CrtcControllerRegisters, + pub color_palette_registers: ColorPaletteRegisters, most_recent_video_mode: Option, } @@ -280,7 +242,7 @@ impl Vga { ); // Write font to plane - self.set_plane_mask(PlaneMask::PLANE2); + self.sequencer_registers.set_plane_mask(PlaneMask::PLANE2); let frame_buffer = u32::from(self.get_frame_buffer()) as *mut u8; @@ -340,12 +302,6 @@ impl Vga { ) } - /// Sets the plane mask of the sequencer controller, as specified by `plane_mask`. - pub fn set_plane_mask(&mut self, plane_mask: PlaneMask) { - self.sequencer_registers - .write(SequencerIndex::PlaneMask, u8::from(plane_mask)); - } - /// Sets the read plane of the graphics controller, as specified by `read_plane`. pub fn set_read_plane(&mut self, read_plane: ReadPlane) { let read_plane = u8::from(read_plane) & 0x3; diff --git a/src/writers/graphics_640x480x16.rs b/src/writers/graphics_640x480x16.rs index e471cd4..7a186ef 100644 --- a/src/writers/graphics_640x480x16.rs +++ b/src/writers/graphics_640x480x16.rs @@ -1,6 +1,7 @@ use crate::{ colors::{Color16Bit, DEFAULT_PALETTE}, - vga::{PlaneMask, Vga, VideoMode, VGA}, + registers::PlaneMask, + vga::{Vga, VideoMode, VGA}, }; use core::convert::TryInto; use spinning_top::SpinlockGuard; @@ -35,7 +36,8 @@ impl Graphics640x480x16 { /// Clears the screen by setting all pixels to `Color16Bit::Black`. pub fn clear_screen(&self) { let (mut vga, frame_buffer) = self.get_frame_buffer(); - vga.set_plane_mask(PlaneMask::ALL_PLANES); + vga.sequencer_registers + .set_plane_mask(PlaneMask::ALL_PLANES); vga.set_graphics_enable_set_reset(PlaneMask::NONE); for offset in 0..ALL_PLANES_SCREEN_SIZE { unsafe { @@ -58,7 +60,8 @@ impl Graphics640x480x16 { for plane in 0u8..4u8 { vga.set_read_plane(plane.try_into().unwrap()); - vga.set_plane_mask(plane.try_into().unwrap()); + vga.sequencer_registers + .set_plane_mask(plane.try_into().unwrap()); let current_value = unsafe { frame_buffer.add(offset).read_volatile() }; let new_value = if plane_mask & color as u8 != 0 { current_value | mask From b5906f42d5011a97a0be9575ee048a62ebe5ffaf Mon Sep 17 00:00:00 2001 From: Ryan Kennedy Date: Sun, 22 Mar 2020 19:02:01 -0500 Subject: [PATCH 07/25] Bunch of docs --- src/registers/attribute_controller.rs | 37 +++++++++++++++------------ src/registers/color_palette.rs | 5 ++++ src/registers/crtc_controller.rs | 5 ++++ src/registers/general.rs | 3 +++ src/registers/graphics_controller.rs | 5 ++++ src/registers/sequencer.rs | 3 +++ src/vga.rs | 6 +++++ 7 files changed, 48 insertions(+), 16 deletions(-) diff --git a/src/registers/attribute_controller.rs b/src/registers/attribute_controller.rs index 7fdd0b3..9a1e214 100644 --- a/src/registers/attribute_controller.rs +++ b/src/registers/attribute_controller.rs @@ -58,6 +58,7 @@ impl From for u8 { } } +/// Represents the attribute controller registers on vga hardware. #[derive(Debug)] pub struct AttributeControllerRegisters { arx_index: Port, @@ -76,12 +77,16 @@ impl AttributeControllerRegisters { } } + /// Reads the current value of the attribute controller, as specified + /// by `emulation_mode` and `index`. pub fn read(&mut self, emulation_mode: EmulationMode, index: AttributeControllerIndex) -> u8 { self.toggle_index(emulation_mode); self.set_index(index); unsafe { self.arx_data.read() } } + /// Writes the `value` to the attribute controller, as specified + /// `emulation_mode` and `index`. pub fn write( &mut self, emulation_mode: EmulationMode, @@ -95,22 +100,6 @@ impl AttributeControllerRegisters { } } - fn set_index(&mut self, index: AttributeControllerIndex) { - unsafe { - self.arx_index.write(u8::from(index)); - } - } - - fn toggle_index(&mut self, emulation_mode: EmulationMode) { - let st01_read = match emulation_mode { - EmulationMode::Cga => &mut self.st01_read_cga, - EmulationMode::Mda => &mut self.st01_read_mda, - }; - unsafe { - st01_read.read(); - } - } - /// Video Enable. Note that In the VGA standard, this is called the "Palette Address Source" bit. /// Clearing this bit will cause the VGA display data to become all 00 index values. For the default /// palette, this will cause a black screen. The video timing signals continue. Another control bit will @@ -142,4 +131,20 @@ impl AttributeControllerRegisters { self.arx_index.write(arx_index_value | 0x20); } } + + fn set_index(&mut self, index: AttributeControllerIndex) { + unsafe { + self.arx_index.write(u8::from(index)); + } + } + + fn toggle_index(&mut self, emulation_mode: EmulationMode) { + let st01_read = match emulation_mode { + EmulationMode::Cga => &mut self.st01_read_cga, + EmulationMode::Mda => &mut self.st01_read_mda, + }; + unsafe { + st01_read.read(); + } + } } diff --git a/src/registers/color_palette.rs b/src/registers/color_palette.rs index 83a595d..e148e03 100644 --- a/src/registers/color_palette.rs +++ b/src/registers/color_palette.rs @@ -4,6 +4,7 @@ use super::{ }; use x86_64::instructions::port::Port; +/// Represents the color palette registers on vga hardware. #[derive(Debug)] pub struct ColorPaletteRegisters { data_port: Port, @@ -20,6 +21,8 @@ impl ColorPaletteRegisters { } } + /// Loads a 256 color palette, as specified by `palette`, with every 3 + /// bytes representing a color. pub fn load_palette(&mut self, palette: &[u8; PALETTE_SIZE]) { unsafe { self.index_write_port.write(0); @@ -31,6 +34,8 @@ impl ColorPaletteRegisters { } } + /// Reads the current 256 color palette into `palette`, with every 3 + /// bytes representing a color. pub fn read_palette(&mut self, palette: &mut [u8; PALETTE_SIZE]) { unsafe { self.index_read_port.write(0); diff --git a/src/registers/crtc_controller.rs b/src/registers/crtc_controller.rs index d9df5d4..b8bb34a 100644 --- a/src/registers/crtc_controller.rs +++ b/src/registers/crtc_controller.rs @@ -70,6 +70,7 @@ impl From for u8 { } } +/// Represents the crtc controller registers on vga hardware. #[derive(Debug)] pub struct CrtcControllerRegisters { crx_index_cga: Port, @@ -88,11 +89,15 @@ impl CrtcControllerRegisters { } } + /// Reads the current value from the crtc controller, as specified + /// by `emulation_mode` and `index`. pub fn read(&mut self, emulation_mode: EmulationMode, index: CrtcControllerIndex) -> u8 { self.set_index(emulation_mode, index); unsafe { self.get_data_port(emulation_mode).read() } } + /// Writes the `value` to the crtc_controller, as specified + /// by `emulation_mode` and `index`. pub fn write(&mut self, emulation_mode: EmulationMode, index: CrtcControllerIndex, value: u8) { self.set_index(emulation_mode, index); unsafe { diff --git a/src/registers/general.rs b/src/registers/general.rs index 6f4ddca..e6392c5 100644 --- a/src/registers/general.rs +++ b/src/registers/general.rs @@ -4,6 +4,7 @@ use super::{ }; use x86_64::instructions::port::{PortReadOnly, PortWriteOnly}; +/// Represents the general registers on vga hardware. #[derive(Debug)] pub struct GeneralRegisters { st00_read: PortReadOnly, @@ -30,10 +31,12 @@ impl GeneralRegisters { } } + /// Reads the current value from the miscellaneous output register. pub fn read_msr(&mut self) -> u8 { unsafe { self.msr_read.read() } } + /// Writes the `value` to the miscellaneous output register. pub fn write_msr(&mut self, value: u8) { unsafe { self.msr_write.write(value); diff --git a/src/registers/graphics_controller.rs b/src/registers/graphics_controller.rs index 1aca93b..716cd02 100644 --- a/src/registers/graphics_controller.rs +++ b/src/registers/graphics_controller.rs @@ -37,6 +37,7 @@ impl From for u8 { } } +/// Represents the graphics controller registers on vga hardware. #[derive(Debug)] pub struct GraphicsControllerRegisters { grx_index: Port, @@ -51,11 +52,15 @@ impl GraphicsControllerRegisters { } } + /// Reads the current value from the graphics controller, as specified + /// by `index`. pub fn read(&mut self, index: GraphicsControllerIndex) -> u8 { self.set_index(index); unsafe { self.grx_data.read() } } + /// Writes the `value` to the graphics controller, as specified + /// by `index. pub fn write(&mut self, index: GraphicsControllerIndex, value: u8) { self.set_index(index); unsafe { diff --git a/src/registers/sequencer.rs b/src/registers/sequencer.rs index 097cdeb..1612dc9 100644 --- a/src/registers/sequencer.rs +++ b/src/registers/sequencer.rs @@ -65,6 +65,7 @@ impl From for u8 { } } +/// Represents the sequencer registers on vga hardware. #[derive(Debug)] pub struct SequencerRegisters { srx_index: Port, @@ -79,11 +80,13 @@ impl SequencerRegisters { } } + /// Reads the current value from the sequencer, as specified by `index`. pub fn read(&mut self, index: SequencerIndex) -> u8 { self.set_index(index); unsafe { self.srx_data.read() } } + /// Writes the `value` to the sequencer, as specified by `index`. pub fn write(&mut self, index: SequencerIndex, value: u8) { self.set_index(index); unsafe { diff --git a/src/vga.rs b/src/vga.rs index 6f8955c..81ecdc2 100644 --- a/src/vga.rs +++ b/src/vga.rs @@ -102,11 +102,17 @@ pub enum VideoMode { /// Represents a vga graphics card with it's common registers, /// as well as the most recent video mode. pub struct Vga { + /// Represents the general registers on vga hardware. pub general_registers: GeneralRegisters, + /// Represents the sequencer registers on vga hardware. pub sequencer_registers: SequencerRegisters, + /// Represents the graphics controller registers on vga hardware. pub graphics_controller_registers: GraphicsControllerRegisters, + /// Represents the attribute controller registers on vga hardware. pub attribute_controller_registers: AttributeControllerRegisters, + /// Represents the crtc controller registers on vga hardware. pub crtc_controller_registers: CrtcControllerRegisters, + /// Represents the color palette registers on vga hardware. pub color_palette_registers: ColorPaletteRegisters, most_recent_video_mode: Option, } From 15825f7828d0aaba7a059a8d5d989e7cdeb39a7a Mon Sep 17 00:00:00 2001 From: Ryan Kennedy Date: Sun, 22 Mar 2020 20:03:06 -0500 Subject: [PATCH 08/25] Moved general read/write out of vga --- src/vga.rs | 63 ------------------------------ src/writers/graphics_640x480x16.rs | 2 +- src/writers/mod.rs | 34 +++++++++------- src/writers/text_40x25.rs | 2 +- src/writers/text_40x50.rs | 2 +- src/writers/text_80x25.rs | 2 +- 6 files changed, 24 insertions(+), 81 deletions(-) diff --git a/src/vga.rs b/src/vga.rs index 81ecdc2..c7a361b 100644 --- a/src/vga.rs +++ b/src/vga.rs @@ -156,74 +156,11 @@ impl Vga { self.most_recent_video_mode } - /// Returns the current value of the miscellaneous output register. - pub fn read_msr(&mut self) -> u8 { - self.general_registers.read_msr() - } - - /// Returns the current value of the sequencer register, as determined by `index`. - pub fn read_sequencer(&mut self, index: SequencerIndex) -> u8 { - self.sequencer_registers.read(index) - } - - /// Returns the current value of the graphics controller register, as determined by `index`. - pub fn read_graphics_controller(&mut self, index: GraphicsControllerIndex) -> u8 { - self.graphics_controller_registers.read(index) - } - - /// Returns the current value of the attribute controller register, as determined by `emulation_mode` - /// and `index`. - pub fn read_attribute_controller( - &mut self, - emulation_mode: EmulationMode, - index: AttributeControllerIndex, - ) -> u8 { - self.attribute_controller_registers - .read(emulation_mode, index) - } - - /// Returns the current value of the crtc controller, as determined by `emulation_mode` - /// and `index`. - pub fn read_crtc_controller( - &mut self, - emulation_mode: EmulationMode, - index: CrtcControllerIndex, - ) -> u8 { - self.crtc_controller_registers.read(emulation_mode, index) - } - - /// Writes `value` to the crtc controller, as determined by `index`. - pub fn write_crtc_controller( - &mut self, - emulation_mode: EmulationMode, - index: CrtcControllerIndex, - value: u8, - ) { - self.crtc_controller_registers - .write(emulation_mode, index, value); - } - /// Returns the current `EmulationMode` as determined by the miscellaneous output register. pub fn get_emulation_mode(&mut self) -> EmulationMode { EmulationMode::from(self.general_registers.read_msr() & 0x1) } - /// Loads a new palette into the vga, as specified by `palette`. - /// - /// Each palette must be `PALETTE_SIZE` bytes long, with every 3 - /// bytes representing one color `(R, G, B)`. - pub fn load_palette(&mut self, palette: &[u8; PALETTE_SIZE]) { - self.color_palette_registers.load_palette(palette); - } - - /// Reads the current vga palette into `palette`. - /// - /// Each palette must be `PALETTE_SIZE` bytes long, with every 3 - /// bytes representing one color `(R, G, B)`. - pub fn read_palette(&mut self, palette: &mut [u8; PALETTE_SIZE]) { - self.color_palette_registers.read_palette(palette); - } - /// Loads a vga text mode font as specified by `vga_font`. pub fn load_font(&mut self, vga_font: &VgaFont) { // Save registers diff --git a/src/writers/graphics_640x480x16.rs b/src/writers/graphics_640x480x16.rs index 7a186ef..dcace91 100644 --- a/src/writers/graphics_640x480x16.rs +++ b/src/writers/graphics_640x480x16.rs @@ -82,7 +82,7 @@ impl Graphics640x480x16 { // Some bios mess up the palette when switching modes, // so explicitly set it. - vga.load_palette(&DEFAULT_PALETTE); + vga.color_palette_registers.load_palette(&DEFAULT_PALETTE); } /// Returns the start of the `FrameBuffer` as `*mut u8` as diff --git a/src/writers/mod.rs b/src/writers/mod.rs index 02719c2..1154faa 100644 --- a/src/writers/mod.rs +++ b/src/writers/mod.rs @@ -84,9 +84,10 @@ pub trait TextWriter { fn disable_cursor(&self) { let (mut vga, _frame_buffer) = self.get_frame_buffer(); let emulation_mode = vga.get_emulation_mode(); - let cursor_start = - vga.read_crtc_controller(emulation_mode, CrtcControllerIndex::TextCursorStart); - vga.write_crtc_controller( + let cursor_start = vga + .crtc_controller_registers + .read(emulation_mode, CrtcControllerIndex::TextCursorStart); + vga.crtc_controller_registers.write( emulation_mode, CrtcControllerIndex::TextCursorStart, cursor_start | 0x20, @@ -97,9 +98,10 @@ pub trait TextWriter { fn enable_cursor(&self) { let (mut vga, _frame_buffer) = self.get_frame_buffer(); let emulation_mode = vga.get_emulation_mode(); - let cursor_start = - vga.read_crtc_controller(emulation_mode, CrtcControllerIndex::TextCursorStart); - vga.write_crtc_controller( + let cursor_start = vga + .crtc_controller_registers + .read(emulation_mode, CrtcControllerIndex::TextCursorStart); + vga.crtc_controller_registers.write( emulation_mode, CrtcControllerIndex::TextCursorStart, cursor_start & 0xDF, @@ -122,16 +124,20 @@ pub trait TextWriter { fn set_cursor(&self, scan_line_start: u8, scan_line_end: u8) { let (mut vga, _frame_buffer) = self.get_frame_buffer(); let emulation_mode = vga.get_emulation_mode(); - let cursor_start = - vga.read_crtc_controller(emulation_mode, CrtcControllerIndex::TextCursorStart) & 0xC0; - let cursor_end = - vga.read_crtc_controller(emulation_mode, CrtcControllerIndex::TextCursorEnd) & 0xE0; - vga.write_crtc_controller( + let cursor_start = vga + .crtc_controller_registers + .read(emulation_mode, CrtcControllerIndex::TextCursorStart) + & 0xC0; + let cursor_end = vga + .crtc_controller_registers + .read(emulation_mode, CrtcControllerIndex::TextCursorEnd) + & 0xE0; + vga.crtc_controller_registers.write( emulation_mode, CrtcControllerIndex::TextCursorStart, cursor_start | scan_line_start, ); - vga.write_crtc_controller( + vga.crtc_controller_registers.write( emulation_mode, CrtcControllerIndex::TextCursorEnd, cursor_end | scan_line_end, @@ -146,12 +152,12 @@ pub trait TextWriter { let emulation_mode = vga.get_emulation_mode(); let cursor_start = offset & 0xFF; let cursor_end = (offset >> 8) & 0xFF; - vga.write_crtc_controller( + vga.crtc_controller_registers.write( emulation_mode, CrtcControllerIndex::TextCursorLocationLow, cursor_start as u8, ); - vga.write_crtc_controller( + vga.crtc_controller_registers.write( emulation_mode, CrtcControllerIndex::TextCursorLocationHigh, cursor_end as u8, diff --git a/src/writers/text_40x25.rs b/src/writers/text_40x25.rs index 5eb92f2..29f9cd8 100644 --- a/src/writers/text_40x25.rs +++ b/src/writers/text_40x25.rs @@ -45,7 +45,7 @@ impl TextWriter for Text40x25 { // Some bios mess up the palette when switching modes, // so explicitly set it. - vga.load_palette(&DEFAULT_PALETTE); + vga.color_palette_registers.load_palette(&DEFAULT_PALETTE); vga.load_font(&TEXT_8X16_FONT); } } diff --git a/src/writers/text_40x50.rs b/src/writers/text_40x50.rs index ac604f4..e7a45c5 100644 --- a/src/writers/text_40x50.rs +++ b/src/writers/text_40x50.rs @@ -45,7 +45,7 @@ impl TextWriter for Text40x50 { // Some bios mess up the palette when switching modes, // so explicitly set it. - vga.load_palette(&DEFAULT_PALETTE); + vga.color_palette_registers.load_palette(&DEFAULT_PALETTE); vga.load_font(&TEXT_8X8_FONT); } } diff --git a/src/writers/text_80x25.rs b/src/writers/text_80x25.rs index aadb975..9929755 100644 --- a/src/writers/text_80x25.rs +++ b/src/writers/text_80x25.rs @@ -44,7 +44,7 @@ impl TextWriter for Text80x25 { // Some bios mess up the palette when switching modes, // so explicitly set it. - vga.load_palette(&DEFAULT_PALETTE); + vga.color_palette_registers.load_palette(&DEFAULT_PALETTE); vga.load_font(&TEXT_8X16_FONT); } } From c424b3134efc68783a3c425d729f3b66a399a0e8 Mon Sep 17 00:00:00 2001 From: Ryan Kennedy Date: Sun, 22 Mar 2020 20:09:22 -0500 Subject: [PATCH 09/25] More refactoring --- src/registers/graphics_controller.rs | 64 ++++++++++++++++++++++- src/registers/mod.rs | 2 +- src/vga.rs | 77 ++-------------------------- src/writers/graphics_640x480x16.rs | 6 ++- 4 files changed, 71 insertions(+), 78 deletions(-) diff --git a/src/registers/graphics_controller.rs b/src/registers/graphics_controller.rs index 716cd02..5769ff9 100644 --- a/src/registers/graphics_controller.rs +++ b/src/registers/graphics_controller.rs @@ -1,6 +1,42 @@ -use super::{GRX_DATA_ADDRESS, GRX_INDEX_ADDRESS}; +use super::{Color16Bit, PlaneMask, GRX_DATA_ADDRESS, GRX_INDEX_ADDRESS}; +use core::convert::TryFrom; use x86_64::instructions::port::Port; +/// Represents a plane for the `GraphicsControllerIndex::ReadPlaneSelect` register. +#[allow(dead_code)] +#[derive(Debug, Copy, Clone)] +#[repr(u8)] +pub enum ReadPlane { + /// Represents `Plane 0 (0x0)`. + Plane0 = 0x0, + /// Represents `Plane 1 (0x1)`. + Plane1 = 0x1, + /// Represents `Plane 2 (0x2)`. + Plane2 = 0x2, + /// Represents `Plane 3 (0x3)`. + Plane3 = 0x3, +} + +impl TryFrom for ReadPlane { + type Error = &'static str; + + fn try_from(value: u8) -> Result { + match value { + 0 => Ok(ReadPlane::Plane0), + 1 => Ok(ReadPlane::Plane1), + 2 => Ok(ReadPlane::Plane2), + 3 => Ok(ReadPlane::Plane3), + _ => Err("ReadPlane only accepts values between 0-3!"), + } + } +} + +impl From for u8 { + fn from(value: ReadPlane) -> u8 { + value as u8 + } +} + /// Represents an index for the graphics controller registers. #[derive(Debug, Copy, Clone)] #[repr(u8)] @@ -68,6 +104,32 @@ impl GraphicsControllerRegisters { } } + /// Sets the read plane of the graphics controller, as specified by `read_plane`. + pub fn write_read_plane(&mut self, read_plane: ReadPlane) { + let read_plane = u8::from(read_plane) & 0x3; + self.write(GraphicsControllerIndex::ReadPlaneSelect, read_plane); + } + + /// Sets the value to use for `GraphicsControllerIndex::SetReset`, + /// as spcified by `color`. + pub fn write_set_reset(&mut self, color: Color16Bit) { + let original_value = self.read(GraphicsControllerIndex::SetReset) & 0xF0; + self.write( + GraphicsControllerIndex::SetReset, + original_value | u8::from(color), + ); + } + + /// Sets which planes are effected by `GraphicsControllerIndex::SetReset`, + /// as specified by `plane_mask`. + pub fn write_enable_set_reset(&mut self, plane_mask: PlaneMask) { + let original_value = self.read(GraphicsControllerIndex::EnableSetReset) & 0xF0; + self.write( + GraphicsControllerIndex::EnableSetReset, + original_value | u8::from(plane_mask), + ); + } + fn set_index(&mut self, index: GraphicsControllerIndex) { unsafe { self.grx_index.write(u8::from(index)); diff --git a/src/registers/mod.rs b/src/registers/mod.rs index 5c582c6..32f0a80 100644 --- a/src/registers/mod.rs +++ b/src/registers/mod.rs @@ -7,7 +7,7 @@ mod general; mod graphics_controller; mod sequencer; -use crate::colors::PALETTE_SIZE; +use crate::colors::{Color16Bit, PALETTE_SIZE}; pub use attribute_controller::{AttributeControllerIndex, AttributeControllerRegisters}; pub use color_palette::ColorPaletteRegisters; diff --git a/src/vga.rs b/src/vga.rs index c7a361b..e46ad63 100644 --- a/src/vga.rs +++ b/src/vga.rs @@ -1,21 +1,18 @@ //! Provides access to the vga graphics card. use super::{ - colors::{Color16Bit, PALETTE_SIZE}, configurations::{ VgaConfiguration, MODE_40X25_CONFIGURATION, MODE_40X50_CONFIGURATION, MODE_640X480X16_CONFIGURATION, MODE_80X25_CONFIGURATION, }, fonts::VgaFont, registers::{ - AttributeControllerIndex, AttributeControllerRegisters, ColorPaletteRegisters, - CrtcControllerIndex, CrtcControllerRegisters, EmulationMode, GeneralRegisters, - GraphicsControllerIndex, GraphicsControllerRegisters, PlaneMask, SequencerIndex, - SequencerRegisters, + AttributeControllerRegisters, ColorPaletteRegisters, CrtcControllerIndex, + CrtcControllerRegisters, EmulationMode, GeneralRegisters, GraphicsControllerIndex, + GraphicsControllerRegisters, PlaneMask, SequencerIndex, SequencerRegisters, }, }; use conquer_once::spin::Lazy; -use core::convert::TryFrom; use spinning_top::Spinlock; /// Provides mutable access to the vga graphics card. @@ -51,41 +48,6 @@ impl From for u32 { } } -/// Represents a plane for the `GraphicsControllerIndex::ReadPlaneSelect` register. -#[allow(dead_code)] -#[derive(Debug, Copy, Clone)] -#[repr(u8)] -pub enum ReadPlane { - /// Represents `Plane 0 (0x0)`. - Plane0 = 0x0, - /// Represents `Plane 1 (0x1)`. - Plane1 = 0x1, - /// Represents `Plane 2 (0x2)`. - Plane2 = 0x2, - /// Represents `Plane 3 (0x3)`. - Plane3 = 0x3, -} - -impl TryFrom for ReadPlane { - type Error = &'static str; - - fn try_from(value: u8) -> Result { - match value { - 0 => Ok(ReadPlane::Plane0), - 1 => Ok(ReadPlane::Plane1), - 2 => Ok(ReadPlane::Plane2), - 3 => Ok(ReadPlane::Plane3), - _ => Err("ReadPlane only accepts values between 0-3!"), - } - } -} - -impl From for u8 { - fn from(value: ReadPlane) -> u8 { - value as u8 - } -} - /// Represents a specified vga video mode. #[derive(Debug, Clone, Copy)] pub enum VideoMode { @@ -245,39 +207,6 @@ impl Vga { ) } - /// Sets the read plane of the graphics controller, as specified by `read_plane`. - pub fn set_read_plane(&mut self, read_plane: ReadPlane) { - let read_plane = u8::from(read_plane) & 0x3; - self.graphics_controller_registers - .write(GraphicsControllerIndex::ReadPlaneSelect, read_plane); - } - - /// Sets the value to use for `GraphicsControllerIndex::SetReset`, - /// as spcified by `color`. - pub fn set_graphics_set_reset(&mut self, color: Color16Bit) { - let original_value = self - .graphics_controller_registers - .read(GraphicsControllerIndex::SetReset) - & 0xF0; - self.graphics_controller_registers.write( - GraphicsControllerIndex::SetReset, - original_value | u8::from(color), - ); - } - - /// Sets which planes are effected by `GraphicsControllerIndex::SetReset`, - /// as specified by `plane_mask`. - pub fn set_graphics_enable_set_reset(&mut self, plane_mask: PlaneMask) { - let original_value = self - .graphics_controller_registers - .read(GraphicsControllerIndex::EnableSetReset) - & 0xF0; - self.graphics_controller_registers.write( - GraphicsControllerIndex::EnableSetReset, - original_value | u8::from(plane_mask), - ); - } - fn set_registers(&mut self, configuration: &VgaConfiguration) { let emulation_mode = self.get_emulation_mode(); diff --git a/src/writers/graphics_640x480x16.rs b/src/writers/graphics_640x480x16.rs index dcace91..ccb2514 100644 --- a/src/writers/graphics_640x480x16.rs +++ b/src/writers/graphics_640x480x16.rs @@ -38,7 +38,8 @@ impl Graphics640x480x16 { let (mut vga, frame_buffer) = self.get_frame_buffer(); vga.sequencer_registers .set_plane_mask(PlaneMask::ALL_PLANES); - vga.set_graphics_enable_set_reset(PlaneMask::NONE); + vga.graphics_controller_registers + .write_enable_set_reset(PlaneMask::NONE); for offset in 0..ALL_PLANES_SCREEN_SIZE { unsafe { frame_buffer @@ -59,7 +60,8 @@ impl Graphics640x480x16 { let mut plane_mask = 0x01; for plane in 0u8..4u8 { - vga.set_read_plane(plane.try_into().unwrap()); + vga.graphics_controller_registers + .write_read_plane(plane.try_into().unwrap()); vga.sequencer_registers .set_plane_mask(plane.try_into().unwrap()); let current_value = unsafe { frame_buffer.add(offset).read_volatile() }; From 51bdc82df9cb81dfca69970def70e814fbddca1a Mon Sep 17 00:00:00 2001 From: Ryan Kennedy Date: Sun, 22 Mar 2020 22:06:45 -0500 Subject: [PATCH 10/25] Screen trait --- src/writers/mod.rs | 14 +++++++++----- src/writers/text_40x25.rs | 10 ++++++++-- src/writers/text_40x50.rs | 10 ++++++++-- src/writers/text_80x25.rs | 10 ++++++++-- 4 files changed, 33 insertions(+), 11 deletions(-) diff --git a/src/writers/mod.rs b/src/writers/mod.rs index 1154faa..5dc74af 100644 --- a/src/writers/mod.rs +++ b/src/writers/mod.rs @@ -46,14 +46,18 @@ static BLANK_CHARACTER: ScreenCharacter = ScreenCharacter { 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`. +/// A helper trait used to interact with various vga screens. +pub trait Screen { + /// Returns the width of the `Screen`. fn get_width(&self) -> usize; - - /// Returns the height of the `TextWriter`. + /// Returns the height of the `Screen`. fn get_height(&self) -> usize; + /// Returns the size of the `Screen`. + fn get_size(&self) -> usize; +} +/// A helper trait used to interact with various vga text modes. +pub trait TextWriter: Screen { /// Sets the graphics device to a video mode as determined by /// the `TextWriter` implementation. fn set_mode(&self); diff --git a/src/writers/text_40x25.rs b/src/writers/text_40x25.rs index 29f9cd8..545a10a 100644 --- a/src/writers/text_40x25.rs +++ b/src/writers/text_40x25.rs @@ -1,4 +1,4 @@ -use super::TextWriter; +use super::{Screen, TextWriter}; use crate::{ colors::DEFAULT_PALETTE, fonts::TEXT_8X16_FONT, @@ -29,7 +29,7 @@ const HEIGHT: usize = 25; #[derive(Default)] pub struct Text40x25; -impl TextWriter for Text40x25 { +impl Screen for Text40x25 { fn get_width(&self) -> usize { WIDTH } @@ -38,6 +38,12 @@ impl TextWriter for Text40x25 { HEIGHT } + fn get_size(&self) -> usize { + WIDTH * HEIGHT + } +} + +impl TextWriter for Text40x25 { /// Sets the graphics device to `VideoMode::Mode40x25`. fn set_mode(&self) { let mut vga = VGA.lock(); diff --git a/src/writers/text_40x50.rs b/src/writers/text_40x50.rs index e7a45c5..02766b9 100644 --- a/src/writers/text_40x50.rs +++ b/src/writers/text_40x50.rs @@ -1,4 +1,4 @@ -use super::TextWriter; +use super::{Screen, TextWriter}; use crate::{ colors::DEFAULT_PALETTE, fonts::TEXT_8X8_FONT, @@ -29,7 +29,7 @@ const HEIGHT: usize = 50; #[derive(Default)] pub struct Text40x50; -impl TextWriter for Text40x50 { +impl Screen for Text40x50 { fn get_width(&self) -> usize { WIDTH } @@ -38,6 +38,12 @@ impl TextWriter for Text40x50 { HEIGHT } + fn get_size(&self) -> usize { + WIDTH * HEIGHT + } +} + +impl TextWriter for Text40x50 { /// Sets the graphics device to `VideoMode::Mode40x50`. fn set_mode(&self) { let mut vga = VGA.lock(); diff --git a/src/writers/text_80x25.rs b/src/writers/text_80x25.rs index 9929755..ed0d473 100644 --- a/src/writers/text_80x25.rs +++ b/src/writers/text_80x25.rs @@ -1,4 +1,4 @@ -use super::TextWriter; +use super::{Screen, TextWriter}; use crate::{ colors::DEFAULT_PALETTE, fonts::TEXT_8X16_FONT, @@ -29,7 +29,7 @@ const HEIGHT: usize = 25; #[derive(Default)] pub struct Text80x25; -impl TextWriter for Text80x25 { +impl Screen for Text80x25 { fn get_width(&self) -> usize { WIDTH } @@ -38,6 +38,12 @@ impl TextWriter for Text80x25 { HEIGHT } + fn get_size(&self) -> usize { + WIDTH * HEIGHT + } +} + +impl TextWriter for Text80x25 { fn set_mode(&self) { let mut vga = VGA.lock(); vga.set_video_mode(VideoMode::Mode80x25); From c4486427c076c8c4e65c4b2a5a43bbee96d092c4 Mon Sep 17 00:00:00 2001 From: Ryan Kennedy Date: Sun, 22 Mar 2020 23:11:22 -0500 Subject: [PATCH 11/25] Faster set_pixel --- src/registers/graphics_controller.rs | 27 ++++++++++++++++++ src/registers/mod.rs | 2 +- src/registers/sequencer.rs | 6 +++- src/writers/graphics_640x480x16.rs | 41 +++++++++------------------- 4 files changed, 46 insertions(+), 30 deletions(-) diff --git a/src/registers/graphics_controller.rs b/src/registers/graphics_controller.rs index 5769ff9..057c3e7 100644 --- a/src/registers/graphics_controller.rs +++ b/src/registers/graphics_controller.rs @@ -73,6 +73,21 @@ impl From for u8 { } } +#[derive(Debug)] +#[repr(u8)] +pub enum WriteMode { + Mode0 = 0x0, + Mode1 = 0x1, + Mode2 = 0x2, + Mode3 = 0x3, +} + +impl From for u8 { + fn from(value: WriteMode) -> u8 { + value as u8 + } +} + /// Represents the graphics controller registers on vga hardware. #[derive(Debug)] pub struct GraphicsControllerRegisters { @@ -130,6 +145,18 @@ impl GraphicsControllerRegisters { ); } + pub fn set_write_mode(&mut self, write_mode: WriteMode) { + let original_value = self.read(GraphicsControllerIndex::GraphicsMode) & 0xFC; + self.write( + GraphicsControllerIndex::GraphicsMode, + original_value | u8::from(write_mode), + ); + } + + pub fn set_bit_mask(&mut self, bit_mask: u8) { + self.write(GraphicsControllerIndex::BitMask, bit_mask); + } + fn set_index(&mut self, index: GraphicsControllerIndex) { unsafe { self.grx_index.write(u8::from(index)); diff --git a/src/registers/mod.rs b/src/registers/mod.rs index 32f0a80..c53850e 100644 --- a/src/registers/mod.rs +++ b/src/registers/mod.rs @@ -13,7 +13,7 @@ pub use attribute_controller::{AttributeControllerIndex, AttributeControllerRegi pub use color_palette::ColorPaletteRegisters; pub use crtc_controller::{CrtcControllerIndex, CrtcControllerRegisters}; pub use general::GeneralRegisters; -pub use graphics_controller::{GraphicsControllerIndex, GraphicsControllerRegisters}; +pub use graphics_controller::{GraphicsControllerIndex, GraphicsControllerRegisters, WriteMode}; pub use sequencer::{PlaneMask, SequencerIndex, SequencerRegisters}; const ST00_READ_ADDRESS: u16 = 0x3C2; diff --git a/src/registers/sequencer.rs b/src/registers/sequencer.rs index 1612dc9..a161929 100644 --- a/src/registers/sequencer.rs +++ b/src/registers/sequencer.rs @@ -96,7 +96,11 @@ impl SequencerRegisters { /// Sets the plane mask of the sequencer controller, as specified by `plane_mask`. pub fn set_plane_mask(&mut self, plane_mask: PlaneMask) { - self.write(SequencerIndex::PlaneMask, u8::from(plane_mask)); + let original_value = self.read(SequencerIndex::PlaneMask); + self.write( + SequencerIndex::PlaneMask, + original_value | u8::from(plane_mask), + ); } fn set_index(&mut self, index: SequencerIndex) { diff --git a/src/writers/graphics_640x480x16.rs b/src/writers/graphics_640x480x16.rs index ccb2514..faed7b8 100644 --- a/src/writers/graphics_640x480x16.rs +++ b/src/writers/graphics_640x480x16.rs @@ -1,9 +1,8 @@ use crate::{ colors::{Color16Bit, DEFAULT_PALETTE}, - registers::PlaneMask, + registers::{PlaneMask, WriteMode}, vga::{Vga, VideoMode, VGA}, }; -use core::convert::TryInto; use spinning_top::SpinlockGuard; const WIDTH: usize = 640; @@ -34,17 +33,16 @@ impl Graphics640x480x16 { } /// Clears the screen by setting all pixels to `Color16Bit::Black`. - pub fn clear_screen(&self) { + pub fn clear_screen(&self, color: Color16Bit) { let (mut vga, frame_buffer) = self.get_frame_buffer(); vga.sequencer_registers .set_plane_mask(PlaneMask::ALL_PLANES); + vga.graphics_controller_registers.set_bit_mask(0xFF); vga.graphics_controller_registers - .write_enable_set_reset(PlaneMask::NONE); + .set_write_mode(WriteMode::Mode2); for offset in 0..ALL_PLANES_SCREEN_SIZE { unsafe { - frame_buffer - .add(offset) - .write_volatile(Color16Bit::Black as u8); + frame_buffer.add(offset).write_volatile(u8::from(color)); } } } @@ -53,27 +51,14 @@ impl Graphics640x480x16 { pub fn set_pixel(&self, x: usize, y: usize, color: Color16Bit) { let (mut vga, frame_buffer) = self.get_frame_buffer(); let offset = x / 8 + (WIDTH / 8) * y; - - // Store the current value for masking. - let x = x & 7; - let mask = 0x80 >> (x & 7); - let mut plane_mask = 0x01; - - for plane in 0u8..4u8 { - vga.graphics_controller_registers - .write_read_plane(plane.try_into().unwrap()); - vga.sequencer_registers - .set_plane_mask(plane.try_into().unwrap()); - let current_value = unsafe { frame_buffer.add(offset).read_volatile() }; - let new_value = if plane_mask & color as u8 != 0 { - current_value | mask - } else { - current_value & !mask - }; - unsafe { - frame_buffer.add(offset).write_volatile(new_value); - } - plane_mask <<= 1; + let pixel_offset = x & 7; + vga.graphics_controller_registers + .set_write_mode(WriteMode::Mode2); + vga.graphics_controller_registers + .set_bit_mask(1 << pixel_offset); + unsafe { + frame_buffer.add(offset).read_volatile(); + frame_buffer.add(offset).write_volatile(u8::from(color)); } } From 4ab8f4d1d2eb763e65de421f8969e24ba9f23e52 Mon Sep 17 00:00:00 2001 From: Ryan Kennedy Date: Sun, 22 Mar 2020 23:22:51 -0500 Subject: [PATCH 12/25] Always set mode 2 --- src/writers/graphics_640x480x16.rs | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/src/writers/graphics_640x480x16.rs b/src/writers/graphics_640x480x16.rs index faed7b8..03d6a13 100644 --- a/src/writers/graphics_640x480x16.rs +++ b/src/writers/graphics_640x480x16.rs @@ -7,7 +7,7 @@ use spinning_top::SpinlockGuard; const WIDTH: usize = 640; const HEIGHT: usize = 480; -const ALL_PLANES_SCREEN_SIZE: usize = (WIDTH * HEIGHT) / 4; +const ALL_PLANES_SCREEN_SIZE: usize = (WIDTH * HEIGHT) / 8; /// A basic interface for interacting with vga graphics mode 640x480x16 /// @@ -32,14 +32,17 @@ impl Graphics640x480x16 { Graphics640x480x16 {} } - /// Clears the screen by setting all pixels to `Color16Bit::Black`. + /// Clears the screen by setting all pixels to the specified `color`. pub fn clear_screen(&self, color: Color16Bit) { let (mut vga, frame_buffer) = self.get_frame_buffer(); - vga.sequencer_registers - .set_plane_mask(PlaneMask::ALL_PLANES); - vga.graphics_controller_registers.set_bit_mask(0xFF); + // Set write mode 2 so data is modified by the bitmask vga.graphics_controller_registers .set_write_mode(WriteMode::Mode2); + // Write to all 4 planes at once + vga.sequencer_registers + .set_plane_mask(PlaneMask::ALL_PLANES); + // Every bit should be set to the same color + vga.graphics_controller_registers.set_bit_mask(0xFF); for offset in 0..ALL_PLANES_SCREEN_SIZE { unsafe { frame_buffer.add(offset).write_volatile(u8::from(color)); @@ -51,13 +54,22 @@ impl Graphics640x480x16 { pub fn set_pixel(&self, x: usize, y: usize, color: Color16Bit) { let (mut vga, frame_buffer) = self.get_frame_buffer(); let offset = x / 8 + (WIDTH / 8) * y; + // Which pixel to modify this write let pixel_offset = x & 7; + // Set write mode 2 so screen data is only modified by the bitmask vga.graphics_controller_registers .set_write_mode(WriteMode::Mode2); + // Write to all 4 planes at once + vga.sequencer_registers + .set_plane_mask(PlaneMask::ALL_PLANES); + // Only modify 1 pixel, based on the offset vga.graphics_controller_registers .set_bit_mask(1 << pixel_offset); unsafe { + // Reads the current offset into the memory latches frame_buffer.add(offset).read_volatile(); + // Sets the pixel specified by the offset to the color. The + // pixels not inlcuded in the bit mask remain untouched. frame_buffer.add(offset).write_volatile(u8::from(color)); } } From f43ef646a74d570191e9e4c3e6fbd7881a6f5ce6 Mon Sep 17 00:00:00 2001 From: Ryan Kennedy Date: Sun, 22 Mar 2020 23:34:31 -0500 Subject: [PATCH 13/25] Added some documentation --- src/registers/graphics_controller.rs | 45 ++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/src/registers/graphics_controller.rs b/src/registers/graphics_controller.rs index 057c3e7..8d3c78f 100644 --- a/src/registers/graphics_controller.rs +++ b/src/registers/graphics_controller.rs @@ -73,12 +73,54 @@ impl From for u8 { } } +/// Represents a write mode for vga hardware. #[derive(Debug)] #[repr(u8)] pub enum WriteMode { + /// Represents `WriteMode` 0. + /// + /// During a CPU write to the frame buffer, the + /// addressed byte in each of the 4 memory planes is written with the + /// CPU write data after it has been rotated by the number of counts + /// specified in the `GraphicsControllerIndex::DataRotate` register. If, however, the bit(s) + /// in the `GraphicsControllerIndex::EnableSetReset` register corresponding to one or + /// more of the memory planes is set to 1, then those memory planes + /// will be written to with the data stored in the corresponding bits in + /// the `GraphicsControllerIndex::SetReset` register. Mode0 = 0x0, + /// Represents `WriteMode` 1. + /// + /// During a CPU write to the frame buffer, the + /// addressed byte in each of the 4 memory planes is written to with + /// the data stored in the memory read latches. (The memory read + /// latches stores an unaltered copy of the data last read from any + /// location in the frame buffer.) Mode1 = 0x1, + /// Represents `WriteMode` 2. + /// + /// During a CPU write to the frame buffer, the least + /// significant 4 data bits of the CPU write data is treated as the color + /// value for the pixels in the addressed byte in all 4 memory planes. + /// The 8 bits of the `GraphicsControllerIndex::BitMask` register are used to selectively + /// enable or disable the ability to write to the corresponding bit in + /// each of the 4 memory planes that correspond to a given pixel. A + /// setting of 0 in a bit in the Bit Mask Register at a given bit position + /// causes the bits in the corresponding bit positions in the addressed + /// byte in all 4 memory planes to be written with value of their + /// counterparts in the memory read latches. A setting of 1 in a Bit + /// Mask Register at a given bit position causes the bits in the + /// corresponding bit positions in the addressed byte in all 4 memory + /// planes to be written with the 4 bits taken from the CPU write data + /// to thereby cause the pixel corresponding to these bits to be set to + /// the color value. Mode2 = 0x2, + /// Represents `WriteMode` 3. + /// + /// During a CPU write to the frame buffer, the CPU + /// write data is logically ANDed with the contents of the `GraphicsControllerIndex::BitMask` + /// register. The result of this ANDing is treated as the bit + /// mask used in writing the contents of the `GraphicsControllerIndex::SetReset` register + /// are written to addressed byte in all 4 memory planes. Mode3 = 0x3, } @@ -145,6 +187,7 @@ impl GraphicsControllerRegisters { ); } + /// Sets which mode the vga writes in, as specified by `write_mode`. pub fn set_write_mode(&mut self, write_mode: WriteMode) { let original_value = self.read(GraphicsControllerIndex::GraphicsMode) & 0xFC; self.write( @@ -153,6 +196,8 @@ impl GraphicsControllerRegisters { ); } + /// Sets which bits are effected by certain operations, as specified + /// by `bit_mask`. pub fn set_bit_mask(&mut self, bit_mask: u8) { self.write(GraphicsControllerIndex::BitMask, bit_mask); } From ec9f652de04782f2d7ac5ef7554bd161979b9c2a Mon Sep 17 00:00:00 2001 From: Ryan Kennedy Date: Mon, 23 Mar 2020 13:33:04 -0500 Subject: [PATCH 14/25] Don't need to recalculate width in bytes --- src/writers/graphics_640x480x16.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/writers/graphics_640x480x16.rs b/src/writers/graphics_640x480x16.rs index 03d6a13..258c503 100644 --- a/src/writers/graphics_640x480x16.rs +++ b/src/writers/graphics_640x480x16.rs @@ -8,6 +8,7 @@ use spinning_top::SpinlockGuard; const WIDTH: usize = 640; const HEIGHT: usize = 480; 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 /// @@ -53,7 +54,7 @@ impl Graphics640x480x16 { /// Sets the given pixel at `(x, y)` to the given `color`. pub fn set_pixel(&self, x: usize, y: usize, color: Color16Bit) { let (mut vga, frame_buffer) = self.get_frame_buffer(); - let offset = x / 8 + (WIDTH / 8) * y; + let offset = x / 8 + y * WIDTH_IN_BYTES; // Which pixel to modify this write let pixel_offset = x & 7; // Set write mode 2 so screen data is only modified by the bitmask From d5dec0159ccd83c6fa99e9f04634d7d6c95eb475 Mon Sep 17 00:00:00 2001 From: Ryan Kennedy Date: Mon, 23 Mar 2020 17:03:41 -0500 Subject: [PATCH 15/25] Rough draft line drawing --- Cargo.toml | 4 ++ src/drawing/bresenham.rs | 57 +++++++++++++++++++++ src/drawing/mod.rs | 20 ++++++++ src/drawing/octant.rs | 75 ++++++++++++++++++++++++++++ src/lib.rs | 1 + src/registers/graphics_controller.rs | 10 ++-- src/writers/graphics_640x480x16.rs | 50 ++++++++++++++----- 7 files changed, 200 insertions(+), 17 deletions(-) create mode 100644 src/drawing/bresenham.rs create mode 100644 src/drawing/mod.rs create mode 100644 src/drawing/octant.rs diff --git a/Cargo.toml b/Cargo.toml index e931d8d..653dc02 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,3 +23,7 @@ bitflags = "1.2.1" conquer-once = { version = "0.2.0", default-features = false } spinning_top = { version = "0.1.0", features = ["nightly"] } x86_64 = "0.9.6" + +[dependencies.num-traits] +version = "0.2.11" +default-features = false diff --git a/src/drawing/bresenham.rs b/src/drawing/bresenham.rs new file mode 100644 index 0000000..6a13e72 --- /dev/null +++ b/src/drawing/bresenham.rs @@ -0,0 +1,57 @@ +use super::{Octant, Point, SignedNum}; + +pub(crate) struct Bresenham { + point: Point, + end_x: T, + delta_x: T, + delta_y: T, + error: T, + octant: Octant, +} + +impl Bresenham { + #[inline] + pub fn new(start: Point, end: Point) -> Self { + let octant = Octant::new(start, end); + let start = octant.to(start); + let end = octant.to(end); + + let delta_x = end.0 - start.0; + let delta_y = end.1 - start.1; + + Self { + delta_x, + delta_y, + octant, + point: start, + end_x: end.0, + error: delta_y - delta_x, + } + } +} + +impl Iterator for Bresenham +where + T: SignedNum, +{ + type Item = Point; + + #[inline] + fn next(&mut self) -> Option { + if self.point.0 <= self.end_x { + let point = self.octant.from(self.point); + + if self.error >= T::zero() { + self.point.1 += T::one(); + self.error -= self.delta_x; + } + + self.point.0 += T::one(); + self.error += self.delta_y; + + Some(point) + } else { + None + } + } +} diff --git a/src/drawing/mod.rs b/src/drawing/mod.rs new file mode 100644 index 0000000..5d15c03 --- /dev/null +++ b/src/drawing/mod.rs @@ -0,0 +1,20 @@ +//! Common functionality for drawing in vga graphics mode. +//! Original implementation here https://github.com/expenses/line_drawing. +use num_traits::{NumAssignOps, NumCast, Signed}; + +mod bresenham; +mod octant; + +pub(crate) use bresenham::Bresenham; +use octant::Octant; + +/// A point in 2D space. +pub type Point = (T, T); + +pub(crate) trait SignedNum: Signed + Ord + Copy + NumCast + NumAssignOps { + fn cast(value: T) -> Self { + NumCast::from(value).unwrap() + } +} + +impl SignedNum for T {} diff --git a/src/drawing/octant.rs b/src/drawing/octant.rs new file mode 100644 index 0000000..caf2431 --- /dev/null +++ b/src/drawing/octant.rs @@ -0,0 +1,75 @@ +use super::Point; +use core::ops::{Neg, Sub}; +use num_traits::Zero; + +/// A simple octant struct for transforming line points. +pub struct Octant { + value: u8, +} + +impl Octant { + #[inline] + /// Get the relevant octant from a start and end point. + pub fn new(start: Point, end: Point) -> Self + where + T: Sub + Neg + PartialOrd + Zero, + { + let mut value = 0; + let mut dx = end.0 - start.0; + let mut dy = end.1 - start.1; + + if dy < T::zero() { + dx = -dx; + dy = -dy; + value += 4; + } + + if dx < T::zero() { + let tmp = dx; + dx = dy; + dy = -tmp; + value += 2; + } + + if dx < dy { + value += 1; + } + + Self { value } + } + + /// Convert a point to its position in the octant. + #[inline] + pub fn to(&self, point: Point) -> Point + where + T: Neg, + { + match self.value { + 0 => (point.0, point.1), + 1 => (point.1, point.0), + 2 => (point.1, -point.0), + 3 => (-point.0, point.1), + 4 => (-point.0, -point.1), + 5 => (-point.1, -point.0), + 6 => (-point.1, point.0), + 7 => (point.0, -point.1), + _ => unreachable!(), + } + } + + /// Convert a point from its position in the octant. + #[inline] + pub fn from>(&self, point: Point) -> Point { + match self.value { + 0 => (point.0, point.1), + 1 => (point.1, point.0), + 2 => (-point.1, point.0), + 3 => (-point.0, point.1), + 4 => (-point.0, -point.1), + 5 => (-point.1, -point.0), + 6 => (point.1, -point.0), + 7 => (point.0, -point.1), + _ => unreachable!(), + } + } +} diff --git a/src/lib.rs b/src/lib.rs index 7b11932..06b9ff5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,6 +9,7 @@ pub mod colors; pub mod configurations; +pub mod drawing; pub mod fonts; pub mod registers; pub mod vga; diff --git a/src/registers/graphics_controller.rs b/src/registers/graphics_controller.rs index 8d3c78f..c4afa41 100644 --- a/src/registers/graphics_controller.rs +++ b/src/registers/graphics_controller.rs @@ -1,4 +1,4 @@ -use super::{Color16Bit, PlaneMask, GRX_DATA_ADDRESS, GRX_INDEX_ADDRESS}; +use super::{Color16Bit, GRX_DATA_ADDRESS, GRX_INDEX_ADDRESS}; use core::convert::TryFrom; use x86_64::instructions::port::Port; @@ -177,13 +177,13 @@ impl GraphicsControllerRegisters { ); } - /// Sets which planes are effected by `GraphicsControllerIndex::SetReset`, - /// as specified by `plane_mask`. - pub fn write_enable_set_reset(&mut self, plane_mask: PlaneMask) { + /// Sets which bits are effected by `GraphicsControllerIndex::SetReset`, + /// as specified by `bit_mask`. + pub fn write_enable_set_reset(&mut self, bit_mask: u8) { let original_value = self.read(GraphicsControllerIndex::EnableSetReset) & 0xF0; self.write( GraphicsControllerIndex::EnableSetReset, - original_value | u8::from(plane_mask), + original_value | bit_mask, ); } diff --git a/src/writers/graphics_640x480x16.rs b/src/writers/graphics_640x480x16.rs index 258c503..1f59122 100644 --- a/src/writers/graphics_640x480x16.rs +++ b/src/writers/graphics_640x480x16.rs @@ -1,5 +1,6 @@ use crate::{ colors::{Color16Bit, DEFAULT_PALETTE}, + drawing::{Bresenham, Point}, registers::{PlaneMask, WriteMode}, vga::{Vga, VideoMode, VGA}, }; @@ -17,12 +18,13 @@ const WIDTH_IN_BYTES: usize = WIDTH / 8; /// Basic usage: /// /// ```no_run +/// use vga::colors::Color16Bit; /// use vga::writers::Graphics640x480x16; /// /// let graphics_mode = Graphics640x480x16::new(); /// /// graphics_mode.set_mode(); -/// graphics_mode.clear_screen(); +/// graphics_mode.clear_screen(Color16Bit::Black); /// ``` #[derive(Default)] pub struct Graphics640x480x16; @@ -36,14 +38,11 @@ impl Graphics640x480x16 { /// Clears the screen by setting all pixels to the specified `color`. pub fn clear_screen(&self, color: Color16Bit) { let (mut vga, frame_buffer) = self.get_frame_buffer(); - // Set write mode 2 so data is modified by the bitmask vga.graphics_controller_registers .set_write_mode(WriteMode::Mode2); - // Write to all 4 planes at once + vga.graphics_controller_registers.set_bit_mask(0xFF); vga.sequencer_registers .set_plane_mask(PlaneMask::ALL_PLANES); - // Every bit should be set to the same color - vga.graphics_controller_registers.set_bit_mask(0xFF); for offset in 0..ALL_PLANES_SCREEN_SIZE { unsafe { frame_buffer.add(offset).write_volatile(u8::from(color)); @@ -51,21 +50,32 @@ impl Graphics640x480x16 { } } + /// Draws a line from `start` to `end` with the specified `color`. + pub fn draw_line(&self, start: Point, end: Point, color: Color16Bit) { + { + let (mut vga, _frame_buffer) = self.get_frame_buffer(); + 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); + } + + for (x, y) in Bresenham::new(start, end) { + self.set_pixel_with_set_reset(x as usize, y as usize); + } + } + /// Sets the given pixel at `(x, y)` to the given `color`. pub fn set_pixel(&self, x: usize, y: usize, color: Color16Bit) { let (mut vga, frame_buffer) = self.get_frame_buffer(); let offset = x / 8 + y * WIDTH_IN_BYTES; // Which pixel to modify this write - let pixel_offset = x & 7; - // Set write mode 2 so screen data is only modified by the bitmask + let pixel_mask = 0x80 >> (x & 0x07); vga.graphics_controller_registers .set_write_mode(WriteMode::Mode2); - // Write to all 4 planes at once - vga.sequencer_registers - .set_plane_mask(PlaneMask::ALL_PLANES); // Only modify 1 pixel, based on the offset - vga.graphics_controller_registers - .set_bit_mask(1 << pixel_offset); + vga.graphics_controller_registers.set_bit_mask(pixel_mask); unsafe { // Reads the current offset into the memory latches frame_buffer.add(offset).read_volatile(); @@ -75,6 +85,22 @@ impl Graphics640x480x16 { } } + fn set_pixel_with_set_reset(&self, x: usize, y: usize) { + let (mut vga, frame_buffer) = self.get_frame_buffer(); + let offset = x / 8 + y * WIDTH_IN_BYTES; + // Which pixel to modify this write + let pixel_mask = 0x80 >> (x & 0x07); + // Only modify 1 pixel, based on the offset + vga.graphics_controller_registers.set_bit_mask(pixel_mask); + unsafe { + // Reads the current offset into the memory latches + frame_buffer.add(offset).read_volatile(); + // Sets the pixel specified by the offset to the color. The + // pixels not inlcuded in the bit mask remain untouched. + frame_buffer.add(offset).write_volatile(0x00); + } + } + /// Sets the graphics device to `VideoMode::Mode640x480x16`. pub fn set_mode(&self) { let mut vga = VGA.lock(); From 81f48f8153eef10ff6f8acce4a7745e4d02a25b6 Mon Sep 17 00:00:00 2001 From: Ryan Kennedy Date: Mon, 23 Mar 2020 18:24:07 -0500 Subject: [PATCH 16/25] Some cleanup and optimizations --- src/writers/graphics_640x480x16.rs | 48 +++++++++++------------------- 1 file changed, 18 insertions(+), 30 deletions(-) diff --git a/src/writers/graphics_640x480x16.rs b/src/writers/graphics_640x480x16.rs index 1f59122..829674f 100644 --- a/src/writers/graphics_640x480x16.rs +++ b/src/writers/graphics_640x480x16.rs @@ -52,55 +52,43 @@ impl Graphics640x480x16 { /// Draws a line from `start` to `end` with the specified `color`. pub fn draw_line(&self, start: Point, end: Point, color: Color16Bit) { - { - let (mut vga, _frame_buffer) = self.get_frame_buffer(); - 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); - } + let (mut vga, frame_buffer) = self.get_frame_buffer(); + 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); for (x, y) in Bresenham::new(start, end) { - self.set_pixel_with_set_reset(x as usize, y as usize); + let offset = (x as usize / 8) + (y as usize * WIDTH_IN_BYTES); + let pixel_mask = 0x80 >> (x & 0x07); + vga.graphics_controller_registers.set_bit_mask(pixel_mask); + unsafe { + frame_buffer.add(offset).read_volatile(); + frame_buffer.add(offset).write_volatile(0x00); + } } } /// 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`. pub fn set_pixel(&self, x: usize, y: usize, color: Color16Bit) { let (mut vga, frame_buffer) = self.get_frame_buffer(); let offset = x / 8 + y * WIDTH_IN_BYTES; - // Which pixel to modify this write let pixel_mask = 0x80 >> (x & 0x07); vga.graphics_controller_registers .set_write_mode(WriteMode::Mode2); - // Only modify 1 pixel, based on the offset vga.graphics_controller_registers.set_bit_mask(pixel_mask); unsafe { - // Reads the current offset into the memory latches frame_buffer.add(offset).read_volatile(); - // Sets the pixel specified by the offset to the color. The - // pixels not inlcuded in the bit mask remain untouched. frame_buffer.add(offset).write_volatile(u8::from(color)); } } - fn set_pixel_with_set_reset(&self, x: usize, y: usize) { - let (mut vga, frame_buffer) = self.get_frame_buffer(); - let offset = x / 8 + y * WIDTH_IN_BYTES; - // Which pixel to modify this write - let pixel_mask = 0x80 >> (x & 0x07); - // Only modify 1 pixel, based on the offset - vga.graphics_controller_registers.set_bit_mask(pixel_mask); - unsafe { - // Reads the current offset into the memory latches - frame_buffer.add(offset).read_volatile(); - // Sets the pixel specified by the offset to the color. The - // pixels not inlcuded in the bit mask remain untouched. - frame_buffer.add(offset).write_volatile(0x00); - } - } - /// Sets the graphics device to `VideoMode::Mode640x480x16`. pub fn set_mode(&self) { let mut vga = VGA.lock(); From 543eddbef7e46206c08c91657847d335f23dcd8c Mon Sep 17 00:00:00 2001 From: Ryan Kennedy Date: Mon, 23 Mar 2020 19:38:56 -0500 Subject: [PATCH 17/25] _set_pixel --- src/writers/graphics_640x480x16.rs | 49 ++++++++++++++++-------------- 1 file changed, 27 insertions(+), 22 deletions(-) diff --git a/src/writers/graphics_640x480x16.rs b/src/writers/graphics_640x480x16.rs index 829674f..58d77a3 100644 --- a/src/writers/graphics_640x480x16.rs +++ b/src/writers/graphics_640x480x16.rs @@ -52,21 +52,17 @@ impl Graphics640x480x16 { /// Draws a line from `start` to `end` with the specified `color`. pub fn draw_line(&self, start: Point, end: Point, color: Color16Bit) { - let (mut vga, frame_buffer) = self.get_frame_buffer(); - 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); + { + let (mut vga, _frame_buffer) = self.get_frame_buffer(); + 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); + } for (x, y) in Bresenham::new(start, end) { - let offset = (x as usize / 8) + (y as usize * WIDTH_IN_BYTES); - let pixel_mask = 0x80 >> (x & 0x07); - vga.graphics_controller_registers.set_bit_mask(pixel_mask); - unsafe { - frame_buffer.add(offset).read_volatile(); - frame_buffer.add(offset).write_volatile(0x00); - } + self._set_pixel(x as usize, y as usize, color); } } @@ -77,16 +73,13 @@ impl Graphics640x480x16 { /// drawn. If you need to draw more then one pixel, consider using a method /// such as `draw_line`. pub fn set_pixel(&self, x: usize, y: usize, color: Color16Bit) { - let (mut vga, frame_buffer) = self.get_frame_buffer(); - let offset = x / 8 + y * WIDTH_IN_BYTES; - let pixel_mask = 0x80 >> (x & 0x07); - vga.graphics_controller_registers - .set_write_mode(WriteMode::Mode2); - vga.graphics_controller_registers.set_bit_mask(pixel_mask); - unsafe { - frame_buffer.add(offset).read_volatile(); - frame_buffer.add(offset).write_volatile(u8::from(color)); + { + let (mut vga, _frame_buffer) = self.get_frame_buffer(); + vga.graphics_controller_registers + .set_write_mode(WriteMode::Mode2); } + + self._set_pixel(x, y, color); } /// Sets the graphics device to `VideoMode::Mode640x480x16`. @@ -107,4 +100,16 @@ impl Graphics640x480x16 { let frame_buffer = vga.get_frame_buffer(); (vga, u32::from(frame_buffer) as *mut u8) } + + #[inline] + fn _set_pixel(&self, x: usize, y: usize, color: Color16Bit) { + let (mut vga, frame_buffer) = self.get_frame_buffer(); + let offset = x / 8 + y * WIDTH_IN_BYTES; + let pixel_mask = 0x80 >> (x & 0x07); + vga.graphics_controller_registers.set_bit_mask(pixel_mask); + unsafe { + frame_buffer.add(offset).read_volatile(); + frame_buffer.add(offset).write_volatile(u8::from(color)); + } + } } From 5a072f53820dd8e1b6cef4e24379a4eab049bd80 Mon Sep 17 00:00:00 2001 From: Ryan Kennedy Date: Tue, 24 Mar 2020 13:04:32 -0500 Subject: [PATCH 18/25] Moved to a GraphicsWriter trait --- src/writers/graphics_640x480x16.rs | 25 ++++++++++++++----------- src/writers/mod.rs | 18 ++++++++++++++++++ 2 files changed, 32 insertions(+), 11 deletions(-) diff --git a/src/writers/graphics_640x480x16.rs b/src/writers/graphics_640x480x16.rs index 58d77a3..b7e9de3 100644 --- a/src/writers/graphics_640x480x16.rs +++ b/src/writers/graphics_640x480x16.rs @@ -1,3 +1,4 @@ +use super::GraphicsWriter; use crate::{ colors::{Color16Bit, DEFAULT_PALETTE}, drawing::{Bresenham, Point}, @@ -19,7 +20,7 @@ const WIDTH_IN_BYTES: usize = WIDTH / 8; /// /// ```no_run /// use vga::colors::Color16Bit; -/// use vga::writers::Graphics640x480x16; +/// use vga::writers::{GraphicsWriter, Graphics640x480x16}; /// /// let graphics_mode = Graphics640x480x16::new(); /// @@ -29,14 +30,9 @@ const WIDTH_IN_BYTES: usize = WIDTH / 8; #[derive(Default)] pub struct Graphics640x480x16; -impl Graphics640x480x16 { - /// Creates a new `Graphics640x480x16`. - pub fn new() -> Graphics640x480x16 { - Graphics640x480x16 {} - } - +impl GraphicsWriter for Graphics640x480x16 { /// Clears the screen by setting all pixels to the specified `color`. - pub fn clear_screen(&self, color: Color16Bit) { + fn clear_screen(&self, color: Color16Bit) { let (mut vga, frame_buffer) = self.get_frame_buffer(); vga.graphics_controller_registers .set_write_mode(WriteMode::Mode2); @@ -51,7 +47,7 @@ impl Graphics640x480x16 { } /// Draws a line from `start` to `end` with the specified `color`. - pub fn draw_line(&self, start: Point, end: Point, color: Color16Bit) { + fn draw_line(&self, start: Point, end: Point, color: Color16Bit) { { let (mut vga, _frame_buffer) = self.get_frame_buffer(); vga.graphics_controller_registers.write_set_reset(color); @@ -72,7 +68,7 @@ impl Graphics640x480x16 { /// 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`. - pub fn set_pixel(&self, x: usize, y: usize, color: Color16Bit) { + fn set_pixel(&self, x: usize, y: usize, color: Color16Bit) { { let (mut vga, _frame_buffer) = self.get_frame_buffer(); vga.graphics_controller_registers @@ -83,7 +79,7 @@ impl Graphics640x480x16 { } /// Sets the graphics device to `VideoMode::Mode640x480x16`. - pub fn set_mode(&self) { + fn set_mode(&self) { let mut vga = VGA.lock(); vga.set_video_mode(VideoMode::Mode640x480x16); @@ -91,6 +87,13 @@ impl Graphics640x480x16 { // so explicitly set it. vga.color_palette_registers.load_palette(&DEFAULT_PALETTE); } +} + +impl Graphics640x480x16 { + /// Creates a new `Graphics640x480x16`. + pub fn new() -> Graphics640x480x16 { + Graphics640x480x16 {} + } /// Returns the start of the `FrameBuffer` as `*mut u8` as /// well as a lock to the vga driver. This ensures the vga diff --git a/src/writers/mod.rs b/src/writers/mod.rs index 5dc74af..1f738a1 100644 --- a/src/writers/mod.rs +++ b/src/writers/mod.rs @@ -6,6 +6,7 @@ mod text_80x25; use super::{ colors::{Color16Bit, TextModeColor}, + drawing::Point, registers::CrtcControllerIndex, vga::{Vga, VGA}, }; @@ -177,3 +178,20 @@ 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); + /// /// Draws a line from `start` to `end` with the specified `color`. + fn draw_line(&self, start: Point, end: Point, 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 42d2f4836e5196c23d02de9f2618a216613a4110 Mon Sep 17 00:00:00 2001 From: Ryan Kennedy Date: Tue, 24 Mar 2020 15:44:30 -0500 Subject: [PATCH 19/25] Drawing text --- Cargo.toml | 1 + src/writers/graphics_640x480x16.rs | 26 ++++++++++++++++++++++++++ src/writers/mod.rs | 2 ++ 3 files changed, 29 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index 653dc02..fd9d993 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,6 +21,7 @@ repository = "https://github.com/rust-osdev/vga" [dependencies] bitflags = "1.2.1" conquer-once = { version = "0.2.0", default-features = false } +font8x8 = { version = "0.2.5", default-features = false, features = ["unicode"] } spinning_top = { version = "0.1.0", features = ["nightly"] } x86_64 = "0.9.6" diff --git a/src/writers/graphics_640x480x16.rs b/src/writers/graphics_640x480x16.rs index b7e9de3..f9d955e 100644 --- a/src/writers/graphics_640x480x16.rs +++ b/src/writers/graphics_640x480x16.rs @@ -5,6 +5,7 @@ use crate::{ registers::{PlaneMask, WriteMode}, vga::{Vga, VideoMode, VGA}, }; +use font8x8::UnicodeFonts; use spinning_top::SpinlockGuard; const WIDTH: usize = 640; @@ -62,6 +63,31 @@ impl GraphicsWriter for Graphics640x480x16 { } } + fn draw_character(&self, x: usize, y: usize, character: char, color: Color16Bit) { + let character = match font8x8::BASIC_FONTS.get(character) { + Some(character) => character, + None => font8x8::unicode::BLOCK_UNICODE[8].byte_array(), + }; + + { + let (mut vga, _frame_buffer) = self.get_frame_buffer(); + 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); + } + + for (y1, byte) in character.iter().enumerate() { + for bit in 0..8 { + match *byte & 1 << bit { + 0 => {} + _ => self._set_pixel(x + bit, y + y1, color), + } + } + } + } + /// Sets the given pixel at `(x, y)` to the given `color`. /// /// **Note:** This method is provided for convenience, but has terrible diff --git a/src/writers/mod.rs b/src/writers/mod.rs index 1f738a1..2965ec3 100644 --- a/src/writers/mod.rs +++ b/src/writers/mod.rs @@ -185,6 +185,8 @@ pub trait GraphicsWriter { fn clear_screen(&self, color: 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 From 36d92e41232f6a8f07e699407785ad32c86c2f6f Mon Sep 17 00:00:00 2001 From: Ryan Kennedy Date: Tue, 24 Mar 2020 16:08:24 -0500 Subject: [PATCH 20/25] Code cleanup --- src/writers/graphics_640x480x16.rs | 74 +++++++++++++----------------- 1 file changed, 32 insertions(+), 42 deletions(-) diff --git a/src/writers/graphics_640x480x16.rs b/src/writers/graphics_640x480x16.rs index f9d955e..e8537f2 100644 --- a/src/writers/graphics_640x480x16.rs +++ b/src/writers/graphics_640x480x16.rs @@ -32,14 +32,9 @@ const WIDTH_IN_BYTES: usize = WIDTH / 8; pub struct Graphics640x480x16; impl GraphicsWriter for Graphics640x480x16 { - /// Clears the screen by setting all pixels to the specified `color`. fn clear_screen(&self, color: Color16Bit) { - let (mut vga, frame_buffer) = self.get_frame_buffer(); - 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); + self.set_write_mode_2(); + let (_vga, frame_buffer) = self.get_frame_buffer(); for offset in 0..ALL_PLANES_SCREEN_SIZE { unsafe { frame_buffer.add(offset).write_volatile(u8::from(color)); @@ -47,64 +42,36 @@ impl GraphicsWriter for Graphics640x480x16 { } } - /// Draws a line from `start` to `end` with the specified `color`. fn draw_line(&self, start: Point, end: Point, color: Color16Bit) { - { - let (mut vga, _frame_buffer) = self.get_frame_buffer(); - 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); - } - + self.set_write_mode_0(color); for (x, y) in Bresenham::new(start, end) { self._set_pixel(x as usize, y as usize, color); } } fn draw_character(&self, x: usize, y: usize, character: char, color: Color16Bit) { + self.set_write_mode_2(); 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(), }; - { - let (mut vga, _frame_buffer) = self.get_frame_buffer(); - 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); - } - - for (y1, byte) in character.iter().enumerate() { + for (row, byte) in character.iter().enumerate() { for bit in 0..8 { match *byte & 1 << bit { - 0 => {} - _ => self._set_pixel(x + bit, y + y1, color), + 0 => (), + _ => self._set_pixel(x + bit, y + row, 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: Color16Bit) { - { - let (mut vga, _frame_buffer) = self.get_frame_buffer(); - vga.graphics_controller_registers - .set_write_mode(WriteMode::Mode2); - } - + self.set_write_mode_2(); self._set_pixel(x, y, color); } - /// Sets the graphics device to `VideoMode::Mode640x480x16`. fn set_mode(&self) { let mut vga = VGA.lock(); vga.set_video_mode(VideoMode::Mode640x480x16); @@ -121,6 +88,29 @@ impl Graphics640x480x16 { Graphics640x480x16 {} } + /// Sets the vga to 'WriteMode::Mode0`. This also sets `GraphicsControllerIndex::SetReset` + /// to the specified `color`, `GraphicsControllerIndex::EnableSetReset` to `0xFF` and + /// `SequencerIndex::PlaneMask` to `PlaneMask::ALL_PLANES`. + pub fn set_write_mode_0(&self, color: Color16Bit) { + let (mut vga, _frame_buffer) = self.get_frame_buffer(); + 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); + } + + /// Sets the vga to `WriteMode::Mode2`. This also sets the `GraphicsControllerIndex::BitMask` + /// to `0xFF` and `SequencerIndex::PlaneMask` to `PlaneMask::ALL_PLANES`. + pub fn set_write_mode_2(&self) { + let (mut vga, _frame_buffer) = self.get_frame_buffer(); + 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); + } + /// 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. From 244594c4760fd35f35262dbe186af88be7775d61 Mon Sep 17 00:00:00 2001 From: Ryan Kennedy Date: Tue, 24 Mar 2020 16:17:13 -0500 Subject: [PATCH 21/25] Fixed tests and renamed Color16Bit --- Changelog.md | 2 +- README.md | 4 ++-- src/colors.rs | 24 ++++++++++++------------ src/registers/graphics_controller.rs | 4 ++-- src/registers/mod.rs | 2 +- src/writers/graphics_640x480x16.rs | 20 ++++++++++---------- src/writers/mod.rs | 8 ++++---- src/writers/text_40x25.rs | 4 ++-- src/writers/text_40x50.rs | 4 ++-- src/writers/text_80x25.rs | 4 ++-- testing/tests/vga.rs | 21 ++++++++++++++------- 11 files changed, 52 insertions(+), 45 deletions(-) diff --git a/Changelog.md b/Changelog.md index 7b170a9..e8b149f 100644 --- a/Changelog.md +++ b/Changelog.md @@ -2,7 +2,7 @@ ## Breaking -- `ScreenCharacter::new` now takes a `TextModeColor` instead of 2 `Color16Bit`. +- `ScreenCharacter::new` now takes a `TextModeColor` instead of 2 `Color16`. ## Other - Added `ScreenCharacter::get_character`. diff --git a/README.md b/README.md index 354b15d..4ad8ec0 100644 --- a/README.md +++ b/README.md @@ -11,11 +11,11 @@ this crate to work properly. ## Usage ```rust -use vga::colors::{Color16Bit, TextModeColor}; +use vga::colors::{Color16, TextModeColor}; use vga::writers::{ScreenCharacter, TextWriter, Text80x25}; let text_mode = Text80x25::new(); -let color = TextModeColor::new(Color16Bit::Yellow, Color16Bit::Black); +let color = TextModeColor::new(Color16::Yellow, Color16::Black); let screen_character = ScreenCharacter::new(b'T', color); text_mode.set_mode(); diff --git a/src/colors.rs b/src/colors.rs index 8bc44de..11ad75c 100644 --- a/src/colors.rs +++ b/src/colors.rs @@ -6,7 +6,7 @@ pub const PALETTE_SIZE: usize = 768; /// Represents a 16 bit color used for vga display. #[derive(Debug, Clone, Copy)] #[repr(u8)] -pub enum Color16Bit { +pub enum Color16 { /// Represents the color `Black (0x0)`. Black = 0x0, /// Represents the color `Blue (0x1)`. @@ -41,8 +41,8 @@ pub enum Color16Bit { White = 0xF, } -impl From for u8 { - fn from(value: Color16Bit) -> u8 { +impl From for u8 { + fn from(value: Color16) -> u8 { value as u8 } } @@ -55,17 +55,17 @@ pub struct TextModeColor(u8); impl TextModeColor { /// Returns a new `TextModeColor` given the specified `foreground` /// and `background` color. - pub const fn new(foreground: Color16Bit, background: Color16Bit) -> TextModeColor { + pub const fn new(foreground: Color16, background: Color16) -> TextModeColor { TextModeColor((background as u8) << 4 | (foreground as u8)) } /// Sets the background color given the specified `background`; - pub fn set_background(&mut self, background: Color16Bit) { + pub fn set_background(&mut self, background: Color16) { self.0 = (background as u8) << 4 | (self.0 & 0x0F); } /// Sets the foreground color given the specified `foreground`. - pub fn set_foreground(&mut self, foreground: Color16Bit) { + pub fn set_foreground(&mut self, foreground: Color16) { self.0 = foreground as u8; } } @@ -128,15 +128,15 @@ mod test { #[test] fn test_set_foreground() { - let mut color = TextModeColor::new(Color16Bit::Yellow, Color16Bit::Black); - color.set_foreground(Color16Bit::Red); - assert_eq!(color.0 & 0x0F, Color16Bit::Red as u8); + let mut color = TextModeColor::new(Color16::Yellow, Color16::Black); + color.set_foreground(Color16::Red); + assert_eq!(color.0 & 0x0F, Color16::Red as u8); } #[test] fn test_set_background() { - let mut color = TextModeColor::new(Color16Bit::Yellow, Color16Bit::Black); - color.set_background(Color16Bit::DarkGrey); - assert_eq!(color.0 >> 4, Color16Bit::DarkGrey as u8); + let mut color = TextModeColor::new(Color16::Yellow, Color16::Black); + color.set_background(Color16::DarkGrey); + assert_eq!(color.0 >> 4, Color16::DarkGrey as u8); } } diff --git a/src/registers/graphics_controller.rs b/src/registers/graphics_controller.rs index c4afa41..eaa698a 100644 --- a/src/registers/graphics_controller.rs +++ b/src/registers/graphics_controller.rs @@ -1,4 +1,4 @@ -use super::{Color16Bit, GRX_DATA_ADDRESS, GRX_INDEX_ADDRESS}; +use super::{Color16, GRX_DATA_ADDRESS, GRX_INDEX_ADDRESS}; use core::convert::TryFrom; use x86_64::instructions::port::Port; @@ -169,7 +169,7 @@ impl GraphicsControllerRegisters { /// Sets the value to use for `GraphicsControllerIndex::SetReset`, /// as spcified by `color`. - pub fn write_set_reset(&mut self, color: Color16Bit) { + pub fn write_set_reset(&mut self, color: Color16) { let original_value = self.read(GraphicsControllerIndex::SetReset) & 0xF0; self.write( GraphicsControllerIndex::SetReset, diff --git a/src/registers/mod.rs b/src/registers/mod.rs index c53850e..d3223d4 100644 --- a/src/registers/mod.rs +++ b/src/registers/mod.rs @@ -7,7 +7,7 @@ mod general; mod graphics_controller; mod sequencer; -use crate::colors::{Color16Bit, PALETTE_SIZE}; +use crate::colors::{Color16, PALETTE_SIZE}; pub use attribute_controller::{AttributeControllerIndex, AttributeControllerRegisters}; pub use color_palette::ColorPaletteRegisters; diff --git a/src/writers/graphics_640x480x16.rs b/src/writers/graphics_640x480x16.rs index e8537f2..48583de 100644 --- a/src/writers/graphics_640x480x16.rs +++ b/src/writers/graphics_640x480x16.rs @@ -1,6 +1,6 @@ use super::GraphicsWriter; use crate::{ - colors::{Color16Bit, DEFAULT_PALETTE}, + colors::{Color16, DEFAULT_PALETTE}, drawing::{Bresenham, Point}, registers::{PlaneMask, WriteMode}, vga::{Vga, VideoMode, VGA}, @@ -20,19 +20,19 @@ const WIDTH_IN_BYTES: usize = WIDTH / 8; /// Basic usage: /// /// ```no_run -/// use vga::colors::Color16Bit; +/// use vga::colors::Color16; /// use vga::writers::{GraphicsWriter, Graphics640x480x16}; /// /// let graphics_mode = Graphics640x480x16::new(); /// /// graphics_mode.set_mode(); -/// graphics_mode.clear_screen(Color16Bit::Black); +/// graphics_mode.clear_screen(Color16::Black); /// ``` #[derive(Default)] pub struct Graphics640x480x16; -impl GraphicsWriter for Graphics640x480x16 { - fn clear_screen(&self, color: Color16Bit) { +impl GraphicsWriter for Graphics640x480x16 { + fn clear_screen(&self, color: Color16) { self.set_write_mode_2(); let (_vga, frame_buffer) = self.get_frame_buffer(); for offset in 0..ALL_PLANES_SCREEN_SIZE { @@ -42,14 +42,14 @@ impl GraphicsWriter for Graphics640x480x16 { } } - fn draw_line(&self, start: Point, end: Point, color: Color16Bit) { + fn draw_line(&self, start: Point, end: Point, color: Color16) { self.set_write_mode_0(color); for (x, y) in Bresenham::new(start, end) { self._set_pixel(x as usize, y as usize, color); } } - fn draw_character(&self, x: usize, y: usize, character: char, color: Color16Bit) { + fn draw_character(&self, x: usize, y: usize, character: char, color: Color16) { self.set_write_mode_2(); let character = match font8x8::BASIC_FONTS.get(character) { Some(character) => character, @@ -67,7 +67,7 @@ impl GraphicsWriter for Graphics640x480x16 { } } - fn set_pixel(&self, x: usize, y: usize, color: Color16Bit) { + fn set_pixel(&self, x: usize, y: usize, color: Color16) { self.set_write_mode_2(); self._set_pixel(x, y, color); } @@ -91,7 +91,7 @@ impl Graphics640x480x16 { /// Sets the vga to 'WriteMode::Mode0`. This also sets `GraphicsControllerIndex::SetReset` /// to the specified `color`, `GraphicsControllerIndex::EnableSetReset` to `0xFF` and /// `SequencerIndex::PlaneMask` to `PlaneMask::ALL_PLANES`. - pub fn set_write_mode_0(&self, color: Color16Bit) { + pub fn set_write_mode_0(&self, color: Color16) { let (mut vga, _frame_buffer) = self.get_frame_buffer(); vga.graphics_controller_registers.write_set_reset(color); vga.graphics_controller_registers @@ -121,7 +121,7 @@ impl Graphics640x480x16 { } #[inline] - fn _set_pixel(&self, x: usize, y: usize, color: Color16Bit) { + fn _set_pixel(&self, x: usize, y: usize, color: Color16) { let (mut vga, frame_buffer) = self.get_frame_buffer(); let offset = x / 8 + y * WIDTH_IN_BYTES; let pixel_mask = 0x80 >> (x & 0x07); diff --git a/src/writers/mod.rs b/src/writers/mod.rs index 2965ec3..b2e0b6a 100644 --- a/src/writers/mod.rs +++ b/src/writers/mod.rs @@ -5,7 +5,7 @@ mod text_40x50; mod text_80x25; use super::{ - colors::{Color16Bit, TextModeColor}, + colors::{Color16, TextModeColor}, drawing::Point, registers::CrtcControllerIndex, vga::{Vga, VGA}, @@ -44,7 +44,7 @@ impl ScreenCharacter { static BLANK_CHARACTER: ScreenCharacter = ScreenCharacter { character: b' ', - color: TextModeColor::new(Color16Bit::Yellow, Color16Bit::Black), + color: TextModeColor::new(Color16::Yellow, Color16::Black), }; /// A helper trait used to interact with various vga screens. @@ -73,8 +73,8 @@ pub trait TextWriter: Screen { } /// Clears the screen by setting all cells to `b' '` with - /// a background color of `Color16Bit::Black` and a foreground - /// color of `Color16Bit::Yellow`. + /// a background color of `Color16::Black` and a foreground + /// color of `Color16::Yellow`. fn clear_screen(&self) { let (_vga, frame_buffer) = self.get_frame_buffer(); let screen_size = self.get_width() * self.get_height(); diff --git a/src/writers/text_40x25.rs b/src/writers/text_40x25.rs index 545a10a..452d2d8 100644 --- a/src/writers/text_40x25.rs +++ b/src/writers/text_40x25.rs @@ -15,11 +15,11 @@ const HEIGHT: usize = 25; /// Basic usage: /// /// ```no_run -/// use vga::colors::{Color16Bit, TextModeColor}; +/// use vga::colors::{Color16, TextModeColor}; /// use vga::writers::{ScreenCharacter, TextWriter, Text40x25}; /// /// let text_mode = Text40x25::new(); -/// let color = TextModeColor::new(Color16Bit::Yellow, Color16Bit::Black); +/// let color = TextModeColor::new(Color16::Yellow, Color16::Black); /// let screen_character = ScreenCharacter::new(b'T', color); /// /// text_mode.set_mode(); diff --git a/src/writers/text_40x50.rs b/src/writers/text_40x50.rs index 02766b9..62b057b 100644 --- a/src/writers/text_40x50.rs +++ b/src/writers/text_40x50.rs @@ -15,11 +15,11 @@ const HEIGHT: usize = 50; /// Basic usage: /// /// ```no_run -/// use vga::colors::{Color16Bit, TextModeColor}; +/// use vga::colors::{Color16, TextModeColor}; /// use vga::writers::{ScreenCharacter, TextWriter, Text40x50}; /// /// let text_mode = Text40x50::new(); -/// let color = TextModeColor::new(Color16Bit::Yellow, Color16Bit::Black); +/// let color = TextModeColor::new(Color16::Yellow, Color16::Black); /// let screen_character = ScreenCharacter::new(b'T', color); /// /// text_mode.set_mode(); diff --git a/src/writers/text_80x25.rs b/src/writers/text_80x25.rs index ed0d473..72a4354 100644 --- a/src/writers/text_80x25.rs +++ b/src/writers/text_80x25.rs @@ -15,11 +15,11 @@ const HEIGHT: usize = 25; /// Basic usage: /// /// ```no_run -/// use vga::colors::{Color16Bit, TextModeColor}; +/// use vga::colors::{Color16, TextModeColor}; /// use vga::writers::{ScreenCharacter, TextWriter, Text80x25}; /// /// let text_mode = Text80x25::new(); -/// let color = TextModeColor::new(Color16Bit::Yellow, Color16Bit::Black); +/// let color = TextModeColor::new(Color16::Yellow, Color16::Black); /// let screen_character = ScreenCharacter::new(b'T', color); /// /// text_mode.set_mode(); diff --git a/testing/tests/vga.rs b/testing/tests/vga.rs index 3c772c4..52a7d6f 100644 --- a/testing/tests/vga.rs +++ b/testing/tests/vga.rs @@ -83,8 +83,8 @@ fn load_palette() { let mut palette = [0u8; PALETTE_SIZE]; let mut vga = VGA.lock(); - vga.load_palette(&DEFAULT_PALETTE); - vga.read_palette(&mut palette); + vga.color_palette_registers.load_palette(&DEFAULT_PALETTE); + vga.color_palette_registers.read_palette(&mut palette); for i in 0..PALETTE_SIZE { assert_eq!(palette[i], DEFAULT_PALETTE[i]); @@ -95,23 +95,30 @@ fn load_palette() { fn check_registers(vga: &mut Vga, configuration: &VgaConfiguration) { let emulation_mode = vga.get_emulation_mode(); - assert_eq!(vga.read_msr(), configuration.miscellaneous_output); + assert_eq!( + vga.general_registers.read_msr(), + configuration.miscellaneous_output + ); for (index, value) in configuration.sequencer_registers { - assert_eq!(vga.read_sequencer(*index), *value); + assert_eq!(vga.sequencer_registers.read(*index), *value); } for (index, value) in configuration.crtc_controller_registers { - assert_eq!(vga.read_crtc_controller(emulation_mode, *index), *value); + assert_eq!( + vga.crtc_controller_registers.read(emulation_mode, *index), + *value + ); } for (index, value) in configuration.graphics_controller_registers { - assert_eq!(vga.read_graphics_controller(*index), *value); + assert_eq!(vga.graphics_controller_registers.read(*index), *value); } for (index, value) in configuration.attribute_controller_registers { assert_eq!( - vga.read_attribute_controller(emulation_mode, *index), + vga.attribute_controller_registers + .read(emulation_mode, *index), *value ); } From 595aef15bce4d4b8755e31392d20d386d4362b98 Mon Sep 17 00:00:00 2001 From: Ryan Kennedy Date: Tue, 24 Mar 2020 17:51:05 -0500 Subject: [PATCH 22/25] Updated example --- src/writers/graphics_640x480x16.rs | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/src/writers/graphics_640x480x16.rs b/src/writers/graphics_640x480x16.rs index 48583de..e035d6f 100644 --- a/src/writers/graphics_640x480x16.rs +++ b/src/writers/graphics_640x480x16.rs @@ -21,12 +21,19 @@ const WIDTH_IN_BYTES: usize = WIDTH / 8; /// /// ```no_run /// use vga::colors::Color16; -/// use vga::writers::{GraphicsWriter, Graphics640x480x16}; -/// -/// let graphics_mode = Graphics640x480x16::new(); -/// -/// graphics_mode.set_mode(); -/// graphics_mode.clear_screen(Color16::Black); +/// use vga::writers::{Graphics640x480x16, GraphicsWriter}; + +/// let mode = Graphics640x480x16::new(); +/// mode.set_mode(); +/// mode.clear_screen(Color16::Black); +/// mode.draw_line((80, 60), (80, 420), Color16::White); +/// mode.draw_line((80, 60), (540, 60), Color16::White); +/// mode.draw_line((80, 420), (540, 420), Color16::White); +/// mode.draw_line((540, 420), (540, 60), Color16::White); +/// mode.draw_line((80, 90), (540, 90), Color16::White); +/// for (offset, character) in "Hello World!".chars().enumerate() { +/// mode.draw_character(270 + offset * 8, 72, character, Color16::White) +/// } /// ``` #[derive(Default)] pub struct Graphics640x480x16; @@ -88,10 +95,7 @@ impl Graphics640x480x16 { Graphics640x480x16 {} } - /// Sets the vga to 'WriteMode::Mode0`. This also sets `GraphicsControllerIndex::SetReset` - /// to the specified `color`, `GraphicsControllerIndex::EnableSetReset` to `0xFF` and - /// `SequencerIndex::PlaneMask` to `PlaneMask::ALL_PLANES`. - pub fn set_write_mode_0(&self, color: Color16) { + fn set_write_mode_0(&self, color: Color16) { let (mut vga, _frame_buffer) = self.get_frame_buffer(); vga.graphics_controller_registers.write_set_reset(color); vga.graphics_controller_registers @@ -100,9 +104,7 @@ impl Graphics640x480x16 { .set_write_mode(WriteMode::Mode0); } - /// Sets the vga to `WriteMode::Mode2`. This also sets the `GraphicsControllerIndex::BitMask` - /// to `0xFF` and `SequencerIndex::PlaneMask` to `PlaneMask::ALL_PLANES`. - pub fn set_write_mode_2(&self) { + fn set_write_mode_2(&self) { let (mut vga, _frame_buffer) = self.get_frame_buffer(); vga.graphics_controller_registers .set_write_mode(WriteMode::Mode2); From d6c01c7309c974341e50ab0df1ec219f7f96eefd Mon Sep 17 00:00:00 2001 From: Ryan Kennedy Date: Tue, 24 Mar 2020 17:54:09 -0500 Subject: [PATCH 23/25] updated readme --- README.md | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 4ad8ec0..4efb9e8 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ this crate to work properly. **Note: This crate is currently experimental and subject to change since it's in active development.** -## Usage +## Text Mode ```rust use vga::colors::{Color16, TextModeColor}; use vga::writers::{ScreenCharacter, TextWriter, Text80x25}; @@ -22,3 +22,21 @@ text_mode.set_mode(); text_mode.clear_screen(); text_mode.write_character(0, 0, screen_character); ``` + +## Graphics Mode +```rust +use vga::colors::Color16; +use vga::writers::{Graphics640x480x16, GraphicsWriter}; + +let mode = Graphics640x480x16::new(); +mode.set_mode(); +mode.clear_screen(Color16::Black); +mode.draw_line((80, 60), (80, 420), Color16::White); +mode.draw_line((80, 60), (540, 60), Color16::White); +mode.draw_line((80, 420), (540, 420), Color16::White); +mode.draw_line((540, 420), (540, 60), Color16::White); +mode.draw_line((80, 90), (540, 90), Color16::White); +for (offset, character) in "Hello World!".chars().enumerate() { + mode.draw_character(270 + offset * 8, 72, character, Color16::White) +} +``` From a23698720d03232b29a0496e6165b2625c9675f8 Mon Sep 17 00:00:00 2001 From: Ryan Kennedy Date: Tue, 24 Mar 2020 19:22:21 -0500 Subject: [PATCH 24/25] Updated changelog --- Changelog.md | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index e8b149f..0ed738d 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,10 +1,31 @@ +# 0.2.0 + +## Breaking + +- Registers moved `vga::registers`. +- `Plane` converted to `ReadPlane` and `PlaneMask`. +- Register read/write ability removed from `Vga`. +- Public access added to `Vga` fields. +- `TextWriter::get_width` and `TextWriter::get_height` moved to a `Screen` trait. +- `Color16Bit` renamed to `Color16`. + +## Other + +- Added a new `Screen` trait for dealing with the size of a screen. +- Added a `GraphicsWriter` trait for dealing with vga graphics. +- Added `Graphics640x480x16::clear_screen`. +- Added `Graphics640x480x16::draw_line`. +- Added `Graphics640x480x16::draw_character`. +- Added `vga::drawing::Point` for drawing lines. + # 0.1.2 ## Breaking -- `ScreenCharacter::new` now takes a `TextModeColor` instead of 2 `Color16`. +- `ScreenCharacter::new` now takes a `TextModeColor` instead of 2 `Color16Bit`. ## Other + - Added `ScreenCharacter::get_character`. - Added `ScreenCharacter::get_color`. - Added `TextWriter::read_character`. From 03c2501e7bee2bb6fbcf6fd7aec964e0088ef9d2 Mon Sep 17 00:00:00 2001 From: Ryan Kennedy Date: Tue, 24 Mar 2020 19:22:54 -0500 Subject: [PATCH 25/25] version bump --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index fd9d993..16ba9b4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "vga" -version = "0.1.2" +version = "0.2.0" authors = ["Ryan Kennedy "] edition = "2018" description = "Support for vga specific functions, data structures, and registers."