2019-05-28 21:12:41 +01:00
|
|
|
const arch = @import("arch.zig").internals;
|
2019-05-22 20:12:46 +01:00
|
|
|
|
|
|
|
/// The port address for the VGA register selection.
|
2019-09-08 20:48:23 +01:00
|
|
|
pub const PORT_ADDRESS: u16 = 0x03D4;
|
2019-05-22 20:12:46 +01:00
|
|
|
|
|
|
|
/// The port address for the VGA data.
|
2019-09-08 20:48:23 +01:00
|
|
|
pub const PORT_DATA: u16 = 0x03D5;
|
|
|
|
|
|
|
|
/// The indexes that is passed to the address port to select the register for the data to be
|
|
|
|
/// read or written to.
|
|
|
|
pub const REG_HORIZONTAL_TOTAL: u8 = 0x00;
|
|
|
|
pub const REG_HORIZONTAL_DISPLAY_ENABLE_END: u8 = 0x01;
|
|
|
|
pub const REG_START_HORIZONTAL_BLINKING: u8 = 0x02;
|
|
|
|
pub const REG_END_HORIZONTAL_BLINKING: u8 = 0x03;
|
|
|
|
pub const REG_START_HORIZONTAL_RETRACE_PULSE: u8 = 0x04;
|
|
|
|
pub const REG_END_HORIZONTAL_RETRACE_PULSE: u8 = 0x05;
|
|
|
|
pub const REG_VERTICAL_TOTAL: u8 = 0x06;
|
|
|
|
pub const REG_OVERFLOW: u8 = 0x07;
|
|
|
|
pub const REG_PRESET_ROW_SCAN: u8 = 0x08;
|
|
|
|
pub const REG_MAXIMUM_SCAN_LINE: u8 = 0x09;
|
|
|
|
|
|
|
|
/// The register select for setting the cursor scan lines.
|
|
|
|
pub const REG_CURSOR_START: u8 = 0x0A;
|
|
|
|
pub const REG_CURSOR_END: u8 = 0x0B;
|
|
|
|
pub const REG_START_ADDRESS_HIGH: u8 = 0x0C;
|
|
|
|
pub const REG_START_ADDRESS_LOW: u8 = 0x0D;
|
|
|
|
|
|
|
|
/// The command for setting the cursor's linear location.
|
|
|
|
pub const REG_CURSOR_LOCATION_HIGH: u8 = 0x0E;
|
|
|
|
pub const REG_CURSOR_LOCATION_LOW: u8 = 0x0F;
|
|
|
|
|
|
|
|
/// Other VGA registers.
|
|
|
|
pub const REG_VERTICAL_RETRACE_START: u8 = 0x10;
|
|
|
|
pub const REG_VERTICAL_RETRACE_END: u8 = 0x11;
|
|
|
|
pub const REG_VERTICAL_DISPLAY_ENABLE_END: u8 = 0x12;
|
|
|
|
pub const REG_OFFSET: u8 = 0x13;
|
|
|
|
pub const REG_UNDERLINE_LOCATION: u8 = 0x14;
|
|
|
|
pub const REG_START_VERTICAL_BLINKING: u8 = 0x15;
|
|
|
|
pub const REG_END_VERTICAL_BLINKING: u8 = 0x16;
|
|
|
|
pub const REG_CRT_MODE_CONTROL: u8 = 0x17;
|
|
|
|
pub const REG_LINE_COMPARE: u8 = 0x18;
|
|
|
|
|
|
|
|
/// The start of the cursor scan line, the very beginning.
|
|
|
|
pub const CURSOR_SCANLINE_START: u8 = 0x0;
|
|
|
|
|
|
|
|
/// The scan line for use in the underline cursor shape.
|
|
|
|
pub const CURSOR_SCANLINE_MIDDLE: u8 = 0xE;
|
|
|
|
|
|
|
|
/// The end of the cursor scan line, the very end.
|
|
|
|
pub const CURSOR_SCANLINE_END: u8 = 0xF;
|
2019-05-22 20:12:46 +01:00
|
|
|
|
|
|
|
/// If set, disables the cursor.
|
2019-09-08 20:48:23 +01:00
|
|
|
pub const CURSOR_DISABLE: u8 = 0x20;
|
2019-05-22 20:12:46 +01:00
|
|
|
|
2019-09-08 20:48:23 +01:00
|
|
|
/// The number of characters wide the screen is.
|
2019-05-22 20:12:46 +01:00
|
|
|
pub const WIDTH: u16 = 80;
|
2019-09-08 20:48:23 +01:00
|
|
|
|
|
|
|
/// The number of characters heigh the screen is.
|
2019-05-22 20:12:46 +01:00
|
|
|
pub const HEIGHT: u16 = 25;
|
|
|
|
|
2019-09-08 20:48:23 +01:00
|
|
|
/// The set of colours that VGA supports and can display for the foreground and background.
|
2019-05-22 20:12:46 +01:00
|
|
|
pub const COLOUR_BLACK: u4 = 0x00;
|
|
|
|
pub const COLOUR_BLUE: u4 = 0x01;
|
|
|
|
pub const COLOUR_GREEN: u4 = 0x02;
|
|
|
|
pub const COLOUR_CYAN: u4 = 0x03;
|
|
|
|
pub const COLOUR_RED: u4 = 0x04;
|
|
|
|
pub const COLOUR_MAGENTA: u4 = 0x05;
|
|
|
|
pub const COLOUR_BROWN: u4 = 0x06;
|
|
|
|
pub const COLOUR_LIGHT_GREY: u4 = 0x07;
|
|
|
|
pub const COLOUR_DARK_GREY: u4 = 0x08;
|
|
|
|
pub const COLOUR_LIGHT_BLUE: u4 = 0x09;
|
|
|
|
pub const COLOUR_LIGHT_GREEN: u4 = 0x0A;
|
|
|
|
pub const COLOUR_LIGHT_CYAN: u4 = 0x0B;
|
|
|
|
pub const COLOUR_LIGHT_RED: u4 = 0x0C;
|
|
|
|
pub const COLOUR_LIGHT_MAGENTA: u4 = 0x0D;
|
|
|
|
pub const COLOUR_LIGHT_BROWN: u4 = 0x0E;
|
|
|
|
pub const COLOUR_WHITE: u4 = 0x0F;
|
|
|
|
|
|
|
|
/// The set of shapes that can be displayed.
|
2019-09-08 20:48:23 +01:00
|
|
|
pub const CursorShape = enum {
|
2019-05-22 20:12:46 +01:00
|
|
|
/// The cursor has the underline shape.
|
|
|
|
UNDERLINE,
|
|
|
|
|
|
|
|
/// The cursor has the block shape.
|
|
|
|
BLOCK,
|
|
|
|
};
|
|
|
|
|
|
|
|
/// The cursor scan line start so to know whether is in block or underline mode.
|
|
|
|
var cursor_scanline_start: u8 = undefined;
|
|
|
|
|
|
|
|
/// The cursor scan line end so to know whether is in block or underline mode.
|
|
|
|
var cursor_scanline_end: u8 = undefined;
|
|
|
|
|
2019-09-08 20:48:23 +01:00
|
|
|
/// A inline function for setting the VGA register port to read from or write to.
|
|
|
|
inline fn sendPort(port: u8) void {
|
|
|
|
arch.outb(PORT_ADDRESS, port);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// A inline function for sending data to the set VGA register port.
|
|
|
|
inline fn sendData(data: u8) void {
|
|
|
|
arch.outb(PORT_DATA, data);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// A inline function for setting the VGA register port to read from or write toa and sending data
|
|
|
|
/// to the set VGA register port.
|
|
|
|
inline fn sendPortData(port: u8, data: u8) void {
|
|
|
|
sendPort(port);
|
|
|
|
sendData(data);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// A inline function for getting data from a set VGA register port.
|
|
|
|
inline fn getData() u8 {
|
|
|
|
return arch.inb(PORT_DATA);
|
|
|
|
}
|
|
|
|
|
2019-05-22 20:12:46 +01:00
|
|
|
///
|
|
|
|
/// Takes two 4 bit values that represent the foreground and background colour of the text and
|
|
|
|
/// returns a 8 bit value that gives both to be displayed.
|
|
|
|
///
|
|
|
|
/// Arguments:
|
|
|
|
/// IN fg: u4 - The foreground colour.
|
|
|
|
/// IN bg: u4 - The background colour.
|
|
|
|
///
|
2019-09-08 20:48:23 +01:00
|
|
|
/// Return: u8
|
2019-05-22 20:12:46 +01:00
|
|
|
/// Both combined into 1 byte for the colour to be displayed.
|
|
|
|
///
|
|
|
|
pub fn entryColour(fg: u4, bg: u4) u8 {
|
|
|
|
return u8(fg) | u8(bg) << 4;
|
|
|
|
}
|
|
|
|
|
|
|
|
///
|
|
|
|
/// Create the 2 bytes entry that the VGA used to display a character with a foreground and
|
|
|
|
/// background colour.
|
|
|
|
///
|
|
|
|
/// Arguments:
|
2019-09-08 20:48:23 +01:00
|
|
|
/// IN char: u8 - The character ro display.
|
2019-05-22 20:12:46 +01:00
|
|
|
/// IN colour: u8 - The foreground and background colour.
|
|
|
|
///
|
2019-09-08 20:48:23 +01:00
|
|
|
/// Return: u16
|
|
|
|
/// A VGA entry.
|
2019-05-22 20:12:46 +01:00
|
|
|
///
|
2019-09-08 20:48:23 +01:00
|
|
|
pub fn entry(char: u8, colour: u8) u16 {
|
|
|
|
return u16(char) | u16(colour) << 8;
|
2019-05-22 20:12:46 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
///
|
|
|
|
/// Update the hardware on screen cursor.
|
|
|
|
///
|
|
|
|
/// Arguments:
|
2019-09-08 20:48:23 +01:00
|
|
|
/// IN x: u16 - The horizontal position of the cursor (column).
|
|
|
|
/// IN y: u16 - The vertical position of the cursor (row).
|
2019-05-22 20:12:46 +01:00
|
|
|
///
|
|
|
|
pub fn updateCursor(x: u16, y: u16) void {
|
|
|
|
var pos: u16 = undefined;
|
|
|
|
|
|
|
|
// Make sure new cursor position is within the screen
|
2019-05-31 07:41:28 +01:00
|
|
|
if (x < WIDTH and y < HEIGHT) {
|
2019-05-22 20:12:46 +01:00
|
|
|
pos = y * WIDTH + x;
|
|
|
|
} else {
|
|
|
|
// If not within the screen, then just put the cursor at the very end
|
|
|
|
pos = (HEIGHT - 1) * WIDTH + (WIDTH - 1);
|
|
|
|
}
|
|
|
|
|
2019-09-08 20:48:23 +01:00
|
|
|
const pos_upper = (pos >> 8) & 0x00FF;
|
|
|
|
const pos_lower = pos & 0x00FF;
|
2019-05-22 20:12:46 +01:00
|
|
|
|
|
|
|
// Set the cursor position
|
2019-09-08 20:48:23 +01:00
|
|
|
sendPortData(REG_CURSOR_LOCATION_LOW, @truncate(u8, pos_lower));
|
|
|
|
sendPortData(REG_CURSOR_LOCATION_HIGH, @truncate(u8, pos_upper));
|
2019-05-22 20:12:46 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
///
|
2019-09-08 20:48:23 +01:00
|
|
|
/// Get the linear position of the hardware cursor.
|
2019-05-22 20:12:46 +01:00
|
|
|
///
|
2019-09-08 20:48:23 +01:00
|
|
|
/// Return: u16
|
2019-05-22 20:12:46 +01:00
|
|
|
/// The linear cursor position.
|
|
|
|
///
|
|
|
|
pub fn getCursor() u16 {
|
|
|
|
var cursor: u16 = 0;
|
|
|
|
|
2019-09-08 20:48:23 +01:00
|
|
|
sendPort(REG_CURSOR_LOCATION_LOW);
|
|
|
|
cursor |= u16(getData());
|
2019-05-22 20:12:46 +01:00
|
|
|
|
2019-09-08 20:48:23 +01:00
|
|
|
sendPort(REG_CURSOR_LOCATION_HIGH);
|
|
|
|
cursor |= u16(getData()) << 8;
|
2019-05-22 20:12:46 +01:00
|
|
|
|
|
|
|
return cursor;
|
|
|
|
}
|
|
|
|
|
|
|
|
///
|
|
|
|
/// Enables the blinking cursor to that is is visible.
|
|
|
|
///
|
|
|
|
pub fn enableCursor() void {
|
2019-09-08 20:48:23 +01:00
|
|
|
sendPortData(REG_CURSOR_START, cursor_scanline_start);
|
|
|
|
sendPortData(REG_CURSOR_END, cursor_scanline_end);
|
2019-05-22 20:12:46 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
///
|
|
|
|
/// Disables the blinking cursor to that is is visible.
|
|
|
|
///
|
|
|
|
pub fn disableCursor() void {
|
2019-09-08 20:48:23 +01:00
|
|
|
sendPortData(REG_CURSOR_START, CURSOR_DISABLE);
|
2019-05-22 20:12:46 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
///
|
|
|
|
/// Set the shape of the cursor. This can be and underline or block shape.
|
|
|
|
///
|
|
|
|
/// Arguments:
|
2019-09-08 20:48:23 +01:00
|
|
|
/// IN shape: CursorShape - The enum CursorShape that selects which shape to use.
|
2019-05-22 20:12:46 +01:00
|
|
|
///
|
|
|
|
pub fn setCursorShape(shape: CursorShape) void {
|
2019-05-28 21:12:41 +01:00
|
|
|
switch (shape) {
|
2019-05-22 20:12:46 +01:00
|
|
|
CursorShape.UNDERLINE => {
|
|
|
|
cursor_scanline_start = CURSOR_SCANLINE_MIDDLE;
|
|
|
|
cursor_scanline_end = CURSOR_SCANLINE_END;
|
|
|
|
},
|
|
|
|
CursorShape.BLOCK => {
|
|
|
|
cursor_scanline_start = CURSOR_SCANLINE_START;
|
|
|
|
cursor_scanline_end = CURSOR_SCANLINE_END;
|
|
|
|
},
|
|
|
|
}
|
2019-09-08 20:48:23 +01:00
|
|
|
|
|
|
|
sendPortData(REG_CURSOR_START, cursor_scanline_start);
|
|
|
|
sendPortData(REG_CURSOR_END, cursor_scanline_end);
|
2019-05-22 20:12:46 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
///
|
|
|
|
/// Initialise the VGA text mode. This sets the cursor and underline shape.
|
|
|
|
///
|
|
|
|
pub fn init() void {
|
|
|
|
// Set the maximum scan line to 0x0F
|
2019-09-08 20:48:23 +01:00
|
|
|
sendPortData(REG_MAXIMUM_SCAN_LINE, CURSOR_SCANLINE_END);
|
2019-05-22 20:12:46 +01:00
|
|
|
|
|
|
|
// Set by default the underline cursor
|
|
|
|
setCursorShape(CursorShape.UNDERLINE);
|
|
|
|
}
|