Add TTY and VGA interface (#20)
Line endings Removed redundant files Line ending
This commit is contained in:
		
							parent
							
								
									0196ef7824
								
							
						
					
					
						commit
						ad2204cab2
					
				
					 7 changed files with 3016 additions and 98 deletions
				
			
		|  | @ -53,6 +53,7 @@ fn buildTest(b: *Builder, src_path: []const u8) void { | ||||||
|         var file_src = concat(b.allocator, src_path2.toSlice(), file) catch unreachable; |         var file_src = concat(b.allocator, src_path2.toSlice(), file) catch unreachable; | ||||||
|         file_src.append(".zig") catch unreachable; |         file_src.append(".zig") catch unreachable; | ||||||
|         const tst = b.addTest(file_src.toSlice()); |         const tst = b.addTest(file_src.toSlice()); | ||||||
|  |         tst.setMainPkgPath("."); | ||||||
|         step.dependOn(&tst.step); |         step.dependOn(&tst.step); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -1,2 +1,32 @@ | ||||||
|  | // Zig version: 0.4.0 | ||||||
|  | 
 | ||||||
|  | /// | ||||||
| /// Initialise the architecture | /// Initialise the architecture | ||||||
|  | /// | ||||||
| pub extern fn init() void; | pub extern fn init() void; | ||||||
|  | 
 | ||||||
|  | /// | ||||||
|  | /// Inline assembly to write to a given port with a byte of data. | ||||||
|  | /// | ||||||
|  | /// Arguments: | ||||||
|  | ///     IN port: u16 - The port to write to. | ||||||
|  | ///     IN data: u8  - The byte of data that will be sent. | ||||||
|  | /// | ||||||
|  | pub extern fn outb(port: u16, data: u8) void; | ||||||
|  | 
 | ||||||
|  | /// | ||||||
|  | /// Inline assembly that reads data from a given port and returns its value. | ||||||
|  | /// | ||||||
|  | /// Arguments: | ||||||
|  | ///     IN port: u16 - The port to read data from. | ||||||
|  | /// | ||||||
|  | /// Return: | ||||||
|  | ///     The data that the port returns. | ||||||
|  | /// | ||||||
|  | pub extern fn inb(port: u16) u8; | ||||||
|  | 
 | ||||||
|  | /// | ||||||
|  | /// A simple way of waiting for I/O event to happen by doing an I/O event to flush the I/O | ||||||
|  | /// event being waited. | ||||||
|  | /// | ||||||
|  | pub extern fn ioWait() void; | ||||||
|  |  | ||||||
|  | @ -1,3 +1,17 @@ | ||||||
|  | // Zig version: 0.4.0 | ||||||
|  | 
 | ||||||
|  | const is_test = @import("builtin").is_test; | ||||||
|  | 
 | ||||||
|  | const ALIGN = 1 << 0; | ||||||
|  | const MEMINFO = 1 << 1; | ||||||
|  | const MAGIC = 0x1BADB002; | ||||||
|  | const FLAGS = ALIGN | MEMINFO; | ||||||
|  | 
 | ||||||
|  | const KERNEL_ADDR_OFFSET = 0xC0000000; | ||||||
|  | const KERNEL_PAGE_NUMBER = KERNEL_ADDR_OFFSET >> 22; | ||||||
|  | // The number of pages occupied by the kernel, will need to be increased as we add a heap etc. | ||||||
|  | const KERNEL_NUM_PAGES = 1; | ||||||
|  | 
 | ||||||
| extern fn kmain() void; | extern fn kmain() void; | ||||||
| 
 | 
 | ||||||
| const MultiBoot = packed struct { | const MultiBoot = packed struct { | ||||||
|  | @ -6,22 +20,12 @@ const MultiBoot = packed struct { | ||||||
|     checksum: i32, |     checksum: i32, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| const ALIGN = 1 << 0; |  | ||||||
| const MEMINFO = 1 << 1; |  | ||||||
| const MAGIC = 0x1BADB002; |  | ||||||
| const FLAGS = ALIGN | MEMINFO; |  | ||||||
| 
 |  | ||||||
| export var multiboot align(4) linksection(".rodata.boot") = MultiBoot { | export var multiboot align(4) linksection(".rodata.boot") = MultiBoot { | ||||||
|     .magic = MAGIC, |     .magic = MAGIC, | ||||||
|     .flags = FLAGS, |     .flags = FLAGS, | ||||||
|     .checksum = -(MAGIC + FLAGS), |     .checksum = -(MAGIC + FLAGS), | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| const KERNEL_ADDR_OFFSET = 0xC0000000; |  | ||||||
| const KERNEL_PAGE_NUMBER = KERNEL_ADDR_OFFSET >> 22; |  | ||||||
| // The number of pages occupied by the kernel, will need to be increased as we add a heap etc. |  | ||||||
| const KERNEL_NUM_PAGES = 1; |  | ||||||
| 
 |  | ||||||
| // The initial page directory used for booting into the higher half. Should be overwritten later | // The initial page directory used for booting into the higher half. Should be overwritten later | ||||||
| export var boot_page_directory: [1024]u32 align(4096) linksection(".rodata.boot") = init: { | export var boot_page_directory: [1024]u32 align(4096) linksection(".rodata.boot") = init: { | ||||||
|     // Increase max number of branches done by comptime evaluator |     // Increase max number of branches done by comptime evaluator | ||||||
|  | @ -35,7 +39,7 @@ export var boot_page_directory: [1024]u32 align(4096) linksection(".rodata.boot" | ||||||
|     var i = 0; |     var i = 0; | ||||||
|     var idx = 1; |     var idx = 1; | ||||||
| 
 | 
 | ||||||
|     // Fill preceding pages with zeroes. May be unecessary but incurs no runtime cost |     // Fill preceding pages with zeroes. May be unnecessary but incurs no runtime cost | ||||||
|     while (i < KERNEL_PAGE_NUMBER - 1) : ({ |     while (i < KERNEL_PAGE_NUMBER - 1) : ({ | ||||||
|         i += 1; |         i += 1; | ||||||
|         idx += 1; |         idx += 1; | ||||||
|  | @ -51,7 +55,7 @@ export var boot_page_directory: [1024]u32 align(4096) linksection(".rodata.boot" | ||||||
|     }) { |     }) { | ||||||
|         dir[idx] = 0x00000083 | (i << 22); |         dir[idx] = 0x00000083 | (i << 22); | ||||||
|     } |     } | ||||||
|     // Fill suceeding pages with zeroes. May be unecessary but incurs no runtime cost |     // Fill succeeding pages with zeroes. May be unnecessary but incurs no runtime cost | ||||||
|     i = 0; |     i = 0; | ||||||
|     while (i < 1024 - KERNEL_PAGE_NUMBER - KERNEL_NUM_PAGES) : ({ |     while (i < 1024 - KERNEL_PAGE_NUMBER - KERNEL_NUM_PAGES) : ({ | ||||||
|         i += 1; |         i += 1; | ||||||
|  | @ -107,4 +111,43 @@ export nakedcc fn start_higher_half() noreturn { | ||||||
|     while (true) {} |     while (true) {} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | /// | ||||||
|  | /// Initialise the architecture | ||||||
|  | /// | ||||||
| export fn init() void {} | export fn init() void {} | ||||||
|  | 
 | ||||||
|  | /// | ||||||
|  | /// Inline assembly to write to a given port with a byte of data. | ||||||
|  | /// | ||||||
|  | /// Arguments: | ||||||
|  | ///     IN port: u16 - The port to write to. | ||||||
|  | ///     IN data: u8  - The byte of data that will be sent. | ||||||
|  | /// | ||||||
|  | export fn outb(port: u16, data: u8) void { | ||||||
|  |     asm volatile ("outb %[data], %[port]" | ||||||
|  |         : | ||||||
|  |         : [port] "{dx}" (port), [data] "{al}" (data)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /// | ||||||
|  | /// Inline assembly that reads data from a given port and returns its value. | ||||||
|  | /// | ||||||
|  | /// Arguments: | ||||||
|  | ///     IN port: u16 - The port to read data from. | ||||||
|  | /// | ||||||
|  | /// Return: | ||||||
|  | ///     The data that the port returns. | ||||||
|  | /// | ||||||
|  | export fn inb(port: u16) u8 { | ||||||
|  |     return asm volatile ("inb %[port], %[result]" | ||||||
|  |         : [result] "={al}" (-> u8) | ||||||
|  |         : [port] "N{dx}" (port)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /// | ||||||
|  | /// A simple way of waiting for I/O event to happen by doing an I/O event to flush the I/O | ||||||
|  | /// event being waited. | ||||||
|  | /// | ||||||
|  | export fn ioWait() void { | ||||||
|  |     outb(0x80, 0); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -1,17 +1,14 @@ | ||||||
| // | // Zig version: 0.4.0 | ||||||
| // kmain | 
 | ||||||
| // Zig version: |  | ||||||
| // Author: DrDeano |  | ||||||
| // Date: 2019-03-30 |  | ||||||
| // |  | ||||||
| const builtin = @import("builtin"); | const builtin = @import("builtin"); | ||||||
| const arch = @import("arch.zig"); | const arch = if (builtin.is_test) @import("../../test/kernel/arch_mock.zig") else @import("arch.zig"); | ||||||
| const multiboot = @import("multiboot.zig"); | const multiboot = @import("multiboot.zig"); | ||||||
|  | const tty = @import("tty.zig"); | ||||||
|  | const vga = @import("vga.zig"); | ||||||
| 
 | 
 | ||||||
| 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); | ||||||
|     terminal.write("KERNEL PANIC: "); |     tty.print("\nKERNEL PANIC: {}\n", msg); | ||||||
|     terminal.write(msg); |  | ||||||
|     while (true) {} |     while (true) {} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -19,81 +16,8 @@ pub export fn kmain(mb_info: *multiboot.multiboot_info_t, mb_magic: u32) void { | ||||||
|     if (mb_magic == multiboot.MULTIBOOT_BOOTLOADER_MAGIC) { |     if (mb_magic == multiboot.MULTIBOOT_BOOTLOADER_MAGIC) { | ||||||
|         // Booted with compatible bootloader |         // Booted with compatible bootloader | ||||||
|         arch.init(); |         arch.init(); | ||||||
|         terminal.initialize(); |         vga.init(); | ||||||
|         terminal.write("Hello, kernel World!"); |         tty.init(); | ||||||
|  |         tty.print("\nHello Pluto from kernel :)\n"); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 |  | ||||||
| // Hardware text mode color constants |  | ||||||
| const VGA_COLOUR = enum(u8) { |  | ||||||
|     VGA_COLOUR_BLACK, |  | ||||||
|     VGA_COLOUR_BLUE, |  | ||||||
|     VGA_COLOUR_GREEN, |  | ||||||
|     VGA_COLOUR_CYAN, |  | ||||||
|     VGA_COLOUR_RED, |  | ||||||
|     VGA_COLOUR_MAGENTA, |  | ||||||
|     VGA_COLOUR_BROWN, |  | ||||||
|     VGA_COLOUR_LIGHT_GREY, |  | ||||||
|     VGA_COLOUR_DARK_GREY, |  | ||||||
|     VGA_COLOUR_LIGHT_BLUE, |  | ||||||
|     VGA_COLOUR_LIGHT_GREEN, |  | ||||||
|     VGA_COLOUR_LIGHT_CYAN, |  | ||||||
|     VGA_COLOUR_LIGHT_RED, |  | ||||||
|     VGA_COLOUR_LIGHT_MAGENTA, |  | ||||||
|     VGA_COLOUR_LIGHT_BROWN, |  | ||||||
|     VGA_COLOUR_WHITE, |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| fn vga_entry_colour(fg: VGA_COLOUR, bg: VGA_COLOUR) u8 { |  | ||||||
|     return @enumToInt(fg) | (@enumToInt(bg) << 4); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| fn vga_entry(uc: u8, colour: u8) u16 { |  | ||||||
|     return u16(uc) | (u16(colour) << 8); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| const VGA_WIDTH = 80; |  | ||||||
| const VGA_HEIGHT = 25; |  | ||||||
| 
 |  | ||||||
| const terminal = struct { |  | ||||||
|     var row = usize(0); |  | ||||||
|     var column = usize(0); |  | ||||||
|     var colour = vga_entry_colour(VGA_COLOUR.VGA_COLOUR_LIGHT_GREY, VGA_COLOUR.VGA_COLOUR_BLACK); |  | ||||||
| 
 |  | ||||||
|     const buffer = @intToPtr([*]volatile u16, 0xC00B8000); |  | ||||||
| 
 |  | ||||||
|     fn initialize() void { |  | ||||||
|         var y = usize(0); |  | ||||||
|         while (y < VGA_HEIGHT) : (y += 1) { |  | ||||||
|             var x = usize(0); |  | ||||||
|             while (x < VGA_WIDTH) : (x += 1) { |  | ||||||
|                 putCharAt(' ', colour, x, y); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fn setColour(new_colour: u8) void { |  | ||||||
|         colour = new_colour; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fn putCharAt(c: u8, new_colour: u8, x: usize, y: usize) void { |  | ||||||
|         const index = y * VGA_WIDTH + x; |  | ||||||
|         buffer[index] = vga_entry(c, new_colour); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fn putChar(c: u8) void { |  | ||||||
|         putCharAt(c, colour, column, row); |  | ||||||
|         column += 1; |  | ||||||
|         if (column == VGA_WIDTH) { |  | ||||||
|             column = 0; |  | ||||||
|             row += 1; |  | ||||||
|             if (row == VGA_HEIGHT) |  | ||||||
|                 row = 0; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fn write(data: []const u8) void { |  | ||||||
|         for (data) |c| |  | ||||||
|             putChar(c); |  | ||||||
|     } |  | ||||||
| }; |  | ||||||
|  |  | ||||||
							
								
								
									
										2475
									
								
								src/kernel/tty.zig
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										2475
									
								
								src/kernel/tty.zig
									
										
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										413
									
								
								src/kernel/vga.zig
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										413
									
								
								src/kernel/vga.zig
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,413 @@ | ||||||
|  | // Zig version: 0.4.0 | ||||||
|  | 
 | ||||||
|  | const builtin = @import("builtin"); | ||||||
|  | const arch = if (builtin.is_test) @import("../../test/kernel/arch_mock.zig") | ||||||
|  |              else @import("arch.zig"); | ||||||
|  | 
 | ||||||
|  | const expectEqual = @import("std").testing.expectEqual; | ||||||
|  | const warn = @import("std").debug.warn; | ||||||
|  | 
 | ||||||
|  | /// 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 register for 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; | ||||||
|  | 
 | ||||||
|  | /// The command for setting the start of the cursor scan line. | ||||||
|  | const REG_CURSOR_START: u8                    = 0x0A; | ||||||
|  | 
 | ||||||
|  | /// The command for setting the end of the cursor scan line. | ||||||
|  | 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 upper byte of the cursor's linear location. | ||||||
|  | const REG_CURSOR_LOCATION_HIGH: u8            = 0x0E; | ||||||
|  | 
 | ||||||
|  | /// The command for setting the lower byte of the cursor's linear location. | ||||||
|  | const REG_CURSOR_LOCATION_LOW: u8             = 0x0F; | ||||||
|  | const REG_VERTICAL_RETRACE_START: u8          = 0x10; | ||||||
|  | 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. | ||||||
|  | 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; | ||||||
|  | 
 | ||||||
|  | pub const WIDTH: u16        = 80; | ||||||
|  | pub const HEIGHT: u16       = 25; | ||||||
|  | 
 | ||||||
|  | // The set of colours that VGA supports and can display for the foreground and background. | ||||||
|  | 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. | ||||||
|  | pub const CursorShape = enum(u1) { | ||||||
|  |     /// 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; | ||||||
|  | 
 | ||||||
|  | /// | ||||||
|  | /// 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: | ||||||
|  | ///     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: | ||||||
|  | ///     IN uc: u8     - The character. | ||||||
|  | ///     IN colour: u8 - The foreground and background colour. | ||||||
|  | /// | ||||||
|  | /// Return: | ||||||
|  | ///     The VGA entry. | ||||||
|  | /// | ||||||
|  | pub fn entry(uc: u8, colour: u8) u16 { | ||||||
|  |     return u16(uc) | u16(colour) << 8; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /// | ||||||
|  | /// Update the hardware on screen cursor. | ||||||
|  | /// | ||||||
|  | /// Arguments: | ||||||
|  | ///     IN x: u16 - The horizontal position of the cursor. | ||||||
|  | ///     IN y: u16 - The vertical position of the cursor. | ||||||
|  | /// | ||||||
|  | /// Return: | ||||||
|  | ///     The VGA entry. | ||||||
|  | /// | ||||||
|  | pub fn updateCursor(x: u16, y: u16) void { | ||||||
|  |     var pos: u16 = undefined; | ||||||
|  |     var pos_upper: u16 = undefined; | ||||||
|  |     var pos_lower: u16 = undefined; | ||||||
|  | 
 | ||||||
|  |     // Make sure new cursor position is within the screen | ||||||
|  |     if (x < HEIGHT and y < WIDTH) { | ||||||
|  |         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); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pos_upper = (pos >> 8) & 0x00FF; | ||||||
|  |     pos_lower = pos & 0x00FF; | ||||||
|  | 
 | ||||||
|  |     // Set the cursor position | ||||||
|  |     arch.outb(PORT_ADDRESS, REG_CURSOR_LOCATION_LOW); | ||||||
|  |     arch.outb(PORT_DATA, @truncate(u8, pos_lower)); | ||||||
|  | 
 | ||||||
|  |     arch.outb(PORT_ADDRESS, REG_CURSOR_LOCATION_HIGH); | ||||||
|  |     arch.outb(PORT_DATA, @truncate(u8, pos_upper)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /// | ||||||
|  | /// Get the hardware cursor position. | ||||||
|  | /// | ||||||
|  | /// Return: | ||||||
|  | ///     The linear cursor position. | ||||||
|  | /// | ||||||
|  | pub fn getCursor() u16 { | ||||||
|  |     var cursor: u16 = 0; | ||||||
|  | 
 | ||||||
|  |     arch.outb(PORT_ADDRESS, REG_CURSOR_LOCATION_LOW); | ||||||
|  |     cursor |= u16(arch.inb(PORT_DATA)); | ||||||
|  | 
 | ||||||
|  |     arch.outb(PORT_ADDRESS, REG_CURSOR_LOCATION_HIGH); | ||||||
|  |     cursor |= u16(arch.inb(PORT_DATA)) << 8; | ||||||
|  | 
 | ||||||
|  |     return cursor; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /// | ||||||
|  | /// Enables the blinking cursor to that is is visible. | ||||||
|  | /// | ||||||
|  | pub fn enableCursor() void { | ||||||
|  |     arch.outb(PORT_ADDRESS, REG_CURSOR_START); | ||||||
|  |     arch.outb(PORT_DATA, cursor_scanline_start); | ||||||
|  | 
 | ||||||
|  |     arch.outb(PORT_ADDRESS, REG_CURSOR_END); | ||||||
|  |     arch.outb(PORT_DATA, cursor_scanline_end); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /// | ||||||
|  | /// Disables the blinking cursor to that is is visible. | ||||||
|  | /// | ||||||
|  | pub fn disableCursor() void { | ||||||
|  |     arch.outb(PORT_ADDRESS, REG_CURSOR_START); | ||||||
|  |     arch.outb(PORT_DATA, CURSOR_DISABLE); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /// | ||||||
|  | /// Set the shape of the cursor. This can be and underline or block shape. | ||||||
|  | /// | ||||||
|  | /// Arguments: | ||||||
|  | ///     IN shape: CURSOR_SHAPE - The enum CURSOR_SHAPE that selects which shape to use. | ||||||
|  | /// | ||||||
|  | pub fn setCursorShape(shape: CursorShape) void { | ||||||
|  |     switch(shape) { | ||||||
|  |         CursorShape.UNDERLINE => { | ||||||
|  |             arch.outb(PORT_ADDRESS, REG_CURSOR_START); | ||||||
|  |             arch.outb(PORT_DATA, CURSOR_SCANLINE_MIDDLE); | ||||||
|  | 
 | ||||||
|  |             arch.outb(PORT_ADDRESS, REG_CURSOR_END); | ||||||
|  |             arch.outb(PORT_DATA, CURSOR_SCANLINE_END); | ||||||
|  | 
 | ||||||
|  |             cursor_scanline_start = CURSOR_SCANLINE_MIDDLE; | ||||||
|  |             cursor_scanline_end = CURSOR_SCANLINE_END; | ||||||
|  |         }, | ||||||
|  |         CursorShape.BLOCK => { | ||||||
|  |             arch.outb(PORT_ADDRESS, REG_CURSOR_START); | ||||||
|  |             arch.outb(PORT_DATA, CURSOR_SCANLINE_START); | ||||||
|  | 
 | ||||||
|  |             arch.outb(PORT_ADDRESS, REG_CURSOR_END); | ||||||
|  |             arch.outb(PORT_DATA, CURSOR_SCANLINE_END); | ||||||
|  | 
 | ||||||
|  |             cursor_scanline_start = CURSOR_SCANLINE_START; | ||||||
|  |             cursor_scanline_end = CURSOR_SCANLINE_END; | ||||||
|  |         }, | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /// | ||||||
|  | /// Initialise the VGA text mode. This sets the cursor and underline shape. | ||||||
|  | /// | ||||||
|  | pub fn init() void { | ||||||
|  |     // Set the maximum scan line to 0x0F | ||||||
|  |     arch.outb(PORT_ADDRESS, REG_MAXIMUM_SCAN_LINE); | ||||||
|  |     arch.outb(PORT_DATA, CURSOR_SCANLINE_END); | ||||||
|  | 
 | ||||||
|  |     // Set by default the underline cursor | ||||||
|  |     setCursorShape(CursorShape.UNDERLINE); | ||||||
|  |     cursor_scanline_start = CURSOR_SCANLINE_MIDDLE; | ||||||
|  |     cursor_scanline_end = CURSOR_SCANLINE_END; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | test "entryColour" { | ||||||
|  |     var fg: u4 = COLOUR_BLACK; | ||||||
|  |     var bg: u4 = COLOUR_BLACK; | ||||||
|  |     var res: u8 = entryColour(fg, bg); | ||||||
|  |     expectEqual(u8(0x00), res); | ||||||
|  | 
 | ||||||
|  |     fg = COLOUR_LIGHT_GREEN; | ||||||
|  |     bg = COLOUR_BLACK; | ||||||
|  |     res = entryColour(fg, bg); | ||||||
|  |     expectEqual(u8(0x0A), res); | ||||||
|  | 
 | ||||||
|  |     fg = COLOUR_BLACK; | ||||||
|  |     bg = COLOUR_LIGHT_GREEN; | ||||||
|  |     res = entryColour(fg, bg); | ||||||
|  |     expectEqual(u8(0xA0), res); | ||||||
|  | 
 | ||||||
|  |     fg = COLOUR_BROWN; | ||||||
|  |     bg = COLOUR_LIGHT_GREEN; | ||||||
|  |     res = entryColour(fg, bg); | ||||||
|  |     expectEqual(u8(0xA6), res); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | test "entry" { | ||||||
|  |     var colour: u8 = entryColour(COLOUR_BROWN, COLOUR_LIGHT_GREEN); | ||||||
|  |     expectEqual(u8(0xA6), colour); | ||||||
|  | 
 | ||||||
|  |     // Character '0' is 0x30 | ||||||
|  |     var video_entry: u16 = entry('0', colour); | ||||||
|  |     expectEqual(u16(0xA630), video_entry); | ||||||
|  | 
 | ||||||
|  |     video_entry = entry(0x55, colour); | ||||||
|  |     expectEqual(u16(0xA655), video_entry); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn testOutOfBounds(x: u16, y: u16) bool { | ||||||
|  |     if(x < HEIGHT and y < WIDTH) { | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
|  |     return false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn testUpperVal(x: u16, y: u16) u16 { | ||||||
|  |     const pos: u16 = x * WIDTH + y; | ||||||
|  |     const pos_upper: u16 = (pos >> 8) & 0x00FF; | ||||||
|  |     return pos_upper; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn testLowerVal(x: u16, y: u16) u16 { | ||||||
|  |     const pos: u16 = x * WIDTH + y; | ||||||
|  |     const pos_lower: u16 = pos & 0x00FF; | ||||||
|  |     return pos_lower; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | test "updateCursor out of bounds" { | ||||||
|  |     var x: u16 = 0; | ||||||
|  |     var y: u16 = 0; | ||||||
|  |     var res: bool = testOutOfBounds(x, y); | ||||||
|  |     expectEqual(true, res); | ||||||
|  | 
 | ||||||
|  |     x = HEIGHT - 1; | ||||||
|  |     res = testOutOfBounds(x, y); | ||||||
|  |     expectEqual(true, res); | ||||||
|  | 
 | ||||||
|  |     y = WIDTH - 1; | ||||||
|  |     res = testOutOfBounds(x, y); | ||||||
|  |     expectEqual(true, res); | ||||||
|  | 
 | ||||||
|  |     x = HEIGHT; | ||||||
|  |     y = WIDTH; | ||||||
|  |     res = testOutOfBounds(x, y); | ||||||
|  |     expectEqual(false, res); | ||||||
|  | 
 | ||||||
|  |     x = HEIGHT - 1; | ||||||
|  |     y = WIDTH; | ||||||
|  |     res = testOutOfBounds(x, y); | ||||||
|  |     expectEqual(false, res); | ||||||
|  | 
 | ||||||
|  |     x = HEIGHT; | ||||||
|  |     y = WIDTH - 1; | ||||||
|  |     res = testOutOfBounds(x, y); | ||||||
|  |     expectEqual(false, res); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | test "updateCursor lower values" { | ||||||
|  |     var x: u16 = 0x0000; | ||||||
|  |     var y: u16 = 0x0000; | ||||||
|  |     var res: u16 = testLowerVal(x, y); | ||||||
|  |     var expected: u16 = 0x0000; | ||||||
|  |     expectEqual(expected, res); | ||||||
|  | 
 | ||||||
|  |     x = 0x0000; | ||||||
|  |     y = 0x000A; | ||||||
|  |     res = testLowerVal(x, y); | ||||||
|  |     expected = 0x000A; | ||||||
|  |     expectEqual(expected, res); | ||||||
|  | 
 | ||||||
|  |     x = 0x000A; | ||||||
|  |     y = 0x0000; | ||||||
|  |     res = testLowerVal(x, y); | ||||||
|  |     expected = 0x0020; | ||||||
|  |     expectEqual(expected, res); | ||||||
|  | 
 | ||||||
|  |     x = 0x000A; | ||||||
|  |     y = 0x000A; | ||||||
|  |     res = testLowerVal(x, y); | ||||||
|  |     expected = 0x002A; | ||||||
|  |     expectEqual(expected, res); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | test "updateCursor upper values" { | ||||||
|  |     var x: u16 = 0x0000; | ||||||
|  |     var y: u16 = 0x0000; | ||||||
|  |     var res: u16 = testUpperVal(x, y); | ||||||
|  |     var expected: u16 = 0x0000; | ||||||
|  |     expectEqual(expected, res); | ||||||
|  | 
 | ||||||
|  |     x = 0x0000; | ||||||
|  |     y = 0x000A; | ||||||
|  |     res = testUpperVal(x, y); | ||||||
|  |     expected = 0x0000; | ||||||
|  |     expectEqual(expected, res); | ||||||
|  | 
 | ||||||
|  |     x = 0x000A; | ||||||
|  |     y = 0x0000; | ||||||
|  |     res = testUpperVal(x, y); | ||||||
|  |     expected = 0x0003; | ||||||
|  |     expectEqual(expected, res); | ||||||
|  | 
 | ||||||
|  |     x = 0x000A; | ||||||
|  |     y = 0x000A; | ||||||
|  |     res = testUpperVal(x, y); | ||||||
|  |     expected = 0x0003; | ||||||
|  |     expectEqual(expected, res); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | test "getCursor all" { | ||||||
|  |     warn(" Waiting for mocking "); | ||||||
|  |     var res = getCursor(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | test "enableCursor all" { | ||||||
|  |     warn(" Waiting for mocking "); | ||||||
|  |     enableCursor(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | test "disableCursor all" { | ||||||
|  |     warn(" Waiting for mocking "); | ||||||
|  |     disableCursor(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | test "setCursorShape all" { | ||||||
|  |     setCursorShape(CursorShape.UNDERLINE); | ||||||
|  |     expectEqual(CURSOR_SCANLINE_MIDDLE, cursor_scanline_start); | ||||||
|  |     expectEqual(CURSOR_SCANLINE_END, cursor_scanline_end); | ||||||
|  | 
 | ||||||
|  |     setCursorShape(CursorShape.BLOCK); | ||||||
|  |     expectEqual(CURSOR_SCANLINE_START, cursor_scanline_start); | ||||||
|  |     expectEqual(CURSOR_SCANLINE_END, cursor_scanline_end); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | test "init all" { | ||||||
|  |     warn(" Waiting for mocking "); | ||||||
|  |     init(); | ||||||
|  |     expectEqual(CURSOR_SCANLINE_MIDDLE, cursor_scanline_start); | ||||||
|  |     expectEqual(CURSOR_SCANLINE_END, cursor_scanline_end); | ||||||
|  | } | ||||||
							
								
								
									
										32
									
								
								test/kernel/arch_mock.zig
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								test/kernel/arch_mock.zig
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,32 @@ | ||||||
|  | // Zig version: 0.4.0 | ||||||
|  | 
 | ||||||
|  | /// | ||||||
|  | /// Initialise the architecture | ||||||
|  | /// | ||||||
|  | pub fn init() void {} | ||||||
|  | 
 | ||||||
|  | /// | ||||||
|  | /// Inline assembly to write to a given port with a byte of data. | ||||||
|  | /// | ||||||
|  | /// Arguments: | ||||||
|  | ///     IN port: u16 - The port to write to. | ||||||
|  | ///     IN data: u8  - The byte of data that will be sent. | ||||||
|  | /// | ||||||
|  | pub fn outb(port: u16, data: u8) void {} | ||||||
|  | 
 | ||||||
|  | /// | ||||||
|  | /// Inline assembly that reads data from a given port and returns its value. | ||||||
|  | /// | ||||||
|  | /// Arguments: | ||||||
|  | ///     IN port: u16 - The port to read data from. | ||||||
|  | /// | ||||||
|  | /// Return: | ||||||
|  | ///     The data that the port returns. | ||||||
|  | /// | ||||||
|  | pub fn inb(port: u16) u8 {return 0;} | ||||||
|  | 
 | ||||||
|  | /// | ||||||
|  | /// A simple way of waiting for I/O event to happen by doing an I/O event to flush the I/O | ||||||
|  | /// event being waited. | ||||||
|  | /// | ||||||
|  | pub fn ioWait() void {} | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Edward Dean
						Edward Dean