Ability to manipulate palettes

This commit is contained in:
Ryan Kennedy 2020-03-15 17:30:28 -05:00
parent 27ee8090ff
commit ac6b76b4d1
9 changed files with 169 additions and 13 deletions

View file

@ -1,5 +1,7 @@
//! Common color structures used in vga.
pub const PALETTE_SIZE: usize = 768;
/// Represents a 16 bit color used for vga display.
#[derive(Debug, Clone, Copy)]
#[repr(u8)]
@ -50,3 +52,55 @@ impl TextModeColor {
TextModeColor((background as u8) << 4 | (foreground as u8))
}
}
/// Represents the default vga 256 color palette.
pub const DEFAULT_PALETTE: [u8; PALETTE_SIZE] = [
0x0, 0x0, 0x0, 0x0, 0x0, 0x2A, 0x0, 0x2A, 0x0, 0x0, 0x2A, 0x2A, 0x2A, 0x0, 0x0, 0x2A, 0x0,
0x2A, 0x2A, 0x2A, 0x0, 0x2A, 0x2A, 0x2A, 0x0, 0x0, 0x15, 0x0, 0x0, 0x3F, 0x0, 0x2A, 0x15, 0x0,
0x2A, 0x3F, 0x2A, 0x0, 0x15, 0x2A, 0x0, 0x3F, 0x2A, 0x2A, 0x15, 0x2A, 0x2A, 0x3F, 0x0, 0x15,
0x0, 0x0, 0x15, 0x2A, 0x0, 0x3F, 0x0, 0x0, 0x3F, 0x2A, 0x2A, 0x15, 0x0, 0x2A, 0x15, 0x2A, 0x2A,
0x3F, 0x0, 0x2A, 0x3F, 0x2A, 0x0, 0x15, 0x15, 0x0, 0x15, 0x3F, 0x0, 0x3F, 0x15, 0x0, 0x3F,
0x3F, 0x2A, 0x15, 0x15, 0x2A, 0x15, 0x3F, 0x2A, 0x3F, 0x15, 0x2A, 0x3F, 0x3F, 0x15, 0x0, 0x0,
0x15, 0x0, 0x2A, 0x15, 0x2A, 0x0, 0x15, 0x2A, 0x2A, 0x3F, 0x0, 0x0, 0x3F, 0x0, 0x2A, 0x3F,
0x2A, 0x0, 0x3F, 0x2A, 0x2A, 0x15, 0x0, 0x15, 0x15, 0x0, 0x3F, 0x15, 0x2A, 0x15, 0x15, 0x2A,
0x3F, 0x3F, 0x0, 0x15, 0x3F, 0x0, 0x3F, 0x3F, 0x2A, 0x15, 0x3F, 0x2A, 0x3F, 0x15, 0x15, 0x0,
0x15, 0x15, 0x2A, 0x15, 0x3F, 0x0, 0x15, 0x3F, 0x2A, 0x3F, 0x15, 0x0, 0x3F, 0x15, 0x2A, 0x3F,
0x3F, 0x0, 0x3F, 0x3F, 0x2A, 0x15, 0x15, 0x15, 0x15, 0x15, 0x3F, 0x15, 0x3F, 0x15, 0x15, 0x3F,
0x3F, 0x3F, 0x15, 0x15, 0x3F, 0x15, 0x3F, 0x3F, 0x3F, 0x15, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
];

View file

