From 5394a541b868a191598e59e7107ecc08f28c2af1 Mon Sep 17 00:00:00 2001 From: Fred Hornsey Date: Thu, 5 Jan 2023 23:31:19 -0600 Subject: [PATCH] Intial Support For Mice, test-mouse Program Disabled for now because using the keyboard and mouse at the same time mess up what both are doing. Mouse can be used by building with `make mouse=true` and running `test-mouse`, Also updated README to add this and tweaked other content. --- Makefile | 3 + README.md | 45 ++-- build.zig | 27 +- kernel/platform/interrupts.zig | 11 +- kernel/platform/platform.zig | 2 +- kernel/platform/ps2.zig | 413 +++++++++++++++++++++++++++-- kernel/platform/system_calls.zig | 25 ++ kernel/platform/vbe.zig | 39 ++- kernel/platform/vbe_console.zig | 14 +- kernel/threading.zig | 21 +- libs/georgios/georgios.zig | 7 + libs/georgios/system_calls.zig | 18 ++ libs/utils/Bdf.zig | 4 +- libs/utils/utils.zig | 8 +- misc/qemu.gdb | 7 +- programs/test-mouse/test-mouse.zig | 198 ++++++++++++++ programs/test-prog/test-prog.zig | 5 +- 17 files changed, 760 insertions(+), 87 deletions(-) create mode 100644 programs/test-mouse/test-mouse.zig diff --git a/Makefile b/Makefile index 4a6b921..50a1347 100644 --- a/Makefile +++ b/Makefile @@ -36,6 +36,9 @@ endif ifdef halt_when_done zig_build_args+="-Dhalt_when_done=$(halt_when_done)" endif +ifdef mouse + zig_build_args+="-Dmouse=$(mouse)" +endif all: $(ISO) $(DISK) $(USBDRIVE) diff --git a/README.md b/README.md index f4ad303..da5e6ed 100644 --- a/README.md +++ b/README.md @@ -2,18 +2,14 @@ ![Screenshot of text mode](misc/screenshot1.png) -Georgios (Greek for George, said like *GORE-GEE-OS*) is an operating system I'm -making for fun which currently targets i386/IA-32. The purpose of this project -is to serve as a learning experience. +Georgios (Greek version of the name George, said like *GORE-GEE-OS*) is an +operating system I'm making for fun which currently targets i386/IA-32. The +purpose of this project is to serve as a learning experience. Work in progress graphics mode: https://user-images.githubusercontent.com/5941194/180702578-91270793-c91c-4f24-b7e1-f86bc2b48c53.mp4 -Georgios is so simplistic right that now the most impressive application is a -snake clone. This is probably going to be the case until applications can be -ported. - ## Features ### Working on at least some minimal level @@ -26,22 +22,39 @@ ported. - In-memory filesystem mounted at boot (read/write) - Basic preemptive multitasking between processes that can be loaded from ELF files -- ACPI shutdown (thanks in part to [ACPICA](https://www.acpica.org/)) +- ACPI shutdown using [ACPICA](https://www.acpica.org/) ### Started on, but not really working yet - A graphics mode using VESA BIOS Extensions (VBE) - - This makes use of [libx86emu](https://github.com/wfeldt/libx86emu) to - invoke the BIOS code required to access VBE. - - Currently requires building with `make multiboot_vbe=true` + - This will use [libx86emu](https://github.com/wfeldt/libx86emu) to + invoke the BIOS code required to switch to VBE graphics modes. This doesn't + really work yet though. + - This can be bypassed with `make multiboot_vbe=true`, which has GRUB set a + fixed VBE graphics mode. This is how the demo above was ran. This is not + the default for a number of reasons: + - The major reason is the graphics are slow. This can be seen in the + demo, especially when the Apollo earthrise picture takes a moment to + get drawn on the screen. + - It's a fixed graphics mode when the kernel starts and so nothing gets + printed to the screen until the graphical console is ready. So an error + before this wouldn't get printed, which is a problem when running on + real hardware. + - The graphical console is mostly done but missing things like the cursor + and text rendering is a bit off. - USB 2.0 stack - Porting real applications written in Zig and C - - The applications currently written in Zig are "real", but are using the - freestanding target and are using system calls directly. To be able to use - a Zig or C hello world program without any modification, the standard - libraries would have to be ported and toolchains would have to be modified - to target Georgios properly. + - The applications currently written in Zig are "real" as in they are + compiled and ran separately from the kernel, are running in x86 ring3, and + can't take the whole system down (for the most part). The issue is they are + compiled using the freestanding target. To be able to use a Zig or C hello + world program without any modification, the standard libraries would have to be + ported and toolchains would have to be modified to target Georgios properly. - Freeing the OS from the need of a boot CD +- PS/2 Mouse support + - Can be tried out by building with `make mouse=true` and running + `test-mouse`. This isn't enabled by default becuase currently the keyboard + and mouse cross talk when being used at the same time. ## Building diff --git a/build.zig b/build.zig index abc8b93..318f88c 100644 --- a/build.zig +++ b/build.zig @@ -56,6 +56,8 @@ pub fn build(builder: *std.build.Builder) void { const multiboot_vbe = b.option(bool, "multiboot_vbe", \\Ask the bootloader to switch to a graphics mode for us. ) orelse false; + // TODO: Change default to true when Georgios can properly switch to VBE + // itself. const vbe = b.option(bool, "vbe", \\Use VBE Graphics if possible. ) orelse multiboot_vbe; @@ -74,6 +76,10 @@ pub fn build(builder: *std.build.Builder) void { const halt_when_done = b.option(bool, "halt_when_done", \\Halt instead of shutting down. ) orelse false; + // TODO: Change default to true when mouse and keyboard don't conflict. + const mouse = b.option(bool, "mouse", + \\Enable Mouse Support + ) orelse false; target = b.standardTargetOptions(.{ .default_target = std.zig.CrossTarget.parse(.{ @@ -116,6 +122,7 @@ pub fn build(builder: *std.build.Builder) void { kernel_options.addOption(bool, "direct_disk", direct_disk); kernel_options.addOption(bool, "run_rc", run_rc); kernel_options.addOption(bool, "halt_when_done", halt_when_done); + kernel_options.addOption(bool, "mouse", mouse); kernel_options.addOption(bool, "is_kernel", true); kernel.addOptions("build_options", kernel_options); // Packages @@ -136,18 +143,14 @@ pub fn build(builder: *std.build.Builder) void { catch @panic("generate_builtin_font failed"); // Programs - build_program("shell"); - build_program("hello"); - build_program("ls"); - build_program("cat"); - build_program("snake"); - build_program("cksum"); - build_program("img"); - build_program("check-test-file"); - build_program("test-prog"); - build_program("ed"); - // build_zig_program("hello-zig"); - // build_c_program("hello-c"); + var programs_dir = + std.fs.cwd().openDir("programs", .{.iterate = true}) catch unreachable; + var programs_dir_it = programs_dir.iterate(); + while (programs_dir_it.next() catch unreachable) |entry| { + if (entry.kind == .Directory) { + build_program(entry.name); + } + } } const disable_ubsan = "-fsanitize-blacklist=misc/clang-sanitize-blacklist.txt"; diff --git a/kernel/platform/interrupts.zig b/kernel/platform/interrupts.zig index c71f6ea..526c98c 100644 --- a/kernel/platform/interrupts.zig +++ b/kernel/platform/interrupts.zig @@ -61,7 +61,7 @@ var table = table_init: { break :table_init entries; }; -// TODO: Bochs error for interrupt 39 is missing default error messeage, figure +// TODO: Bochs error for interrupt 39 is missing default error message, figure // out why. const invalid_index = "Interrupt number is invalid"; var names = names_init: { @@ -395,7 +395,10 @@ pub const pic = struct { } pub fn allow_irq(irq: u8, enabled: bool) void { - _ = enabled; // TODO + // TODO + if (!enabled) { + @panic("Trying to use allow_irq(*, false)"); + } var port = irq_0_7_data_port; if (irq >= 8) { port = irq_8_15_data_port; @@ -429,11 +432,13 @@ pub const pic = struct { putil.out8(irq_8_15_data_port, 1); busywork(); - // Disable All IRQs for Now + // Disable All IRQs for Now... putil.out8(irq_0_7_data_port, 0xff); busywork(); putil.out8(irq_8_15_data_port, 0xff); busywork(); + // Expect for 2 which chains the secondary PIC. + allow_irq(2, true); // Enable Interrupts putil.enable_interrupts(); diff --git a/kernel/platform/platform.zig b/kernel/platform/platform.zig index 6982a2d..10bef9c 100644 --- a/kernel/platform/platform.zig +++ b/kernel/platform/platform.zig @@ -141,7 +141,7 @@ pub fn init() !void { // Setup Devices kernel.device_mgr.init(kernel.alloc); - ps2.init(); + try ps2.init(); pci.find_pci_devices(); bios_int.init(); vbe.init(); diff --git a/kernel/platform/ps2.zig b/kernel/platform/ps2.zig index 93a35a7..38d4a5d 100644 --- a/kernel/platform/ps2.zig +++ b/kernel/platform/ps2.zig @@ -1,5 +1,12 @@ // ============================================================================ -// PS/2 Keyboard Interface +// PS/2 Driver for Mouse and Keyboard +// Interacts through the Intel 8042 controller. +// +// For reference: +// https://wiki.osdev.org/%228042%22_PS/2_Controller +// https://wiki.osdev.org/PS/2_Keyboard +// https://isdaman.com/alsos/hardware/mouse/ps2interface.htm +// https://wiki.osdev.org/PS/2_Mouse // ============================================================================ const build_options = @import("build_options"); @@ -8,7 +15,8 @@ const utils = @import("utils"); const georgios = @import("georgios"); const keyboard = georgios.keyboard; const Key = keyboard.Key; -const Event = keyboard.Event; +const KeyboardEvent = keyboard.Event; +const MouseEvent = georgios.MouseEvent; const kernel = @import("root").kernel; const print = kernel.print; @@ -19,16 +27,331 @@ const interrupts = @import("interrupts.zig"); const segments = @import("segments.zig"); const scan_codes = @import("ps2_scan_codes.zig"); -var modifiers = keyboard.Modifiers{}; +const Error = error { + Ps2ResetFailed, + Ps2DeviceIssue, +}; + +var keyboard_initialized = false; +var keyboard_modifiers = keyboard.Modifiers{}; +var keyboard_buffer = utils.CircularBuffer(KeyboardEvent, 128, .DiscardNewest){}; -var buffer = utils.CircularBuffer(Event, 128, .DiscardNewest){}; +var mouse_initialized = false; +var mouse_buffer = utils.CircularBuffer(MouseEvent, 256, .DiscardNewest){}; -const intel8042 = struct { +const controller = struct { const data_port: u16 = 0x60; const command_status_port: u16 = 0x64; + const ack: u8 = 0xfa; + + const DeviceKind = enum { + Unknown, + Keyboard, + Mouse, + }; + + var port1_device: ?DeviceKind = null; + var port2_device: ?DeviceKind = null; + + fn port1_is_keyboard() bool { + return port1_device == DeviceKind.Keyboard; + } + + fn port2_is_mouse() bool { + return port2_device == DeviceKind.Mouse; + } + + const Status = packed struct { + const Dest = enum(u1) { + Device = 0, + Controller = 1, + }; + + has_data: bool, + not_ready_for_write: bool, + system_flag: bool, + write_dest: Dest, + unknown: u2, + timeout_error: bool, + parity_error: bool, + }; + + fn read_status() Status { + return @bitCast(Status, putil.in8(command_status_port)); + } + + fn read_data_with_timeout(timeout: u16) ?u8 { + var n: usize = 0; + while (true) { + if (read_status().has_data) { + const byte = putil.in8(data_port); + // print.format("read_data_with_timeout: {:x}\n", .{byte}); + return byte; + } + if (timeout > 0) { + n += 1; + if (n >= timeout) break; + } + } + // print.string("read_data_with_timeout: null\n"); + return null; + } + + fn read_data() u8 { + return read_data_with_timeout(0).?; + } + + fn read_data_as(comptime Type: type, timeout: u16) Error!Type { + var status: [@sizeOf(Type)]u8 = undefined; + for (status) |*byte| { + byte.* = read_data_with_timeout(timeout) orelse { + return Error.Ps2DeviceIssue; + }; + } + + return @bitCast(Type, status); + } + + fn write(port: u16, value: u8) void { + while (read_status().not_ready_for_write) { + } + return putil.out8(port, value); + } + + fn write_data(value: u8) void { + write(data_port, value); + } + + const HasResponse = enum { + NoRes, + HasRes, + }; + + fn command(cmd: u8, arg: ?u8, has_res: HasResponse) ?u8 { + write(command_status_port, cmd); + if (arg) |arg_byte| { + write_data(arg_byte); + } + return if (has_res == .HasRes) read_data() else null; + } + + const Config = packed struct { + port1_interrupt_enabled: bool, + port2_interrupt_enabled: bool, + system_flag: bool, + zero1: u1 = 0, + port1_check: bool, + port2_check: bool, + port1_translation: bool, + zero2: u1 = 0, + }; + + fn read_config() Config { + return @bitCast(Config, command(0x20, null, .HasRes).?); + } + + fn write_config(config: Config) void { + _ = command(0x60, @bitCast(u8, config), .NoRes); + } + + const Port = enum(u2) { + Port1 = 0, + Port2 = 1, + }; + + fn enable_port(port: Port, enabled: bool) void { + const cmds = [_]u8{0xad, 0xae, 0xa7, 0xa8}; + _ = command(cmds[(@enumToInt(port) << 1) | @boolToInt(enabled)], null, .NoRes); + } + + fn device_command(port: Port, cmd: u8) u8 { + if (port == .Port1) { + write_data(cmd); + } else { + _ = command(0xd4, cmd, .NoRes); + } + return read_data(); + } + + const MouseStatus = packed struct { + const Scaling = enum(u1) { + OneToOne = 0, + TwoToOne = 1, + }; + + const Mode = enum(u1) { + Stream = 0, + Remote = 1, + }; + + rmb_pressed: bool, + mmb_pressed: bool, + lmb_pressed: bool, + zero1: u1 = 0, + scaling: Scaling, + data_enabled: bool, + mode: Mode, + zero2: u1 = 0, + resolution: u8, + sample_rate: u8, + }; + + fn get_mouse_status(port: Port) Error!MouseStatus { + const ack_res = device_command(port, 0xe9); + if (ack_res != ack) { + print.format("WARNING: get_mouse_status: PS/2 device on {} replied {} for ack\n", + .{port, ack_res}); + return Error.Ps2DeviceIssue; + } - pub fn get_kb_byte() callconv(.Inline) u8 { - return putil.in8(data_port); + return read_data_as(MouseStatus, 512); + } + + fn mouse_data_enabled(port: Port, enabled: bool) Error!void { + const ack_res = device_command(port, if (enabled) 0xf4 else 0xf5); + if (ack_res != ack) { + print.format("WARNING: get_mouse_status: PS/2 device on {} replied {} for ack\n", + .{port, ack_res}); + return Error.Ps2DeviceIssue; + } + } + + fn reset_device(port: Port, device: *?DeviceKind) Error!void { + device.* = null; + + // Reset: This returns an ack, a self-test result, and a 0 to 2 byte + // byte sequence id. The last part doesn't seem to be properly + // documented on the osdev wiki. (TODO?) + // TODO: Detect no device + const ack_res = device_command(port, 0xff); + if (ack_res != ack) { + print.format("WARNING: reset_device: PS/2 device on {} replied {} for ack\n", + .{port, ack_res}); + return; + } + + // Self-test result + const self_test_res = read_data(); + if (self_test_res != 0xaa) { + print.format("WARNING: reset_device: PS/2 device on {} replied {} for self test\n", + .{port, self_test_res}); + } + + // Get id byte sequence + const timeout: u16 = 2048; + if (read_data_with_timeout(timeout)) |id0| { + if (read_data_with_timeout(timeout)) |id1| { + print.format(" - PS/2 {}: Unknown device {:x}, {:x}\n", .{port, id0, id1}); + device.* = DeviceKind.Unknown; + } else if (id0 == 0) { + print.format(" - PS/2 {}: Mouse\n", .{port}); + device.* = DeviceKind.Mouse; + } else { + print.format(" - PS/2 {}: Unknown device {:x}\n", .{port, id0}); + device.* = DeviceKind.Unknown; + } + } else { + print.format(" - PS/2 {}: Keyboard\n", .{port}); + device.* = DeviceKind.Keyboard; + } + + // TODO: Zig bug, can't combine this into a single "and" expr, LLVM crashes + if (build_options.mouse) { + if (device.* == DeviceKind.Mouse) { + const mouse_status = try get_mouse_status(port); + // print.format("MouseStatus: {}\n", .{mouse_status}); + if (!mouse_status.data_enabled) { + try mouse_data_enabled(port, true); + // const mouse_status2 = try get_mouse_status(port); + // print.format("MouseStatus: {}\n", .{mouse_status2}); + } + } + } + } + + fn reset() Error!void { + // Disable ports and flush output buffer + enable_port(.Port1, false); + enable_port(.Port2, false); + _ = putil.in8(data_port); + var config = read_config(); + config.port1_interrupt_enabled = false; + config.port2_interrupt_enabled = false; + write_config(config); + + // Controller self-test + const controller_status = command(0xaa, null, .HasRes).?; + if (controller_status != 0x55) { + print.format("ERROR: PS/2 controller self-test status {}\n", .{controller_status}); + return Error.Ps2ResetFailed; + } + + // Test ports + const port1_status = command(0xab, null, .HasRes).?; + if (port1_status != 0x00) { + print.format("ERROR: PS/2 port 1 test status {}\n", .{port1_status}); + return Error.Ps2ResetFailed; + } + const port2_status = command(0xa9, null, .HasRes).?; + if (port2_status != 0x00) { + print.format("ERROR: PS/2 port 2 test status {}\n", .{port2_status}); + return Error.Ps2ResetFailed; + } + + // Enable ports + enable_port(.Port1, true); + enable_port(.Port2, true); + + // Set controller config + config.port1_interrupt_enabled = true; + config.port2_interrupt_enabled = true; + // Have controller translate to scan code set 1 for us. That's + // what's currently in ps2_scan_codes.zig. + config.port1_translation = true; + write_config(config); + + // Reset/detect devices on ports + reset_device(.Port1, &port1_device) catch |e| { + print.format("ERROR: PS/2 port 1 init error: {}\n", .{@errorName(e)}); + port1_device = null; + }; + reset_device(.Port2, &port2_device) catch |e| { + print.format("ERROR: PS/2 port 2 init error: {}\n", .{@errorName(e)}); + port2_device = null; + }; + } + + const MouseData = packed struct { + lmb_pressed: bool, + rmb_pressed: bool, + mmb_pressed: bool, + one: u1 = 1, + x_sign: u1, + y_sign: u1, + x_overflow: u1, + y_overflow: u1, + x: u8, + y: u8, + + fn get_value(sign: u1, value: u8) i9 { + return @bitCast(i9, (@intCast(u9, sign) << 8) | value); + } + + fn to_mouse_event(self: *const MouseData) MouseEvent { + return .{ + .rmb_pressed = self.rmb_pressed, + .mmb_pressed = self.mmb_pressed, + .lmb_pressed = self.lmb_pressed, + .delta = .{ + .x = get_value(self.x_sign, self.x), + .y = get_value(self.y_sign, self.y), + }, + }; + } + }; + + fn get_mouse_data() Error!MouseData { + return read_data_as(MouseData, 128); } }; @@ -45,12 +368,12 @@ const Pattern = enum { /// Current multibyte patterns that are possible. var pattern: Pattern = undefined; -pub fn keyboard_event_occured( - interrupt_number: u32, interrupt_stack: *const interrupts.Stack) void { +fn keyboard_event_occurred(interrupt_number: u32, interrupt_stack: *const interrupts.Stack) void { _ = interrupt_number; _ = interrupt_stack; - var event: ?Event = null; - const byte = intel8042.get_kb_byte(); + if (!keyboard_initialized) return; + var event: ?KeyboardEvent = null; + const byte = controller.read_data(); // print.format("[{:x}]", .{byte}); var reset = false; switch (byte_count) { @@ -65,7 +388,8 @@ pub fn keyboard_event_occured( else => { const entry = scan_codes.one_byte[byte]; if (entry.key) |key| { - event = Event.new(key, entry.shifted_key, entry.kind.?, &modifiers); + event = KeyboardEvent.new( + key, entry.shifted_key, entry.kind.?, &keyboard_modifiers); } reset = true; }, @@ -83,7 +407,8 @@ pub fn keyboard_event_occured( else => { const entry = scan_codes.two_byte[byte]; if (entry.key) |key| { - event = Event.new(key, entry.shifted_key, entry.kind.?, &modifiers); + event = KeyboardEvent.new( + key, entry.shifted_key, entry.kind.?, &keyboard_modifiers); } reset = true; }, @@ -109,7 +434,8 @@ pub fn keyboard_event_occured( .PrintScreenPressed => { if (byte == 0x37) { // Reached PrintScreen Pressed - event = Event.new(.Key_PrintScreen, null, .Pressed, &modifiers); + event = KeyboardEvent.new( + .Key_PrintScreen, null, .Pressed, &keyboard_modifiers); } reset = true; }, @@ -117,7 +443,8 @@ pub fn keyboard_event_occured( .PrintScreenReleased => { if (byte == 0xaa) { // Reached PrintScreen Released - event = Event.new(.Key_PrintScreen, null, .Released, &modifiers); + event = KeyboardEvent.new( + .Key_PrintScreen, null, .Released, &keyboard_modifiers); } reset = true; }, @@ -141,7 +468,7 @@ pub fn keyboard_event_occured( .Pause => { if (byte == 0xc5) { // Reached Pause - event = Event.new(.Key_Pause, null, .Hit, &modifiers); + event = KeyboardEvent.new(.Key_Pause, null, .Hit, &keyboard_modifiers); } reset = true; }, @@ -160,33 +487,67 @@ pub fn keyboard_event_occured( if (event != null) { const e = &event.?; - modifiers.update(e); + keyboard_modifiers.update(e); // print.format("<{}: {}>", .{@tagName(e.key), @tagName(e.kind)}); if (e.kind == .Pressed) { if (key_to_char(e.key)) |c| { e.char = c; - if (modifiers.alt_is_pressed() and modifiers.control_is_pressed() and c == 'D') { + if (keyboard_modifiers.alt_is_pressed() and + keyboard_modifiers.control_is_pressed() and c == 'D') { kernel.quick_debug(); return; } } } - buffer.push(e.*); - kernel.threading_mgr.keyboard_event_occured(); + keyboard_buffer.push(e.*); + kernel.threading_mgr.keyboard_event_occurred(); } } -pub fn get_key() ?Event { +pub fn get_key() ?KeyboardEvent { putil.disable_interrupts(); defer putil.enable_interrupts(); - return buffer.pop(); + return keyboard_buffer.pop(); +} + +fn mouse_event_occurred(interrupt_number: u32, interrupt_stack: *const interrupts.Stack) void { + _ = interrupt_number; + _ = interrupt_stack; + if (!mouse_initialized) return; + const data = controller.get_mouse_data() catch return; + const event = data.to_mouse_event(); + //print.format("mouse: {}\n", .{event}); + mouse_buffer.push(event); } -pub fn init() void { - interrupts.IrqInterruptHandler(1, keyboard_event_occured).set( - "IRQ1: Keyboard", segments.kernel_code_selector, interrupts.kernel_flags); +pub fn get_mouse_event() ?MouseEvent { + putil.disable_interrupts(); + defer putil.enable_interrupts(); + return mouse_buffer.pop(); +} + +pub fn init() !void { + try controller.reset(); + + // Set up IRQs + if (controller.port1_is_keyboard()) { + interrupts.IrqInterruptHandler(1, keyboard_event_occurred).set( + "IRQ1: PS/2 Keyboard", segments.kernel_code_selector, interrupts.kernel_flags); + } + if (controller.port2_is_mouse()) { + interrupts.IrqInterruptHandler(12, mouse_event_occurred).set( + "IRQ12: PS/2 Mouse", segments.kernel_code_selector, interrupts.kernel_flags); + } interrupts.load(); - interrupts.pic.allow_irq(1, true); + if (controller.port1_is_keyboard()) { + interrupts.pic.allow_irq(1, true); + } + if (controller.port2_is_mouse()) { + interrupts.pic.allow_irq(12, true); + } + + keyboard_initialized = controller.port1_is_keyboard(); + mouse_initialized = controller.port2_is_mouse(); } pub fn anykey() void { diff --git a/kernel/platform/system_calls.zig b/kernel/platform/system_calls.zig index 49ca3aa..939d261 100644 --- a/kernel/platform/system_calls.zig +++ b/kernel/platform/system_calls.zig @@ -136,6 +136,24 @@ pub fn handle(_: u32, interrupt_stack: *const interrupts.Stack) void { } }, + // SYSCALL: get_mouse_event(&blocking: georgios.Blocking) key: ?georgios.MouseEvent + // IMPORT: georgios "georgios.zig" + 28 => { + const blocking = @intToPtr(*georgios.Blocking, arg1).* == .Blocking; + const rv = @intToPtr(*?georgios.MouseEvent, arg2); + while (true) { + if (ps2.get_mouse_event()) |event| { + rv.* = event; + break; + } else if (blocking) { + kernel.threading_mgr.wait_for_mouse(); + } else { + rv.* = null; + break; + } + } + }, + // SYSCALL: print_uint(value: u32, base: u8) void 7 => { switch (arg2) { @@ -321,6 +339,13 @@ pub fn handle(_: u32, interrupt_stack: *const interrupts.Stack) void { vbe_console.get_info(last_scroll_count, size, pos, glyph_size); }, + // SYSCALL: vbe_fill_rect(rect: *const utils.U32Rect, pixel: u32) void + 27 => { + const rect = @intToPtr(*utils.U32Rect, arg1); + const color = @truncate(u32, arg2); + vbe.fill_rect(rect, color); + }, + else => @panic("Invalid System Call"), } } diff --git a/kernel/platform/vbe.zig b/kernel/platform/vbe.zig index b0946d7..51cede8 100644 --- a/kernel/platform/vbe.zig +++ b/kernel/platform/vbe.zig @@ -8,7 +8,8 @@ const build_options = @import("build_options"); const utils = @import("utils"); const U32Point = utils.U32Point; -pub const Box = utils.Box(u32, u32); +const I32Point = utils.I32Point; +const U32Rect = utils.U32Rect; const multiboot = @import("multiboot.zig"); const pmemory = @import("memory.zig"); @@ -175,12 +176,12 @@ pub fn draw_line(a: U32Point, b: U32Point, color: u32) void { } } -pub fn draw_box(box: Box, color: u32) void { +pub fn draw_rect(rect: *const U32Rect, color: u32) void { // a > b // V V // c > d - const a = box.pos; - const d = box.pos.plus_point(box.size); + const a = rect.pos; + const d = rect.pos.plus_point(rect.size); const b = .{.x = d.x, .y = a.y}; const c = .{.x = a.x, .y = d.y}; draw_line(a, b, color); @@ -271,14 +272,14 @@ pub fn flush_buffer() void { buffer_clean = true; } -pub fn flush_buffer_area(area: Box) void { +pub fn flush_buffer_rect(rect: *const U32Rect) void { buffer_sync(); - var offset = video_memory_offset(area.pos.x, area.pos.y); - var row = area.pos.y; - const end = area.pos.y + area.size.y; - const row_size = area.size.x * bytes_per_pixel; - while (row < end) { - const row_end = offset + row_size; + var offset = video_memory_offset(rect.pos.x, rect.pos.y); + var row = rect.pos.y; + const end = rect.pos.y + rect.size.y; + const row_size = rect.size.x * bytes_per_pixel; + while (row < end and offset < buffer.len) { + const row_end = @minimum(buffer.len, offset + row_size); const b = buffer[offset..row_end]; for (video_memory[offset..row_end]) |*p, i| { p.* = b[i]; @@ -288,6 +289,22 @@ pub fn flush_buffer_area(area: Box) void { } } +pub fn fill_rect(rect: *const U32Rect, color: u32) void { + var x: usize = rect.pos.x; + const x_end = x + rect.size.x; + var y: usize = rect.pos.y; + const y_end = y + rect.size.y; + while (y < y_end) { + draw_pixel(x, y, color); + x += 1; + if (x >= x_end) { + x = rect.pos.x; + y += 1; + } + } + flush_buffer_rect(rect); +} + const vbe_result_ptr: u16 = 0x8000; const VbeFuncArgs = struct { diff --git a/kernel/platform/vbe_console.zig b/kernel/platform/vbe_console.zig index 6de15b9..d1ac5bf 100644 --- a/kernel/platform/vbe_console.zig +++ b/kernel/platform/vbe_console.zig @@ -1,5 +1,6 @@ const utils = @import("utils"); const Point = utils.U32Point; +const Rect = utils.U32Rect; const kernel = @import("root").kernel; const Console = kernel.Console; @@ -8,7 +9,6 @@ const BitmapFont = kernel.BitmapFont; const platform = @import("platform.zig"); const vbe = platform.vbe; -const Box = vbe.Box; pub fn color_value_from_hex_color(hex_color: HexColor) u32 { return switch (hex_color) { @@ -65,7 +65,7 @@ pub fn init(screen_width: u32, screen_height: u32, bitmap_font: *const BitmapFon clear_screen_impl(&console); } -fn place_impl_no_flush(c: *Console, utf32_value: u32, row: u32, col: u32) Box { +fn place_impl_no_flush(c: *Console, utf32_value: u32, row: u32, col: u32) Rect { _ = c; const x = col * glyph_width; const y = row * glyph_height; @@ -74,7 +74,7 @@ fn place_impl_no_flush(c: *Console, utf32_value: u32, row: u32, col: u32) Box { } fn place_impl(c: *Console, utf32_value: u32, row: u32, col: u32) void { - vbe.flush_buffer_area(place_impl_no_flush(c, utf32_value, row, col)); + vbe.flush_buffer_rect(&place_impl_no_flush(c, utf32_value, row, col)); } pub fn set_hex_color_impl(c: *Console, color: HexColor, layer: Console.Layer) void { @@ -111,10 +111,10 @@ pub fn clear_screen_impl(c: *Console) void { pub fn move_cursor_impl(c: *Console, row: u32, col: u32) void { _ = c; if (show_cursor and false) { // TODO: Finish cursor - const pos = Box.Pos{.x = col * glyph_width, .y = row * glyph_height}; - const size = font.bdf_font.bounds.size.as(Box.Size.Num); - vbe.draw_box(.{.pos = pos, .size = size.minus_int(1)}, 0xff000000); - vbe.flush_buffer_area(.{.pos = pos, .size = size}); + const pos = Rect.Pos{.x = col * glyph_width, .y = row * glyph_height}; + const size = font.bdf_font.bounds.size.as(Rect.Size.Num); + vbe.draw_rect(&.{.pos = pos, .size = size.minus_int(1)}, 0xff000000); + vbe.flush_buffer_rect(&.{.pos = pos, .size = size}); } } diff --git a/kernel/threading.zig b/kernel/threading.zig index 1a337fb..82ca8fa 100644 --- a/kernel/threading.zig +++ b/kernel/threading.zig @@ -269,6 +269,7 @@ pub const Manager = struct { next_process_id: Process.Id = 0, current_process: ?*Process = null, waiting_for_keyboard: ?*Thread = null, + waiting_for_mouse: ?*Thread = null, time_queue: TimeQueue = .{}, pub fn init(self: *Manager) Error!void { @@ -488,15 +489,14 @@ pub const Manager = struct { } } - // TODO Make this and keyboard_event_occured generic + // TODO Make this and keyboard_event_occurred generic pub fn wait_for_keyboard(self: *Manager) void { platform.disable_interrupts(); self.current_thread.?.state = .Wait; self.waiting_for_keyboard = self.current_thread; self.yield(); } - - pub fn keyboard_event_occured(self: *Manager) void { + pub fn keyboard_event_occurred(self: *Manager) void { platform.disable_interrupts(); if (self.waiting_for_keyboard) |t| { t.state = .Run; @@ -505,6 +505,21 @@ pub const Manager = struct { } } + // Same problems as the keyboard functions + pub fn wait_for_mouse(self: *Manager) void { + platform.disable_interrupts(); + self.current_thread.?.state = .Wait; + self.waiting_for_mouse = self.current_thread; + self.yield(); + } + pub fn mouse_event_occurred(self: *Manager) void { + platform.disable_interrupts(); + if (self.waiting_for_mouse) |t| { + t.state = .Run; + self.waiting_for_mouse = null; + } + } + pub fn get_cwd(self: *Manager, buffer: []u8) Error![]const u8 { if (self.current_process) |proc| { return buffer[0..try utils.memory_copy_error(buffer, proc.cwd.?)]; diff --git a/libs/georgios/georgios.zig b/libs/georgios/georgios.zig index 4593b3d..518f29c 100644 --- a/libs/georgios/georgios.zig +++ b/libs/georgios/georgios.zig @@ -204,3 +204,10 @@ pub fn get_console_writer() ConsoleWriter.Writer { const cw = ConsoleWriter{}; return cw.writer(); } + +pub const MouseEvent = struct { + rmb_pressed: bool, + mmb_pressed: bool, + lmb_pressed: bool, + delta: utils.Point(i32), +}; diff --git a/libs/georgios/system_calls.zig b/libs/georgios/system_calls.zig index bd2f10b..68043ea 100644 --- a/libs/georgios/system_calls.zig +++ b/libs/georgios/system_calls.zig @@ -338,6 +338,16 @@ pub fn get_key(blocking: georgios.Blocking) callconv(.Inline) ?georgios.keyboard return key; } +pub fn get_mouse_event(blocking: georgios.Blocking) callconv(.Inline) ?georgios.MouseEvent { + var key: ?georgios.MouseEvent = undefined; + asm volatile ("int $100" :: + [syscall_number] "{eax}" (@as(u32, 28)), + [arg1] "{ebx}" (@ptrToInt(&blocking)), + [arg2] "{ecx}" (@ptrToInt(&key)), + ); + return key; +} + pub fn print_uint(value: u32, base: u8) callconv(.Inline) void { asm volatile ("int $100" :: [syscall_number] "{eax}" (@as(u32, 7)), @@ -520,3 +530,11 @@ pub fn get_vbe_console_info(last_scroll_count: *u32, size: *utils.U32Point, pos: [arg4] "{edi}" (glyph_size), ); } + +pub fn vbe_fill_rect(rect: *const utils.U32Rect, pixel: u32) callconv(.Inline) void { + asm volatile ("int $100" :: + [syscall_number] "{eax}" (@as(u32, 27)), + [arg1] "{ebx}" (rect), + [arg2] "{ecx}" (pixel), + ); +} diff --git a/libs/utils/Bdf.zig b/libs/utils/Bdf.zig index 822a635..6777fac 100644 --- a/libs/utils/Bdf.zig +++ b/libs/utils/Bdf.zig @@ -12,10 +12,10 @@ const std = @import("std"); const utils = @import("utils.zig"); const WordIterator = utils.WordIterator; -const Box = utils.Box; +const Rect = utils.Rect; const streq = utils.memory_compare; -const Bounds = Box(i16, u16); +const Bounds = Rect(i16, u16); pub const Error = error { BdfBadKeyword, diff --git a/libs/utils/utils.zig b/libs/utils/utils.zig index 2cfa53a..4512d5c 100644 --- a/libs/utils/utils.zig +++ b/libs/utils/utils.zig @@ -387,6 +387,10 @@ pub fn Point(comptime TheNum: type) type { pub fn abs(self: *const Self) Self { return .{.x = absolute(self.x), .y = absolute(self.y)}; } + + pub fn eq(self: *const Self, other: Self) bool { + return self.x == other.x and self.y == other.y; + } }; } @@ -431,7 +435,7 @@ pub fn Point3d(comptime TheNum: type) type { pub const I32Point3d = Point3d(i32); -pub fn Box(comptime PosNum: type, comptime SizeNum: type) type { +pub fn Rect(comptime PosNum: type, comptime SizeNum: type) type { return struct { pub const Pos = Point(PosNum); pub const Size = Point(SizeNum); @@ -441,6 +445,8 @@ pub fn Box(comptime PosNum: type, comptime SizeNum: type) type { }; } +pub const U32Rect = Rect(u32, u32); + pub fn any_equal(a: anytype, b: @TypeOf(a)) bool { const Type = @TypeOf(a); const ti = @typeInfo(Type); diff --git a/misc/qemu.gdb b/misc/qemu.gdb index ad27348..1c5413b 100644 --- a/misc/qemu.gdb +++ b/misc/qemu.gdb @@ -23,9 +23,12 @@ target remote | qemu-system-i386 \ -soundhw pcspk \ -usb \ -device usb-ehci,id=ehci \ - -drive if=none,id=flashdrive,file=usbdrive.img \ + -drive if=none,id=flashdrive,file=usbdrive.img,format=raw \ -device usb-storage,bus=ehci.0,drive=flashdrive \ - disk.img + -drive file=disk.img,format=raw + + # -device usb-mouse,bus=ehci.0 \ + # -device usb-kbd,bus=ehci.0 \ # -trace 'vga*' \ diff --git a/programs/test-mouse/test-mouse.zig b/programs/test-mouse/test-mouse.zig new file mode 100644 index 0000000..4cf784e --- /dev/null +++ b/programs/test-mouse/test-mouse.zig @@ -0,0 +1,198 @@ +const std = @import("std"); +const georgios = @import("georgios"); +comptime {_ = georgios;} + +const I32Point = georgios.utils.I32Point; +const U32Point = georgios.utils.U32Point; +const system_calls = georgios.system_calls; + +const console = georgios.get_console_writer(); +const esc = "\x1b"; +const reset_console = esc ++ "c"; +const ansi_esc = esc ++ "["; +const invert_colors = ansi_esc ++ "7m"; + +var vbe: bool = undefined; +var scale: u16 = undefined; +var screen = U32Point{}; +var last_pos = U32Point{}; +var pos = U32Point{}; +var last_delta = I32Point{}; +var buttons = [_]bool{false} ** 3; + +fn draw_at(p: U32Point, comptime fmt: []const u8, args: anytype) void { + try console.print(ansi_esc ++ "{};{}H", .{p.y / scale, p.x / scale}); + try console.print(fmt, args); +} + +const vbe_cursor = U32Point{.x = 4, .y = 4}; +var vbe_index: u8 = 0; + +// import math +// for i, n in enumerate([int(math.sin(i / 255 * 2 * math.pi) * 128 + 127) for i in range(0, 256)]): +// if (i % 8 == 0): +// print() +// print('0x{:02x}, '.format(n), end='') +// print() +const sin = [_]u8 { + 0x7f, 0x82, 0x85, 0x88, 0x8b, 0x8e, 0x91, 0x94, + 0x98, 0x9b, 0x9e, 0xa1, 0xa4, 0xa7, 0xaa, 0xad, + 0xb0, 0xb3, 0xb5, 0xb8, 0xbb, 0xbe, 0xc1, 0xc3, + 0xc6, 0xc8, 0xcb, 0xce, 0xd0, 0xd2, 0xd5, 0xd7, + 0xd9, 0xdb, 0xde, 0xe0, 0xe2, 0xe4, 0xe6, 0xe7, + 0xe9, 0xeb, 0xed, 0xee, 0xf0, 0xf1, 0xf2, 0xf4, + 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, + 0xfc, 0xfd, 0xfd, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, + 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfd, 0xfd, 0xfc, + 0xfc, 0xfb, 0xfa, 0xfa, 0xf9, 0xf8, 0xf7, 0xf6, + 0xf4, 0xf3, 0xf2, 0xf0, 0xef, 0xed, 0xec, 0xea, + 0xe8, 0xe7, 0xe5, 0xe3, 0xe1, 0xdf, 0xdd, 0xda, + 0xd8, 0xd6, 0xd4, 0xd1, 0xcf, 0xcc, 0xca, 0xc7, + 0xc5, 0xc2, 0xbf, 0xbc, 0xba, 0xb7, 0xb4, 0xb1, + 0xae, 0xab, 0xa8, 0xa5, 0xa2, 0x9f, 0x9c, 0x99, + 0x96, 0x93, 0x90, 0x8d, 0x8a, 0x86, 0x83, 0x80, + 0x7d, 0x7a, 0x77, 0x73, 0x70, 0x6d, 0x6a, 0x67, + 0x64, 0x61, 0x5e, 0x5b, 0x58, 0x55, 0x52, 0x4f, + 0x4c, 0x49, 0x46, 0x43, 0x41, 0x3e, 0x3b, 0x38, + 0x36, 0x33, 0x31, 0x2e, 0x2c, 0x29, 0x27, 0x25, + 0x23, 0x20, 0x1e, 0x1c, 0x1a, 0x18, 0x16, 0x15, + 0x13, 0x11, 0x10, 0x0e, 0x0d, 0x0b, 0x0a, 0x09, + 0x07, 0x06, 0x05, 0x04, 0x03, 0x03, 0x02, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0b, 0x0c, 0x0d, 0x0f, 0x10, 0x12, 0x14, + 0x16, 0x17, 0x19, 0x1b, 0x1d, 0x1f, 0x22, 0x24, + 0x26, 0x28, 0x2b, 0x2d, 0x2f, 0x32, 0x35, 0x37, + 0x3a, 0x3c, 0x3f, 0x42, 0x45, 0x48, 0x4a, 0x4d, + 0x50, 0x53, 0x56, 0x59, 0x5c, 0x5f, 0x62, 0x65, + 0x69, 0x6c, 0x6f, 0x72, 0x75, 0x78, 0x7b, 0x7e, +}; +const cos = [_]u8 { + 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfd, 0xfd, + 0xfc, 0xfb, 0xfb, 0xfa, 0xf9, 0xf8, 0xf7, 0xf6, + 0xf5, 0xf3, 0xf2, 0xf1, 0xef, 0xee, 0xec, 0xea, + 0xe9, 0xe7, 0xe5, 0xe3, 0xe1, 0xdf, 0xdd, 0xdb, + 0xd9, 0xd6, 0xd4, 0xd2, 0xcf, 0xcd, 0xca, 0xc8, + 0xc5, 0xc3, 0xc0, 0xbd, 0xba, 0xb8, 0xb5, 0xb2, + 0xaf, 0xac, 0xa9, 0xa6, 0xa3, 0xa0, 0x9d, 0x9a, + 0x97, 0x94, 0x91, 0x8d, 0x8a, 0x87, 0x84, 0x81, + 0x7e, 0x7b, 0x77, 0x74, 0x71, 0x6e, 0x6b, 0x68, + 0x65, 0x62, 0x5f, 0x5b, 0x58, 0x55, 0x52, 0x50, + 0x4d, 0x4a, 0x47, 0x44, 0x41, 0x3f, 0x3c, 0x39, + 0x36, 0x34, 0x31, 0x2f, 0x2c, 0x2a, 0x28, 0x25, + 0x23, 0x21, 0x1f, 0x1d, 0x1b, 0x19, 0x17, 0x15, + 0x13, 0x12, 0x10, 0x0e, 0x0d, 0x0c, 0x0a, 0x09, + 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0c, 0x0d, 0x0e, 0x10, 0x12, 0x13, + 0x15, 0x17, 0x19, 0x1b, 0x1d, 0x1f, 0x21, 0x23, + 0x25, 0x28, 0x2a, 0x2c, 0x2f, 0x31, 0x34, 0x36, + 0x39, 0x3c, 0x3e, 0x41, 0x44, 0x47, 0x4a, 0x4d, + 0x50, 0x52, 0x55, 0x58, 0x5b, 0x5f, 0x62, 0x65, + 0x68, 0x6b, 0x6e, 0x71, 0x74, 0x77, 0x7b, 0x7e, + 0x81, 0x84, 0x87, 0x8a, 0x8d, 0x91, 0x94, 0x97, + 0x9a, 0x9d, 0xa0, 0xa3, 0xa6, 0xa9, 0xac, 0xaf, + 0xb2, 0xb5, 0xb8, 0xba, 0xbd, 0xc0, 0xc3, 0xc5, + 0xc8, 0xca, 0xcd, 0xcf, 0xd2, 0xd4, 0xd6, 0xd9, + 0xdb, 0xdd, 0xdf, 0xe1, 0xe3, 0xe5, 0xe7, 0xe9, + 0xea, 0xec, 0xee, 0xef, 0xf1, 0xf2, 0xf3, 0xf5, + 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfb, 0xfc, + 0xfd, 0xfd, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, +}; + +fn draw_cursor(e: *const georgios.MouseEvent) void { + if (vbe) { + vbe_index +%= 1; + const ri = vbe_index +% @intCast(u8, @boolToInt(e.lmb_pressed)) * 128; + const gi = vbe_index +% @intCast(u8, @boolToInt(e.mmb_pressed)) * 128; + const bi = vbe_index +% @intCast(u8, @boolToInt(e.rmb_pressed)) * 128; + system_calls.vbe_fill_rect(&.{.size = vbe_cursor, .pos = pos}, + (@intCast(u32, sin[ri]) << 16) | (@intCast(u32, cos[gi]) << 8) | bi | 0xff000000); + } else { + var char: u8 = ' '; + if (e.lmb_pressed) { + char = 'L'; + } else if (e.mmb_pressed) { + char = 'M'; + } else if (e.rmb_pressed) { + char = 'R'; + } + draw_at(last_pos, "{c}", .{char}); + draw_at(pos, "{s}{c}{s}", .{invert_colors, char, invert_colors}); + } + last_pos = pos; +} + +fn draw_status() void { + if (vbe) return; // TODO: Way too slow right now + const pressed = [_][]const u8{invert_colors, invert_colors}; + const not = [_][]const u8{"", ""}; + const lmb: []const []const u8 = if (buttons[0]) pressed[0..] else not[0..]; + const mmb: []const []const u8 = if (buttons[1]) pressed[0..] else not[0..]; + const rmb: []const []const u8 = if (buttons[2]) pressed[0..] else not[0..]; + draw_at(.{.y = screen.y + scale}, + "[{s}L{s}]({s}M{s})[{s}R{s}] x = {} ({}), y = {} ({}) ", .{ + lmb[0], lmb[1], mmb[0], mmb[1], rmb[0], rmb[1], + pos.x, last_delta.x, pos.y, last_delta.y}); +} + +fn mouse_move(axis: *u32, delta: i32, max: u32) i32 { + const signed_max = @intCast(i32, max); + var r: i32 = 0; + if (@addWithOverflow(i32, @intCast(i32, axis.*), delta, &r)) { + r = if (delta > 0) signed_max else 0; + } + if (r < 0) r = 0; + if (r > signed_max) r = signed_max; + axis.* = @intCast(u32, r); + return delta; +} + +fn reset() void { + try console.print(reset_console ++ ansi_esc ++ "25l", .{}); + if (system_calls.vbe_res()) |vbe_res| { + vbe = true; + scale = 1; + screen = vbe_res; + } else { + vbe = false; + scale = 16; + screen = .{.x = system_calls.console_width() - 1, .y = system_calls.console_height() - 2}; + } + screen = screen.multiply(scale); + pos = screen.divide(2); + draw_status(); +} + +pub fn main() void { + reset(); + while (true) { + if (system_calls.get_key(.NonBlocking)) |e| { + if (e.kind == .Pressed) { + if (e.unshifted_key == .Key_Escape) { + break; + } if (e.unshifted_key == .Key_Delete) { + reset(); + } + } + } + if (system_calls.get_mouse_event(.NonBlocking)) |e| { + last_delta = .{ + .x = mouse_move(&pos.x, e.delta.x, screen.x), + .y = mouse_move(&pos.y, -e.delta.y, screen.y), + }; + buttons[0] = e.lmb_pressed; + buttons[1] = e.mmb_pressed; + buttons[2] = e.rmb_pressed; + if (!last_pos.eq(pos)) { + draw_cursor(&e); + } + draw_status(); + } + system_calls.sleep_milliseconds(5); + } + try console.print(reset_console, .{}); +} diff --git a/programs/test-prog/test-prog.zig b/programs/test-prog/test-prog.zig index e7dcebf..4d693ae 100644 --- a/programs/test-prog/test-prog.zig +++ b/programs/test-prog/test-prog.zig @@ -3,13 +3,12 @@ const georgios = @import("georgios"); comptime {_ = georgios;} const streq = georgios.utils.memory_compare; const system_calls = georgios.system_calls; -const print_string = system_calls.print_string; -const print_uint = system_calls.print_uint; + +const console = georgios.get_console_writer(); extern var _end: u32; pub fn main() !u8 { - const console = georgios.get_console_writer(); var exit_with = false; for (georgios.proc_info.args) |arg| { if (exit_with) {