From 7cea388ad0122332b6a278ad2126c14c40c8e3dc Mon Sep 17 00:00:00 2001 From: Ryan Kennedy Date: Tue, 10 Mar 2020 19:18:10 -0500 Subject: [PATCH 1/5] Initial commit --- Cargo.lock | 41 +++ Cargo.toml | 4 +- src/lib.rs | 1 + src/vga/mod.rs | 12 + src/vga/vga.rs | 307 +++++++++++++++++++++ src/vga/vga_colors.rs | 29 ++ src/vga/vga_configurations.rs | 310 +++++++++++++++++++++ src/vga/vga_fonts.rs | 435 ++++++++++++++++++++++++++++++ src/vga/vga_registers.rs | 385 ++++++++++++++++++++++++++ src/vga/vga_writers/mod.rs | 3 + src/vga/vga_writers/text_80x25.rs | 79 ++++++ 11 files changed, 1605 insertions(+), 1 deletion(-) create mode 100644 src/vga/mod.rs create mode 100644 src/vga/vga.rs create mode 100644 src/vga/vga_colors.rs create mode 100644 src/vga/vga_configurations.rs create mode 100644 src/vga/vga_fonts.rs create mode 100644 src/vga/vga_registers.rs create mode 100644 src/vga/vga_writers/mod.rs create mode 100644 src/vga/vga_writers/text_80x25.rs diff --git a/Cargo.lock b/Cargo.lock index a5268be5..29ff9dc4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -20,9 +20,11 @@ name = "bootloader" version = "0.8.9" dependencies = [ "bit_field 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "conquer-once 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "fixedvec 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", "font8x8 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", "llvm-tools 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "spinning_top 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "toml 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "usize_conversions 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "x86_64 0.8.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -34,6 +36,19 @@ name = "cast" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "conquer-once" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "conquer-util 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "conquer-util" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "fixedvec" version = "0.2.4" @@ -49,11 +64,32 @@ name = "llvm-tools" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "lock_api" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "scopeguard 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "serde" version = "1.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "spinning_top" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "lock_api 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "toml" version = "0.5.1" @@ -95,10 +131,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum bit_field 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ed8765909f9009617974ab6b7d332625b320b33c326b1e9321382ef1999b5d56" "checksum bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3d155346769a6855b86399e9bc3814ab343cd3d62c7e985113d46a0ec3c281fd" "checksum cast 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "926013f2860c46252efceabb19f4a6b308197505082c609025aa6706c011d427" +"checksum conquer-once 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6f7644600a548ecad74e4a918392af1798f7dd045be610be3203b9e129b4f98f" +"checksum conquer-util 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "654fb2472cc369d311c547103a1fa81d467bef370ae7a0680f65939895b1182a" "checksum fixedvec 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b395ef2adf62bdeefcd1b59ad0dd2225c6c333ec79656ea79ac5285c46d051ea" "checksum font8x8 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "44226c40489fb1d602344a1d8f1b544570c3435e396dda1eda7b5ef010d8f1be" "checksum llvm-tools 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "955be5d0ca0465caf127165acb47964f911e2bc26073e865deb8be7189302faf" +"checksum lock_api 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "79b2de95ecb4691949fea4716ca53cdbcfccb2c612e19644a8bad05edcf9f47b" +"checksum scopeguard 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" "checksum serde 1.0.98 (registry+https://github.com/rust-lang/crates.io-index)" = "7fe5626ac617da2f2d9c48af5515a21d5a480dbd151e01bb1c355e26a3e68113" +"checksum spinning_top 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "32d801a3a53bcf5071f85fef8d5cab9e5f638fc5580a37e6eb7aba4b37438d24" "checksum toml 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b8c96d7873fa7ef8bdeb3a9cda3ac48389b4154f32b9803b4bc26220b677b039" "checksum usize_conversions 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f70329e2cbe45d6c97a5112daad40c34cd9a4e18edb5a2a18fefeb584d8d25e5" "checksum x86_64 0.8.3 (registry+https://github.com/rust-lang/crates.io-index)" = "5d9e3e26fcb51976eafa310e8f2b7a5a83ae8c185443efe50cbc6534a4fffa0d" diff --git a/Cargo.toml b/Cargo.toml index f968d342..18055ad8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,7 +16,9 @@ required-features = ["binary"] xmas-elf = { version = "0.6.2", optional = true } x86_64 = { version = "0.8.3", optional = true } usize_conversions = { version = "0.2.0", optional = true } +spinning_top = { version = "0.1.0", optional = true, features = ["nightly"] } fixedvec = { version = "0.2.4", optional = true } +conquer-once = { version = "0.2.0", optional = true, default-features = false } bit_field = { version = "0.10.0", optional = true } [dependencies.font8x8] @@ -31,7 +33,7 @@ toml = { version = "0.5.1", optional = true } [features] default = [] -binary = ["xmas-elf", "x86_64", "usize_conversions", "fixedvec", "llvm-tools", "toml"] +binary = ["xmas-elf", "x86_64", "usize_conversions", "spinning_top", "fixedvec", "conquer-once", "llvm-tools", "toml"] vga_320x200 = ["font8x8"] recursive_page_table = [] map_physical_memory = [] diff --git a/src/lib.rs b/src/lib.rs index 90d8eebf..e2c800c5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,6 +11,7 @@ pub use crate::bootinfo::BootInfo; pub mod bootinfo; +pub mod vga; #[cfg(target_arch = "x86")] compile_error!( diff --git a/src/vga/mod.rs b/src/vga/mod.rs new file mode 100644 index 00000000..6fb17486 --- /dev/null +++ b/src/vga/mod.rs @@ -0,0 +1,12 @@ +//! Provides access to the VGA driver. + +mod vga; +mod vga_colors; +mod vga_configurations; +mod vga_fonts; +mod vga_registers; +mod vga_writers; + +pub use vga::{VideoMode, VGA}; +pub use vga_colors::{Color16Bit, TextModeColor}; +pub use vga_writers::Text80x25; diff --git a/src/vga/vga.rs b/src/vga/vga.rs new file mode 100644 index 00000000..4af1a8c2 --- /dev/null +++ b/src/vga/vga.rs @@ -0,0 +1,307 @@ +use super::{ + vga_configurations::{ + VgaConfiguration, MODE_40X25_CONFIGURATION, MODE_40X50_CONFIGURATION, + MODE_640X480X16_CONFIGURATION, MODE_80X25_CONFIGURATION, + }, + vga_fonts::{VgaFont, TEXT_8X16_FONT, TEXT_8X8_FONT}, + vga_registers::{ + AttributeControllerRegisters, CrtcControllerIndex, CrtcControllerRegisters, EmulationMode, + GeneralRegisters, GraphicsControllerIndex, GraphicsControllerRegisters, SequencerIndex, + SequencerRegisters, + }, +}; +use conquer_once::spin::Lazy; +use spinning_top::Spinlock; + +pub static VGA: Lazy> = Lazy::new(|| Spinlock::new(Vga::new())); + +#[derive(Debug, Copy, Clone)] +#[repr(u32)] +pub enum FrameBuffer { + GraphicsMode = 0xa0000, + CgaMode = 0xb8000, + MdaMode = 0xb0000, +} + +impl From for FrameBuffer { + fn from(value: u8) -> FrameBuffer { + match value { + 0x1 => FrameBuffer::GraphicsMode, + 0x2 => FrameBuffer::MdaMode, + 0x3 => FrameBuffer::CgaMode, + _ => panic!("{:X} is not a valid FrameBuffer value", value), + } + } +} + +impl From for u32 { + fn from(value: FrameBuffer) -> u32 { + value as u32 + } +} + +#[allow(dead_code)] +#[derive(Debug, Copy, Clone)] +#[repr(u8)] +pub enum Plane { + Plane0 = 0x0, + Plane1 = 0x1, + Plane2 = 0x2, + Plane3 = 0x3, +} + +impl From for u8 { + fn from(value: Plane) -> u8 { + value as u8 + } +} + +#[derive(Debug)] +pub enum VideoMode { + Mode40x25, + Mode40x50, + Mode80x25, + Mode640x480x16, +} + +pub struct Vga { + general_registers: GeneralRegisters, + sequencer_registers: SequencerRegisters, + graphics_controller_registers: GraphicsControllerRegisters, + attribute_controller_registers: AttributeControllerRegisters, + crtc_controller_registers: CrtcControllerRegisters, +} + +impl Vga { + fn new() -> Vga { + Vga { + general_registers: GeneralRegisters::new(), + sequencer_registers: SequencerRegisters::new(), + graphics_controller_registers: GraphicsControllerRegisters::new(), + attribute_controller_registers: AttributeControllerRegisters::new(), + crtc_controller_registers: CrtcControllerRegisters::new(), + } + } + + pub fn set_video_mode(&mut self, video_mode: VideoMode) { + match video_mode { + VideoMode::Mode40x25 => self.set_video_mode_40x25(), + VideoMode::Mode40x50 => self.set_video_mode_40x50(), + VideoMode::Mode80x25 => self.set_video_mode_80x25(), + VideoMode::Mode640x480x16 => self.set_video_mode_640x480x16(), + } + } + + pub fn get_frame_buffer(&mut self) -> FrameBuffer { + let miscellaneous_graphics = self + .graphics_controller_registers + .read(GraphicsControllerIndex::Miscellaneous); + let memory_map_mode = (miscellaneous_graphics >> 0x2) & 0x3; + FrameBuffer::from(memory_map_mode) + } + + /// `I/O Address Select` Bit 0 `(value & 0x1)` of MSR selects 3Bxh or 3Dxh as the I/O address for the CRT Controller + /// registers, the Feature Control Register (FCR), and Input Status Register 1 (ST01). Presently + /// ignored (whole range is claimed), but will "ignore" 3Bx for color configuration or 3Dx for + /// monochrome. + /// Note that it is typical in AGP chipsets to shadow this bit and properly steer I/O cycles to the + /// proper bus for operation where a MDA exists on another bus such as ISA. + /// + /// 0 = Select 3Bxh I/O address `(EmulationMode::Mda)` + /// + /// 1 = Select 3Dxh I/O address `(EmulationMode:Cga)` + fn get_emulation_mode(&mut self) -> EmulationMode { + EmulationMode::from(self.general_registers.read_msr() & 0x1) + } + + fn load_font(&mut self, vga_font: &VgaFont) { + // Save registers + let ( + plane_mask, + sequencer_memory_mode, + read_plane_select, + graphics_mode, + miscellaneous_graphics, + ) = self.save_font_registers(); + + // Switch to flat addressing + self.sequencer_registers + .write(SequencerIndex::MemoryMode, sequencer_memory_mode | 0x04); + + // Disable Even/Odd addressing + self.graphics_controller_registers + .write(GraphicsControllerIndex::GraphicsMode, graphics_mode & !0x10); + self.graphics_controller_registers.write( + GraphicsControllerIndex::Miscellaneous, + miscellaneous_graphics & !0x02, + ); + + // Write font to plane + self.set_plane(Plane::Plane2); + + let frame_buffer = u32::from(self.get_frame_buffer()) as *mut u8; + + for character in 0..vga_font.characters { + for row in 0..vga_font.character_height { + let offset = (character * 32) + row; + let font_offset = (character * vga_font.character_height) + row; + unsafe { + frame_buffer + .offset(offset as isize) + .write_volatile(vga_font.font_data[font_offset as usize]); + } + } + } + + self.restore_font_registers( + plane_mask, + sequencer_memory_mode, + read_plane_select, + graphics_mode, + miscellaneous_graphics, + ); + } + + fn restore_font_registers( + &mut self, + plane_mask: u8, + sequencer_memory_mode: u8, + read_plane_select: u8, + graphics_mode: u8, + miscellaneous_graphics: u8, + ) { + self.sequencer_registers + .write(SequencerIndex::PlaneMask, plane_mask); + self.sequencer_registers + .write(SequencerIndex::MemoryMode, sequencer_memory_mode); + self.graphics_controller_registers + .write(GraphicsControllerIndex::ReadPlaneSelect, read_plane_select); + self.graphics_controller_registers + .write(GraphicsControllerIndex::GraphicsMode, graphics_mode); + self.graphics_controller_registers.write( + GraphicsControllerIndex::Miscellaneous, + miscellaneous_graphics, + ); + } + + fn save_font_registers(&mut self) -> (u8, u8, u8, u8, u8) { + ( + self.sequencer_registers.read(SequencerIndex::PlaneMask), + self.sequencer_registers.read(SequencerIndex::MemoryMode), + self.graphics_controller_registers + .read(GraphicsControllerIndex::ReadPlaneSelect), + self.graphics_controller_registers + .read(GraphicsControllerIndex::GraphicsMode), + self.graphics_controller_registers + .read(GraphicsControllerIndex::Miscellaneous), + ) + } + + pub fn set_plane(&mut self, plane: Plane) { + let mut plane = u8::from(plane); + + plane &= 0x3; + + self.graphics_controller_registers + .write(GraphicsControllerIndex::ReadPlaneSelect, plane); + self.sequencer_registers + .write(SequencerIndex::PlaneMask, 0x1 << plane); + } + + fn set_registers(&mut self, configuration: &VgaConfiguration) { + let emulation_mode = self.get_emulation_mode(); + + // Set miscellaneous output + self.general_registers + .write_msr(configuration.miscellaneous_output); + + // Set the sequencer registers. + for (index, value) in configuration.sequencer_registers { + self.sequencer_registers.write(*index, *value); + } + + // Unlock the crtc registers. + self.unlock_crtc_registers(emulation_mode); + + // Set the crtc registers. + for (index, value) in configuration.crtc_controller_registers { + self.crtc_controller_registers + .write(emulation_mode, *index, *value); + } + + // Set the grx registers. + for (index, value) in configuration.graphics_controller_registers { + self.graphics_controller_registers.write(*index, *value); + } + + // Blank the screen so the palette registers are unlocked. + self.attribute_controller_registers + .blank_screen(emulation_mode); + + // Set the arx registers. + for (index, value) in configuration.attribute_controller_registers { + self.attribute_controller_registers + .write(emulation_mode, *index, *value); + } + + // Unblank the screen so the palette registers are locked. + self.attribute_controller_registers + .unblank_screen(emulation_mode); + } + + /// Sets the video card to Mode 40x25. + fn set_video_mode_40x25(&mut self) { + self.set_registers(&MODE_40X25_CONFIGURATION); + self.load_font(&TEXT_8X16_FONT); + } + + /// Sets the video card to Mode 40x50. + fn set_video_mode_40x50(&mut self) { + self.set_registers(&MODE_40X50_CONFIGURATION); + self.load_font(&TEXT_8X8_FONT); + } + + /// Sets the video card to Mode 80x25. + fn set_video_mode_80x25(&mut self) { + self.set_registers(&MODE_80X25_CONFIGURATION); + self.load_font(&TEXT_8X16_FONT); + } + + /// Sets the video card to Mode 320x200x4 + fn set_video_mode_640x480x16(&mut self) { + self.set_registers(&MODE_640X480X16_CONFIGURATION); + } + + /// 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) + /// is not affected by this bit (i.e., bit 4 of the Overflow Register is always writeable). + /// + /// 0 = Enable writes to registers `CR[00:07]` + /// + /// 1 = Disable writes to registers `CR[00:07]` + fn unlock_crtc_registers(&mut self, emulation_mode: EmulationMode) { + // Setting bit 7 to 1 used to be required for `VGA`, but says it's + // ignored in modern hardware. Setting it to 1 just to be safe for older + // hardware. More information can be found here + // https://01.org/sites/default/files/documentation/intel-gfx-prm-osrc-hsw-display.pdf + // under `CR03 - Horizontal Blanking End Register`. + let horizontal_blanking_end = self + .crtc_controller_registers + .read(emulation_mode, CrtcControllerIndex::HorizontalBlankingEnd); + self.crtc_controller_registers.write( + emulation_mode, + CrtcControllerIndex::HorizontalBlankingEnd, + horizontal_blanking_end | 0x80, + ); + + let vertical_sync_end = self + .crtc_controller_registers + .read(emulation_mode, CrtcControllerIndex::VerticalSyncEnd); + self.crtc_controller_registers.write( + emulation_mode, + CrtcControllerIndex::VerticalSyncEnd, + vertical_sync_end & 0x7F, + ); + } +} diff --git a/src/vga/vga_colors.rs b/src/vga/vga_colors.rs new file mode 100644 index 00000000..570523ed --- /dev/null +++ b/src/vga/vga_colors.rs @@ -0,0 +1,29 @@ +#[repr(u8)] +pub enum Color16Bit { + Black = 0, + Blue = 1, + Green = 2, + Cyan = 3, + Red = 4, + Magenta = 5, + Brown = 6, + LightGrey = 7, + DarkGrey = 8, + LightBlue = 9, + LightGreen = 10, + LightCyan = 11, + LightRed = 12, + Pink = 13, + Yellow = 14, + White = 15, +} + +#[derive(Debug, Copy, Clone)] +#[repr(transparent)] +pub struct TextModeColor(u8); + +impl TextModeColor { + pub const fn new(foreground: Color16Bit, background: Color16Bit) -> TextModeColor { + TextModeColor((background as u8) << 4 | (foreground as u8)) + } +} diff --git a/src/vga/vga_configurations.rs b/src/vga/vga_configurations.rs new file mode 100644 index 00000000..cd462ded --- /dev/null +++ b/src/vga/vga_configurations.rs @@ -0,0 +1,310 @@ +use super::vga_registers::{ + AttributeControllerIndex, CrtcControllerIndex, GraphicsControllerIndex, SequencerIndex, +}; + +/// Represents a set of vga registers for a given mode. +#[derive(Debug)] +pub struct VgaConfiguration { + pub miscellaneous_output: u8, + pub sequencer_registers: &'static [(SequencerIndex, u8)], + pub crtc_controller_registers: &'static [(CrtcControllerIndex, u8)], + pub graphics_controller_registers: &'static [(GraphicsControllerIndex, u8)], + pub attribute_controller_registers: &'static [(AttributeControllerIndex, u8)], +} + +/// Register values for Vga mode 40x25 Text. +pub const MODE_40X25_CONFIGURATION: VgaConfiguration = VgaConfiguration { + // Configuration values acquired from https://www.singlix.com/trdos/archive/vga/Graphics%20in%20pmode.pdf + miscellaneous_output: 0x67, + sequencer_registers: &[ + (SequencerIndex::SequencerReset, 0x03), + (SequencerIndex::ClockingMode, 0x08), + (SequencerIndex::PlaneMask, 0x03), + (SequencerIndex::CharacterFont, 0x00), + (SequencerIndex::MemoryMode, 0x02), + ], + crtc_controller_registers: &[ + (CrtcControllerIndex::HorizontalTotal, 0x2D), + (CrtcControllerIndex::HorizontalDisplayEnableEnd, 0x27), + (CrtcControllerIndex::HorizontalBlankingStart, 0x28), + (CrtcControllerIndex::HorizontalBlankingEnd, 0x90), + (CrtcControllerIndex::HorizontalSyncStart, 0x2B), + (CrtcControllerIndex::HorizontalSyncEnd, 0xA0), + (CrtcControllerIndex::VeritcalTotal, 0xBF), + (CrtcControllerIndex::Overflow, 0x1F), + (CrtcControllerIndex::PresetRowScan, 0x00), + (CrtcControllerIndex::MaximumScanLine, 0x4F), + (CrtcControllerIndex::TextCursorStart, 0x0A), + (CrtcControllerIndex::TextCursorEnd, 0x20), + (CrtcControllerIndex::StartAddressHigh, 0x00), + (CrtcControllerIndex::StartAddressLow, 0x00), + (CrtcControllerIndex::TextCursorLocationHigh, 0x00), + (CrtcControllerIndex::TextCursorLocationLow, 0xA0), + (CrtcControllerIndex::VerticalSyncStart, 0x9C), + (CrtcControllerIndex::VerticalSyncEnd, 0x8E), + (CrtcControllerIndex::VerticalDisplayEnableEnd, 0x8F), + (CrtcControllerIndex::Offset, 0x14), + (CrtcControllerIndex::UnderlineLocationRegister, 0x1F), + (CrtcControllerIndex::VerticalBlankingStart, 0x96), + (CrtcControllerIndex::VerticalBlankingEnd, 0xB9), + (CrtcControllerIndex::ModeControl, 0xA3), + (CrtcControllerIndex::LineCompare, 0xFF), + ], + graphics_controller_registers: &[ + (GraphicsControllerIndex::SetReset, 0x00), + (GraphicsControllerIndex::EnableSetReset, 0x00), + (GraphicsControllerIndex::ColorCompare, 0x00), + (GraphicsControllerIndex::DataRotate, 0x00), + (GraphicsControllerIndex::ReadPlaneSelect, 0x00), + (GraphicsControllerIndex::GraphicsMode, 0x10), + (GraphicsControllerIndex::Miscellaneous, 0x0E), + (GraphicsControllerIndex::ColorDontCare, 0x00), + (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, 0x14), + (AttributeControllerIndex::PaletteRegister7, 0x07), + (AttributeControllerIndex::PaletteRegister8, 0x38), + (AttributeControllerIndex::PaletteRegister9, 0x39), + (AttributeControllerIndex::PaletteRegisterA, 0x3A), + (AttributeControllerIndex::PaletteRegisterB, 0x3B), + (AttributeControllerIndex::PaletteRegisterC, 0x3C), + (AttributeControllerIndex::PaletteRegisterD, 0x3D), + (AttributeControllerIndex::PaletteRegisterE, 0x3E), + (AttributeControllerIndex::PaletteRegisterF, 0x3F), + (AttributeControllerIndex::ModeControl, 0x0C), + (AttributeControllerIndex::OverscanColor, 0x00), + (AttributeControllerIndex::MemoryPlaneEnable, 0x0F), + (AttributeControllerIndex::HorizontalPixelPanning, 0x08), + (AttributeControllerIndex::ColorSelect, 0x00), + ], +}; + +/// Register values for Vga mode 40x50 Text. +pub const MODE_40X50_CONFIGURATION: VgaConfiguration = VgaConfiguration { + // Configuration values acquired from https://www.singlix.com/trdos/archive/vga/Graphics%20in%20pmode.pdf + miscellaneous_output: 0x67, + sequencer_registers: &[ + (SequencerIndex::SequencerReset, 0x03), + (SequencerIndex::ClockingMode, 0x08), + (SequencerIndex::PlaneMask, 0x03), + (SequencerIndex::CharacterFont, 0x00), + (SequencerIndex::MemoryMode, 0x02), + ], + crtc_controller_registers: &[ + (CrtcControllerIndex::HorizontalTotal, 0x2D), + (CrtcControllerIndex::HorizontalDisplayEnableEnd, 0x27), + (CrtcControllerIndex::HorizontalBlankingStart, 0x28), + (CrtcControllerIndex::HorizontalBlankingEnd, 0x90), + (CrtcControllerIndex::HorizontalSyncStart, 0x2B), + (CrtcControllerIndex::HorizontalSyncEnd, 0xA0), + (CrtcControllerIndex::VeritcalTotal, 0xBF), + (CrtcControllerIndex::Overflow, 0x1F), + (CrtcControllerIndex::PresetRowScan, 0x00), + (CrtcControllerIndex::MaximumScanLine, 0x47), + (CrtcControllerIndex::TextCursorStart, 0x0A), + (CrtcControllerIndex::TextCursorEnd, 0x20), + (CrtcControllerIndex::StartAddressHigh, 0x00), + (CrtcControllerIndex::StartAddressLow, 0x00), + (CrtcControllerIndex::TextCursorLocationHigh, 0x04), + (CrtcControllerIndex::TextCursorLocationLow, 0x60), + (CrtcControllerIndex::VerticalSyncStart, 0x9C), + (CrtcControllerIndex::VerticalSyncEnd, 0x8E), + (CrtcControllerIndex::VerticalDisplayEnableEnd, 0x8F), + (CrtcControllerIndex::Offset, 0x14), + (CrtcControllerIndex::UnderlineLocationRegister, 0x1F), + (CrtcControllerIndex::VerticalBlankingStart, 0x96), + (CrtcControllerIndex::VerticalBlankingEnd, 0xB9), + (CrtcControllerIndex::ModeControl, 0xA3), + (CrtcControllerIndex::LineCompare, 0xFF), + ], + graphics_controller_registers: &[ + (GraphicsControllerIndex::SetReset, 0x00), + (GraphicsControllerIndex::EnableSetReset, 0x00), + (GraphicsControllerIndex::ColorCompare, 0x00), + (GraphicsControllerIndex::DataRotate, 0x00), + (GraphicsControllerIndex::ReadPlaneSelect, 0x00), + (GraphicsControllerIndex::GraphicsMode, 0x10), + (GraphicsControllerIndex::Miscellaneous, 0x0E), + (GraphicsControllerIndex::ColorDontCare, 0x00), + (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, 0x14), + (AttributeControllerIndex::PaletteRegister7, 0x07), + (AttributeControllerIndex::PaletteRegister8, 0x38), + (AttributeControllerIndex::PaletteRegister9, 0x39), + (AttributeControllerIndex::PaletteRegisterA, 0x3A), + (AttributeControllerIndex::PaletteRegisterB, 0x3B), + (AttributeControllerIndex::PaletteRegisterC, 0x3C), + (AttributeControllerIndex::PaletteRegisterD, 0x3D), + (AttributeControllerIndex::PaletteRegisterE, 0x3E), + (AttributeControllerIndex::PaletteRegisterF, 0x3F), + (AttributeControllerIndex::ModeControl, 0x0C), + (AttributeControllerIndex::OverscanColor, 0x00), + (AttributeControllerIndex::MemoryPlaneEnable, 0x0F), + (AttributeControllerIndex::HorizontalPixelPanning, 0x08), + (AttributeControllerIndex::ColorSelect, 0x00), + ], +}; + +/// Register values for Vga mode 80x25 Text. +pub const MODE_80X25_CONFIGURATION: VgaConfiguration = VgaConfiguration { + // Configuration values acquired from https://www.singlix.com/trdos/archive/vga/Graphics%20in%20pmode.pdf + miscellaneous_output: 0x67, + sequencer_registers: &[ + (SequencerIndex::SequencerReset, 0x03), + (SequencerIndex::ClockingMode, 0x00), + (SequencerIndex::PlaneMask, 0x03), + (SequencerIndex::CharacterFont, 0x00), + (SequencerIndex::MemoryMode, 0x02), + ], + crtc_controller_registers: &[ + (CrtcControllerIndex::HorizontalTotal, 0x5F), + (CrtcControllerIndex::HorizontalDisplayEnableEnd, 0x4F), + (CrtcControllerIndex::HorizontalBlankingStart, 0x50), + (CrtcControllerIndex::HorizontalBlankingEnd, 0x82), + (CrtcControllerIndex::HorizontalSyncStart, 0x55), + (CrtcControllerIndex::HorizontalSyncEnd, 0x81), + (CrtcControllerIndex::VeritcalTotal, 0xBF), + (CrtcControllerIndex::Overflow, 0x1F), + (CrtcControllerIndex::PresetRowScan, 0x00), + (CrtcControllerIndex::MaximumScanLine, 0x4F), + (CrtcControllerIndex::TextCursorStart, 0x0D), + (CrtcControllerIndex::TextCursorEnd, 0x0E), + (CrtcControllerIndex::StartAddressHigh, 0x00), + (CrtcControllerIndex::StartAddressLow, 0x00), + (CrtcControllerIndex::TextCursorLocationHigh, 0x00), + (CrtcControllerIndex::TextCursorLocationLow, 0x50), + (CrtcControllerIndex::VerticalSyncStart, 0x9C), + (CrtcControllerIndex::VerticalSyncEnd, 0x0E), + (CrtcControllerIndex::VerticalDisplayEnableEnd, 0x8F), + (CrtcControllerIndex::Offset, 0x28), + (CrtcControllerIndex::UnderlineLocationRegister, 0x1F), + (CrtcControllerIndex::VerticalBlankingStart, 0x96), + (CrtcControllerIndex::VerticalBlankingEnd, 0xB9), + (CrtcControllerIndex::ModeControl, 0xA3), + (CrtcControllerIndex::LineCompare, 0xFF), + ], + graphics_controller_registers: &[ + (GraphicsControllerIndex::SetReset, 0x00), + (GraphicsControllerIndex::EnableSetReset, 0x00), + (GraphicsControllerIndex::ColorCompare, 0x00), + (GraphicsControllerIndex::DataRotate, 0x00), + (GraphicsControllerIndex::ReadPlaneSelect, 0x00), + (GraphicsControllerIndex::GraphicsMode, 0x10), + (GraphicsControllerIndex::Miscellaneous, 0x0E), + (GraphicsControllerIndex::ColorDontCare, 0x00), + (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, 0x14), + (AttributeControllerIndex::PaletteRegister7, 0x07), + (AttributeControllerIndex::PaletteRegister8, 0x38), + (AttributeControllerIndex::PaletteRegister9, 0x39), + (AttributeControllerIndex::PaletteRegisterA, 0x3A), + (AttributeControllerIndex::PaletteRegisterB, 0x3B), + (AttributeControllerIndex::PaletteRegisterC, 0x3C), + (AttributeControllerIndex::PaletteRegisterD, 0x3D), + (AttributeControllerIndex::PaletteRegisterE, 0x3E), + (AttributeControllerIndex::PaletteRegisterF, 0x3F), + (AttributeControllerIndex::ModeControl, 0x0C), + (AttributeControllerIndex::OverscanColor, 0x00), + (AttributeControllerIndex::MemoryPlaneEnable, 0x0F), + (AttributeControllerIndex::HorizontalPixelPanning, 0x08), + (AttributeControllerIndex::ColorSelect, 0x00), + ], +}; + +/// Register values for Vga mode 640x480x16 Graphics. +pub const MODE_640X480X16_CONFIGURATION: VgaConfiguration = VgaConfiguration { + // Configuration values acquired from https://www.singlix.com/trdos/archive/vga/Graphics%20in%20pmode.pdf + // and https://forum.osdev.org/viewtopic.php?f=1&t=20137&hilit=640x480x16 + miscellaneous_output: 0xE3, + 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, 0x0B), + (CrtcControllerIndex::Overflow, 0x3E), + (CrtcControllerIndex::PresetRowScan, 0x00), + (CrtcControllerIndex::MaximumScanLine, 0x40), + (CrtcControllerIndex::TextCursorStart, 0x00), + (CrtcControllerIndex::TextCursorEnd, 0x00), + (CrtcControllerIndex::StartAddressHigh, 0x00), + (CrtcControllerIndex::StartAddressLow, 0x00), + (CrtcControllerIndex::TextCursorLocationHigh, 0x00), + (CrtcControllerIndex::TextCursorLocationLow, 0x59), + (CrtcControllerIndex::VerticalSyncStart, 0xEA), + (CrtcControllerIndex::VerticalSyncEnd, 0x8C), + (CrtcControllerIndex::VerticalDisplayEnableEnd, 0xDF), + (CrtcControllerIndex::Offset, 0x28), + (CrtcControllerIndex::UnderlineLocationRegister, 0x00), + (CrtcControllerIndex::VerticalBlankingStart, 0xE7), + (CrtcControllerIndex::VerticalBlankingEnd, 0x04), + (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, 0x00), + (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, 0x14), + (AttributeControllerIndex::PaletteRegister7, 0x07), + (AttributeControllerIndex::PaletteRegister8, 0x38), + (AttributeControllerIndex::PaletteRegister9, 0x39), + (AttributeControllerIndex::PaletteRegisterA, 0x3A), + (AttributeControllerIndex::PaletteRegisterB, 0x3B), + (AttributeControllerIndex::PaletteRegisterC, 0x3C), + (AttributeControllerIndex::PaletteRegisterD, 0x3D), + (AttributeControllerIndex::PaletteRegisterE, 0x3E), + (AttributeControllerIndex::PaletteRegisterF, 0x3F), + (AttributeControllerIndex::ModeControl, 0x01), + (AttributeControllerIndex::OverscanColor, 0x00), + (AttributeControllerIndex::MemoryPlaneEnable, 0x0F), + (AttributeControllerIndex::HorizontalPixelPanning, 0x00), + (AttributeControllerIndex::ColorSelect, 0x00), + ], +}; diff --git a/src/vga/vga_fonts.rs b/src/vga/vga_fonts.rs new file mode 100644 index 00000000..a3b1ea4d --- /dev/null +++ b/src/vga/vga_fonts.rs @@ -0,0 +1,435 @@ +/// Represents a font to be used for text mode. +pub struct VgaFont { + pub characters: u16, + pub character_height: u16, + pub font_data: &'static [u8], +} + +/// Standard 8x8 character font. +pub const TEXT_8X8_FONT: VgaFont = VgaFont { + characters: 256, + character_height: 8, + // Font data acquired from https://www.singlix.com/trdos/archive/vga/Graphics%20in%20pmode.pdf + font_data: &[ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x81, 0xA5, 0x81, 0xBD, 0x99, 0x81, + 0x7E, 0x7E, 0xFF, 0xDB, 0xFF, 0xC3, 0xE7, 0xFF, 0x7E, 0x6C, 0xFE, 0xFE, 0xFE, 0x7C, 0x38, + 0x10, 0x00, 0x10, 0x38, 0x7C, 0xFE, 0x7C, 0x38, 0x10, 0x00, 0x38, 0x7C, 0x38, 0xFE, 0xFE, + 0x92, 0x10, 0x7C, 0x00, 0x10, 0x38, 0x7C, 0xFE, 0x7C, 0x38, 0x7C, 0x00, 0x00, 0x18, 0x3C, + 0x3C, 0x18, 0x00, 0x00, 0xFF, 0xFF, 0xE7, 0xC3, 0xC3, 0xE7, 0xFF, 0xFF, 0x00, 0x3C, 0x66, + 0x42, 0x42, 0x66, 0x3C, 0x00, 0xFF, 0xC3, 0x99, 0xBD, 0xBD, 0x99, 0xC3, 0xFF, 0x0F, 0x07, + 0x0F, 0x7D, 0xCC, 0xCC, 0xCC, 0x78, 0x3C, 0x66, 0x66, 0x66, 0x3C, 0x18, 0x7E, 0x18, 0x3F, + 0x33, 0x3F, 0x30, 0x30, 0x70, 0xF0, 0xE0, 0x7F, 0x63, 0x7F, 0x63, 0x63, 0x67, 0xE6, 0xC0, + 0x99, 0x5A, 0x3C, 0xE7, 0xE7, 0x3C, 0x5A, 0x99, 0x80, 0xE0, 0xF8, 0xFE, 0xF8, 0xE0, 0x80, + 0x00, 0x02, 0x0E, 0x3E, 0xFE, 0x3E, 0x0E, 0x02, 0x00, 0x18, 0x3C, 0x7E, 0x18, 0x18, 0x7E, + 0x3C, 0x18, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x66, 0x00, 0x7F, 0xDB, 0xDB, 0x7B, 0x1B, + 0x1B, 0x1B, 0x00, 0x3E, 0x63, 0x38, 0x6C, 0x6C, 0x38, 0x86, 0xFC, 0x00, 0x00, 0x00, 0x00, + 0x7E, 0x7E, 0x7E, 0x00, 0x18, 0x3C, 0x7E, 0x18, 0x7E, 0x3C, 0x18, 0xFF, 0x18, 0x3C, 0x7E, + 0x18, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x18, 0x7E, 0x3C, 0x18, 0x00, 0x00, 0x18, + 0x0C, 0xFE, 0x0C, 0x18, 0x00, 0x00, 0x00, 0x30, 0x60, 0xFE, 0x60, 0x30, 0x00, 0x00, 0x00, + 0x00, 0xC0, 0xC0, 0xC0, 0xFE, 0x00, 0x00, 0x00, 0x24, 0x66, 0xFF, 0x66, 0x24, 0x00, 0x00, + 0x00, 0x18, 0x3C, 0x7E, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x7E, 0x3C, 0x18, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3C, 0x3C, 0x18, 0x18, 0x00, + 0x18, 0x00, 0x6C, 0x6C, 0x6C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6C, 0x6C, 0xFE, 0x6C, 0xFE, + 0x6C, 0x6C, 0x00, 0x18, 0x7E, 0xC0, 0x7C, 0x06, 0xFC, 0x18, 0x00, 0x00, 0xC6, 0xCC, 0x18, + 0x30, 0x66, 0xC6, 0x00, 0x38, 0x6C, 0x38, 0x76, 0xDC, 0xCC, 0x76, 0x00, 0x30, 0x30, 0x60, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x30, 0x60, 0x60, 0x60, 0x30, 0x18, 0x00, 0x60, 0x30, + 0x18, 0x18, 0x18, 0x30, 0x60, 0x00, 0x00, 0x66, 0x3C, 0xFF, 0x3C, 0x66, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x7E, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x30, + 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, + 0x00, 0x06, 0x0C, 0x18, 0x30, 0x60, 0xC0, 0x80, 0x00, 0x7C, 0xCE, 0xDE, 0xF6, 0xE6, 0xC6, + 0x7C, 0x00, 0x30, 0x70, 0x30, 0x30, 0x30, 0x30, 0xFC, 0x00, 0x78, 0xCC, 0x0C, 0x38, 0x60, + 0xCC, 0xFC, 0x00, 0x78, 0xCC, 0x0C, 0x38, 0x0C, 0xCC, 0x78, 0x00, 0x1C, 0x3C, 0x6C, 0xCC, + 0xFE, 0x0C, 0x1E, 0x00, 0xFC, 0xC0, 0xF8, 0x0C, 0x0C, 0xCC, 0x78, 0x00, 0x38, 0x60, 0xC0, + 0xF8, 0xCC, 0xCC, 0x78, 0x00, 0xFC, 0xCC, 0x0C, 0x18, 0x30, 0x30, 0x30, 0x00, 0x78, 0xCC, + 0xCC, 0x78, 0xCC, 0xCC, 0x78, 0x00, 0x78, 0xCC, 0xCC, 0x7C, 0x0C, 0x18, 0x70, 0x00, 0x00, + 0x18, 0x18, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x18, 0x18, 0x30, + 0x18, 0x30, 0x60, 0xC0, 0x60, 0x30, 0x18, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x7E, 0x00, 0x00, + 0x00, 0x60, 0x30, 0x18, 0x0C, 0x18, 0x30, 0x60, 0x00, 0x3C, 0x66, 0x0C, 0x18, 0x18, 0x00, + 0x18, 0x00, 0x7C, 0xC6, 0xDE, 0xDE, 0xDC, 0xC0, 0x7C, 0x00, 0x30, 0x78, 0xCC, 0xCC, 0xFC, + 0xCC, 0xCC, 0x00, 0xFC, 0x66, 0x66, 0x7C, 0x66, 0x66, 0xFC, 0x00, 0x3C, 0x66, 0xC0, 0xC0, + 0xC0, 0x66, 0x3C, 0x00, 0xF8, 0x6C, 0x66, 0x66, 0x66, 0x6C, 0xF8, 0x00, 0xFE, 0x62, 0x68, + 0x78, 0x68, 0x62, 0xFE, 0x00, 0xFE, 0x62, 0x68, 0x78, 0x68, 0x60, 0xF0, 0x00, 0x3C, 0x66, + 0xC0, 0xC0, 0xCE, 0x66, 0x3A, 0x00, 0xCC, 0xCC, 0xCC, 0xFC, 0xCC, 0xCC, 0xCC, 0x00, 0x78, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x78, 0x00, 0x1E, 0x0C, 0x0C, 0x0C, 0xCC, 0xCC, 0x78, 0x00, + 0xE6, 0x66, 0x6C, 0x78, 0x6C, 0x66, 0xE6, 0x00, 0xF0, 0x60, 0x60, 0x60, 0x62, 0x66, 0xFE, + 0x00, 0xC6, 0xEE, 0xFE, 0xFE, 0xD6, 0xC6, 0xC6, 0x00, 0xC6, 0xE6, 0xF6, 0xDE, 0xCE, 0xC6, + 0xC6, 0x00, 0x38, 0x6C, 0xC6, 0xC6, 0xC6, 0x6C, 0x38, 0x00, 0xFC, 0x66, 0x66, 0x7C, 0x60, + 0x60, 0xF0, 0x00, 0x7C, 0xC6, 0xC6, 0xC6, 0xD6, 0x7C, 0x0E, 0x00, 0xFC, 0x66, 0x66, 0x7C, + 0x6C, 0x66, 0xE6, 0x00, 0x7C, 0xC6, 0xE0, 0x78, 0x0E, 0xC6, 0x7C, 0x00, 0xFC, 0xB4, 0x30, + 0x30, 0x30, 0x30, 0x78, 0x00, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xFC, 0x00, 0xCC, 0xCC, + 0xCC, 0xCC, 0xCC, 0x78, 0x30, 0x00, 0xC6, 0xC6, 0xC6, 0xC6, 0xD6, 0xFE, 0x6C, 0x00, 0xC6, + 0xC6, 0x6C, 0x38, 0x6C, 0xC6, 0xC6, 0x00, 0xCC, 0xCC, 0xCC, 0x78, 0x30, 0x30, 0x78, 0x00, + 0xFE, 0xC6, 0x8C, 0x18, 0x32, 0x66, 0xFE, 0x00, 0x78, 0x60, 0x60, 0x60, 0x60, 0x60, 0x78, + 0x00, 0xC0, 0x60, 0x30, 0x18, 0x0C, 0x06, 0x02, 0x00, 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x78, 0x00, 0x10, 0x38, 0x6C, 0xC6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xFF, 0x30, 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x0C, + 0x7C, 0xCC, 0x76, 0x00, 0xE0, 0x60, 0x60, 0x7C, 0x66, 0x66, 0xDC, 0x00, 0x00, 0x00, 0x78, + 0xCC, 0xC0, 0xCC, 0x78, 0x00, 0x1C, 0x0C, 0x0C, 0x7C, 0xCC, 0xCC, 0x76, 0x00, 0x00, 0x00, + 0x78, 0xCC, 0xFC, 0xC0, 0x78, 0x00, 0x38, 0x6C, 0x64, 0xF0, 0x60, 0x60, 0xF0, 0x00, 0x00, + 0x00, 0x76, 0xCC, 0xCC, 0x7C, 0x0C, 0xF8, 0xE0, 0x60, 0x6C, 0x76, 0x66, 0x66, 0xE6, 0x00, + 0x30, 0x00, 0x70, 0x30, 0x30, 0x30, 0x78, 0x00, 0x0C, 0x00, 0x1C, 0x0C, 0x0C, 0xCC, 0xCC, + 0x78, 0xE0, 0x60, 0x66, 0x6C, 0x78, 0x6C, 0xE6, 0x00, 0x70, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x78, 0x00, 0x00, 0x00, 0xCC, 0xFE, 0xFE, 0xD6, 0xD6, 0x00, 0x00, 0x00, 0xB8, 0xCC, 0xCC, + 0xCC, 0xCC, 0x00, 0x00, 0x00, 0x78, 0xCC, 0xCC, 0xCC, 0x78, 0x00, 0x00, 0x00, 0xDC, 0x66, + 0x66, 0x7C, 0x60, 0xF0, 0x00, 0x00, 0x76, 0xCC, 0xCC, 0x7C, 0x0C, 0x1E, 0x00, 0x00, 0xDC, + 0x76, 0x62, 0x60, 0xF0, 0x00, 0x00, 0x00, 0x7C, 0xC0, 0x70, 0x1C, 0xF8, 0x00, 0x10, 0x30, + 0xFC, 0x30, 0x30, 0x34, 0x18, 0x00, 0x00, 0x00, 0xCC, 0xCC, 0xCC, 0xCC, 0x76, 0x00, 0x00, + 0x00, 0xCC, 0xCC, 0xCC, 0x78, 0x30, 0x00, 0x00, 0x00, 0xC6, 0xC6, 0xD6, 0xFE, 0x6C, 0x00, + 0x00, 0x00, 0xC6, 0x6C, 0x38, 0x6C, 0xC6, 0x00, 0x00, 0x00, 0xCC, 0xCC, 0xCC, 0x7C, 0x0C, + 0xF8, 0x00, 0x00, 0xFC, 0x98, 0x30, 0x64, 0xFC, 0x00, 0x1C, 0x30, 0x30, 0xE0, 0x30, 0x30, + 0x1C, 0x00, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x00, 0xE0, 0x30, 0x30, 0x1C, 0x30, + 0x30, 0xE0, 0x00, 0x76, 0xDC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x6C, + 0xC6, 0xC6, 0xFE, 0x00, 0x7C, 0xC6, 0xC0, 0xC6, 0x7C, 0x0C, 0x06, 0x7C, 0x00, 0xCC, 0x00, + 0xCC, 0xCC, 0xCC, 0x76, 0x00, 0x1C, 0x00, 0x78, 0xCC, 0xFC, 0xC0, 0x78, 0x00, 0x7E, 0x81, + 0x3C, 0x06, 0x3E, 0x66, 0x3B, 0x00, 0xCC, 0x00, 0x78, 0x0C, 0x7C, 0xCC, 0x76, 0x00, 0xE0, + 0x00, 0x78, 0x0C, 0x7C, 0xCC, 0x76, 0x00, 0x30, 0x30, 0x78, 0x0C, 0x7C, 0xCC, 0x76, 0x00, + 0x00, 0x00, 0x7C, 0xC6, 0xC0, 0x78, 0x0C, 0x38, 0x7E, 0x81, 0x3C, 0x66, 0x7E, 0x60, 0x3C, + 0x00, 0xCC, 0x00, 0x78, 0xCC, 0xFC, 0xC0, 0x78, 0x00, 0xE0, 0x00, 0x78, 0xCC, 0xFC, 0xC0, + 0x78, 0x00, 0xCC, 0x00, 0x70, 0x30, 0x30, 0x30, 0x78, 0x00, 0x7C, 0x82, 0x38, 0x18, 0x18, + 0x18, 0x3C, 0x00, 0xE0, 0x00, 0x70, 0x30, 0x30, 0x30, 0x78, 0x00, 0xC6, 0x10, 0x7C, 0xC6, + 0xFE, 0xC6, 0xC6, 0x00, 0x30, 0x30, 0x00, 0x78, 0xCC, 0xFC, 0xCC, 0x00, 0x1C, 0x00, 0xFC, + 0x60, 0x78, 0x60, 0xFC, 0x00, 0x00, 0x00, 0x7F, 0x0C, 0x7F, 0xCC, 0x7F, 0x00, 0x3E, 0x6C, + 0xCC, 0xFE, 0xCC, 0xCC, 0xCE, 0x00, 0x78, 0x84, 0x00, 0x78, 0xCC, 0xCC, 0x78, 0x00, 0x00, + 0xCC, 0x00, 0x78, 0xCC, 0xCC, 0x78, 0x00, 0x00, 0xE0, 0x00, 0x78, 0xCC, 0xCC, 0x78, 0x00, + 0x78, 0x84, 0x00, 0xCC, 0xCC, 0xCC, 0x76, 0x00, 0x00, 0xE0, 0x00, 0xCC, 0xCC, 0xCC, 0x76, + 0x00, 0x00, 0xCC, 0x00, 0xCC, 0xCC, 0x7C, 0x0C, 0xF8, 0xC3, 0x18, 0x3C, 0x66, 0x66, 0x3C, + 0x18, 0x00, 0xCC, 0x00, 0xCC, 0xCC, 0xCC, 0xCC, 0x78, 0x00, 0x18, 0x18, 0x7E, 0xC0, 0xC0, + 0x7E, 0x18, 0x18, 0x38, 0x6C, 0x64, 0xF0, 0x60, 0xE6, 0xFC, 0x00, 0xCC, 0xCC, 0x78, 0x30, + 0xFC, 0x30, 0xFC, 0x30, 0xF8, 0xCC, 0xCC, 0xFA, 0xC6, 0xCF, 0xC6, 0xC3, 0x0E, 0x1B, 0x18, + 0x3C, 0x18, 0x18, 0xD8, 0x70, 0x1C, 0x00, 0x78, 0x0C, 0x7C, 0xCC, 0x76, 0x00, 0x38, 0x00, + 0x70, 0x30, 0x30, 0x30, 0x78, 0x00, 0x00, 0x1C, 0x00, 0x78, 0xCC, 0xCC, 0x78, 0x00, 0x00, + 0x1C, 0x00, 0xCC, 0xCC, 0xCC, 0x76, 0x00, 0x00, 0xF8, 0x00, 0xB8, 0xCC, 0xCC, 0xCC, 0x00, + 0xFC, 0x00, 0xCC, 0xEC, 0xFC, 0xDC, 0xCC, 0x00, 0x3C, 0x6C, 0x6C, 0x3E, 0x00, 0x7E, 0x00, + 0x00, 0x38, 0x6C, 0x6C, 0x38, 0x00, 0x7C, 0x00, 0x00, 0x18, 0x00, 0x18, 0x18, 0x30, 0x66, + 0x3C, 0x00, 0x00, 0x00, 0x00, 0xFC, 0xC0, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0x0C, + 0x0C, 0x00, 0x00, 0xC6, 0xCC, 0xD8, 0x36, 0x6B, 0xC2, 0x84, 0x0F, 0xC3, 0xC6, 0xCC, 0xDB, + 0x37, 0x6D, 0xCF, 0x03, 0x18, 0x00, 0x18, 0x18, 0x3C, 0x3C, 0x18, 0x00, 0x00, 0x33, 0x66, + 0xCC, 0x66, 0x33, 0x00, 0x00, 0x00, 0xCC, 0x66, 0x33, 0x66, 0xCC, 0x00, 0x00, 0x22, 0x88, + 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0xDB, + 0xF6, 0xDB, 0x6F, 0xDB, 0x7E, 0xD7, 0xED, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0xF8, 0x18, 0x18, 0x18, 0x18, 0x18, 0xF8, 0x18, 0xF8, 0x18, 0x18, + 0x18, 0x36, 0x36, 0x36, 0x36, 0xF6, 0x36, 0x36, 0x36, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x36, + 0x36, 0x36, 0x00, 0x00, 0xF8, 0x18, 0xF8, 0x18, 0x18, 0x18, 0x36, 0x36, 0xF6, 0x06, 0xF6, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x00, 0x00, 0xFE, 0x06, + 0xF6, 0x36, 0x36, 0x36, 0x36, 0x36, 0xF6, 0x06, 0xFE, 0x00, 0x00, 0x00, 0x36, 0x36, 0x36, + 0x36, 0xFE, 0x00, 0x00, 0x00, 0x18, 0x18, 0xF8, 0x18, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xF8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1F, 0x00, 0x00, 0x00, 0x18, + 0x18, 0x18, 0x18, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x1F, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, + 0x00, 0x18, 0x18, 0x18, 0x18, 0xFF, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1F, 0x18, 0x1F, 0x18, + 0x18, 0x18, 0x36, 0x36, 0x36, 0x36, 0x37, 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x30, 0x3F, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x30, 0x37, 0x36, 0x36, 0x36, 0x36, 0x36, 0xF7, 0x00, + 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xF7, 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, + 0x30, 0x37, 0x36, 0x36, 0x36, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x36, 0x36, + 0xF7, 0x00, 0xF7, 0x36, 0x36, 0x36, 0x18, 0x18, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x36, + 0x36, 0x36, 0x36, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x18, 0x18, 0x18, + 0x00, 0x00, 0x00, 0x00, 0xFF, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x3F, 0x00, 0x00, + 0x00, 0x18, 0x18, 0x1F, 0x18, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x18, 0x1F, 0x18, + 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xFF, + 0x36, 0x36, 0x36, 0x18, 0x18, 0xFF, 0x18, 0xFF, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x18, 0x18, 0x18, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0xF0, + 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xDC, 0xC8, 0xDC, 0x76, 0x00, + 0x00, 0x78, 0xCC, 0xF8, 0xCC, 0xF8, 0xC0, 0xC0, 0x00, 0xFC, 0xCC, 0xC0, 0xC0, 0xC0, 0xC0, + 0x00, 0x00, 0x00, 0xFE, 0x6C, 0x6C, 0x6C, 0x6C, 0x00, 0xFC, 0xCC, 0x60, 0x30, 0x60, 0xCC, + 0xFC, 0x00, 0x00, 0x00, 0x7E, 0xD8, 0xD8, 0xD8, 0x70, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, + 0x7C, 0x60, 0xC0, 0x00, 0x76, 0xDC, 0x18, 0x18, 0x18, 0x18, 0x00, 0xFC, 0x30, 0x78, 0xCC, + 0xCC, 0x78, 0x30, 0xFC, 0x38, 0x6C, 0xC6, 0xFE, 0xC6, 0x6C, 0x38, 0x00, 0x38, 0x6C, 0xC6, + 0xC6, 0x6C, 0x6C, 0xEE, 0x00, 0x1C, 0x30, 0x18, 0x7C, 0xCC, 0xCC, 0x78, 0x00, 0x00, 0x00, + 0x7E, 0xDB, 0xDB, 0x7E, 0x00, 0x00, 0x06, 0x0C, 0x7E, 0xDB, 0xDB, 0x7E, 0x60, 0xC0, 0x38, + 0x60, 0xC0, 0xF8, 0xC0, 0x60, 0x38, 0x00, 0x78, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0x00, + 0x00, 0x7E, 0x00, 0x7E, 0x00, 0x7E, 0x00, 0x00, 0x18, 0x18, 0x7E, 0x18, 0x18, 0x00, 0x7E, + 0x00, 0x60, 0x30, 0x18, 0x30, 0x60, 0x00, 0xFC, 0x00, 0x18, 0x30, 0x60, 0x30, 0x18, 0x00, + 0xFC, 0x00, 0x0E, 0x1B, 0x1B, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0xD8, 0xD8, 0x70, 0x18, 0x18, 0x00, 0x7E, 0x00, 0x18, 0x18, 0x00, 0x00, 0x76, 0xDC, 0x00, + 0x76, 0xDC, 0x00, 0x00, 0x38, 0x6C, 0x6C, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x0F, 0x0C, + 0x0C, 0x0C, 0xEC, 0x6C, 0x3C, 0x1C, 0x58, 0x6C, 0x6C, 0x6C, 0x6C, 0x00, 0x00, 0x00, 0x70, + 0x98, 0x30, 0x60, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x3C, 0x3C, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + ], +}; + +/// Standard 8x16 character font. +pub const TEXT_8X16_FONT: VgaFont = VgaFont { + characters: 256, + character_height: 16, + // Font data acquired from https://www.singlix.com/trdos/archive/vga/Graphics%20in%20pmode.pdf + font_data: &[ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x7E, 0x81, 0xA5, 0x81, 0x81, 0xBD, 0x99, 0x81, 0x81, 0x7E, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7E, 0xFF, 0xDB, 0xFF, 0xFF, 0xC3, 0xE7, 0xFF, 0xFF, 0x7E, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6C, 0xFE, 0xFE, 0xFE, 0xFE, 0x7C, 0x38, 0x10, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x7C, 0xFE, 0x7C, 0x38, 0x10, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3C, 0x3C, 0xE7, 0xE7, 0xE7, 0x99, + 0x18, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3C, 0x7E, 0xFF, 0xFF, 0x7E, + 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3C, + 0x3C, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE7, + 0xC3, 0xC3, 0xE7, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, + 0x66, 0x42, 0x42, 0x66, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xC3, 0x99, 0xBD, 0xBD, 0x99, 0xC3, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x1E, 0x0E, + 0x1A, 0x32, 0x78, 0xCC, 0xCC, 0xCC, 0xCC, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, + 0x66, 0x66, 0x66, 0x66, 0x3C, 0x18, 0x7E, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3F, 0x33, 0x3F, 0x30, 0x30, 0x30, 0x30, 0x70, 0xF0, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x7F, 0x63, 0x7F, 0x63, 0x63, 0x63, 0x63, 0x67, 0xE7, 0xE6, 0xC0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x18, 0x18, 0xDB, 0x3C, 0xE7, 0x3C, 0xDB, 0x18, 0x18, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x80, 0xC0, 0xE0, 0xF0, 0xF8, 0xFE, 0xF8, 0xF0, 0xE0, 0xC0, 0x80, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x06, 0x0E, 0x1E, 0x3E, 0xFE, 0x3E, 0x1E, 0x0E, 0x06, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3C, 0x7E, 0x18, 0x18, 0x18, 0x18, 0x7E, 0x3C, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x66, + 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xDB, 0xDB, 0xDB, 0x7B, 0x1B, 0x1B, 0x1B, + 0x1B, 0x1B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0xC6, 0x60, 0x38, 0x6C, 0xC6, 0xC6, 0x6C, + 0x38, 0x0C, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFE, 0xFE, 0xFE, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3C, 0x7E, 0x18, 0x18, + 0x18, 0x18, 0x7E, 0x3C, 0x18, 0x7E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3C, 0x7E, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x7E, 0x3C, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x0C, 0xFE, 0x0C, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x30, 0x60, 0xFE, 0x60, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xC0, 0xC0, 0xC0, 0xC0, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x28, 0x6C, 0xFE, 0x6C, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x38, 0x7C, 0x7C, 0xFE, 0xFE, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xFE, 0x7C, 0x7C, 0x38, 0x38, 0x10, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3C, 0x3C, 0x3C, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6C, 0x6C, 0xFE, 0x6C, 0x6C, 0x6C, 0xFE, + 0x6C, 0x6C, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7C, 0xC6, 0xC2, 0xC0, 0x7C, 0x06, 0x86, + 0xC6, 0x7C, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC2, 0xC6, 0x0C, 0x18, + 0x30, 0x60, 0xC6, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6C, 0x6C, 0x38, 0x76, + 0xDC, 0xCC, 0xCC, 0xCC, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x30, 0x60, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x18, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x18, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x18, + 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x66, 0x3C, 0xFF, 0x3C, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x18, 0x18, 0x7E, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x06, 0x0C, 0x18, 0x30, 0x60, 0xC0, 0x80, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0xC6, 0xC6, 0xCE, 0xD6, 0xD6, 0xE6, 0xC6, 0xC6, 0x7C, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x38, 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x7E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0xC6, 0x06, 0x0C, 0x18, 0x30, 0x60, 0xC0, + 0xC6, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0xC6, 0x06, 0x06, 0x3C, 0x06, 0x06, + 0x06, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x1C, 0x3C, 0x6C, 0xCC, 0xFE, + 0x0C, 0x0C, 0x0C, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xC0, 0xC0, 0xC0, 0xFC, + 0x0E, 0x06, 0x06, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x60, 0xC0, 0xC0, + 0xFC, 0xC6, 0xC6, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xC6, 0x06, + 0x06, 0x0C, 0x18, 0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0xC6, + 0xC6, 0xC6, 0x7C, 0xC6, 0xC6, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, + 0xC6, 0xC6, 0xC6, 0x7E, 0x06, 0x06, 0x06, 0x0C, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x06, 0x0C, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0C, 0x06, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x30, 0x18, 0x0C, 0x06, 0x0C, 0x18, 0x30, 0x60, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0xC6, 0xC6, 0x0C, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0xC6, 0xC6, 0xDE, 0xDE, 0xDE, 0xDC, 0xC0, + 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x6C, 0xC6, 0xC6, 0xFE, 0xC6, 0xC6, + 0xC6, 0xC6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0x66, 0x66, 0x66, 0x7C, 0x66, 0x66, + 0x66, 0x66, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x66, 0xC2, 0xC0, 0xC0, 0xC0, + 0xC0, 0xC2, 0x66, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0x6C, 0x66, 0x66, 0x66, + 0x66, 0x66, 0x66, 0x6C, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x66, 0x62, 0x68, + 0x78, 0x68, 0x60, 0x62, 0x66, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x66, 0x62, + 0x68, 0x78, 0x68, 0x60, 0x60, 0x60, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x66, + 0xC2, 0xC0, 0xC0, 0xDE, 0xC6, 0xC6, 0x66, 0x3A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC6, + 0xC6, 0xC6, 0xC6, 0xFE, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3C, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x1E, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0xCC, 0xCC, 0xCC, 0x78, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xE6, 0x66, 0x6C, 0x6C, 0x78, 0x78, 0x6C, 0x66, 0x66, 0xE6, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xF0, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x62, 0x66, 0xFE, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xC6, 0xEE, 0xFE, 0xFE, 0xD6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xC6, 0xE6, 0xF6, 0xFE, 0xDE, 0xCE, 0xC6, 0xC6, 0xC6, 0xC6, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6C, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0x6C, + 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0x66, 0x66, 0x66, 0x7C, 0x60, 0x60, 0x60, + 0x60, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, + 0xD6, 0xDE, 0x7C, 0x0C, 0x0E, 0x00, 0x00, 0x00, 0x00, 0xFC, 0x66, 0x66, 0x66, 0x7C, 0x6C, + 0x66, 0x66, 0x66, 0xE6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0xC6, 0xC6, 0x60, 0x38, + 0x0C, 0x06, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x7E, 0x5A, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC6, 0xC6, 0xC6, + 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC6, 0xC6, + 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0x6C, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC6, + 0xC6, 0xC6, 0xC6, 0xC6, 0xD6, 0xD6, 0xFE, 0x6C, 0x6C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xC6, 0xC6, 0x6C, 0x6C, 0x38, 0x38, 0x6C, 0x6C, 0xC6, 0xC6, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x18, 0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xFE, 0xC6, 0x86, 0x0C, 0x18, 0x30, 0x60, 0xC2, 0xC6, 0xFE, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x3C, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xC0, 0xE0, 0x70, 0x38, 0x1C, 0x0E, 0x06, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x3C, + 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x6C, 0xC6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x30, 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x0C, 0x7C, + 0xCC, 0xCC, 0xCC, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x60, 0x60, 0x78, 0x6C, + 0x66, 0x66, 0x66, 0x66, 0xDC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, + 0xC6, 0xC0, 0xC0, 0xC0, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x0C, 0x0C, + 0x3C, 0x6C, 0xCC, 0xCC, 0xCC, 0xCC, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x7C, 0xC6, 0xFE, 0xC0, 0xC0, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, + 0x6C, 0x64, 0x60, 0xF0, 0x60, 0x60, 0x60, 0x60, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x76, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0x7C, 0x0C, 0xCC, 0x78, 0x00, 0x00, + 0x00, 0xE0, 0x60, 0x60, 0x6C, 0x76, 0x66, 0x66, 0x66, 0x66, 0xE6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x06, 0x06, 0x00, 0x0E, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x66, 0x66, + 0x3C, 0x00, 0x00, 0x00, 0xE0, 0x60, 0x60, 0x66, 0x6C, 0x78, 0x78, 0x6C, 0x66, 0xE6, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3C, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEC, 0xFE, 0xD6, 0xD6, 0xD6, 0xD6, + 0xD6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDC, 0x66, 0x66, 0x66, 0x66, + 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0xC6, 0xC6, 0xC6, + 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDC, 0x66, 0x66, + 0x66, 0x66, 0x66, 0x7C, 0x60, 0x60, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xCC, + 0xCC, 0xCC, 0xCC, 0xCC, 0x7C, 0x0C, 0x0C, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDC, + 0x76, 0x62, 0x60, 0x60, 0x60, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7C, 0xC6, 0x60, 0x38, 0x0C, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x30, + 0x30, 0xFC, 0x30, 0x30, 0x30, 0x30, 0x36, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xC6, 0xC6, 0xC6, 0xD6, 0xD6, 0xFE, 0x6C, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xC6, 0x6C, 0x38, 0x38, 0x38, 0x6C, 0xC6, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0x7E, 0x06, 0x0C, + 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xCC, 0x18, 0x30, 0x60, 0xC6, 0xFE, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x18, 0x18, 0x18, 0x70, 0x18, 0x18, 0x18, 0x18, 0x0E, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x18, 0x18, 0x18, 0x0E, 0x18, 0x18, 0x18, + 0x18, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xDC, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x6C, 0xC6, + 0xC6, 0xC6, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x66, 0xC2, 0xC0, 0xC0, + 0xC0, 0xC2, 0x66, 0x3C, 0x0C, 0x06, 0x7C, 0x00, 0x00, 0x00, 0x00, 0xCC, 0xCC, 0x00, 0xCC, + 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x18, 0x30, 0x00, + 0x7C, 0xC6, 0xFE, 0xC0, 0xC0, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x6C, + 0x00, 0x78, 0x0C, 0x7C, 0xCC, 0xCC, 0xCC, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCC, + 0xCC, 0x00, 0x78, 0x0C, 0x7C, 0xCC, 0xCC, 0xCC, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, + 0x30, 0x18, 0x00, 0x78, 0x0C, 0x7C, 0xCC, 0xCC, 0xCC, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x38, 0x6C, 0x38, 0x00, 0x78, 0x0C, 0x7C, 0xCC, 0xCC, 0xCC, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3C, 0x66, 0x60, 0x60, 0x66, 0x3C, 0x0C, 0x06, 0x3C, 0x00, 0x00, + 0x00, 0x00, 0x10, 0x38, 0x6C, 0x00, 0x7C, 0xC6, 0xFE, 0xC0, 0xC0, 0xC6, 0x7C, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xC6, 0xC6, 0x00, 0x7C, 0xC6, 0xFE, 0xC0, 0xC0, 0xC6, 0x7C, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x60, 0x30, 0x18, 0x00, 0x7C, 0xC6, 0xFE, 0xC0, 0xC0, 0xC6, 0x7C, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3C, 0x66, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x30, 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC6, 0xC6, 0x10, 0x38, 0x6C, 0xC6, 0xC6, + 0xFE, 0xC6, 0xC6, 0xC6, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6C, 0x38, 0x00, 0x38, 0x6C, 0xC6, + 0xC6, 0xFE, 0xC6, 0xC6, 0xC6, 0x00, 0x00, 0x00, 0x00, 0x18, 0x30, 0x60, 0x00, 0xFE, 0x66, + 0x60, 0x7C, 0x60, 0x60, 0x66, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xCC, 0x76, 0x36, 0x7E, 0xD8, 0xD8, 0x6E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x6C, + 0xCC, 0xCC, 0xFE, 0xCC, 0xCC, 0xCC, 0xCC, 0xCE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, + 0x6C, 0x00, 0x7C, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xC6, 0xC6, 0x00, 0x7C, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x60, 0x30, 0x18, 0x00, 0x7C, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x30, 0x78, 0xCC, 0x00, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0x76, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x60, 0x30, 0x18, 0x00, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0x76, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xC6, 0xC6, 0x00, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0x7E, 0x06, + 0x0C, 0x78, 0x00, 0x00, 0xC6, 0xC6, 0x00, 0x38, 0x6C, 0xC6, 0xC6, 0xC6, 0xC6, 0x6C, 0x38, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xC6, 0xC6, 0x00, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, + 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x3C, 0x66, 0x60, 0x60, 0x60, 0x66, 0x3C, + 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6C, 0x64, 0x60, 0xF0, 0x60, 0x60, 0x60, + 0x60, 0xE6, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x3C, 0x18, 0x7E, 0x18, + 0x7E, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xCC, 0xCC, 0xF8, 0xC4, 0xCC, + 0xDE, 0xCC, 0xCC, 0xCC, 0xC6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x1B, 0x18, 0x18, 0x18, + 0x7E, 0x18, 0x18, 0x18, 0x18, 0x18, 0xD8, 0x70, 0x00, 0x00, 0x00, 0x18, 0x30, 0x60, 0x00, + 0x78, 0x0C, 0x7C, 0xCC, 0xCC, 0xCC, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x18, 0x30, + 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x30, + 0x60, 0x00, 0x7C, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, + 0x30, 0x60, 0x00, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x76, 0xDC, 0x00, 0xDC, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, + 0x76, 0xDC, 0x00, 0xC6, 0xE6, 0xF6, 0xFE, 0xDE, 0xCE, 0xC6, 0xC6, 0xC6, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3C, 0x6C, 0x6C, 0x3E, 0x00, 0x7E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x38, 0x6C, 0x6C, 0x38, 0x00, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x00, 0x30, 0x30, 0x60, 0xC0, 0xC6, 0xC6, 0x7C, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xC0, 0xC0, 0xC0, 0xC0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x06, 0x06, 0x06, + 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xC0, 0xC2, 0xC6, 0xCC, 0x18, 0x30, 0x60, + 0xCE, 0x93, 0x06, 0x0C, 0x1F, 0x00, 0x00, 0x00, 0xC0, 0xC0, 0xC2, 0xC6, 0xCC, 0x18, 0x30, + 0x66, 0xCE, 0x9A, 0x3F, 0x06, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x18, 0x18, + 0x18, 0x3C, 0x3C, 0x3C, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, + 0x66, 0xCC, 0x66, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xCC, 0x66, 0x33, 0x66, 0xCC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x44, 0x11, 0x44, + 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x55, 0xAA, 0x55, + 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0xDD, 0x77, + 0xDD, 0x77, 0xDD, 0x77, 0xDD, 0x77, 0xDD, 0x77, 0xDD, 0x77, 0xDD, 0x77, 0xDD, 0x77, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xF8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xF8, 0x18, 0xF8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xF6, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0x18, 0xF8, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x36, 0x36, 0x36, 0x36, 0x36, 0xF6, 0x06, 0xF6, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x06, 0xF6, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xF6, 0x06, + 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, + 0xF8, 0x18, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xF8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1F, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xFF, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1F, 0x18, 0x1F, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x30, 0x3F, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x30, 0x37, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xF7, 0x00, 0xFF, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, + 0xF7, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, + 0x30, 0x37, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x36, 0x36, 0x36, + 0x36, 0xF7, 0x00, 0xF7, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x18, 0x18, 0x18, + 0x18, 0x18, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1F, 0x18, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x18, 0x1F, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x36, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xFF, 0x36, 0x36, + 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x18, 0x18, 0x18, 0x18, 0x18, 0xFF, 0x18, 0xFF, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xF8, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1F, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0xF0, 0xF0, 0xF0, + 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0x0F, 0x0F, 0x0F, + 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x76, 0xDC, 0xD8, 0xD8, 0xD8, 0xDC, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0xC6, 0xFC, 0xC6, 0xC6, 0xFC, 0xC0, 0xC0, 0xC0, 0x00, + 0x00, 0x00, 0x00, 0xFE, 0xC6, 0xC6, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFE, 0x6C, 0x6C, 0x6C, 0x6C, 0x6C, 0x6C, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xC6, 0x60, 0x30, 0x18, 0x30, 0x60, 0xC6, 0xFE, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7E, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, + 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x7C, + 0x60, 0x60, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xDC, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x18, 0x3C, 0x66, 0x66, + 0x66, 0x3C, 0x18, 0x7E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6C, 0xC6, 0xC6, + 0xFE, 0xC6, 0xC6, 0x6C, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6C, 0xC6, 0xC6, + 0xC6, 0x6C, 0x6C, 0x6C, 0x6C, 0xEE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E, 0x30, 0x18, + 0x0C, 0x3E, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x7E, 0xDB, 0xDB, 0xDB, 0x7E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x06, 0x7E, 0xCF, 0xDB, 0xF3, 0x7E, 0x60, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1C, 0x30, 0x60, 0x60, 0x7C, 0x60, 0x60, 0x60, 0x30, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7C, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xFE, 0x00, 0x00, 0xFE, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7E, 0x18, 0x18, 0x00, 0x00, 0xFF, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x18, 0x0C, 0x06, 0x0C, 0x18, 0x30, 0x00, 0x7E, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0C, 0x00, 0x7E, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x1B, 0x1B, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, + 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xD8, 0xD8, + 0xD8, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x7E, 0x00, + 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xDC, 0x00, + 0x76, 0xDC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6C, 0x6C, 0x38, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x0C, 0x0C, + 0x0C, 0x0C, 0x0C, 0xEC, 0x6C, 0x6C, 0x3C, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD8, 0x6C, + 0x6C, 0x6C, 0x6C, 0x6C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, + 0x98, 0x30, 0x60, 0xC8, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x7C, 0x7C, 0x7C, 0x7C, 0x7C, 0x7C, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, + ], +}; diff --git a/src/vga/vga_registers.rs b/src/vga/vga_registers.rs new file mode 100644 index 00000000..0bd2ccbb --- /dev/null +++ b/src/vga/vga_registers.rs @@ -0,0 +1,385 @@ +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; + +#[derive(Debug, Copy, Clone)] +#[repr(u8)] +pub enum EmulationMode { + Mda = 0x0, + 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), + } + } +} + +#[derive(Debug, Clone, Copy)] +#[repr(u8)] +pub enum SequencerIndex { + SequencerReset = 0x0, + ClockingMode = 0x1, + PlaneMask = 0x2, + CharacterFont = 0x3, + MemoryMode = 0x4, + CounterReset = 0x7, +} + +impl From for u8 { + fn from(value: SequencerIndex) -> u8 { + value as u8 + } +} + +#[derive(Debug, Copy, Clone)] +#[repr(u8)] +pub enum GraphicsControllerIndex { + SetReset = 0x0, + EnableSetReset = 0x1, + ColorCompare = 0x2, + DataRotate = 0x3, + ReadPlaneSelect = 0x4, + GraphicsMode = 0x5, + Miscellaneous = 0x6, + ColorDontCare = 0x7, + BitMask = 0x8, + AddressMapping = 0x10, + PageSelector = 0x11, + SoftwareFlags = 0x18, +} + +impl From for u8 { + fn from(value: GraphicsControllerIndex) -> u8 { + value as u8 + } +} + +#[derive(Debug, Copy, Clone)] +#[repr(u8)] +pub enum AttributeControllerIndex { + PaletteRegister0 = 0x00, + PaletteRegister1 = 0x01, + PaletteRegister2 = 0x02, + PaletteRegister3 = 0x03, + PaletteRegister4 = 0x04, + PaletteRegister5 = 0x05, + PaletteRegister6 = 0x06, + PaletteRegister7 = 0x07, + PaletteRegister8 = 0x08, + PaletteRegister9 = 0x09, + PaletteRegisterA = 0x0A, + PaletteRegisterB = 0x0B, + PaletteRegisterC = 0x0C, + PaletteRegisterD = 0x0D, + PaletteRegisterE = 0x0E, + PaletteRegisterF = 0x0F, + ModeControl = 0x10, + OverscanColor = 0x11, + MemoryPlaneEnable = 0x12, + HorizontalPixelPanning = 0x13, + ColorSelect = 0x14, +} + +impl From for u8 { + fn from(value: AttributeControllerIndex) -> u8 { + value as u8 + } +} + +#[derive(Debug, Copy, Clone)] +#[repr(u8)] +pub enum CrtcControllerIndex { + HorizontalTotal = 0x00, + HorizontalDisplayEnableEnd = 0x01, + HorizontalBlankingStart = 0x02, + HorizontalBlankingEnd = 0x03, + HorizontalSyncStart = 0x04, + HorizontalSyncEnd = 0x05, + VeritcalTotal = 0x06, + Overflow = 0x07, + PresetRowScan = 0x08, + MaximumScanLine = 0x09, + TextCursorStart = 0x0A, + TextCursorEnd = 0x0B, + StartAddressHigh = 0x0C, + StartAddressLow = 0x0D, + TextCursorLocationHigh = 0x0E, + TextCursorLocationLow = 0x0F, + VerticalSyncStart = 0x10, + VerticalSyncEnd = 0x11, + VerticalDisplayEnableEnd = 0x12, + Offset = 0x13, + UnderlineLocationRegister = 0x14, + VerticalBlankingStart = 0x15, + VerticalBlankingEnd = 0x16, + ModeControl = 0x17, + LineCompare = 0x18, + MemoryReadLatchData = 0x22, + ToggleStateOfAttributeController = 0x24, +} + +impl From for u8 { + fn from(value: CrtcControllerIndex) -> u8 { + value as u8 + } +} + +#[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); + } + } +} + +#[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)); + } + } +} + +#[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)); + } + } +} + +#[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 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 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/vga/vga_writers/mod.rs b/src/vga/vga_writers/mod.rs new file mode 100644 index 00000000..0fbc1a72 --- /dev/null +++ b/src/vga/vga_writers/mod.rs @@ -0,0 +1,3 @@ +mod text_80x25; + +pub use text_80x25::Text80x25; diff --git a/src/vga/vga_writers/text_80x25.rs b/src/vga/vga_writers/text_80x25.rs new file mode 100644 index 00000000..a0d25a95 --- /dev/null +++ b/src/vga/vga_writers/text_80x25.rs @@ -0,0 +1,79 @@ +use crate::vga::{ + vga_colors::{Color16Bit, TextModeColor}, + VideoMode, VGA, +}; + +const WIDTH: usize = 80; +const HEIGHT: usize = 25; +const SCREEN_SIZE: usize = WIDTH * HEIGHT; + +static BLANK_CHARACTER: ScreenCharacter = ScreenCharacter { + character: b' ', + color: TextModeColor::new(Color16Bit::Yellow, Color16Bit::Black), +}; + +#[derive(Debug, Copy, Clone)] +#[repr(C)] +struct ScreenCharacter { + character: u8, + color: TextModeColor, +} + +/// A basic interface for interacting with vga text mode 80x25 +/// +/// # Examples +/// +/// Basic usage: +/// +/// ``` +/// let text_mode = Text80x25::new(); +/// text_mode.set_mode(); +/// text_mode.clear_screen(); +/// ``` +pub struct Text80x25; + +impl Text80x25 { + /// Creates a new `Text80x25`. + pub fn new() -> Text80x25 { + Text80x25 {} + } + + /// Clears the screen by setting all cells to `b' '` with + /// a background color of `Color16Bit::Black` and a foreground + /// color of `Color16Bit::Yellow`. + pub fn clear_screen(&self) { + let frame_buffer = self.get_frame_buffer(); + for i in 0..SCREEN_SIZE { + unsafe { + frame_buffer + .offset(i as isize) + .write_volatile(BLANK_CHARACTER); + } + } + } + + /// Prints the given `character` and `color` at `(x, y)`. + /// + /// Panics if `x >= 80` or `y >= 25`. + pub fn write_character(&self, x: usize, y: usize, character: u8, color: TextModeColor) { + assert!(x < WIDTH, "x >= {}", WIDTH); + assert!(y < HEIGHT, "y >= {}", HEIGHT); + let frame_buffer = self.get_frame_buffer(); + let offset = (WIDTH * y + x) as isize; + unsafe { + frame_buffer + .offset(offset) + .write_volatile(ScreenCharacter { character, color }); + } + } + + /// Sets the graphics device to `VideoMode::Mode80x25`. + pub fn set_mode(&self) { + VGA.lock().set_video_mode(VideoMode::Mode80x25); + } + + /// Returns the start of the `FrameBuffer` as `*mut ScreenCharacter`. + fn get_frame_buffer(&self) -> *mut ScreenCharacter { + u32::from(VGA.lock().get_frame_buffer()) as *mut ScreenCharacter + } +} From 43b5f7a60026fb2ed96a08ef2e74aa96d5e4a30d Mon Sep 17 00:00:00 2001 From: Ryan Kennedy Date: Tue, 10 Mar 2020 19:30:48 -0500 Subject: [PATCH 2/5] vga_colors documentation and cleanup --- src/vga/vga_colors.rs | 52 ++++++++++++++++++++++++++++++------------- 1 file changed, 36 insertions(+), 16 deletions(-) diff --git a/src/vga/vga_colors.rs b/src/vga/vga_colors.rs index 570523ed..0aa38e0b 100644 --- a/src/vga/vga_colors.rs +++ b/src/vga/vga_colors.rs @@ -1,28 +1,48 @@ +/// Represents a 16 bit color used for vga display. #[repr(u8)] pub enum Color16Bit { - Black = 0, - Blue = 1, - Green = 2, - Cyan = 3, - Red = 4, - Magenta = 5, - Brown = 6, - LightGrey = 7, - DarkGrey = 8, - LightBlue = 9, - LightGreen = 10, - LightCyan = 11, - LightRed = 12, - Pink = 13, - Yellow = 14, - White = 15, + /// Represents the color `Black (0x0)`. + Black = 0x0, + /// Represents the color `Blue (0x1)`. + Blue = 0x1, + /// Represents the color `Green (0x2)`. + Green = 0x2, + /// Represents the color `Cyan (0x3)`. + Cyan = 0x3, + /// Represents the color `Red (0x4)`. + Red = 0x4, + /// Represents the color `Magenta (0x5)`. + Magenta = 0x5, + /// Represents the color `Brown (0x6)`. + Brown = 0x6, + /// Represents the color `LightGrey (0x7)`. + LightGrey = 0x7, + /// Represents the color `DarkGrey (0x8)`. + DarkGrey = 0x8, + /// Represents the color `LightBlue (0x9)`. + LightBlue = 0x9, + /// Represents the color `LightGreen (0xA)`. + LightGreen = 0xA, + /// Represents the color `LightCyan (0xB)`. + LightCyan = 0xB, + /// Represents the color `LightRed (0xC)`. + LightRed = 0xC, + /// Represents the color `Pink (0xD)`. + Pink = 0xD, + /// Represents the color `Yellow (0xE)`. + Yellow = 0xE, + /// Represents the color `White (0xF)`. + White = 0xF, } +/// Represents a color for vga text modes. #[derive(Debug, Copy, Clone)] #[repr(transparent)] 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 { TextModeColor((background as u8) << 4 | (foreground as u8)) } From 0c94ddf08c87ce7b84868b4000e8f34c990d5ff4 Mon Sep 17 00:00:00 2001 From: Ryan Kennedy Date: Tue, 10 Mar 2020 19:37:38 -0500 Subject: [PATCH 3/5] vga documentation and cleanup --- src/vga/vga.rs | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/vga/vga.rs b/src/vga/vga.rs index 4af1a8c2..35579542 100644 --- a/src/vga/vga.rs +++ b/src/vga/vga.rs @@ -13,6 +13,7 @@ use super::{ use conquer_once::spin::Lazy; use spinning_top::Spinlock; +/// Provides mutable access to the static `Vga`. pub static VGA: Lazy> = Lazy::new(|| Spinlock::new(Vga::new())); #[derive(Debug, Copy, Clone)] @@ -56,11 +57,16 @@ impl From for u8 { } } -#[derive(Debug)] +/// Represents a specified vga video mode. +#[derive(Debug, Clone, Copy)] pub enum VideoMode { + /// Represents text mode 40x25. Mode40x25, + /// Represents text mode 40x50. Mode40x50, + /// Represents text mode 80x25. Mode80x25, + /// Represents graphics mode 640x480x16. Mode640x480x16, } @@ -70,6 +76,7 @@ pub struct Vga { graphics_controller_registers: GraphicsControllerRegisters, attribute_controller_registers: AttributeControllerRegisters, crtc_controller_registers: CrtcControllerRegisters, + most_recent_video_mode: Option, } impl Vga { @@ -80,6 +87,7 @@ impl Vga { graphics_controller_registers: GraphicsControllerRegisters::new(), attribute_controller_registers: AttributeControllerRegisters::new(), crtc_controller_registers: CrtcControllerRegisters::new(), + most_recent_video_mode: None, } } @@ -100,6 +108,10 @@ impl Vga { FrameBuffer::from(memory_map_mode) } + pub fn get_most_recent_video_mode(&self) -> Option { + self.most_recent_video_mode + } + /// `I/O Address Select` Bit 0 `(value & 0x1)` of MSR selects 3Bxh or 3Dxh as the I/O address for the CRT Controller /// registers, the Feature Control Register (FCR), and Input Status Register 1 (ST01). Presently /// ignored (whole range is claimed), but will "ignore" 3Bx for color configuration or 3Dx for @@ -253,23 +265,27 @@ impl Vga { fn set_video_mode_40x25(&mut self) { self.set_registers(&MODE_40X25_CONFIGURATION); self.load_font(&TEXT_8X16_FONT); + self.most_recent_video_mode = Some(VideoMode::Mode40x25); } /// Sets the video card to Mode 40x50. fn set_video_mode_40x50(&mut self) { self.set_registers(&MODE_40X50_CONFIGURATION); self.load_font(&TEXT_8X8_FONT); + self.most_recent_video_mode = Some(VideoMode::Mode40x50); } /// Sets the video card to Mode 80x25. fn set_video_mode_80x25(&mut self) { self.set_registers(&MODE_80X25_CONFIGURATION); self.load_font(&TEXT_8X16_FONT); + self.most_recent_video_mode = Some(VideoMode::Mode80x25); } - /// Sets the video card to Mode 320x200x4 + /// Sets the video card to Mode 640x480x16. fn set_video_mode_640x480x16(&mut self) { self.set_registers(&MODE_640X480X16_CONFIGURATION); + self.most_recent_video_mode = Some(VideoMode::Mode640x480x16); } /// Unlocks the CRTC registers by setting bit 7 to 0 `(value & 0x7F)`. From 1f3cc19946696f6139baeaf641697161cdef8f16 Mon Sep 17 00:00:00 2001 From: Ryan Kennedy Date: Tue, 10 Mar 2020 19:47:58 -0500 Subject: [PATCH 4/5] text_40x25 and text_40x50 --- src/vga/mod.rs | 2 +- src/vga/vga_writers/mod.rs | 12 +++++ src/vga/vga_writers/text_40x25.rs | 73 +++++++++++++++++++++++++++++++ src/vga/vga_writers/text_40x50.rs | 73 +++++++++++++++++++++++++++++++ src/vga/vga_writers/text_80x25.rs | 8 +--- 5 files changed, 160 insertions(+), 8 deletions(-) create mode 100644 src/vga/vga_writers/text_40x25.rs create mode 100644 src/vga/vga_writers/text_40x50.rs diff --git a/src/vga/mod.rs b/src/vga/mod.rs index 6fb17486..3f9f7576 100644 --- a/src/vga/mod.rs +++ b/src/vga/mod.rs @@ -9,4 +9,4 @@ mod vga_writers; pub use vga::{VideoMode, VGA}; pub use vga_colors::{Color16Bit, TextModeColor}; -pub use vga_writers::Text80x25; +pub use vga_writers::{Text40x25, Text40x50, Text80x25}; diff --git a/src/vga/vga_writers/mod.rs b/src/vga/vga_writers/mod.rs index 0fbc1a72..c7c75957 100644 --- a/src/vga/vga_writers/mod.rs +++ b/src/vga/vga_writers/mod.rs @@ -1,3 +1,15 @@ +mod text_40x25; +mod text_40x50; mod text_80x25; +use super::vga_colors::TextModeColor; +pub use text_40x25::Text40x25; +pub use text_40x50::Text40x50; pub use text_80x25::Text80x25; + +#[derive(Debug, Copy, Clone)] +#[repr(C)] +struct ScreenCharacter { + character: u8, + color: TextModeColor, +} diff --git a/src/vga/vga_writers/text_40x25.rs b/src/vga/vga_writers/text_40x25.rs new file mode 100644 index 00000000..8b2ba248 --- /dev/null +++ b/src/vga/vga_writers/text_40x25.rs @@ -0,0 +1,73 @@ +use super::ScreenCharacter; +use crate::vga::{ + vga_colors::{Color16Bit, TextModeColor}, + VideoMode, VGA, +}; + +const WIDTH: usize = 40; +const HEIGHT: usize = 25; +const SCREEN_SIZE: usize = WIDTH * HEIGHT; + +static BLANK_CHARACTER: ScreenCharacter = ScreenCharacter { + character: b' ', + color: TextModeColor::new(Color16Bit::Yellow, Color16Bit::Black), +}; + +/// A basic interface for interacting with vga text mode 80x25 +/// +/// # Examples +/// +/// Basic usage: +/// +/// ``` +/// let text_mode = Text40x25::new(); +/// text_mode.set_mode(); +/// text_mode.clear_screen(); +/// ``` +pub struct Text40x25; + +impl Text40x25 { + /// Creates a new `Text40x25`. + pub fn new() -> Text40x25 { + Text40x25 {} + } + + /// Clears the screen by setting all cells to `b' '` with + /// a background color of `Color16Bit::Black` and a foreground + /// color of `Color16Bit::Yellow`. + pub fn clear_screen(&self) { + let frame_buffer = self.get_frame_buffer(); + for i in 0..SCREEN_SIZE { + unsafe { + frame_buffer + .offset(i as isize) + .write_volatile(BLANK_CHARACTER); + } + } + } + + /// Prints the given `character` and `color` at `(x, y)`. + /// + /// Panics if `x >= 40` or `y >= 25`. + pub fn write_character(&self, x: usize, y: usize, character: u8, color: TextModeColor) { + assert!(x < WIDTH, "x >= {}", WIDTH); + assert!(y < HEIGHT, "y >= {}", HEIGHT); + let frame_buffer = self.get_frame_buffer(); + let offset = (WIDTH * y + x) as isize; + unsafe { + frame_buffer + .offset(offset) + .write_volatile(ScreenCharacter { character, color }); + } + } + + /// Sets the graphics device to `VideoMode::Mode40x25`. + pub fn set_mode(&self) { + VGA.lock().set_video_mode(VideoMode::Mode40x25); + } + + /// Returns the start of the `FrameBuffer` as `*mut ScreenCharacter`. + fn get_frame_buffer(&self) -> *mut ScreenCharacter { + u32::from(VGA.lock().get_frame_buffer()) as *mut ScreenCharacter + } +} diff --git a/src/vga/vga_writers/text_40x50.rs b/src/vga/vga_writers/text_40x50.rs new file mode 100644 index 00000000..a082d169 --- /dev/null +++ b/src/vga/vga_writers/text_40x50.rs @@ -0,0 +1,73 @@ +use super::ScreenCharacter; +use crate::vga::{ + vga_colors::{Color16Bit, TextModeColor}, + VideoMode, VGA, +}; + +const WIDTH: usize = 40; +const HEIGHT: usize = 50; +const SCREEN_SIZE: usize = WIDTH * HEIGHT; + +static BLANK_CHARACTER: ScreenCharacter = ScreenCharacter { + character: b' ', + color: TextModeColor::new(Color16Bit::Yellow, Color16Bit::Black), +}; + +/// A basic interface for interacting with vga text mode 80x50 +/// +/// # Examples +/// +/// Basic usage: +/// +/// ``` +/// let text_mode = Text40x50::new(); +/// text_mode.set_mode(); +/// text_mode.clear_screen(); +/// ``` +pub struct Text40x50; + +impl Text40x50 { + /// Creates a new `Text40x50`. + pub fn new() -> Text40x50 { + Text40x50 {} + } + + /// Clears the screen by setting all cells to `b' '` with + /// a background color of `Color16Bit::Black` and a foreground + /// color of `Color16Bit::Yellow`. + pub fn clear_screen(&self) { + let frame_buffer = self.get_frame_buffer(); + for i in 0..SCREEN_SIZE { + unsafe { + frame_buffer + .offset(i as isize) + .write_volatile(BLANK_CHARACTER); + } + } + } + + /// Prints the given `character` and `color` at `(x, y)`. + /// + /// Panics if `x >= 40` or `y >= 50`. + pub fn write_character(&self, x: usize, y: usize, character: u8, color: TextModeColor) { + assert!(x < WIDTH, "x >= {}", WIDTH); + assert!(y < HEIGHT, "y >= {}", HEIGHT); + let frame_buffer = self.get_frame_buffer(); + let offset = (WIDTH * y + x) as isize; + unsafe { + frame_buffer + .offset(offset) + .write_volatile(ScreenCharacter { character, color }); + } + } + + /// Sets the graphics device to `VideoMode::Mode40x50`. + pub fn set_mode(&self) { + VGA.lock().set_video_mode(VideoMode::Mode40x50); + } + + /// Returns the start of the `FrameBuffer` as `*mut ScreenCharacter`. + fn get_frame_buffer(&self) -> *mut ScreenCharacter { + u32::from(VGA.lock().get_frame_buffer()) as *mut ScreenCharacter + } +} diff --git a/src/vga/vga_writers/text_80x25.rs b/src/vga/vga_writers/text_80x25.rs index a0d25a95..4a5dcb40 100644 --- a/src/vga/vga_writers/text_80x25.rs +++ b/src/vga/vga_writers/text_80x25.rs @@ -1,3 +1,4 @@ +use super::ScreenCharacter; use crate::vga::{ vga_colors::{Color16Bit, TextModeColor}, VideoMode, VGA, @@ -12,13 +13,6 @@ static BLANK_CHARACTER: ScreenCharacter = ScreenCharacter { color: TextModeColor::new(Color16Bit::Yellow, Color16Bit::Black), }; -#[derive(Debug, Copy, Clone)] -#[repr(C)] -struct ScreenCharacter { - character: u8, - color: TextModeColor, -} - /// A basic interface for interacting with vga text mode 80x25 /// /// # Examples From f42632059a5ce9cbeed5462f7d412cb3f6244a1b Mon Sep 17 00:00:00 2001 From: Ryan Kennedy Date: Tue, 10 Mar 2020 22:11:07 -0500 Subject: [PATCH 5/5] graphics_640x480x16 + cleanup + comments --- src/vga/mod.rs | 4 +- src/vga/vga.rs | 13 ++++ src/vga/vga_colors.rs | 1 + src/vga/vga_configurations.rs | 8 +-- src/vga/vga_writers/graphics_640x480x16.rs | 79 ++++++++++++++++++++++ src/vga/vga_writers/mod.rs | 3 + src/vga/vga_writers/text_40x25.rs | 17 +++-- src/vga/vga_writers/text_40x50.rs | 17 +++-- src/vga/vga_writers/text_80x25.rs | 17 +++-- 9 files changed, 135 insertions(+), 24 deletions(-) create mode 100644 src/vga/vga_writers/graphics_640x480x16.rs diff --git a/src/vga/mod.rs b/src/vga/mod.rs index 3f9f7576..afa3fd07 100644 --- a/src/vga/mod.rs +++ b/src/vga/mod.rs @@ -7,6 +7,6 @@ mod vga_fonts; mod vga_registers; mod vga_writers; -pub use vga::{VideoMode, VGA}; +pub use vga::{Plane, Vga, VideoMode, VGA}; pub use vga_colors::{Color16Bit, TextModeColor}; -pub use vga_writers::{Text40x25, Text40x50, Text80x25}; +pub use vga_writers::{Graphics640x480x16, Text40x25, Text40x50, Text80x25}; diff --git a/src/vga/vga.rs b/src/vga/vga.rs index 35579542..407d490b 100644 --- a/src/vga/vga.rs +++ b/src/vga/vga.rs @@ -41,13 +41,18 @@ impl From for u32 { } } +/// Represents a plane for reading and writing vga data. #[allow(dead_code)] #[derive(Debug, Copy, Clone)] #[repr(u8)] pub enum Plane { + /// Represents `Plane 0 (0x0)`. Plane0 = 0x0, + /// Represents `Plane 1 (0x1)`. Plane1 = 0x1, + /// Represents `Plane 2 (0x2)`. Plane2 = 0x2, + /// Represents `Plane 3 (0x3)`. Plane3 = 0x3, } @@ -70,6 +75,8 @@ pub enum VideoMode { Mode640x480x16, } +/// 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, @@ -91,6 +98,7 @@ impl Vga { } } + /// Sets the vga graphics card to the given `VideoMode`. pub fn set_video_mode(&mut self, video_mode: VideoMode) { match video_mode { VideoMode::Mode40x25 => self.set_video_mode_40x25(), @@ -100,6 +108,8 @@ impl Vga { } } + /// Gets the `FrameBuffer` address as specified by the + /// `Miscellaneous Output Register`. pub fn get_frame_buffer(&mut self) -> FrameBuffer { let miscellaneous_graphics = self .graphics_controller_registers @@ -108,6 +118,8 @@ impl Vga { FrameBuffer::from(memory_map_mode) } + /// Returns the most recent video mode, or `None` if no + /// video mode has been set yet. pub fn get_most_recent_video_mode(&self) -> Option { self.most_recent_video_mode } @@ -209,6 +221,7 @@ 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); diff --git a/src/vga/vga_colors.rs b/src/vga/vga_colors.rs index 0aa38e0b..294fc31c 100644 --- a/src/vga/vga_colors.rs +++ b/src/vga/vga_colors.rs @@ -1,4 +1,5 @@ /// Represents a 16 bit color used for vga display. +#[derive(Debug, Clone, Copy)] #[repr(u8)] pub enum Color16Bit { /// Represents the color `Black (0x0)`. diff --git a/src/vga/vga_configurations.rs b/src/vga/vga_configurations.rs index cd462ded..fbefcd8f 100644 --- a/src/vga/vga_configurations.rs +++ b/src/vga/vga_configurations.rs @@ -242,7 +242,7 @@ pub const MODE_640X480X16_CONFIGURATION: VgaConfiguration = VgaConfiguration { sequencer_registers: &[ (SequencerIndex::SequencerReset, 0x03), (SequencerIndex::ClockingMode, 0x01), - (SequencerIndex::PlaneMask, 0x0F), + (SequencerIndex::PlaneMask, 0x08), (SequencerIndex::CharacterFont, 0x00), (SequencerIndex::MemoryMode, 0x06), ], @@ -262,9 +262,9 @@ pub const MODE_640X480X16_CONFIGURATION: VgaConfiguration = VgaConfiguration { (CrtcControllerIndex::StartAddressHigh, 0x00), (CrtcControllerIndex::StartAddressLow, 0x00), (CrtcControllerIndex::TextCursorLocationHigh, 0x00), - (CrtcControllerIndex::TextCursorLocationLow, 0x59), + (CrtcControllerIndex::TextCursorLocationLow, 0x00), (CrtcControllerIndex::VerticalSyncStart, 0xEA), - (CrtcControllerIndex::VerticalSyncEnd, 0x8C), + (CrtcControllerIndex::VerticalSyncEnd, 0x0C), (CrtcControllerIndex::VerticalDisplayEnableEnd, 0xDF), (CrtcControllerIndex::Offset, 0x28), (CrtcControllerIndex::UnderlineLocationRegister, 0x00), @@ -278,7 +278,7 @@ pub const MODE_640X480X16_CONFIGURATION: VgaConfiguration = VgaConfiguration { (GraphicsControllerIndex::EnableSetReset, 0x00), (GraphicsControllerIndex::ColorCompare, 0x00), (GraphicsControllerIndex::DataRotate, 0x00), - (GraphicsControllerIndex::ReadPlaneSelect, 0x00), + (GraphicsControllerIndex::ReadPlaneSelect, 0x03), (GraphicsControllerIndex::GraphicsMode, 0x00), (GraphicsControllerIndex::Miscellaneous, 0x05), (GraphicsControllerIndex::ColorDontCare, 0x0F), diff --git a/src/vga/vga_writers/graphics_640x480x16.rs b/src/vga/vga_writers/graphics_640x480x16.rs new file mode 100644 index 00000000..53aba7bf --- /dev/null +++ b/src/vga/vga_writers/graphics_640x480x16.rs @@ -0,0 +1,79 @@ +use crate::vga::{vga_colors::Color16Bit, Plane, Vga, VideoMode, VGA}; +use spinning_top::SpinlockGuard; + +const WIDTH: usize = 640; +const HEIGHT: usize = 480; + +static PLANES: &'static [Plane] = &[Plane::Plane0, Plane::Plane1, Plane::Plane2, Plane::Plane3]; + +/// A basic interface for interacting with vga graphics mode 640x480x16 +/// +/// # Examples +/// +/// Basic usage: +/// +/// ``` +/// let graphics_mode = Graphics640x480x16::new(); +/// graphics_mode.set_mode(); +/// graphics_mode.clear_screen(); +/// ``` +pub struct Graphics640x480x16; + +impl Graphics640x480x16 { + /// Creates a new `Graphics640x480x16`. + pub fn new() -> Graphics640x480x16 { + Graphics640x480x16 {} + } + + /// Clears the screen by setting all pixels to `Color16Bit::Black`. + pub fn clear_screen(&self) { + for x in 0..WIDTH { + for y in 0..HEIGHT { + self.set_pixel(x, y, Color16Bit::Yellow); + } + } + } + + /// Sets the given pixel at `(x, y)` to the given `color`. + /// + /// Panics if `x >= 640` or `y >= 480`. + pub fn set_pixel(&self, x: usize, y: usize, color: Color16Bit) { + assert!(x < WIDTH, "x >= {}", WIDTH); + assert!(y < HEIGHT, "y >= {}", HEIGHT); + let (mut vga, frame_buffer) = self.get_frame_buffer(); + let offset = (x / 8 + (WIDTH / 8) * y) as isize; + + // Store the current value for masking. + let x = x & 7; + let mask = 0x80 >> (x & 7); + let mut plane_mask = 0x01; + + for plane in PLANES { + vga.set_plane(*plane); + let current_value = unsafe { frame_buffer.offset(offset).read_volatile() }; + let new_value = if plane_mask & color as u8 != 0 { + current_value | mask + } else { + current_value & !mask + }; + unsafe { + frame_buffer.offset(offset).write_volatile(new_value); + } + plane_mask <<= 1; + } + } + + /// Sets the graphics device to `VideoMode::Mode640x480x16`. + pub fn set_mode(&self) { + VGA.lock().set_video_mode(VideoMode::Mode640x480x16); + } + + /// Returns the start of the `FrameBuffer` as `*mut u8` as + /// well as a lock to the vga driver. This ensures the vga + /// driver stays locked while the frame buffer is in use. + fn get_frame_buffer(&self) -> (SpinlockGuard, *mut u8) { + let mut vga = VGA.lock(); + let frame_buffer = vga.get_frame_buffer(); + (vga, u32::from(frame_buffer) as *mut u8) + } +} diff --git a/src/vga/vga_writers/mod.rs b/src/vga/vga_writers/mod.rs index c7c75957..3645725d 100644 --- a/src/vga/vga_writers/mod.rs +++ b/src/vga/vga_writers/mod.rs @@ -1,8 +1,11 @@ +mod graphics_640x480x16; mod text_40x25; mod text_40x50; mod text_80x25; use super::vga_colors::TextModeColor; + +pub use graphics_640x480x16::Graphics640x480x16; pub use text_40x25::Text40x25; pub use text_40x50::Text40x50; pub use text_80x25::Text80x25; diff --git a/src/vga/vga_writers/text_40x25.rs b/src/vga/vga_writers/text_40x25.rs index 8b2ba248..61e04c64 100644 --- a/src/vga/vga_writers/text_40x25.rs +++ b/src/vga/vga_writers/text_40x25.rs @@ -1,8 +1,9 @@ use super::ScreenCharacter; use crate::vga::{ vga_colors::{Color16Bit, TextModeColor}, - VideoMode, VGA, + Vga, VideoMode, VGA, }; +use spinning_top::SpinlockGuard; const WIDTH: usize = 40; const HEIGHT: usize = 25; @@ -36,7 +37,7 @@ impl Text40x25 { /// a background color of `Color16Bit::Black` and a foreground /// color of `Color16Bit::Yellow`. pub fn clear_screen(&self) { - let frame_buffer = self.get_frame_buffer(); + let (_vga, frame_buffer) = self.get_frame_buffer(); for i in 0..SCREEN_SIZE { unsafe { frame_buffer @@ -52,7 +53,7 @@ impl Text40x25 { pub fn write_character(&self, x: usize, y: usize, character: u8, color: TextModeColor) { assert!(x < WIDTH, "x >= {}", WIDTH); assert!(y < HEIGHT, "y >= {}", HEIGHT); - let frame_buffer = self.get_frame_buffer(); + let (_vga, frame_buffer) = self.get_frame_buffer(); let offset = (WIDTH * y + x) as isize; unsafe { frame_buffer @@ -66,8 +67,12 @@ impl Text40x25 { VGA.lock().set_video_mode(VideoMode::Mode40x25); } - /// Returns the start of the `FrameBuffer` as `*mut ScreenCharacter`. - fn get_frame_buffer(&self) -> *mut ScreenCharacter { - u32::from(VGA.lock().get_frame_buffer()) as *mut ScreenCharacter + /// Returns the start of the `FrameBuffer` as `*mut ScreenCharacter` + /// as well as a lock to the vga driver. This ensures the vga + /// driver stays locked while the frame buffer is in use. + fn get_frame_buffer(&self) -> (SpinlockGuard, *mut ScreenCharacter) { + let mut vga = VGA.lock(); + let frame_buffer = vga.get_frame_buffer(); + (vga, u32::from(frame_buffer) as *mut ScreenCharacter) } } diff --git a/src/vga/vga_writers/text_40x50.rs b/src/vga/vga_writers/text_40x50.rs index a082d169..0120f4af 100644 --- a/src/vga/vga_writers/text_40x50.rs +++ b/src/vga/vga_writers/text_40x50.rs @@ -1,8 +1,9 @@ use super::ScreenCharacter; use crate::vga::{ vga_colors::{Color16Bit, TextModeColor}, - VideoMode, VGA, + Vga, VideoMode, VGA, }; +use spinning_top::SpinlockGuard; const WIDTH: usize = 40; const HEIGHT: usize = 50; @@ -36,7 +37,7 @@ impl Text40x50 { /// a background color of `Color16Bit::Black` and a foreground /// color of `Color16Bit::Yellow`. pub fn clear_screen(&self) { - let frame_buffer = self.get_frame_buffer(); + let (_vga, frame_buffer) = self.get_frame_buffer(); for i in 0..SCREEN_SIZE { unsafe { frame_buffer @@ -52,7 +53,7 @@ impl Text40x50 { pub fn write_character(&self, x: usize, y: usize, character: u8, color: TextModeColor) { assert!(x < WIDTH, "x >= {}", WIDTH); assert!(y < HEIGHT, "y >= {}", HEIGHT); - let frame_buffer = self.get_frame_buffer(); + let (_vga, frame_buffer) = self.get_frame_buffer(); let offset = (WIDTH * y + x) as isize; unsafe { frame_buffer @@ -66,8 +67,12 @@ impl Text40x50 { VGA.lock().set_video_mode(VideoMode::Mode40x50); } - /// Returns the start of the `FrameBuffer` as `*mut ScreenCharacter`. - fn get_frame_buffer(&self) -> *mut ScreenCharacter { - u32::from(VGA.lock().get_frame_buffer()) as *mut ScreenCharacter + /// Returns the start of the `FrameBuffer` as `*mut ScreenCharacter` + /// as well as a lock to the vga driver. This ensures the vga + /// driver stays locked while the frame buffer is in use. + fn get_frame_buffer(&self) -> (SpinlockGuard, *mut ScreenCharacter) { + let mut vga = VGA.lock(); + let frame_buffer = vga.get_frame_buffer(); + (vga, u32::from(frame_buffer) as *mut ScreenCharacter) } } diff --git a/src/vga/vga_writers/text_80x25.rs b/src/vga/vga_writers/text_80x25.rs index 4a5dcb40..17d82ef9 100644 --- a/src/vga/vga_writers/text_80x25.rs +++ b/src/vga/vga_writers/text_80x25.rs @@ -1,8 +1,9 @@ use super::ScreenCharacter; use crate::vga::{ vga_colors::{Color16Bit, TextModeColor}, - VideoMode, VGA, + Vga, VideoMode, VGA, }; +use spinning_top::SpinlockGuard; const WIDTH: usize = 80; const HEIGHT: usize = 25; @@ -36,7 +37,7 @@ impl Text80x25 { /// a background color of `Color16Bit::Black` and a foreground /// color of `Color16Bit::Yellow`. pub fn clear_screen(&self) { - let frame_buffer = self.get_frame_buffer(); + let (_vga, frame_buffer) = self.get_frame_buffer(); for i in 0..SCREEN_SIZE { unsafe { frame_buffer @@ -52,7 +53,7 @@ impl Text80x25 { pub fn write_character(&self, x: usize, y: usize, character: u8, color: TextModeColor) { assert!(x < WIDTH, "x >= {}", WIDTH); assert!(y < HEIGHT, "y >= {}", HEIGHT); - let frame_buffer = self.get_frame_buffer(); + let (_vga, frame_buffer) = self.get_frame_buffer(); let offset = (WIDTH * y + x) as isize; unsafe { frame_buffer @@ -66,8 +67,12 @@ impl Text80x25 { VGA.lock().set_video_mode(VideoMode::Mode80x25); } - /// Returns the start of the `FrameBuffer` as `*mut ScreenCharacter`. - fn get_frame_buffer(&self) -> *mut ScreenCharacter { - u32::from(VGA.lock().get_frame_buffer()) as *mut ScreenCharacter + /// Returns the start of the `FrameBuffer` as `*mut ScreenCharacter` + /// as well as a lock to the vga driver. This ensures the vga + /// driver stays locked while the frame buffer is in use. + fn get_frame_buffer(&self) -> (SpinlockGuard, *mut ScreenCharacter) { + let mut vga = VGA.lock(); + let frame_buffer = vga.get_frame_buffer(); + (vga, u32::from(frame_buffer) as *mut ScreenCharacter) } }