@ -14,6 +14,7 @@ mod registers;
mod vga;
mod writers;
pub use self::colors::{Color16Bit, TextModeColor, DEFAULT_PALETTE, PALETTE_SIZE};
pub use self::configurations::{
VgaConfiguration, MODE_40X25_CONFIGURATION, MODE_40X50_CONFIGURATION,
MODE_640X480X16_CONFIGURATION, MODE_80X25_CONFIGURATION,

View file

@ -1,3 +1,4 @@
use crate::colors::PALETTE_SIZE;
use x86_64::instructions::port::{Port, PortReadOnly, PortWriteOnly};
const ST00_READ_ADDRESS: u16 = 0x3C2;
@ -23,6 +24,10 @@ const CRX_INDEX_MDA_ADDRESS: u16 = 0x3B4;
const CRX_DATA_CGA_ADDRESS: u16 = 0x3D5;
const CRX_DATA_MDA_ADDRESS: u16 = 0x3B5;
const COLOR_PALETTE_DATA_ADDRESS: u16 = 0x3C9;
const COLOR_PALETTE_INDEX_READ_ADDRESS: u16 = 0x3C7;
const COLOR_PALETTE_INDEX_WRITE_ADDRESSS: u16 = 0x3C8;
#[derive(Debug, Copy, Clone)]
#[repr(u8)]
pub enum EmulationMode {
@ -389,3 +394,42 @@ impl CrtcControllerRegisters {
}
}
}
#[derive(Debug)]
pub struct ColorPaletteRegisters {
data_port: Port<u8>,
index_read_port: Port<u8>,
index_write_port: Port<u8>,
}
impl ColorPaletteRegisters {
pub fn new() -> ColorPaletteRegisters {
ColorPaletteRegisters {
data_port: Port::new(COLOR_PALETTE_DATA_ADDRESS),
index_read_port: Port::new(COLOR_PALETTE_INDEX_READ_ADDRESS),
index_write_port: Port::new(COLOR_PALETTE_INDEX_WRITE_ADDRESSS),
}
}
pub fn load_palette(&mut self, palette: &[u8; PALETTE_SIZE]) {
unsafe {
self.index_write_port.write(0);
}
for i in palette.iter() {
unsafe {
self.data_port.write(*i);
}
}
}
pub fn read_palette(&mut self, palette: &mut [u8; PALETTE_SIZE]) {
unsafe {
self.index_read_port.write(0);
}
for i in 0..PALETTE_SIZE {
unsafe {
palette[i] = self.data_port.read();
}
}
}
}

View file

