use super::{
configurations::{
VgaConfiguration, MODE_320X200X256_CONFIGURATION, MODE_320X240X256_CONFIGURATION,
MODE_40X25_CONFIGURATION, MODE_40X50_CONFIGURATION, MODE_640X480X16_CONFIGURATION,
MODE_80X25_CONFIGURATION,
},
fonts::VgaFont,
registers::{
AttributeControllerRegisters, ColorPaletteRegisters, CrtcControllerIndex,
CrtcControllerRegisters, EmulationMode, GeneralRegisters, GraphicsControllerIndex,
GraphicsControllerRegisters, PlaneMask, SequencerIndex, SequencerRegisters,
},
};
use crate::configurations::MODE_1280X800X256_CONFIGURATION;
use conquer_once::spin::Lazy;
use spinning_top::Spinlock;
pub static VGA: Lazy<Spinlock<Vga>> = Lazy::new(|| Spinlock::new(Vga::new()));
#[derive(Debug, Copy, Clone)]
#[repr(usize)]
pub enum FrameBuffer {
GraphicsMode(usize),
CgaMode(usize),
MdaMode(usize),
}
impl FrameBuffer {
fn new(memory_map_mode: u8, video_memory_start: usize) -> FrameBuffer {
match memory_map_mode {
0x1 => FrameBuffer::GraphicsMode(video_memory_start),
0x2 => FrameBuffer::MdaMode(video_memory_start + 0x10000),
0x3 => FrameBuffer::CgaMode(video_memory_start + 0x18000),
_ => panic!("{:X} is not a valid FrameBuffer map mode", memory_map_mode),
}
}
}
impl From<FrameBuffer> for usize {
fn from(value: FrameBuffer) -> usize {
match value {
FrameBuffer::GraphicsMode(addr) => addr,
FrameBuffer::CgaMode(addr) => addr,
FrameBuffer::MdaMode(addr) => addr,
}
}
}
#[derive(Debug, Clone, Copy)]
pub enum VideoMode {
Mode40x25,
Mode40x50,
Mode80x25,
Mode320x200x256,
Mode320x240x256,
Mode640x480x16,
Mode1280x800x256,
}
pub struct Vga {
pub general_registers: GeneralRegisters,
pub sequencer_registers: SequencerRegisters,
pub graphics_controller_registers: GraphicsControllerRegisters,
pub attribute_controller_registers: AttributeControllerRegisters,
pub crtc_controller_registers: CrtcControllerRegisters,
pub color_palette_registers: ColorPaletteRegisters,
most_recent_video_mode: Option<VideoMode>,
video_memory_start: usize,
}
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(),
color_palette_registers: ColorPaletteRegisters::new(),
most_recent_video_mode: None,
video_memory_start: 0xa0000,
}
}
pub fn set_memory_start(&mut self, video_memory_start: usize) {
self.video_memory_start = video_memory_start;
}
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::Mode320x200x256 => self.set_video_mode_320x200x256(),
VideoMode::Mode320x240x256 => self.set_video_mode_320x240x256(),
VideoMode::Mode640x480x16 => self.set_video_mode_640x480x16(),
VideoMode::Mode1280x800x256 => self.set_video_mode_1280x800x256(),
}
}
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::new(memory_map_mode, self.video_memory_start)
}
pub fn get_most_recent_video_mode(&self) -> Option<VideoMode> {
self.most_recent_video_mode
}
pub fn get_emulation_mode(&mut self) -> EmulationMode {
EmulationMode::from(self.general_registers.read_msr() & 0x1)
}
pub fn load_font(&mut self, vga_font: &VgaFont) {
let (
plane_mask,
sequencer_memory_mode,
read_plane_select,
graphics_mode,
miscellaneous_graphics,
) = self.save_font_registers();
self.sequencer_registers
.write(SequencerIndex::MemoryMode, sequencer_memory_mode | 0x04);
self.graphics_controller_registers
.write(GraphicsControllerIndex::GraphicsMode, graphics_mode & !0x10);
self.graphics_controller_registers.write(
GraphicsControllerIndex::Miscellaneous,
miscellaneous_graphics & !0x02,
);
self.sequencer_registers.set_plane_mask(PlaneMask::PLANE2);
let frame_buffer = usize::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),
)
}
fn set_registers(&mut self, configuration: &VgaConfiguration) {
let emulation_mode = self.get_emulation_mode();
self.general_registers
.write_msr(configuration.miscellaneous_output);
for (index, value) in configuration.sequencer_registers {
self.sequencer_registers.write(*index, *value);
}
self.unlock_crtc_registers(emulation_mode);
for (index, value) in configuration.crtc_controller_registers {
self.crtc_controller_registers
.write(emulation_mode, *index, *value);
}
for (index, value) in configuration.graphics_controller_registers {
self.graphics_controller_registers.write(*index, *value);
}
self.attribute_controller_registers
.blank_screen(emulation_mode);
for (index, value) in configuration.attribute_controller_registers {
self.attribute_controller_registers
.write(emulation_mode, *index, *value);
}
self.attribute_controller_registers
.unblank_screen(emulation_mode);
}
fn set_video_mode_40x25(&mut self) {
self.set_registers(&MODE_40X25_CONFIGURATION);
self.most_recent_video_mode = Some(VideoMode::Mode40x25);
}
fn set_video_mode_40x50(&mut self) {
self.set_registers(&MODE_40X50_CONFIGURATION);
self.most_recent_video_mode = Some(VideoMode::Mode40x50);
}
fn set_video_mode_80x25(&mut self) {
self.set_registers(&MODE_80X25_CONFIGURATION);
self.most_recent_video_mode = Some(VideoMode::Mode80x25);
}
fn set_video_mode_320x200x256(&mut self) {
self.set_registers(&MODE_320X200X256_CONFIGURATION);
self.most_recent_video_mode = Some(VideoMode::Mode320x200x256);
}
fn set_video_mode_320x240x256(&mut self) {
self.set_registers(&MODE_320X240X256_CONFIGURATION);
self.most_recent_video_mode = Some(VideoMode::Mode320x240x256);
}
fn set_video_mode_640x480x16(&mut self) {
self.set_registers(&MODE_640X480X16_CONFIGURATION);
self.most_recent_video_mode = Some(VideoMode::Mode640x480x16);
}
fn set_video_mode_1280x800x256(&mut self) {
self.set_registers(&MODE_1280X800X256_CONFIGURATION);
self.most_recent_video_mode = Some(VideoMode::Mode1280x800x256);
}
fn unlock_crtc_registers(&mut self, emulation_mode: EmulationMode) {
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,
);
}
}