601 lines
18 KiB
Zig
601 lines
18 KiB
Zig
const std = @import("std");
|
|
const builtin = @import("builtin");
|
|
const is_test = builtin.is_test;
|
|
const expectEqual = std.testing.expectEqual;
|
|
const build_options = @import("build_options");
|
|
const mock_path = build_options.mock_path;
|
|
const arch = @import("arch.zig").internals;
|
|
const log = if (is_test) @import(mock_path ++ "log_mock.zig") else @import("log.zig");
|
|
const panic = @import("panic.zig").panic;
|
|
|
|
/// The port address for the VGA register selection.
|
|
const PORT_ADDRESS: u16 = 0x03D4;
|
|
|
|
/// The port address for the VGA data.
|
|
const PORT_DATA: u16 = 0x03D5;
|
|
|
|
/// The indexes that is passed to the address port to select the maximum scan line register for
|
|
/// the data to be read or written to.
|
|
const REG_MAXIMUM_SCAN_LINE: u8 = 0x09;
|
|
|
|
/// The register select for setting the cursor start scan lines.
|
|
const REG_CURSOR_START: u8 = 0x0A;
|
|
|
|
/// The register select for setting the cursor end scan lines.
|
|
const REG_CURSOR_END: u8 = 0x0B;
|
|
|
|
/// The command for setting the cursor's linear location (Upper 8 bits).
|
|
const REG_CURSOR_LOCATION_HIGH: u8 = 0x0E;
|
|
|
|
/// The command for setting the cursor's linear location (Lower 8 bits).
|
|
const REG_CURSOR_LOCATION_LOW: u8 = 0x0F;
|
|
|
|
/// The start of the cursor scan line, the very beginning.
|
|
const CURSOR_SCANLINE_START: u8 = 0x0;
|
|
|
|
/// The scan line for use in the underline cursor shape.
|
|
const CURSOR_SCANLINE_MIDDLE: u8 = 0xE;
|
|
|
|
/// The end of the cursor scan line, the very end.
|
|
const CURSOR_SCANLINE_END: u8 = 0xF;
|
|
|
|
/// If set, disables the cursor.
|
|
const CURSOR_DISABLE: u8 = 0x20;
|
|
|
|
/// The number of characters wide the screen is.
|
|
pub const WIDTH: u16 = 80;
|
|
|
|
/// The number of characters heigh the screen is.
|
|
pub const HEIGHT: u16 = 25;
|
|
|
|
// ----------
|
|
// The set of colours that VGA supports and can display for the foreground and background.
|
|
// ----------
|
|
|
|
/// Foreground/background VGA colour black.
|
|
pub const COLOUR_BLACK: u4 = 0x00;
|
|
|
|
/// Foreground/background VGA colour blue.
|
|
pub const COLOUR_BLUE: u4 = 0x01;
|
|
|
|
/// Foreground/background VGA colour green.
|
|
pub const COLOUR_GREEN: u4 = 0x02;
|
|
|
|
/// Foreground/background VGA colour cyan.
|
|
pub const COLOUR_CYAN: u4 = 0x03;
|
|
|
|
/// Foreground/background VGA colour red.
|
|
pub const COLOUR_RED: u4 = 0x04;
|
|
|
|
/// Foreground/background VGA colour magenta.
|
|
pub const COLOUR_MAGENTA: u4 = 0x05;
|
|
|
|
/// Foreground/background VGA colour brown.
|
|
pub const COLOUR_BROWN: u4 = 0x06;
|
|
|
|
/// Foreground/background VGA colour light grey.
|
|
pub const COLOUR_LIGHT_GREY: u4 = 0x07;
|
|
|
|
/// Foreground/background VGA colour dark grey.
|
|
pub const COLOUR_DARK_GREY: u4 = 0x08;
|
|
|
|
/// Foreground/background VGA colour light blue.
|
|
pub const COLOUR_LIGHT_BLUE: u4 = 0x09;
|
|
|
|
/// Foreground/background VGA colour light green.
|
|
pub const COLOUR_LIGHT_GREEN: u4 = 0x0A;
|
|
|
|
/// Foreground/background VGA colour light cyan.
|
|
pub const COLOUR_LIGHT_CYAN: u4 = 0x0B;
|
|
|
|
/// Foreground/background VGA colour light red.
|
|
pub const COLOUR_LIGHT_RED: u4 = 0x0C;
|
|
|
|
/// Foreground/background VGA colour light magenta.
|
|
pub const COLOUR_LIGHT_MAGENTA: u4 = 0x0D;
|
|
|
|
/// Foreground/background VGA colour light brown.
|
|
pub const COLOUR_LIGHT_BROWN: u4 = 0x0E;
|
|
|
|
/// Foreground/background VGA colour white.
|
|
pub const COLOUR_WHITE: u4 = 0x0F;
|
|
|
|
/// The set of shapes that can be displayed.
|
|
pub const CursorShape = enum {
|
|
/// 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;
|
|
|
|
///
|
|
/// Set the VGA register port to read from or write to.
|
|
///
|
|
/// Arguments:
|
|
/// IN index: u8 - The index to send to the port address to select the register to write data
|
|
/// to.
|
|
///
|
|
inline fn sendPort(index: u8) void {
|
|
arch.outb(PORT_ADDRESS, index);
|
|
}
|
|
|
|
///
|
|
/// Send data to the set VGA register port.
|
|
///
|
|
/// Arguments:
|
|
/// IN data: u8 - The data to send to the selected register.
|
|
///
|
|
inline fn sendData(data: u8) void {
|
|
arch.outb(PORT_DATA, data);
|
|
}
|
|
|
|
///
|
|
/// Get data from a set VGA register port.
|
|
///
|
|
/// Return: u8
|
|
/// The data in the selected register.
|
|
///
|
|
inline fn getData() u8 {
|
|
return arch.inb(PORT_DATA);
|
|
}
|
|
|
|
///
|
|
/// Set the VGA register port to write to and sending data to that VGA register port.
|
|
///
|
|
/// Arguments:
|
|
/// IN index: u8 - The index to send to the port address to select the register to write the
|
|
// data to.
|
|
/// IN data: u8 - The data to send to the selected register.
|
|
///
|
|
inline fn sendPortData(index: u8, data: u8) void {
|
|
sendPort(index);
|
|
sendData(data);
|
|
}
|
|
|
|
///
|
|
/// Set the VGA register port to read from and get the data from that VGA register port.
|
|
///
|
|
/// Arguments:
|
|
/// IN index: u8 - The index to send to the port address to select the register to read the
|
|
/// data from.
|
|
///
|
|
/// Return: u8
|
|
/// The data in the selected register.
|
|
///
|
|
inline fn getPortData(index: u8) u8 {
|
|
sendPort(index);
|
|
return getData();
|
|
}
|
|
|
|
///
|
|
/// 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.
|
|
///
|
|
/// Return: u8
|
|
/// Both combined into 1 byte for the colour to be displayed.
|
|
///
|
|
pub fn entryColour(fg: u4, bg: u4) u8 {
|
|
return fg | @as(u8, bg) << 4;
|
|
}
|
|
|
|
///
|
|
/// Create the 2 bytes entry that the VGA used to display a character with a foreground and
|
|
/// background colour.
|
|
///
|
|
/// Arguments:
|
|
/// IN char: u8 - The character ro display.
|
|
/// IN colour: u8 - The foreground and background colour.
|
|
///
|
|
/// Return: u16
|
|
/// A VGA entry.
|
|
///
|
|
pub fn entry(char: u8, colour: u8) u16 {
|
|
return char | @as(u16, colour) << 8;
|
|
}
|
|
|
|
///
|
|
/// Update the hardware on screen cursor.
|
|
///
|
|
/// Arguments:
|
|
/// IN x: u16 - The horizontal position of the cursor (column).
|
|
/// IN y: u16 - The vertical position of the cursor (row).
|
|
///
|
|
pub fn updateCursor(x: u16, y: u16) void {
|
|
var pos: u16 = undefined;
|
|
|
|
// Make sure new cursor position is within the screen
|
|
if (x < WIDTH and y < HEIGHT) {
|
|
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);
|
|
}
|
|
|
|
const pos_upper = (pos >> 8) & 0x00FF;
|
|
const pos_lower = pos & 0x00FF;
|
|
|
|
// Set the cursor position
|
|
sendPortData(REG_CURSOR_LOCATION_LOW, @truncate(u8, pos_lower));
|
|
sendPortData(REG_CURSOR_LOCATION_HIGH, @truncate(u8, pos_upper));
|
|
}
|
|
|
|
///
|
|
/// Get the linear position of the hardware cursor.
|
|
///
|
|
/// Return: u16
|
|
/// The linear cursor position.
|
|
///
|
|
pub fn getCursor() u16 {
|
|
var cursor: u16 = 0;
|
|
|
|
cursor |= getPortData(REG_CURSOR_LOCATION_LOW);
|
|
cursor |= @as(u16, getPortData(REG_CURSOR_LOCATION_HIGH)) << 8;
|
|
|
|
return cursor;
|
|
}
|
|
|
|
///
|
|
/// Enables the blinking cursor so that is is visible.
|
|
///
|
|
pub fn enableCursor() void {
|
|
sendPortData(REG_CURSOR_START, cursor_scanline_start);
|
|
sendPortData(REG_CURSOR_END, cursor_scanline_end);
|
|
}
|
|
|
|
///
|
|
/// Disables the blinking cursor so that is is invisible.
|
|
///
|
|
pub fn disableCursor() void {
|
|
sendPortData(REG_CURSOR_START, CURSOR_DISABLE);
|
|
}
|
|
|
|
///
|
|
/// Set the shape of the cursor. This can be and underline or block shape.
|
|
///
|
|
/// Arguments:
|
|
/// IN shape: CursorShape - The enum CursorShape that selects which shape to use.
|
|
///
|
|
pub fn setCursorShape(shape: CursorShape) void {
|
|
switch (shape) {
|
|
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;
|
|
},
|
|
}
|
|
|
|
sendPortData(REG_CURSOR_START, cursor_scanline_start);
|
|
sendPortData(REG_CURSOR_END, cursor_scanline_end);
|
|
}
|
|
|
|
///
|
|
/// Initialise the VGA text mode. This sets the cursor and underline shape.
|
|
///
|
|
pub fn init() void {
|
|
log.logInfo("Init vga\n", .{});
|
|
|
|
// Set the maximum scan line to 0x0F
|
|
sendPortData(REG_MAXIMUM_SCAN_LINE, CURSOR_SCANLINE_END);
|
|
|
|
// Set by default the underline cursor
|
|
setCursorShape(CursorShape.UNDERLINE);
|
|
|
|
log.logInfo("Done\n", .{});
|
|
|
|
if (build_options.rt_test) runtimeTests();
|
|
}
|
|
|
|
test "entryColour" {
|
|
var fg = COLOUR_BLACK;
|
|
var bg = COLOUR_BLACK;
|
|
var res = entryColour(fg, bg);
|
|
expectEqual(@as(u8, 0x00), res);
|
|
|
|
fg = COLOUR_LIGHT_GREEN;
|
|
bg = COLOUR_BLACK;
|
|
res = entryColour(fg, bg);
|
|
expectEqual(@as(u8, 0x0A), res);
|
|
|
|
fg = COLOUR_BLACK;
|
|
bg = COLOUR_LIGHT_GREEN;
|
|
res = entryColour(fg, bg);
|
|
expectEqual(@as(u8, 0xA0), res);
|
|
|
|
fg = COLOUR_BROWN;
|
|
bg = COLOUR_LIGHT_GREEN;
|
|
res = entryColour(fg, bg);
|
|
expectEqual(@as(u8, 0xA6), res);
|
|
}
|
|
|
|
test "entry" {
|
|
const colour = entryColour(COLOUR_BROWN, COLOUR_LIGHT_GREEN);
|
|
expectEqual(@as(u8, 0xA6), colour);
|
|
|
|
// Character '0' is 0x30
|
|
var video_entry = entry('0', colour);
|
|
expectEqual(@as(u16, 0xA630), video_entry);
|
|
|
|
video_entry = entry(0x55, colour);
|
|
expectEqual(@as(u16, 0xA655), video_entry);
|
|
}
|
|
|
|
test "updateCursor width out of bounds" {
|
|
const x = WIDTH;
|
|
const y = 0;
|
|
|
|
const max_cursor = (HEIGHT - 1) * WIDTH + (WIDTH - 1);
|
|
const expected_upper = @truncate(u8, (max_cursor >> 8) & 0x00FF);
|
|
const expected_lower = @truncate(u8, max_cursor & 0x00FF);
|
|
|
|
arch.initTest();
|
|
defer arch.freeTest();
|
|
|
|
// Mocking out the arch.outb calls for changing the hardware cursor:
|
|
arch.addTestParams("outb", .{ PORT_ADDRESS, REG_CURSOR_LOCATION_LOW, PORT_DATA, expected_lower, PORT_ADDRESS, REG_CURSOR_LOCATION_HIGH, PORT_DATA, expected_upper });
|
|
|
|
updateCursor(x, y);
|
|
}
|
|
|
|
test "updateCursor height out of bounds" {
|
|
const x = 0;
|
|
const y = HEIGHT;
|
|
|
|
const max_cursor = (HEIGHT - 1) * WIDTH + (WIDTH - 1);
|
|
const expected_upper = @truncate(u8, (max_cursor >> 8) & 0x00FF);
|
|
const expected_lower = @truncate(u8, max_cursor & 0x00FF);
|
|
|
|
arch.initTest();
|
|
defer arch.freeTest();
|
|
|
|
// Mocking out the arch.outb calls for changing the hardware cursor:
|
|
arch.addTestParams("outb", .{ PORT_ADDRESS, REG_CURSOR_LOCATION_LOW, PORT_DATA, expected_lower, PORT_ADDRESS, REG_CURSOR_LOCATION_HIGH, PORT_DATA, expected_upper });
|
|
|
|
updateCursor(x, y);
|
|
}
|
|
|
|
test "updateCursor width and height out of bounds" {
|
|
const x = WIDTH;
|
|
const y = HEIGHT;
|
|
|
|
const max_cursor = (HEIGHT - 1) * WIDTH + (WIDTH - 1);
|
|
const expected_upper = @truncate(u8, (max_cursor >> 8) & 0x00FF);
|
|
const expected_lower = @truncate(u8, max_cursor & 0x00FF);
|
|
|
|
arch.initTest();
|
|
defer arch.freeTest();
|
|
|
|
// Mocking out the arch.outb calls for changing the hardware cursor:
|
|
arch.addTestParams("outb", .{ PORT_ADDRESS, REG_CURSOR_LOCATION_LOW, PORT_DATA, expected_lower, PORT_ADDRESS, REG_CURSOR_LOCATION_HIGH, PORT_DATA, expected_upper });
|
|
|
|
updateCursor(x, y);
|
|
}
|
|
|
|
test "updateCursor width-1 and height out of bounds" {
|
|
const x = WIDTH - 1;
|
|
const y = HEIGHT;
|
|
|
|
const max_cursor = (HEIGHT - 1) * WIDTH + (WIDTH - 1);
|
|
const expected_upper = @truncate(u8, (max_cursor >> 8) & 0x00FF);
|
|
const expected_lower = @truncate(u8, max_cursor & 0x00FF);
|
|
|
|
arch.initTest();
|
|
defer arch.freeTest();
|
|
|
|
// Mocking out the arch.outb calls for changing the hardware cursor:
|
|
arch.addTestParams("outb", .{ PORT_ADDRESS, REG_CURSOR_LOCATION_LOW, PORT_DATA, expected_lower, PORT_ADDRESS, REG_CURSOR_LOCATION_HIGH, PORT_DATA, expected_upper });
|
|
|
|
updateCursor(x, y);
|
|
}
|
|
|
|
test "updateCursor width and height-1 out of bounds" {
|
|
const x = WIDTH;
|
|
const y = HEIGHT - 1;
|
|
|
|
const max_cursor = (HEIGHT - 1) * WIDTH + (WIDTH - 1);
|
|
const expected_upper = @truncate(u8, (max_cursor >> 8) & 0x00FF);
|
|
const expected_lower = @truncate(u8, max_cursor & 0x00FF);
|
|
|
|
arch.initTest();
|
|
defer arch.freeTest();
|
|
|
|
// Mocking out the arch.outb calls for changing the hardware cursor:
|
|
arch.addTestParams("outb", .{ PORT_ADDRESS, REG_CURSOR_LOCATION_LOW, PORT_DATA, expected_lower, PORT_ADDRESS, REG_CURSOR_LOCATION_HIGH, PORT_DATA, expected_upper });
|
|
|
|
updateCursor(x, y);
|
|
}
|
|
|
|
test "updateCursor in bounds" {
|
|
var x: u8 = 0x0A;
|
|
var y: u8 = 0x0A;
|
|
const expected = y * WIDTH + x;
|
|
|
|
var expected_upper = @truncate(u8, (expected >> 8) & 0x00FF);
|
|
var expected_lower = @truncate(u8, expected & 0x00FF);
|
|
|
|
arch.initTest();
|
|
defer arch.freeTest();
|
|
|
|
// Mocking out the arch.outb calls for changing the hardware cursor:
|
|
arch.addTestParams("outb", .{ PORT_ADDRESS, REG_CURSOR_LOCATION_LOW, PORT_DATA, expected_lower, PORT_ADDRESS, REG_CURSOR_LOCATION_HIGH, PORT_DATA, expected_upper });
|
|
updateCursor(x, y);
|
|
}
|
|
|
|
test "getCursor 1: 10" {
|
|
const expect: u16 = 10;
|
|
|
|
// Mocking out the arch.outb and arch.inb calls for getting the hardware cursor:
|
|
arch.initTest();
|
|
defer arch.freeTest();
|
|
|
|
arch.addTestParams("outb", .{ PORT_ADDRESS, REG_CURSOR_LOCATION_LOW });
|
|
arch.addTestParams("inb", .{ PORT_DATA, @as(u8, 10) });
|
|
arch.addTestParams("outb", .{ PORT_ADDRESS, REG_CURSOR_LOCATION_HIGH });
|
|
arch.addTestParams("inb", .{ PORT_DATA, @as(u8, 0) });
|
|
|
|
const actual = getCursor();
|
|
expectEqual(expect, actual);
|
|
}
|
|
|
|
test "getCursor 2: 0xBEEF" {
|
|
const expect: u16 = 0xBEEF;
|
|
|
|
// Mocking out the arch.outb and arch.inb calls for getting the hardware cursor:
|
|
arch.initTest();
|
|
defer arch.freeTest();
|
|
|
|
arch.addTestParams("outb", .{ PORT_ADDRESS, REG_CURSOR_LOCATION_LOW });
|
|
arch.addTestParams("inb", .{ PORT_DATA, @as(u8, 0xEF) });
|
|
arch.addTestParams("outb", .{ PORT_ADDRESS, REG_CURSOR_LOCATION_HIGH });
|
|
arch.addTestParams("inb", .{ PORT_DATA, @as(u8, 0xBE) });
|
|
|
|
const actual = getCursor();
|
|
expectEqual(expect, actual);
|
|
}
|
|
|
|
test "enableCursor" {
|
|
arch.initTest();
|
|
defer arch.freeTest();
|
|
|
|
// Need to init the cursor start and end positions, so call the init() to set this up
|
|
arch.addTestParams("outb", .{
|
|
PORT_ADDRESS, REG_MAXIMUM_SCAN_LINE, PORT_DATA, CURSOR_SCANLINE_END, PORT_ADDRESS, REG_CURSOR_START, PORT_DATA, CURSOR_SCANLINE_MIDDLE, PORT_ADDRESS, REG_CURSOR_END, PORT_DATA, CURSOR_SCANLINE_END,
|
|
// Mocking out the arch.outb calls for enabling the cursor:
|
|
// These are the default cursor positions from init()
|
|
PORT_ADDRESS, REG_CURSOR_START, PORT_DATA, CURSOR_SCANLINE_MIDDLE, PORT_ADDRESS, REG_CURSOR_END, PORT_DATA, CURSOR_SCANLINE_END,
|
|
});
|
|
|
|
init();
|
|
enableCursor();
|
|
}
|
|
|
|
test "disableCursor" {
|
|
arch.initTest();
|
|
defer arch.freeTest();
|
|
|
|
// Mocking out the arch.outb calls for disabling the cursor:
|
|
arch.addTestParams("outb", .{ PORT_ADDRESS, REG_CURSOR_START, PORT_DATA, CURSOR_DISABLE });
|
|
disableCursor();
|
|
}
|
|
|
|
test "setCursorShape UNDERLINE" {
|
|
arch.initTest();
|
|
defer arch.freeTest();
|
|
|
|
// Mocking out the arch.outb calls for setting the cursor shape to underline:
|
|
// This will also check that the scan line variables were set properly as these are using in
|
|
// the arch.outb call
|
|
arch.addTestParams("outb", .{ PORT_ADDRESS, REG_CURSOR_START, PORT_DATA, CURSOR_SCANLINE_MIDDLE, PORT_ADDRESS, REG_CURSOR_END, PORT_DATA, CURSOR_SCANLINE_END });
|
|
|
|
setCursorShape(CursorShape.UNDERLINE);
|
|
}
|
|
|
|
test "setCursorShape BLOCK" {
|
|
arch.initTest();
|
|
defer arch.freeTest();
|
|
|
|
// Mocking out the arch.outb calls for setting the cursor shape to block:
|
|
// This will also check that the scan line variables were set properly as these are using in
|
|
// the arch.outb call
|
|
arch.addTestParams("outb", .{ PORT_ADDRESS, REG_CURSOR_START, PORT_DATA, CURSOR_SCANLINE_START, PORT_ADDRESS, REG_CURSOR_END, PORT_DATA, CURSOR_SCANLINE_END });
|
|
|
|
setCursorShape(CursorShape.BLOCK);
|
|
}
|
|
|
|
test "init" {
|
|
arch.initTest();
|
|
defer arch.freeTest();
|
|
|
|
// Mocking out the arch.outb calls for setting the cursor max scan line and the shape to block:
|
|
// This will also check that the scan line variables were set properly as these are using in
|
|
// the arch.outb call for setting the cursor shape.
|
|
arch.addTestParams("outb", .{ PORT_ADDRESS, REG_MAXIMUM_SCAN_LINE, PORT_DATA, CURSOR_SCANLINE_END, PORT_ADDRESS, REG_CURSOR_START, PORT_DATA, CURSOR_SCANLINE_MIDDLE, PORT_ADDRESS, REG_CURSOR_END, PORT_DATA, CURSOR_SCANLINE_END });
|
|
|
|
init();
|
|
}
|
|
|
|
///
|
|
/// Check that the maximum scan line is CURSOR_SCANLINE_END (0xF) when VGA is initialised.
|
|
///
|
|
fn rt_correctMaxScanLine() void {
|
|
const max_scan_line = getPortData(REG_MAXIMUM_SCAN_LINE);
|
|
|
|
if (max_scan_line != CURSOR_SCANLINE_END) {
|
|
panic(@errorReturnTrace(), "Max scan line not {}, found {}\n", .{ CURSOR_SCANLINE_END, max_scan_line });
|
|
}
|
|
|
|
log.logInfo("VGA: Tested max scan line\n", .{});
|
|
}
|
|
|
|
///
|
|
/// Check that the cursor is an underline when the VGA initialises.
|
|
///
|
|
fn rt_correctCursorShape() void {
|
|
// Check the global variables are correct
|
|
if (cursor_scanline_start != CURSOR_SCANLINE_MIDDLE or cursor_scanline_end != CURSOR_SCANLINE_END) {
|
|
panic(@errorReturnTrace(), "Global cursor scanline incorrect. Start: {}, end: {}\n", .{ cursor_scanline_start, cursor_scanline_end });
|
|
}
|
|
|
|
const cursor_start = getPortData(REG_CURSOR_START);
|
|
const cursor_end = getPortData(REG_CURSOR_END);
|
|
|
|
if (cursor_start != CURSOR_SCANLINE_MIDDLE or cursor_end != CURSOR_SCANLINE_END) {
|
|
panic(@errorReturnTrace(), "Cursor scanline are incorrect. Start: {}, end: {}\n", .{ cursor_start, cursor_end });
|
|
}
|
|
|
|
log.logInfo("VGA: Tested cursor shape\n", .{});
|
|
}
|
|
|
|
///
|
|
/// Update the cursor to a known value. Then get the cursor and check they match. This will also
|
|
/// save the previous cursor position and restore is to the original position.
|
|
///
|
|
fn rt_setCursorGetCursor() void {
|
|
// The known locations
|
|
const x: u16 = 10;
|
|
const y: u16 = 20;
|
|
|
|
// Save the previous location
|
|
const prev_linear_loc = getCursor();
|
|
const prev_x_loc = @truncate(u8, prev_linear_loc % WIDTH);
|
|
const prev_y_loc = @truncate(u8, prev_linear_loc / WIDTH);
|
|
|
|
// Set the known location
|
|
updateCursor(x, y);
|
|
|
|
// Get the cursor
|
|
const actual_linear_loc = getCursor();
|
|
const actual_x_loc = @truncate(u8, actual_linear_loc % WIDTH);
|
|
const actual_y_loc = @truncate(u8, actual_linear_loc / WIDTH);
|
|
|
|
if (x != actual_x_loc or y != actual_y_loc) {
|
|
panic(@errorReturnTrace(), "VGA cursor not the same: a_x: {}, a_y: {}, e_x: {}, e_y: {}\n", .{ x, y, actual_x_loc, actual_y_loc });
|
|
}
|
|
|
|
// Restore the previous x and y
|
|
updateCursor(prev_x_loc, prev_y_loc);
|
|
|
|
log.logInfo("VGA: Tested updating cursor\n", .{});
|
|
}
|
|
|
|
///
|
|
/// Run all the runtime tests.
|
|
///
|
|
fn runtimeTests() void {
|
|
rt_correctMaxScanLine();
|
|
rt_correctCursorShape();
|
|
rt_setCursorGetCursor();
|
|
}
|