Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix VGA issues with real hardware #258

Merged
merged 13 commits into from
Oct 24, 2021
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Changelog

## Unreleased
- Fix VGA issues with real hardware (#258)
- Add rust binaries support (#255)
- Add dynamical disk information (#252)
- Add spawn syscall (#251)
Expand Down
34 changes: 17 additions & 17 deletions dsk/ini/palette.csv
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
# VGA Palette File (Index, Red, Green, Blue)
# VGA Palette File (Red, Green, Blue)
# Dark Gruvbox color palette
0x00, 0x28, 0x28, 0x28 # Black
0x01, 0x45, 0x85, 0x88 # Blue
0x02, 0x98, 0x97, 0x1A # Green
0x03, 0x68, 0x9D, 0x6A # Cyan
0x04, 0xCC, 0x24, 0x1D # Red
0x05, 0xB1, 0x62, 0x86 # Magenta
0x06, 0xD7, 0x99, 0x21 # Brown (Dark Yellow)
0x07, 0xEB, 0xDB, 0xB2 # Light Gray
0x08, 0xA8, 0x99, 0x84 # Dark Gray (Gray)
0x09, 0x83, 0xa5, 0x98 # Light Blue
0x0A, 0xB8, 0xBB, 0x26 # Light Green
0x0B, 0x8E, 0xC0, 0x7C # Light Cyan
0x0C, 0xFB, 0x49, 0x34 # Light Red
0x0D, 0xD3, 0x86, 0x9B # Pink (Light Magenta)
0x0E, 0xFA, 0xBD, 0x2F # Yellow (Light Yellow)
0x0F, 0xFB, 0xF1, 0xF7 # White
0x28, 0x28, 0x28 # Black
0x45, 0x85, 0x88 # Blue
0x98, 0x97, 0x1A # Green
0x68, 0x9D, 0x6A # Cyan
0xCC, 0x24, 0x1D # Red
0xB1, 0x62, 0x86 # Magenta
0xD7, 0x99, 0x21 # Brown (Dark Yellow)
0xEB, 0xDB, 0xB2 # Light Gray
0xA8, 0x99, 0x84 # Dark Gray (Gray)
0x83, 0xa5, 0x98 # Light Blue
0xB8, 0xBB, 0x26 # Light Green
0x8E, 0xC0, 0x7C # Light Cyan
0xFB, 0x49, 0x34 # Light Red
0xD3, 0x86, 0x9B # Pink (Light Magenta)
0xFA, 0xBD, 0x2F # Yellow (Light Yellow)
0xFB, 0xF1, 0xF7 # White
4 changes: 2 additions & 2 deletions src/api/vga/color.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,16 +71,16 @@ pub fn from_ansi(code: u8) -> Color {
}

impl Color {
pub fn to_palette_code(&self) -> u8 {
pub fn to_vga_reg(&self) -> u8 {
match self {
Color::Black => 0x00,
Color::Blue => 0x01,
Color::Green => 0x02,
Color::Cyan => 0x03,
Color::Red => 0x04,
Color::Magenta => 0x05,
Color::LightGray => 0x07,
Color::Brown => 0x14,
Color::LightGray => 0x07,
Color::DarkGray => 0x38,
Color::LightBlue => 0x39,
Color::LightGreen => 0x3A,
Expand Down
35 changes: 30 additions & 5 deletions src/api/vga/palette.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,32 @@ use alloc::vec::Vec;
use core::convert::TryInto;

pub struct Palette {
pub colors: [(u8, u8, u8, u8); 16]
pub colors: [(u8, u8, u8); 16]
}

impl Palette {
pub fn default() -> Palette {
Palette {
colors: [
(0x00, 0x00, 0x00), // Black
(0x00, 0x00, 0x80), // Blue
(0x00, 0x80, 0x00), // Green
(0x00, 0x80, 0x80), // Cyan
(0x80, 0x00, 0x00), // Red
(0x80, 0x00, 0x80), // Magenta
(0x80, 0x80, 0x00), // Brown (Dark Yellow)
(0xC0, 0xC0, 0xC0), // Light Gray
(0x80, 0x80, 0x80), // Dark Gray (Gray)
(0x00, 0x00, 0xFF), // Light Blue
(0x00, 0xFF, 0x00), // Light Green
(0x00, 0xFF, 0xFF), // Light Cyan
(0xFF, 0x00, 0x00), // Light Red
(0xFF, 0x00, 0xFF), // Pink (Light Magenta)
(0xFF, 0xFF, 0x00), // Yellow (Light Yellow)
(0xFF, 0xFF, 0xFF), // White
]
}
}
}

pub fn from_csv(s: &str) -> Result<Palette, ()> {
Expand All @@ -13,8 +38,8 @@ pub fn from_csv(s: &str) -> Result<Palette, ()> {
let value = value.trim().trim_start_matches("0x");
u8::from_str_radix(value, radix).ok()
}).collect();
if color.len() == 4 { // Color index + rgb values
Some((color[0], color[1], color[2], color[3]))
if color.len() == 3 { // RGB values
Some((color[0], color[1], color[2]))
} else {
None
}
Expand All @@ -33,6 +58,6 @@ fn parse_palette_csv() {

let s = include_str!("../../../dsk/ini/palette.csv");
let palette = from_csv(s).unwrap();
assert_eq!(palette.colors[0x03].0, 0x03);
assert_eq!(palette.colors[0x0D].2, 0x86);
assert_eq!(palette.colors[0x03].0, 0x68);
assert_eq!(palette.colors[0x0D].1, 0x86);
}
1 change: 0 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
#![cfg_attr(test, no_main)]
#![feature(abi_x86_interrupt)]
#![feature(alloc_error_handler)]
#![feature(array_map)]
#![feature(asm)]
#![feature(naked_functions)]
#![feature(custom_test_frameworks)]
Expand Down
1 change: 1 addition & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ entry_point!(main);

fn main(boot_info: &'static BootInfo) -> ! {
moros::init(boot_info);
print!("\x1b[?25h"); // Enable cursor
loop {
let bootrc = "/ini/boot.sh";
if sys::fs::File::open(bootrc).is_some() {
Expand Down
2 changes: 1 addition & 1 deletion src/sys/allocator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ pub const HEAP_START: usize = 0x4444_4444_0000;
static ALLOCATOR: LockedHeap = LockedHeap::empty();

pub fn init_heap(mapper: &mut impl Mapper<Size4KiB>, frame_allocator: &mut impl FrameAllocator<Size4KiB>) -> Result<(), MapToError<Size4KiB>> {
// Use half of the memory for the heap, caped to 16 GB because the allocator is too slow
// Use half of the memory for the heap, caped to 16 MB because the allocator is too slow
let heap_size = cmp::min(sys::mem::memory_size() / 2, 16 << 20);

let pages = {
Expand Down
2 changes: 0 additions & 2 deletions src/sys/fs/file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ pub struct File {
name: String,
addr: u32,
size: u32,
time: u64,
dir: Dir, // TODO: Replace with `parent: Some(Dir)` and also add it to `Dir`
offset: u32,
}
Expand All @@ -29,7 +28,6 @@ impl From<DirEntry> for File {
name: entry.name(),
addr: entry.addr(),
size: entry.size(),
time: entry.time(),
dir: entry.dir(),
offset: 0,
}
Expand Down
113 changes: 92 additions & 21 deletions src/sys/vga.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ use vte::{Params, Parser, Perform};
use x86_64::instructions::interrupts;
use x86_64::instructions::port::Port;

// See: https://web.stanford.edu/class/cs140/projects/pintos/specs/freevga/vga/vga.htm
// See https://web.stanford.edu/class/cs140/projects/pintos/specs/freevga/vga/vga.htm
// And https://01.org/sites/default/files/documentation/snb_ihd_os_vol3_part1_0.pdf

const ATTR_ADDR_DATA_REG: u16 = 0x3C0;
const ATTR_DATA_READ_REG: u16 = 0x3C1;
Expand Down Expand Up @@ -192,6 +193,12 @@ impl Writer {
}
}

fn clear_screen(&mut self) {
for y in 0..BUFFER_HEIGHT {
self.clear_row_after(0, y);
}
}

pub fn set_color(&mut self, foreground: Color, background: Color) {
self.color_code = ColorCode::new(foreground, background);
}
Expand Down Expand Up @@ -239,20 +246,25 @@ impl Writer {
pub fn set_palette(&mut self, palette: Palette) {
let mut addr: Port<u8> = Port::new(DAC_ADDR_WRITE_MODE_REG);
let mut data: Port<u8> = Port::new(DAC_DATA_REG);
for (i, r, g, b) in palette.colors {
for (i, (r, g, b)) in palette.colors.iter().enumerate() {
if i < 16 {
let code = color::from_index(i as usize).to_palette_code();
let reg = color::from_index(i as usize).to_vga_reg();
unsafe {
addr.write(code);
data.write(r >> 2); // Convert 8-bit color to 6-bit color
data.write(g >> 2);
data.write(b >> 2);
addr.write(reg);
data.write(vga_color(*r));
data.write(vga_color(*g));
data.write(vga_color(*b));
}
}
}
}
}

// Convert 8-bit to 6-bit color
fn vga_color(color: u8) -> u8 {
color >> 2
}

/// See https://vt100.net/emu/dec_ansi_parser
impl Perform for Writer {
fn print(&mut self, c: char) {
Expand Down Expand Up @@ -356,11 +368,7 @@ impl Perform for Writer {
}
match n {
// TODO: 0 and 1, from cursor to begining or to end of screen
2 => {
for y in 0..BUFFER_HEIGHT {
self.clear_row_after(0, y);
}
}
2 => self.clear_screen(),
_ => return,
}
self.set_writer_position(0, 0);
Expand Down Expand Up @@ -462,16 +470,79 @@ pub fn set_palette(palette: Palette) {
})
}

// 0x00 -> top
// 0x0F -> bottom
// 0x1F -> max (invisible)
fn set_underline_location(location: u8) {
interrupts::without_interrupts(|| {
let mut addr: Port<u8> = Port::new(CRTC_ADDR_REG);
let mut data: Port<u8> = Port::new(CRTC_DATA_REG);
unsafe {
addr.write(0x14); // Underline Location Register
data.write(location);
}
})
}

fn set_attr_ctrl_reg(index: u8, value: u8) {
interrupts::without_interrupts(|| {
let mut isr: Port<u8> = Port::new(INPUT_STATUS_REG);
let mut addr: Port<u8> = Port::new(ATTR_ADDR_DATA_REG);
unsafe {
isr.read(); // Reset to address mode
let tmp = addr.read();
addr.write(index);
addr.write(value);
addr.write(tmp);
}
})
}

fn get_attr_ctrl_reg(index: u8) -> u8 {
interrupts::without_interrupts(|| {
let mut isr: Port<u8> = Port::new(INPUT_STATUS_REG);
let mut addr: Port<u8> = Port::new(ATTR_ADDR_DATA_REG);
let mut data: Port<u8> = Port::new(ATTR_DATA_READ_REG);
let index = index | 0x20; // Set "Palette Address Source" bit
unsafe {
isr.read(); // Reset to address mode
let tmp = addr.read();
addr.write(index);
let res = data.read();
addr.write(tmp);
res
}
})
}

pub fn init() {
let mut isr: Port<u8> = Port::new(INPUT_STATUS_REG);
let mut aadr: Port<u8> = Port::new(ATTR_ADDR_DATA_REG);
let mut adrr: Port<u8> = Port::new(ATTR_DATA_READ_REG);
// Map palette registers to color registers
set_attr_ctrl_reg(0x0, 0x00);
set_attr_ctrl_reg(0x1, 0x01);
set_attr_ctrl_reg(0x2, 0x02);
set_attr_ctrl_reg(0x3, 0x03);
set_attr_ctrl_reg(0x4, 0x04);
set_attr_ctrl_reg(0x5, 0x05);
set_attr_ctrl_reg(0x6, 0x14);
set_attr_ctrl_reg(0x7, 0x07);
set_attr_ctrl_reg(0x8, 0x38);
set_attr_ctrl_reg(0x9, 0x39);
set_attr_ctrl_reg(0xA, 0x3A);
set_attr_ctrl_reg(0xB, 0x3B);
set_attr_ctrl_reg(0xC, 0x3C);
set_attr_ctrl_reg(0xD, 0x3D);
set_attr_ctrl_reg(0xE, 0x3E);
set_attr_ctrl_reg(0xF, 0x3F);

set_palette(Palette::default());

// Disable blinking
unsafe {
isr.read(); // Reset to address mode
aadr.write(0x30); // Select attribute mode control register
let value = adrr.read(); // Read attribute mode control register
aadr.write(value & !0x08); // Use `value | 0x08` to enable and `value ^ 0x08` to toggle
}
let reg = 0x10; // Attribute Mode Control Register
let mut attr = get_attr_ctrl_reg(reg);
attr.set_bit(3, false); // Clear "Blinking Enable" bit
set_attr_ctrl_reg(reg, attr);

set_underline_location(0x1F); // Disable underline

WRITER.lock().clear_screen();
}