const std = @import("std"); const Self = @This(); pub fn init() Self { var self = Self{}; self.clear(); return self; } const vga_width = 80; const vga_height = 25; const vga_size = vga_width * vga_height; pub const ConsoleColor = enum(u8) { black = 0, blue = 1, green = 2, cyan = 3, red = 4, magenta = 5, brown = 6, light_gray = 7, dark_gray = 8, light_blue = 9, light_green = 10, light_cyan = 11, light_red = 12, light_magenta = 13, light_brown = 14, white = 15, }; const default_color = vgaEntryColor(ConsoleColor.light_gray, ConsoleColor.black); column: usize = 0, color: u8 = default_color, buffer: [*]volatile u16 = @ptrFromInt(0xb8000), fn vgaEntryColor(fg: ConsoleColor, bg: ConsoleColor) u8 { return @intFromEnum(fg) | (@intFromEnum(bg) << 4); } fn vgaEntry(uc: u8, new_color: u8) u16 { const c: u16 = new_color; return uc | (c << 8); } fn index(x: usize, y: usize) usize { return y * vga_width + x; } pub fn setColor(self: *Self, fg: ConsoleColor, bg: ConsoleColor) void { self.color = vgaEntryColor(fg, bg); } pub fn resetColor(self: *Self) void { self.color = default_color; } pub fn clear(self: *Self) void { @memset(self.buffer[0..vga_size], vgaEntry(' ', self.color)); } fn clearRow(self: *Self, y: usize) void { for (0..vga_width) |x| { const i = index(x, y); self.buffer[i] = vgaEntry(' ', self.color); } } fn newLine(self: *Self) void { for (1..vga_height) |y| { for (0..vga_width) |x| { const entry = self.getEntryAt(x, y); self.putEntryAt(entry, x, y - 1); } } self.clearRow(vga_height - 1); self.column = 0; } pub fn putCharAt(self: *Self, c: u8, new_color: u8, x: usize, y: usize) void { self.putEntryAt(vgaEntry(c, new_color), x, y); } fn putEntryAt(self: *Self, entry: u16, x: usize, y: usize) void { const i = index(x, y); self.buffer[i] = entry; } fn getEntryAt(self: *Self, x: usize, y: usize) u16 { const i = index(x, y); return self.buffer[i]; } pub fn putChar(self: *Self, c: u8) void { switch (c) { '\n' => self.newLine(), else => { if (self.column >= vga_width) { self.newLine(); } const y = vga_height - 1; const x = self.column; self.putCharAt(c, self.color, x, y); self.column += 1; }, } } pub fn puts(self: *Self, data: []const u8) void { for (data) |c| { self.putChar(c); } } const Writer = struct { console: *Self, interface: std.Io.Writer, fn drain(io_w: *std.Io.Writer, data: []const []const u8, splat: usize) std.Io.Writer.Error!usize { const self: *@This() = @fieldParentPtr("interface", io_w); try self.puts(io_w.buffered()); io_w.end = 0; for (data[0..data.len - 1]) |bytes| { try self.puts(bytes); } const pattern = data[data.len - 1]; for (0..splat) |_| { try self.puts(pattern); } return std.Io.Writer.countSplat(data, splat); } fn puts(self: *@This(), data: []const u8) std.Io.Writer.Error!void { self.console.puts(data); } }; pub fn writer(self: *Self, buffer: []u8) Writer { return .{ .console = self, .interface = .{ .buffer = buffer, .vtable = &.{ .drain = Writer.drain, }, }, }; } pub fn printf(self: *Self, comptime format: []const u8, args: anytype) void { var buffer: [1024]u8 = undefined; var w = self.writer(&buffer); const io_w = &w.interface; io_w.print(format, args) catch unreachable; io_w.flush() catch unreachable; }