@ -1,15 +1,16 @@
//! Provides access to the vga graphics card.
use super::{
colors::PALETTE_SIZE,
configurations::{
VgaConfiguration, MODE_40X25_CONFIGURATION, MODE_40X50_CONFIGURATION,
MODE_640X480X16_CONFIGURATION, MODE_80X25_CONFIGURATION,
},
fonts::{VgaFont, TEXT_8X16_FONT, TEXT_8X8_FONT},
registers::{
AttributeControllerIndex, AttributeControllerRegisters, CrtcControllerIndex,
CrtcControllerRegisters, EmulationMode, GeneralRegisters, GraphicsControllerIndex,
GraphicsControllerRegisters, SequencerIndex, SequencerRegisters,
AttributeControllerIndex, AttributeControllerRegisters, ColorPaletteRegisters,
CrtcControllerIndex, CrtcControllerRegisters, EmulationMode, GeneralRegisters,
GraphicsControllerIndex, GraphicsControllerRegisters, SequencerIndex, SequencerRegisters,
},
};
use conquer_once::spin::Lazy;
@ -90,6 +91,7 @@ pub struct Vga {
graphics_controller_registers: GraphicsControllerRegisters,
attribute_controller_registers: AttributeControllerRegisters,
crtc_controller_registers: CrtcControllerRegisters,
color_palette_registers: ColorPaletteRegisters,
most_recent_video_mode: Option<VideoMode>,
}
@ -101,6 +103,7 @@ impl Vga {
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,
}
}
@ -172,6 +175,22 @@ impl Vga {
EmulationMode::from(self.general_registers.read_msr() & 0x1)
}
/// Loads a new palette into the vga, as specified by `palette`.
///
/// Each palette must be `PALETTE_SIZE` bytes long, with every 3
/// bytes representing one color `(R, G, B)`.
pub fn load_palette(&mut self, palette: &[u8; PALETTE_SIZE]) {
self.color_palette_registers.load_palette(palette);
}
/// Reads the current vga palette into `palette`.
///
/// Each palette must be `PALETTE_SIZE` bytes long, with every 3
/// bytes representing one color `(R, G, B)`.
pub fn read_palette(&mut self, palette: &mut [u8; PALETTE_SIZE]) {
self.color_palette_registers.read_palette(palette);
}
fn load_font(&mut self, vga_font: &VgaFont) {
// Save registers
let (

View file

@ -1,5 +1,5 @@
use crate::{
colors::Color16Bit,
colors::{Color16Bit, DEFAULT_PALETTE},
vga::{Plane, Vga, VideoMode, VGA},
};
use spinning_top::SpinlockGuard;
@ -34,6 +34,7 @@ impl Graphics640x480x16 {
/// Clears the screen by setting all pixels to `Color16Bit::Black`.
pub fn clear_screen(&self) {
// TODO: Clear the screen by using 4-plane mode instead of slow `set_pixel`.
for x in 0..WIDTH {
for y in 0..HEIGHT {
self.set_pixel(x, y, Color16Bit::Yellow);
@ -72,7 +73,12 @@ impl Graphics640x480x16 {
/// Sets the graphics device to `VideoMode::Mode640x480x16`.
pub fn set_mode(&self) {
VGA.lock().set_video_mode(VideoMode::Mode640x480x16);
let mut vga = VGA.lock();
vga.set_video_mode(VideoMode::Mode640x480x16);
// Some bios mess up the palette when switching modes,
// so explicitly set it.
vga.load_palette(&DEFAULT_PALETTE);
}
/// Returns the start of the `FrameBuffer` as `*mut u8` as

View file

@ -1,6 +1,6 @@
use super::ScreenCharacter;
use crate::{
colors::{Color16Bit, TextModeColor},
colors::{Color16Bit, TextModeColor, DEFAULT_PALETTE},
vga::{Vga, VideoMode, VGA},
};
use spinning_top::SpinlockGuard;
@ -66,7 +66,12 @@ impl Text40x25 {
/// Sets the graphics device to `VideoMode::Mode40x25`.
pub fn set_mode(&self) {
VGA.lock().set_video_mode(VideoMode::Mode40x25);
let mut vga = VGA.lock();
vga.set_video_mode(VideoMode::Mode40x25);
// Some bios mess up the palette when switching modes,
// so explicitly set it.
vga.load_palette(&DEFAULT_PALETTE);
}
/// Returns the start of the `FrameBuffer` as `*mut ScreenCharacter`

View file

@ -1,6 +1,6 @@
use super::ScreenCharacter;
use crate::{
colors::{Color16Bit, TextModeColor},
colors::{Color16Bit, TextModeColor, DEFAULT_PALETTE},
vga::{Vga, VideoMode, VGA},
};
use spinning_top::SpinlockGuard;
@ -66,7 +66,12 @@ impl Text40x50 {
/// Sets the graphics device to `VideoMode::Mode40x50`.
pub fn set_mode(&self) {
VGA.lock().set_video_mode(VideoMode::Mode40x50);
let mut vga = VGA.lock();
vga.set_video_mode(VideoMode::Mode40x50);
// Some bios mess up the palette when switching modes,
// so explicitly set it.
vga.load_palette(&DEFAULT_PALETTE);
}
/// Returns the start of the `FrameBuffer` as `*mut ScreenCharacter`

View file

@ -1,6 +1,6 @@
use super::ScreenCharacter;
use crate::{
colors::{Color16Bit, TextModeColor},
colors::{Color16Bit, TextModeColor, DEFAULT_PALETTE},
vga::{Vga, VideoMode, VGA},
};
use spinning_top::SpinlockGuard;
@ -66,7 +66,12 @@ impl Text80x25 {
/// Sets the graphics device to `VideoMode::Mode80x25`.
pub fn set_mode(&self) {
VGA.lock().set_video_mode(VideoMode::Mode80x25);
let mut vga = VGA.lock();
vga.set_video_mode(VideoMode::Mode80x25);
// Some bios mess up the palette when switching modes,
// so explicitly set it.
vga.load_palette(&DEFAULT_PALETTE);
}
/// Returns the start of the `FrameBuffer` as `*mut ScreenCharacter`

View file

@ -7,8 +7,9 @@
use core::panic::PanicInfo;
use testing::{gdt, interrupts, serial_print, serial_println};
use vga::{
Vga, VgaConfiguration, VideoMode, MODE_40X25_CONFIGURATION, MODE_40X50_CONFIGURATION,
MODE_640X480X16_CONFIGURATION, MODE_80X25_CONFIGURATION, VGA,
Vga, VgaConfiguration, VideoMode, DEFAULT_PALETTE, MODE_40X25_CONFIGURATION,
MODE_40X50_CONFIGURATION, MODE_640X480X16_CONFIGURATION, MODE_80X25_CONFIGURATION,
PALETTE_SIZE, VGA,
};
#[no_mangle] // don't mangle the name of this function
@ -75,6 +76,22 @@ fn set_mode_640x480x16() {
serial_println!("[ok]");
}
#[test_case]
fn load_palette() {
serial_print!("load palette... ");
let mut palette = [0u8; PALETTE_SIZE];
let mut vga = VGA.lock();
vga.load_palette(&DEFAULT_PALETTE);
vga.read_palette(&mut palette);
for i in 0..PALETTE_SIZE {
assert_eq!(palette[i], DEFAULT_PALETTE[i]);
}
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);