Added runtime tests for VGA

Added doc comments as well
A little refactor of code
Reordered


Removed types
This commit is contained in:
ED 2019-09-29 12:55:34 +01:00
parent b682afa79d
commit 420a09f039
9 changed files with 267 additions and 150 deletions

View file

@ -1,6 +1,6 @@
// Zig version: 0.4.0 // Zig version: 0.4.0
const panic = @import("../../panic.zig"); const panic = @import("../../panic.zig").panic;
const idt = @import("idt.zig"); const idt = @import("idt.zig");
const arch = @import("arch.zig"); const arch = @import("arch.zig");
const pic = @import("pic.zig"); const pic = @import("pic.zig");
@ -39,7 +39,7 @@ var irq_handlers: [NUMBER_OF_ENTRIES]fn (*arch.InterruptContext) void = [_]fn (*
/// ///
fn unhandled(context: *arch.InterruptContext) void { fn unhandled(context: *arch.InterruptContext) void {
const interrupt_num: u8 = @truncate(u8, context.int_num - IRQ_OFFSET); const interrupt_num: u8 = @truncate(u8, context.int_num - IRQ_OFFSET);
panic.panicFmt(null, "Unhandled IRQ number {}", interrupt_num); panic(null, "Unhandled IRQ number {}", interrupt_num);
} }
/// ///
@ -69,7 +69,7 @@ export fn irqHandler(context: *arch.InterruptContext) void {
fn openIrq(index: u8, handler: idt.InterruptHandler) void { fn openIrq(index: u8, handler: idt.InterruptHandler) void {
idt.openInterruptGate(index, handler) catch |err| switch (err) { idt.openInterruptGate(index, handler) catch |err| switch (err) {
error.IdtEntryExists => { error.IdtEntryExists => {
panic.panicFmt(@errorReturnTrace(), "Error opening IRQ number: {} exists", index); panic(@errorReturnTrace(), "Error opening IRQ number: {} exists", index);
}, },
}; };
} }

View file

@ -1,6 +1,6 @@
// Zig version: 0.4.0 // Zig version: 0.4.0
const panic = @import("../../panic.zig"); const panic = @import("../../panic.zig").panic;
const idt = @import("idt.zig"); const idt = @import("idt.zig");
const arch = @import("arch.zig"); const arch = @import("arch.zig");
const syscalls = @import("syscalls.zig"); const syscalls = @import("syscalls.zig");
@ -98,7 +98,7 @@ var syscall_handler: IsrHandler = unhandled;
/// ///
fn unhandled(context: *arch.InterruptContext) void { fn unhandled(context: *arch.InterruptContext) void {
const interrupt_num = context.int_num; const interrupt_num = context.int_num;
panic.panicFmt(null, "Unhandled exception: {}, number {}", exception_msg[interrupt_num], interrupt_num); panic(null, "Unhandled exception: {}, number {}", exception_msg[interrupt_num], interrupt_num);
} }
/// ///
@ -126,7 +126,7 @@ export fn isrHandler(context: *arch.InterruptContext) void {
} else if (isValidIsr(isr_num)) { } else if (isValidIsr(isr_num)) {
isr_handlers[isr_num](context); isr_handlers[isr_num](context);
} else { } else {
panic.panicFmt(null, "Unrecognised isr: {}\n", isr_num); panic(null, "Unrecognised isr: {}\n", isr_num);
} }
} }
@ -140,7 +140,7 @@ export fn isrHandler(context: *arch.InterruptContext) void {
fn openIsr(index: u8, handler: idt.InterruptHandler) void { fn openIsr(index: u8, handler: idt.InterruptHandler) void {
idt.openInterruptGate(index, handler) catch |err| switch (err) { idt.openInterruptGate(index, handler) catch |err| switch (err) {
error.IdtEntryExists => { error.IdtEntryExists => {
panic.panicFmt(@errorReturnTrace(), "Error opening ISR number: {} exists", index); panic(@errorReturnTrace(), "Error opening ISR number: {} exists", index);
}, },
}; };
} }

View file

@ -2,7 +2,7 @@ const std = @import("std");
const expectEqual = std.testing.expectEqual; const expectEqual = std.testing.expectEqual;
const expect = std.testing.expect; const expect = std.testing.expect;
const builtin = @import("builtin"); const builtin = @import("builtin");
const panic = @import("../../panic.zig"); const panic = @import("../../panic.zig").panic;
const arch = @import("arch.zig"); const arch = @import("arch.zig");
const isr = @import("isr.zig"); const isr = @import("isr.zig");
const MemProfile = @import("../../mem.zig").MemProfile; const MemProfile = @import("../../mem.zig").MemProfile;
@ -319,18 +319,18 @@ pub fn init(mem_profile: *const MemProfile, allocator: *std.mem.Allocator) void
const p_start = std.mem.alignBackward(@ptrToInt(mem_profile.physaddr_start), PAGE_SIZE_4KB); const p_start = std.mem.alignBackward(@ptrToInt(mem_profile.physaddr_start), PAGE_SIZE_4KB);
const p_end = std.mem.alignForward(@ptrToInt(mem_profile.physaddr_end) + mem_profile.fixed_alloc_size, PAGE_SIZE_4KB); const p_end = std.mem.alignForward(@ptrToInt(mem_profile.physaddr_end) + mem_profile.fixed_alloc_size, PAGE_SIZE_4KB);
var tmp = allocator.alignedAlloc(Directory, @truncate(u29, PAGE_SIZE_4KB), 1) catch panic.panicFmt(@errorReturnTrace(), "Failed to allocate page directory"); var tmp = allocator.alignedAlloc(Directory, @truncate(u29, PAGE_SIZE_4KB), 1) catch panic(@errorReturnTrace(), "Failed to allocate page directory");
var kernel_directory = @ptrCast(*Directory, tmp.ptr); var kernel_directory = @ptrCast(*Directory, tmp.ptr);
@memset(@ptrCast([*]u8, kernel_directory), 0, @sizeOf(Directory)); @memset(@ptrCast([*]u8, kernel_directory), 0, @sizeOf(Directory));
// Map in kernel // Map in kernel
mapDir(kernel_directory, p_start, p_end, v_start, v_end, allocator) catch panic.panicFmt(@errorReturnTrace(), "Failed to map kernel directory"); mapDir(kernel_directory, p_start, p_end, v_start, v_end, allocator) catch panic(@errorReturnTrace(), "Failed to map kernel directory");
const tty_addr = tty.getVideoBufferAddress(); const tty_addr = tty.getVideoBufferAddress();
// If the previous mappping space didn't cover the tty buffer, do so now // If the previous mappping space didn't cover the tty buffer, do so now
if (v_start > tty_addr or v_end <= tty_addr) { if (v_start > tty_addr or v_end <= tty_addr) {
const tty_phys = virtToPhys(tty_addr); const tty_phys = virtToPhys(tty_addr);
const tty_buff_size = 32 * 1024; const tty_buff_size = 32 * 1024;
mapDir(kernel_directory, tty_phys, tty_phys + tty_buff_size, tty_addr, tty_addr + tty_buff_size, allocator) catch panic.panicFmt(@errorReturnTrace(), "Failed to map vga buffer in kernel directory"); mapDir(kernel_directory, tty_phys, tty_phys + tty_buff_size, tty_addr, tty_addr + tty_buff_size, allocator) catch panic(@errorReturnTrace(), "Failed to map vga buffer in kernel directory");
} }
const dir_physaddr = @ptrToInt(virtToPhys(kernel_directory)); const dir_physaddr = @ptrToInt(virtToPhys(kernel_directory));
@ -338,7 +338,7 @@ pub fn init(mem_profile: *const MemProfile, allocator: *std.mem.Allocator) void
: :
: [addr] "{eax}" (dir_physaddr) : [addr] "{eax}" (dir_physaddr)
); );
isr.registerIsr(14, pageFault) catch panic.panicFmt(@errorReturnTrace(), "Failed to register page fault ISR"); isr.registerIsr(14, pageFault) catch panic(@errorReturnTrace(), "Failed to register page fault ISR");
} }
fn checkDirEntry(entry: DirectoryEntry, virt_start: usize, virt_end: usize, phys_start: usize, table: *Table) void { fn checkDirEntry(entry: DirectoryEntry, virt_start: usize, virt_end: usize, phys_start: usize, table: *Table) void {

View file

@ -22,13 +22,13 @@ export var KERNEL_ADDR_OFFSET: u32 = if (builtin.is_test) 0xC0000000 else undefi
// Need to import this as we need the panic to be in the root source file, or zig will just use the // Need to import this as we need the panic to be in the root source file, or zig will just use the
// builtin panic and just loop, which is what we don't want // builtin panic and just loop, which is what we don't want
const panic_root = if (builtin.is_test) @import(build_options.mock_path ++ "panic_mock.zig") else @import("panic.zig"); const panic_root = @import("panic.zig").panic;
// Just call the panic function, as this need to be in the root source file // Just call the panic function, as this need to be in the root source file
pub fn panic(msg: []const u8, error_return_trace: ?*builtin.StackTrace) noreturn { pub fn panic(msg: []const u8, error_return_trace: ?*builtin.StackTrace) noreturn {
@setCold(true); @setCold(true);
arch.disableInterrupts(); arch.disableInterrupts();
panic_root.panicFmt(error_return_trace, "{}", msg); panic_root(error_return_trace, "{}", msg);
} }
export fn kmain(mb_info: *multiboot.multiboot_info_t, mb_magic: u32) void { export fn kmain(mb_info: *multiboot.multiboot_info_t, mb_magic: u32) void {

View file

@ -1,13 +1,12 @@
// Zig version: 0.4.0
const builtin = @import("builtin"); const builtin = @import("builtin");
const tty = @import("tty.zig"); const tty = @import("tty.zig");
const arch = @import("arch.zig").internals; const arch = @import("arch.zig").internals;
const log = @import("log.zig");
pub fn panicFmt(trace: ?*builtin.StackTrace, comptime format: []const u8, args: ...) noreturn { pub fn panic(trace: ?*builtin.StackTrace, comptime format: []const u8, args: ...) noreturn {
@setCold(true); @setCold(true);
tty.print("KERNEL PANIC\n"); log.logInfo("KERNEL PANIC\n");
tty.print(format, args); log.logInfo(format, args);
tty.print("\nHALTING\n"); log.logInfo("HALTING\n");
arch.haltNoInterrupts(); arch.haltNoInterrupts();
} }

View file

@ -1,6 +1,12 @@
const std = @import("std"); const std = @import("std");
const builtin = @import("builtin");
const is_test = builtin.is_test;
const expectEqual = std.testing.expectEqual; 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 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. /// The port address for the VGA register selection.
const PORT_ADDRESS: u16 = 0x03D4; const PORT_ADDRESS: u16 = 0x03D4;
@ -8,39 +14,21 @@ const PORT_ADDRESS: u16 = 0x03D4;
/// The port address for the VGA data. /// The port address for the VGA data.
const PORT_DATA: u16 = 0x03D5; const PORT_DATA: u16 = 0x03D5;
/// The indexes that is passed to the address port to select the register for the data to be /// The indexes that is passed to the address port to select the maximum scan line register for
/// read or written to. /// the data to be read or written to.
const REG_HORIZONTAL_TOTAL: u8 = 0x00;
const REG_HORIZONTAL_DISPLAY_ENABLE_END: u8 = 0x01;
const REG_START_HORIZONTAL_BLINKING: u8 = 0x02;
const REG_END_HORIZONTAL_BLINKING: u8 = 0x03;
const REG_START_HORIZONTAL_RETRACE_PULSE: u8 = 0x04;
const REG_END_HORIZONTAL_RETRACE_PULSE: u8 = 0x05;
const REG_VERTICAL_TOTAL: u8 = 0x06;
const REG_OVERFLOW: u8 = 0x07;
const REG_PRESET_ROW_SCAN: u8 = 0x08;
const REG_MAXIMUM_SCAN_LINE: u8 = 0x09; const REG_MAXIMUM_SCAN_LINE: u8 = 0x09;
/// The register select for setting the cursor scan lines. /// The register select for setting the cursor start scan lines.
const REG_CURSOR_START: u8 = 0x0A; const REG_CURSOR_START: u8 = 0x0A;
/// The register select for setting the cursor end scan lines.
const REG_CURSOR_END: u8 = 0x0B; const REG_CURSOR_END: u8 = 0x0B;
const REG_START_ADDRESS_HIGH: u8 = 0x0C;
const REG_START_ADDRESS_LOW: u8 = 0x0D;
/// The command for setting the cursor's linear location. /// The command for setting the cursor's linear location (Upper 8 bits).
const REG_CURSOR_LOCATION_HIGH: u8 = 0x0E; const REG_CURSOR_LOCATION_HIGH: u8 = 0x0E;
const REG_CURSOR_LOCATION_LOW: u8 = 0x0F;
/// Other VGA registers. /// The command for setting the cursor's linear location (Lower 8 bits).
const REG_VERTICAL_RETRACE_START: u8 = 0x10; const REG_CURSOR_LOCATION_LOW: u8 = 0x0F;
const REG_VERTICAL_RETRACE_END: u8 = 0x11;
const REG_VERTICAL_DISPLAY_ENABLE_END: u8 = 0x12;
const REG_OFFSET: u8 = 0x13;
const REG_UNDERLINE_LOCATION: u8 = 0x14;
const REG_START_VERTICAL_BLINKING: u8 = 0x15;
const REG_END_VERTICAL_BLINKING: u8 = 0x16;
const REG_CRT_MODE_CONTROL: u8 = 0x17;
const REG_LINE_COMPARE: u8 = 0x18;
/// The start of the cursor scan line, the very beginning. /// The start of the cursor scan line, the very beginning.
const CURSOR_SCANLINE_START: u8 = 0x0; const CURSOR_SCANLINE_START: u8 = 0x0;
@ -60,22 +48,56 @@ pub const WIDTH: u16 = 80;
/// The number of characters heigh the screen is. /// The number of characters heigh the screen is.
pub const HEIGHT: u16 = 25; pub const HEIGHT: u16 = 25;
/// The set of colours that VGA supports and can display for the foreground and background. // ----------
// 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; pub const COLOUR_BLACK: u4 = 0x00;
/// Foreground/background VGA colour blue.
pub const COLOUR_BLUE: u4 = 0x01; pub const COLOUR_BLUE: u4 = 0x01;
/// Foreground/background VGA colour green.
pub const COLOUR_GREEN: u4 = 0x02; pub const COLOUR_GREEN: u4 = 0x02;
/// Foreground/background VGA colour cyan.
pub const COLOUR_CYAN: u4 = 0x03; pub const COLOUR_CYAN: u4 = 0x03;
/// Foreground/background VGA colour red.
pub const COLOUR_RED: u4 = 0x04; pub const COLOUR_RED: u4 = 0x04;
/// Foreground/background VGA colour magenta.
pub const COLOUR_MAGENTA: u4 = 0x05; pub const COLOUR_MAGENTA: u4 = 0x05;
/// Foreground/background VGA colour brown.
pub const COLOUR_BROWN: u4 = 0x06; pub const COLOUR_BROWN: u4 = 0x06;
/// Foreground/background VGA colour light grey.
pub const COLOUR_LIGHT_GREY: u4 = 0x07; pub const COLOUR_LIGHT_GREY: u4 = 0x07;
/// Foreground/background VGA colour dark grey.
pub const COLOUR_DARK_GREY: u4 = 0x08; pub const COLOUR_DARK_GREY: u4 = 0x08;
/// Foreground/background VGA colour light blue.
pub const COLOUR_LIGHT_BLUE: u4 = 0x09; pub const COLOUR_LIGHT_BLUE: u4 = 0x09;
/// Foreground/background VGA colour light green.
pub const COLOUR_LIGHT_GREEN: u4 = 0x0A; pub const COLOUR_LIGHT_GREEN: u4 = 0x0A;
/// Foreground/background VGA colour light cyan.
pub const COLOUR_LIGHT_CYAN: u4 = 0x0B; pub const COLOUR_LIGHT_CYAN: u4 = 0x0B;
/// Foreground/background VGA colour light red.
pub const COLOUR_LIGHT_RED: u4 = 0x0C; pub const COLOUR_LIGHT_RED: u4 = 0x0C;
/// Foreground/background VGA colour light magenta.
pub const COLOUR_LIGHT_MAGENTA: u4 = 0x0D; pub const COLOUR_LIGHT_MAGENTA: u4 = 0x0D;
/// Foreground/background VGA colour light brown.
pub const COLOUR_LIGHT_BROWN: u4 = 0x0E; pub const COLOUR_LIGHT_BROWN: u4 = 0x0E;
/// Foreground/background VGA colour white.
pub const COLOUR_WHITE: u4 = 0x0F; pub const COLOUR_WHITE: u4 = 0x0F;
/// The set of shapes that can be displayed. /// The set of shapes that can be displayed.
@ -93,26 +115,63 @@ var cursor_scanline_start: u8 = undefined;
/// The cursor scan line end so to know whether is in block or underline mode. /// The cursor scan line end so to know whether is in block or underline mode.
var cursor_scanline_end: u8 = undefined; var cursor_scanline_end: u8 = undefined;
/// A inline function for setting the VGA register port to read from or write to. ///
inline fn sendPort(port: u8) void { /// Set the VGA register port to read from or write to.
arch.outb(PORT_ADDRESS, port); ///
/// 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);
} }
/// A inline function for sending data to the set VGA register port. ///
/// 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 { inline fn sendData(data: u8) void {
arch.outb(PORT_DATA, data); 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. /// Get data from a set VGA register port.
inline fn sendPortData(port: u8, data: u8) void { ///
sendPort(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); sendData(data);
} }
/// A inline function for getting data from a set VGA register port. ///
inline fn getData() u8 { /// Set the VGA register port to read from and get the data from that VGA register port.
return arch.inb(PORT_DATA); ///
/// 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();
} }
/// ///
@ -178,19 +237,16 @@ pub fn updateCursor(x: u16, y: u16) void {
/// The linear cursor position. /// The linear cursor position.
/// ///
pub fn getCursor() u16 { pub fn getCursor() u16 {
var cursor: u16 = 0; var cursor = u16(0);
sendPort(REG_CURSOR_LOCATION_LOW); cursor |= u16(getPortData(REG_CURSOR_LOCATION_LOW));
cursor |= u16(getData()); cursor |= u16(getPortData(REG_CURSOR_LOCATION_HIGH)) << 8;
sendPort(REG_CURSOR_LOCATION_HIGH);
cursor |= u16(getData()) << 8;
return cursor; return cursor;
} }
/// ///
/// Enables the blinking cursor to that is is visible. /// Enables the blinking cursor so that is is visible.
/// ///
pub fn enableCursor() void { pub fn enableCursor() void {
sendPortData(REG_CURSOR_START, cursor_scanline_start); sendPortData(REG_CURSOR_START, cursor_scanline_start);
@ -198,7 +254,7 @@ pub fn enableCursor() void {
} }
/// ///
/// Disables the blinking cursor to that is is visible. /// Disables the blinking cursor so that is is invisible.
/// ///
pub fn disableCursor() void { pub fn disableCursor() void {
sendPortData(REG_CURSOR_START, CURSOR_DISABLE); sendPortData(REG_CURSOR_START, CURSOR_DISABLE);
@ -230,17 +286,23 @@ pub fn setCursorShape(shape: CursorShape) void {
/// Initialise the VGA text mode. This sets the cursor and underline shape. /// Initialise the VGA text mode. This sets the cursor and underline shape.
/// ///
pub fn init() void { pub fn init() void {
log.logInfo("Init vga\n");
// Set the maximum scan line to 0x0F // Set the maximum scan line to 0x0F
sendPortData(REG_MAXIMUM_SCAN_LINE, CURSOR_SCANLINE_END); sendPortData(REG_MAXIMUM_SCAN_LINE, CURSOR_SCANLINE_END);
// Set by default the underline cursor // Set by default the underline cursor
setCursorShape(CursorShape.UNDERLINE); setCursorShape(CursorShape.UNDERLINE);
log.logInfo("Done\n");
if (build_options.rt_test) runtimeTests();
} }
test "entryColour" { test "entryColour" {
var fg: u4 = COLOUR_BLACK; var fg = COLOUR_BLACK;
var bg: u4 = COLOUR_BLACK; var bg = COLOUR_BLACK;
var res: u8 = entryColour(fg, bg); var res = entryColour(fg, bg);
expectEqual(u8(0x00), res); expectEqual(u8(0x00), res);
fg = COLOUR_LIGHT_GREEN; fg = COLOUR_LIGHT_GREEN;
@ -260,11 +322,11 @@ test "entryColour" {
} }
test "entry" { test "entry" {
var colour: u8 = entryColour(COLOUR_BROWN, COLOUR_LIGHT_GREEN); const colour = entryColour(COLOUR_BROWN, COLOUR_LIGHT_GREEN);
expectEqual(u8(0xA6), colour); expectEqual(u8(0xA6), colour);
// Character '0' is 0x30 // Character '0' is 0x30
var video_entry: u16 = entry('0', colour); var video_entry = entry('0', colour);
expectEqual(u16(0xA630), video_entry); expectEqual(u16(0xA630), video_entry);
video_entry = entry(0x55, colour); video_entry = entry(0x55, colour);
@ -272,12 +334,12 @@ test "entry" {
} }
test "updateCursor width out of bounds" { test "updateCursor width out of bounds" {
const x: u16 = WIDTH; const x = WIDTH;
const y: u16 = 0; const y = 0;
const max_cursor: u16 = (HEIGHT - 1) * WIDTH + (WIDTH - 1); const max_cursor = (HEIGHT - 1) * WIDTH + (WIDTH - 1);
const expected_upper: u8 = @truncate(u8, (max_cursor >> 8) & 0x00FF); const expected_upper = @truncate(u8, (max_cursor >> 8) & 0x00FF);
const expected_lower: u8 = @truncate(u8, max_cursor & 0x00FF); const expected_lower = @truncate(u8, max_cursor & 0x00FF);
arch.initTest(); arch.initTest();
defer arch.freeTest(); defer arch.freeTest();
@ -289,12 +351,12 @@ test "updateCursor width out of bounds" {
} }
test "updateCursor height out of bounds" { test "updateCursor height out of bounds" {
const x: u16 = 0; const x = 0;
const y: u16 = HEIGHT; const y = HEIGHT;
const max_cursor: u16 = (HEIGHT - 1) * WIDTH + (WIDTH - 1); const max_cursor = (HEIGHT - 1) * WIDTH + (WIDTH - 1);
const expected_upper: u8 = @truncate(u8, (max_cursor >> 8) & 0x00FF); const expected_upper = @truncate(u8, (max_cursor >> 8) & 0x00FF);
const expected_lower: u8 = @truncate(u8, max_cursor & 0x00FF); const expected_lower = @truncate(u8, max_cursor & 0x00FF);
arch.initTest(); arch.initTest();
defer arch.freeTest(); defer arch.freeTest();
@ -306,12 +368,12 @@ test "updateCursor height out of bounds" {
} }
test "updateCursor width and height out of bounds" { test "updateCursor width and height out of bounds" {
const x: u16 = WIDTH; const x = WIDTH;
const y: u16 = HEIGHT; const y = HEIGHT;
const max_cursor: u16 = (HEIGHT - 1) * WIDTH + (WIDTH - 1); const max_cursor = (HEIGHT - 1) * WIDTH + (WIDTH - 1);
const expected_upper: u8 = @truncate(u8, (max_cursor >> 8) & 0x00FF); const expected_upper = @truncate(u8, (max_cursor >> 8) & 0x00FF);
const expected_lower: u8 = @truncate(u8, max_cursor & 0x00FF); const expected_lower = @truncate(u8, max_cursor & 0x00FF);
arch.initTest(); arch.initTest();
defer arch.freeTest(); defer arch.freeTest();
@ -323,12 +385,12 @@ test "updateCursor width and height out of bounds" {
} }
test "updateCursor width-1 and height out of bounds" { test "updateCursor width-1 and height out of bounds" {
const x: u16 = WIDTH - 1; const x = WIDTH - 1;
const y: u16 = HEIGHT; const y = HEIGHT;
const max_cursor: u16 = (HEIGHT - 1) * WIDTH + (WIDTH - 1); const max_cursor = (HEIGHT - 1) * WIDTH + (WIDTH - 1);
const expected_upper: u8 = @truncate(u8, (max_cursor >> 8) & 0x00FF); const expected_upper = @truncate(u8, (max_cursor >> 8) & 0x00FF);
const expected_lower: u8 = @truncate(u8, max_cursor & 0x00FF); const expected_lower = @truncate(u8, max_cursor & 0x00FF);
arch.initTest(); arch.initTest();
defer arch.freeTest(); defer arch.freeTest();
@ -340,12 +402,12 @@ test "updateCursor width-1 and height out of bounds" {
} }
test "updateCursor width and height-1 out of bounds" { test "updateCursor width and height-1 out of bounds" {
const x: u16 = WIDTH; const x = WIDTH;
const y: u16 = HEIGHT - 1; const y = HEIGHT - 1;
const max_cursor: u16 = (HEIGHT - 1) * WIDTH + (WIDTH - 1); const max_cursor = (HEIGHT - 1) * WIDTH + (WIDTH - 1);
const expected_upper: u8 = @truncate(u8, (max_cursor >> 8) & 0x00FF); const expected_upper = @truncate(u8, (max_cursor >> 8) & 0x00FF);
const expected_lower: u8 = @truncate(u8, max_cursor & 0x00FF); const expected_lower = @truncate(u8, max_cursor & 0x00FF);
arch.initTest(); arch.initTest();
defer arch.freeTest(); defer arch.freeTest();
@ -357,12 +419,12 @@ test "updateCursor width and height-1 out of bounds" {
} }
test "updateCursor in bounds" { test "updateCursor in bounds" {
var x: u16 = 0x000A; var x = u8(0x0A);
var y: u16 = 0x000A; var y = u8(0x0A);
const expected: u16 = y * WIDTH + x; const expected = y * WIDTH + x;
var expected_upper: u8 = @truncate(u8, (expected >> 8) & 0x00FF); var expected_upper = @truncate(u8, (expected >> 8) & 0x00FF);
var expected_lower: u8 = @truncate(u8, expected & 0x00FF); var expected_lower = @truncate(u8, expected & 0x00FF);
arch.initTest(); arch.initTest();
defer arch.freeTest(); defer arch.freeTest();
@ -373,44 +435,38 @@ test "updateCursor in bounds" {
} }
test "getCursor 1: 10" { test "getCursor 1: 10" {
const expect: u16 = u16(10); const expect = u16(10);
// Mocking out the arch.outb and arch.inb calls for getting the hardware cursor: // Mocking out the arch.outb and arch.inb calls for getting the hardware cursor:
arch.initTest(); arch.initTest();
defer arch.freeTest(); defer arch.freeTest();
arch.addTestParams("outb", PORT_ADDRESS, REG_CURSOR_LOCATION_LOW); arch.addTestParams("outb", PORT_ADDRESS, REG_CURSOR_LOCATION_LOW);
arch.addTestParams("inb", PORT_DATA, u8(10)); arch.addTestParams("inb", PORT_DATA, u8(10));
arch.addTestParams("outb", PORT_ADDRESS, REG_CURSOR_LOCATION_HIGH); arch.addTestParams("outb", PORT_ADDRESS, REG_CURSOR_LOCATION_HIGH);
arch.addTestParams("inb", PORT_DATA, u8(0)); arch.addTestParams("inb", PORT_DATA, u8(0));
const actual: u16 = getCursor(); const actual = getCursor();
expectEqual(expect, actual); expectEqual(expect, actual);
} }
test "getCursor 2: 0xBEEF" { test "getCursor 2: 0xBEEF" {
const expect: u16 = u16(0xBEEF); const expect = u16(0xBEEF);
// Mocking out the arch.outb and arch.inb calls for getting the hardware cursor: // Mocking out the arch.outb and arch.inb calls for getting the hardware cursor:
arch.initTest(); arch.initTest();
defer arch.freeTest(); defer arch.freeTest();
arch.addTestParams("outb", PORT_ADDRESS, REG_CURSOR_LOCATION_LOW); arch.addTestParams("outb", PORT_ADDRESS, REG_CURSOR_LOCATION_LOW);
arch.addTestParams("inb", PORT_DATA, u8(0xEF)); arch.addTestParams("inb", PORT_DATA, u8(0xEF));
arch.addTestParams("outb", PORT_ADDRESS, REG_CURSOR_LOCATION_HIGH); arch.addTestParams("outb", PORT_ADDRESS, REG_CURSOR_LOCATION_HIGH);
arch.addTestParams("inb", PORT_DATA, u8(0xBE)); arch.addTestParams("inb", PORT_DATA, u8(0xBE));
const actual: u16 = getCursor(); const actual = getCursor();
expectEqual(expect, actual); expectEqual(expect, actual);
} }
test "enableCursor all" { test "enableCursor" {
arch.initTest(); arch.initTest();
defer arch.freeTest(); defer arch.freeTest();
@ -424,7 +480,7 @@ test "enableCursor all" {
enableCursor(); enableCursor();
} }
test "disableCursor all" { test "disableCursor" {
arch.initTest(); arch.initTest();
defer arch.freeTest(); defer arch.freeTest();
@ -457,7 +513,7 @@ test "setCursorShape BLOCK" {
setCursorShape(CursorShape.BLOCK); setCursorShape(CursorShape.BLOCK);
} }
test "init all" { test "init" {
arch.initTest(); arch.initTest();
defer arch.freeTest(); defer arch.freeTest();
@ -468,3 +524,76 @@ test "init all" {
init(); 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();
}

View file

@ -1,4 +1,4 @@
def getTestCases(TestCase): def get_test_cases(TestCase):
return [ return [
TestCase("GDT init", [r"Init gdt", r"Done"]), TestCase("GDT init", [r"Init gdt", r"Done"]),
TestCase("GDT tests", [r"GDT: Tested loading GDT"]), TestCase("GDT tests", [r"GDT: Tested loading GDT"]),

View file

@ -1,14 +1,17 @@
const mock_framework = @import("mock_framework.zig"); const mock_framework = @import("mock_framework.zig");
pub const initTest = mock_framework.initTest;
pub const freeTest = mock_framework.freeTest;
pub const addTestParams = mock_framework.addTestParams;
pub const addConsumeFunction = mock_framework.addConsumeFunction;
pub const addRepeatFunction = mock_framework.addRepeatFunction;
pub const Level = enum { pub const Level = enum {
INFO, INFO,
DEBUG, DEBUG,
WARNING, WARNING,
ERROR ERROR,
}; };
fn logCallback(context: void, str: []const u8) anyerror!void {}
pub fn log(comptime level: Level, comptime format: []const u8, args: ...) void { pub fn log(comptime level: Level, comptime format: []const u8, args: ...) void {
//return mock_framework.performAction("log", void, level, format, args); //return mock_framework.performAction("log", void, level, format, args);
} }
@ -28,23 +31,3 @@ pub fn logWarning(comptime format: []const u8, args: ...) void {
pub fn logError(comptime format: []const u8, args: ...) void { pub fn logError(comptime format: []const u8, args: ...) void {
//return mock_framework.performAction("logError", void, format, args); //return mock_framework.performAction("logError", void, format, args);
} }
pub fn addRepeatFunction(comptime fun_name: []const u8, function: var) void {
mock_framework.addRepeatFunction(fun_name, function);
}
pub fn addTestFunction(comptime fun_name: []const u8, function: var) void {
mock_framework.addRepeatFunction(fun_name, function);
}
pub fn addTestParams(comptime fun_name: []const u8, params: ...) void {
mock_framework.addTestParams(fun_name, params);
}
pub fn initTest() void {
mock_framework.initTest();
}
pub fn freeTest() void {
mock_framework.freeTest();
}

View file

@ -18,6 +18,20 @@ def test_failure(case, exp, expected_idx, found):
def test_pass(case, exp, expected_idx, found): def test_pass(case, exp, expected_idx, found):
print("PASS: %s #%d, expected '%s', found '%s'" %(case.name, expected_idx + 1, exp, found)) print("PASS: %s #%d, expected '%s', found '%s'" %(case.name, expected_idx + 1, exp, found))
def get_pre_archinit_cases():
return [
TestCase("Arch init starts", [r"Init arch \w+"])
]
def get_post_archinit_cases():
return [
TestCase("Arch init finishes", [r"Arch init done"]),
TestCase("VGA init", [r"Init vga", r"Done"]),
TestCase("VGA tests", [r"VGA: Tested max scan line", r"VGA: Tested cursor shape", r"VGA: Tested updating cursor"]),
TestCase("TTY init", [r"Init tty", r"Done"]),
TestCase("Init finishes", [r"Init done"])
]
if __name__ == "__main__": if __name__ == "__main__":
arch = sys.argv[1] arch = sys.argv[1]
zig_path = sys.argv[2] zig_path = sys.argv[2]
@ -25,18 +39,10 @@ if __name__ == "__main__":
arch_module = importlib.util.module_from_spec(spec) arch_module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(arch_module) spec.loader.exec_module(arch_module)
# The list of log statements to look for before arch init is called # The list of log statements to look for before arch init is called +
pre_archinit_cases = [ # All log statements to look for, including the arch-specific ones +
TestCase("Arch init starts", [r"Init arch \w+"])
]
# The list of log statements to look for after arch init is called # The list of log statements to look for after arch init is called
post_archinit_cases = [ cases = get_pre_archinit_cases() + arch_module.get_test_cases(TestCase) + get_post_archinit_cases()
TestCase("Arch init finishes", [r"Arch init done"]),
TestCase("TTY init", [r"Init tty", r"Done"]),
TestCase("Init finishes", [r"Init done"])
]
# All log statements to look for, including the arch-specific ones
cases = pre_archinit_cases + arch_module.getTestCases(TestCase) + post_archinit_cases
if len(cases) > 0: if len(cases) > 0:
proc = subprocess.Popen(zig_path + " build run -Drt-test=true", stdout=subprocess.PIPE, shell=True) proc = subprocess.Popen(zig_path + " build run -Drt-test=true", stdout=subprocess.PIPE, shell=True)
@ -47,7 +53,7 @@ if __name__ == "__main__":
expected_idx = 0 expected_idx = 0
# Go through the expected log messages # Go through the expected log messages
while expected_idx < len(case.expected): while expected_idx < len(case.expected):
e = "\[INFO\] " + case.expected[expected_idx] e = r"\[INFO\] " + case.expected[expected_idx]
line = proc.stdout.readline().decode("utf-8") line = proc.stdout.readline().decode("utf-8")
if not line: if not line:
break break