diff --git a/src/configurations.rs b/src/configurations.rs index 0ad2c70..3504bc5 100644 --- a/src/configurations.rs +++ b/src/configurations.rs @@ -5,10 +5,15 @@ use super::registers::{ /// Represents a set of vga registers for a given mode. #[derive(Debug)] pub struct VgaConfiguration { + /// Represents the configuration value for the miscellaneous output register. pub miscellaneous_output: u8, + /// Represents the configuration values for the sequencer registers. pub sequencer_registers: &'static [(SequencerIndex, u8)], + /// Represents the configuration values for the crtc controller registers. pub crtc_controller_registers: &'static [(CrtcControllerIndex, u8)], + /// Represents the configuration values for the graphics controller registers. pub graphics_controller_registers: &'static [(GraphicsControllerIndex, u8)], + /// Represents the configuration values for the attribute controller registers. pub attribute_controller_registers: &'static [(AttributeControllerIndex, u8)], } diff --git a/src/lib.rs b/src/lib.rs index b248818..1ced4d9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,12 +7,16 @@ #![no_std] #![warn(missing_docs)] -pub mod colors; +mod colors; mod configurations; mod fonts; mod registers; -pub mod vga; +mod vga; mod writers; -pub use self::vga::{VideoMode, VGA}; +pub use self::configurations::{ + VgaConfiguration, MODE_40X25_CONFIGURATION, MODE_40X50_CONFIGURATION, + MODE_640X480X16_CONFIGURATION, MODE_80X25_CONFIGURATION, +}; +pub use self::vga::{Vga, VideoMode, VGA}; pub use self::writers::{Graphics640x480x16, Text40x25, Text40x50, Text80x25}; diff --git a/src/registers.rs b/src/registers.rs index 0bd2ccb..d0c51ef 100644 --- a/src/registers.rs +++ b/src/registers.rs @@ -271,6 +271,12 @@ impl AttributeControllerRegisters { } } + pub fn read(&mut self, emulation_mode: EmulationMode, index: AttributeControllerIndex) -> u8 { + self.toggle_index(emulation_mode); + self.set_index(index); + unsafe { self.arx_data.read() } + } + pub fn write( &mut self, emulation_mode: EmulationMode, diff --git a/src/vga.rs b/src/vga.rs index 2ff6126..d225626 100644 --- a/src/vga.rs +++ b/src/vga.rs @@ -7,9 +7,9 @@ use super::{ }, fonts::{VgaFont, TEXT_8X16_FONT, TEXT_8X8_FONT}, registers::{ - AttributeControllerRegisters, CrtcControllerIndex, CrtcControllerRegisters, EmulationMode, - GeneralRegisters, GraphicsControllerIndex, GraphicsControllerRegisters, SequencerIndex, - SequencerRegisters, + AttributeControllerIndex, AttributeControllerRegisters, CrtcControllerIndex, + CrtcControllerRegisters, EmulationMode, GeneralRegisters, GraphicsControllerIndex, + GraphicsControllerRegisters, SequencerIndex, SequencerRegisters, }, }; use conquer_once::spin::Lazy; @@ -131,17 +131,44 @@ impl Vga { 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 - /// 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 { + /// Returns the current value of the miscellaneous output register. + pub fn read_msr(&mut self) -> u8 { + self.general_registers.read_msr() + } + + /// Returns the current value of the sequencer register, as determined by `index`. + pub fn read_sequencer(&mut self, index: SequencerIndex) -> u8 { + self.sequencer_registers.read(index) + } + + /// Returns the current value of the graphics controller register, as determined by `index`. + pub fn read_graphics_controller(&mut self, index: GraphicsControllerIndex) -> u8 { + self.graphics_controller_registers.read(index) + } + + /// Returns the current value of the attribute controller register, as determined by `emulation_mode` + /// and `index`. + pub fn read_attribute_controller( + &mut self, + emulation_mode: EmulationMode, + index: AttributeControllerIndex, + ) -> u8 { + self.attribute_controller_registers + .read(emulation_mode, index) + } + + /// Returns the current value of the crtc controller, as determined by `emulation_mode` + /// and `index`. + pub fn read_crtc_controller( + &mut self, + emulation_mode: EmulationMode, + index: CrtcControllerIndex, + ) -> u8 { + self.crtc_controller_registers.read(emulation_mode, index) + } + + /// Returns the current `EmulationMode` as determined by the miscellaneous output register. + pub fn get_emulation_mode(&mut self) -> EmulationMode { EmulationMode::from(self.general_registers.read_msr() & 0x1) } diff --git a/testing/Cargo.toml b/testing/Cargo.toml index b620181..a5f8b6e 100644 --- a/testing/Cargo.toml +++ b/testing/Cargo.toml @@ -5,7 +5,8 @@ authors = ["Ryan Kennedy "] edition = "2018" [dependencies] -bootloader = { version = "0.8.9", features = ["map_physical_memory"] } +# TODO: Move back to crate when vga mapping is merged in. +bootloader = { git = "https://github.com/RKennedy9064/bootloader", branch = "vga-mappings", features = ["map_physical_memory"] } conquer-once = { version = "0.2.0", default-features = false } spinning_top = { version = "0.1.0", features = ["nightly"] } pic8259_simple = "0.1.1" diff --git a/testing/tests/vga.rs b/testing/tests/vga.rs index 50e61f8..0c8be16 100644 --- a/testing/tests/vga.rs +++ b/testing/tests/vga.rs @@ -6,7 +6,10 @@ use core::panic::PanicInfo; use testing::{gdt, interrupts, serial_print, serial_println}; -use vga::{VideoMode, VGA}; +use vga::{ + Vga, VgaConfiguration, VideoMode, MODE_40X25_CONFIGURATION, MODE_40X50_CONFIGURATION, + MODE_640X480X16_CONFIGURATION, MODE_80X25_CONFIGURATION, VGA, +}; #[no_mangle] // don't mangle the name of this function pub extern "C" fn _start() -> ! { @@ -28,12 +31,70 @@ fn panic(info: &PanicInfo) -> ! { testing::test_panic_handler(info) } +#[test_case] +fn set_mode_40x25() { + serial_print!("mode 40x25... "); + + let mut vga = VGA.lock(); + vga.set_video_mode(VideoMode::Mode40x25); + check_registers(&mut vga, &MODE_40X25_CONFIGURATION); + + serial_println!("[ok]"); +} + +#[test_case] +fn set_mode_40x50() { + serial_print!("mode 40x50... "); + + let mut vga = VGA.lock(); + vga.set_video_mode(VideoMode::Mode40x50); + check_registers(&mut vga, &MODE_40X50_CONFIGURATION); + + serial_println!("[ok]"); +} + #[test_case] fn set_mode_80x25() { serial_print!("mode 80x25... "); let mut vga = VGA.lock(); vga.set_video_mode(VideoMode::Mode80x25); + check_registers(&mut vga, &MODE_80X25_CONFIGURATION); serial_println!("[ok]"); } + +#[test_case] +fn set_mode_640x480x16() { + serial_print!("mode 640x480x16... "); + + let mut vga = VGA.lock(); + vga.set_video_mode(VideoMode::Mode640x480x16); + check_registers(&mut vga, &MODE_640X480X16_CONFIGURATION); + + serial_println!("[ok]"); +} + +fn check_registers(vga: &mut Vga, configuration: &VgaConfiguration) { + let emulation_mode = vga.get_emulation_mode(); + assert_eq!(vga.read_msr(), configuration.miscellaneous_output); + + for (index, value) in configuration.sequencer_registers { + assert_eq!(vga.read_sequencer(*index), *value); + } + + for (index, value) in configuration.crtc_controller_registers { + assert_eq!(vga.read_crtc_controller(emulation_mode, *index), *value); + } + + for (index, value) in configuration.graphics_controller_registers { + assert_eq!(vga.read_graphics_controller(*index), *value); + } + + for (index, value) in configuration.attribute_controller_registers { + assert_eq!( + vga.read_attribute_controller(emulation_mode, *index), + *value + ); + } +}