diff --git a/src/configurations.rs b/src/configurations.rs index 1478a9e..b782a29 100644 --- a/src/configurations.rs +++ b/src/configurations.rs @@ -390,7 +390,7 @@ pub const MODE_320X200X256_CONFIGURATION: VgaConfiguration = VgaConfiguration { ], }; -/// Register values for Vga mode 320x200x256x Graphics. +/// Register values for Vga mode 320x240x256 Graphics. pub const MODE_320X240X256_CONFIGURATION: VgaConfiguration = VgaConfiguration { // Configuration values acquired from https://www.singlix.com/trdos/archive/vga/Graphics%20in%20pmode.pdf miscellaneous_output: 0x63, @@ -463,3 +463,77 @@ pub const MODE_320X240X256_CONFIGURATION: VgaConfiguration = VgaConfiguration { (AttributeControllerIndex::ColorSelect, 0x00), ], }; + +/// Register values for Vga mode 1280x800x256 Graphics. +pub const MODE_1280X800X256_CONFIGURATION: VgaConfiguration = VgaConfiguration { + // Configuration values acquired from https://www.singlix.com/trdos/archive/vga/Graphics%20in%20pmode.pdf + miscellaneous_output: 0x63, + sequencer_registers: &[ + (SequencerIndex::SequencerReset, 0x03), + (SequencerIndex::ClockingMode, 0x01), + (SequencerIndex::PlaneMask, 0x0F), + (SequencerIndex::CharacterFont, 0x00), + (SequencerIndex::MemoryMode, 0x06), + ], + crtc_controller_registers: &[ + (CrtcControllerIndex::HorizontalTotal, 0x5F), + (CrtcControllerIndex::HorizontalDisplayEnableEnd, 0x4F), + (CrtcControllerIndex::HorizontalBlankingStart, 0x50), + (CrtcControllerIndex::HorizontalBlankingEnd, 0x82), + (CrtcControllerIndex::HorizontalSyncStart, 0x54), + (CrtcControllerIndex::HorizontalSyncEnd, 0x80), + (CrtcControllerIndex::VeritcalTotal, 0x0D), + (CrtcControllerIndex::Overflow, 0x3E), + (CrtcControllerIndex::PresetRowScan, 0x00), + (CrtcControllerIndex::MaximumScanLine, 0x41), + (CrtcControllerIndex::TextCursorStart, 0x00), + (CrtcControllerIndex::TextCursorEnd, 0x00), + (CrtcControllerIndex::StartAddressHigh, 0x00), + (CrtcControllerIndex::StartAddressLow, 0x00), + (CrtcControllerIndex::TextCursorLocationHigh, 0x00), + (CrtcControllerIndex::TextCursorLocationLow, 0x00), + (CrtcControllerIndex::VerticalSyncStart, 0xEA), + (CrtcControllerIndex::VerticalSyncEnd, 0xAC), + (CrtcControllerIndex::VerticalDisplayEnableEnd, 0xDF), + (CrtcControllerIndex::Offset, 0x28), + (CrtcControllerIndex::UnderlineLocation, 0x00), + (CrtcControllerIndex::VerticalBlankingStart, 0xE7), + (CrtcControllerIndex::VerticalBlankingEnd, 0x06), + (CrtcControllerIndex::ModeControl, 0xE3), + (CrtcControllerIndex::LineCompare, 0xFF), + ], + graphics_controller_registers: &[ + (GraphicsControllerIndex::SetReset, 0x00), + (GraphicsControllerIndex::EnableSetReset, 0x00), + (GraphicsControllerIndex::ColorCompare, 0x00), + (GraphicsControllerIndex::DataRotate, 0x00), + (GraphicsControllerIndex::ReadPlaneSelect, 0x00), + (GraphicsControllerIndex::GraphicsMode, 0x40), + (GraphicsControllerIndex::Miscellaneous, 0x05), + (GraphicsControllerIndex::ColorDontCare, 0x0F), + (GraphicsControllerIndex::BitMask, 0xFF), + ], + attribute_controller_registers: &[ + (AttributeControllerIndex::PaletteRegister0, 0x00), + (AttributeControllerIndex::PaletteRegister1, 0x01), + (AttributeControllerIndex::PaletteRegister2, 0x02), + (AttributeControllerIndex::PaletteRegister3, 0x03), + (AttributeControllerIndex::PaletteRegister4, 0x04), + (AttributeControllerIndex::PaletteRegister5, 0x05), + (AttributeControllerIndex::PaletteRegister6, 0x06), + (AttributeControllerIndex::PaletteRegister7, 0x07), + (AttributeControllerIndex::PaletteRegister8, 0x08), + (AttributeControllerIndex::PaletteRegister9, 0x09), + (AttributeControllerIndex::PaletteRegisterA, 0x0A), + (AttributeControllerIndex::PaletteRegisterB, 0x0B), + (AttributeControllerIndex::PaletteRegisterC, 0x0C), + (AttributeControllerIndex::PaletteRegisterD, 0x0D), + (AttributeControllerIndex::PaletteRegisterE, 0x0E), + (AttributeControllerIndex::PaletteRegisterF, 0x0F), + (AttributeControllerIndex::ModeControl, 0x41), + (AttributeControllerIndex::OverscanColor, 0x00), + (AttributeControllerIndex::MemoryPlaneEnable, 0x0F), + (AttributeControllerIndex::HorizontalPixelPanning, 0x00), + (AttributeControllerIndex::ColorSelect, 0x00), + ], +}; diff --git a/src/vga.rs b/src/vga.rs index 1b513c9..70c5895 100644 --- a/src/vga.rs +++ b/src/vga.rs @@ -13,6 +13,7 @@ use super::{ GraphicsControllerRegisters, PlaneMask, SequencerIndex, SequencerRegisters, }, }; +use crate::configurations::MODE_1280X800X256_CONFIGURATION; use conquer_once::spin::Lazy; use spinning_top::Spinlock; @@ -68,6 +69,8 @@ pub enum VideoMode { Mode320x240x256, /// Represents graphics mode 640x480x16. Mode640x480x16, + /// Represents graphics mode 1280x800x256. + Mode1280x800x256, } /// Represents a vga graphics card with it's common registers, @@ -128,6 +131,7 @@ impl Vga { VideoMode::Mode320x200x256 => self.set_video_mode_320x200x256(), VideoMode::Mode320x240x256 => self.set_video_mode_320x240x256(), VideoMode::Mode640x480x16 => self.set_video_mode_640x480x16(), + VideoMode::Mode1280x800x256 => self.set_video_mode_1280x800x256(), } } @@ -313,6 +317,12 @@ impl Vga { self.most_recent_video_mode = Some(VideoMode::Mode640x480x16); } + /// Sets the video card to Mode 1280x800x256. + fn set_video_mode_1280x800x256(&mut self) { + self.set_registers(&MODE_1280X800X256_CONFIGURATION); + self.most_recent_video_mode = Some(VideoMode::Mode1280x800x256); + } + /// Unlocks the CRTC registers by setting bit 7 to 0 `(value & 0x7F)`. /// /// `Protect Registers [0:7]`: Note that the ability to write to Bit 4 of the Overflow Register (CR07) diff --git a/src/writers/graphics_1280x800x256.rs b/src/writers/graphics_1280x800x256.rs new file mode 100644 index 0000000..4545cd4 --- /dev/null +++ b/src/writers/graphics_1280x800x256.rs @@ -0,0 +1,103 @@ +use super::{GraphicsWriter, Screen}; +use crate::colors::DEFAULT_PALETTE; +use crate::drawing::{Bresenham, Point}; +use crate::registers::PlaneMask; +use crate::vga::VGA; +use core::slice::from_raw_parts_mut; +use font8x8::UnicodeFonts; + +const WIDTH: usize = 1280; +const HEIGHT: usize = 800; +const BYTES_PER_PIXEL: usize = 4; +const PIXEL_COUNT: usize = WIDTH * HEIGHT; +const SIZE: usize = PIXEL_COUNT * BYTES_PER_PIXEL; + +type ColorT = u32; + +/// A basic interface for interacting with vga graphics mode 1280x800x256. +/// +/// # Examples +/// +/// Basic usage: +/// +/// ```no_run +/// use vga::writers::{Graphics1280x800x256, GraphicsWriter}; +/// +/// let mode = Graphics1280x800x256::new(); +/// mode.set_mode(); +/// mode.clear_screen(0x00_FF_00); +/// mode.draw_line((60, 20), (260, 20), 0xFF_00_FF); +/// mode.draw_line((60, 20), (60, 180), 0xFF_00_FF); +/// mode.draw_line((60, 180), (260, 180), 0xFF_00_FF); +/// mode.draw_line((260, 180), (260, 20), 0xFF_00_FF); +/// mode.draw_line((60, 40), (260, 40), 0xFF_00_FF); +/// for (offset, character) in "Hello World!".chars().enumerate() { +/// mode.draw_character(118 + offset * 8, 27, character, 0xFF_00_FF); +/// } +/// ``` +#[derive(Debug, Clone, Copy, Default)] +pub struct Graphics1280x800x256; + +impl Screen for Graphics1280x800x256 { + const WIDTH: usize = WIDTH; + const HEIGHT: usize = HEIGHT; + const SIZE: usize = SIZE; +} + +impl GraphicsWriter for Graphics1280x800x256 { + fn clear_screen(&self, color: ColorT) { + let frame_buffer = self.get_frame_buffer() as *mut ColorT; + VGA.lock() + .sequencer_registers + .set_plane_mask(PlaneMask::ALL_PLANES); + unsafe { + from_raw_parts_mut(frame_buffer, PIXEL_COUNT).fill(color); + } + } + + fn draw_line(&self, start: Point, end: Point, color: ColorT) { + 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: ColorT) { + let character = match font8x8::BASIC_FONTS.get(character) { + Some(character) => character, + // Default to a filled block if the character isn't found + None => font8x8::unicode::BLOCK_UNICODE[8].byte_array(), + }; + + for (row, byte) in character.iter().enumerate() { + for bit in 0..8 { + match *byte & 1 << bit { + 0 => (), + _ => self.set_pixel(x + bit, y + row, color), + } + } + } + } + + fn set_pixel(&self, x: usize, y: usize, color: ColorT) { + let frame_buffer = self.get_frame_buffer() as *mut ColorT; + let offset = WIDTH * y + x; + unsafe { + frame_buffer.add(offset).write_volatile(color); + } + } + + fn set_mode(&self) { + let mut vga = VGA.lock(); + + // Some bios mess up the palette when switching modes, + // so explicitly set it. + vga.color_palette_registers.load_palette(&DEFAULT_PALETTE); + } +} + +impl Graphics1280x800x256 { + /// Creates a new `Graphics1280x800x256`. + pub const fn new() -> Graphics1280x800x256 { + Graphics1280x800x256 + } +} diff --git a/src/writers/mod.rs b/src/writers/mod.rs index 3c7daea..dea28cd 100644 --- a/src/writers/mod.rs +++ b/src/writers/mod.rs @@ -1,4 +1,5 @@ //! Writers for common vga modes. +mod graphics_1280x800x256; mod graphics_320x200x256; mod graphics_320x240x256; mod graphics_640x480x16; @@ -14,6 +15,7 @@ use super::{ }; use spinning_top::SpinlockGuard; +pub use graphics_1280x800x256::Graphics1280x800x256; pub use graphics_320x200x256::Graphics320x200x256; pub use graphics_320x240x256::Graphics320x240x256; pub use graphics_640x480x16::Graphics640x480x16; diff --git a/testing/src/interrupts.rs b/testing/src/interrupts.rs index e6f1d02..f8e6857 100644 --- a/testing/src/interrupts.rs +++ b/testing/src/interrupts.rs @@ -68,10 +68,7 @@ extern "x86-interrupt" fn page_fault_handler( hlt_loop(); } -extern "x86-interrupt" fn segment_not_present( - _stack_frame: InterruptStackFrame, - _error_code: u64, -) { +extern "x86-interrupt" fn segment_not_present(_stack_frame: InterruptStackFrame, _error_code: u64) { // For some reason this sometimes gets thrown when running tests in qemu, // so leave empty so the tests finish for now. } diff --git a/testing/tests/vga.rs b/testing/tests/vga.rs index 52a7d6f..169e566 100644 --- a/testing/tests/vga.rs +++ b/testing/tests/vga.rs @@ -8,8 +8,8 @@ use core::panic::PanicInfo; use testing::{gdt, interrupts, serial_print, serial_println}; use vga::colors::{DEFAULT_PALETTE, PALETTE_SIZE}; use vga::configurations::{ - VgaConfiguration, MODE_40X25_CONFIGURATION, MODE_40X50_CONFIGURATION, - MODE_640X480X16_CONFIGURATION, MODE_80X25_CONFIGURATION, + VgaConfiguration, MODE_1280X800X256_CONFIGURATION, MODE_40X25_CONFIGURATION, + MODE_40X50_CONFIGURATION, MODE_640X480X16_CONFIGURATION, MODE_80X25_CONFIGURATION, }; use vga::vga::{Vga, VideoMode, VGA}; @@ -77,6 +77,17 @@ fn set_mode_640x480x16() { serial_println!("[ok]"); } +#[test_case] +fn set_mode_1280x800x256() { + serial_print!("mode 1280x800x256... "); + + let mut vga = VGA.lock(); + vga.set_video_mode(VideoMode::Mode1280x800x256); + check_registers(&mut vga, &MODE_1280X800X256_CONFIGURATION); + + serial_println!("[ok]"); +} + #[test_case] fn load_palette() { serial_print!("load palette... ");