Microkernel thing OS experiment (Zig ⚡)
1const limine = @import("limine"); 2const builtin = @import("builtin"); 3const psf2 = @import("psf2.zig"); 4pub const Font = psf2.Font; 5const std = @import("std"); 6const fontdata = @embedFile("fonts/spleen-12x24.psf"); 7const are_we_le = builtin.cpu.arch.endian() == .little; 8 9pub const DefaultFont = Font.new(fontdata) catch unreachable; 10 11pub const Framebuffer = struct { 12 const Self = @This(); 13 address: [*]u8, 14 width: u64, 15 height: u64, 16 pitch: u64, 17 bypp: u16, 18 red_mask_size: u8, 19 red_mask_shift: u8, 20 green_mask_size: u8, 21 green_mask_shift: u8, 22 blue_mask_size: u8, 23 blue_mask_shift: u8, 24 25 pub fn from_limine(fb: *const limine.Framebuffer) Self { 26 return .{ 27 .address = @ptrCast(fb.address), 28 .width = fb.width, 29 .height = fb.height, 30 .pitch = fb.pitch, 31 .red_mask_size = fb.red_mask_size, 32 .red_mask_shift = fb.red_mask_shift, 33 .green_mask_size = fb.green_mask_size, 34 .green_mask_shift = fb.green_mask_shift, 35 .blue_mask_size = fb.blue_mask_size, 36 .blue_mask_shift = fb.blue_mask_shift, 37 .bypp = fb.bpp / 8, 38 }; 39 } 40}; 41 42pub const Console = struct { 43 const Self = @This(); 44 const Writer = std.io.GenericWriter(*Self, error{}, write); 45 // framebuffer data 46 fb: Framebuffer, 47 // font 48 font: psf2.Font, 49 // state data 50 current_x: u64 = 0, 51 current_y: u64 = 0, 52 fg_color: u32 = 0xFFFFFFFF, 53 bg_color: u32 = 0, 54 55 pub fn from_font(fb: Framebuffer, font: psf2.Font) Self { 56 return .{ 57 .fb = fb, 58 .font = font, 59 }; 60 } 61 62 // places a character at the given position 63 pub fn putchar(self: *const Self, ch: u8, cx: u64, cy: u64, fg_val: u32, bg_val: u32) void { 64 // convert colors to bytes 65 const fg_bytes: [4]u8 = @bitCast(if (are_we_le) fg_val else @byteSwap(fg_val)); 66 const bg_bytes: [4]u8 = @bitCast(if (are_we_le) bg_val else @byteSwap(bg_val)); 67 // initial calculations 68 const bytes_per_line = self.font.hdr.bytesPerLine(); 69 const mask_shamt: u5 = @intCast(bytes_per_line * 8 - 1); 70 const mask_initial: u32 = @as(u32, 1) << mask_shamt; 71 const glyph = self.font.getGlyph(ch) catch return; 72 73 // find the screen offset for the beignning of the character 74 // add pitch to go to next line... 75 var offset: u64 = (cy * self.font.hdr.height * self.fb.pitch) + (cx * (self.font.hdr.width + 0) * self.fb.bypp); 76 // run for every line 77 var y: u32 = 0; 78 var mask: u32 = 0; 79 while (y < self.font.hdr.height) : (y += 1) { 80 // initialize the mask and the current line 81 mask = mask_initial; 82 83 // get the current line 84 const line_value: u32 = psf2.readIntTo32(glyph[y * bytes_per_line ..][0..bytes_per_line]); 85 var line_offset: u64 = offset; 86 var x: u32 = 0; 87 while (x < self.font.hdr.width) : (x += 1) { 88 // write the pixel value to the correct position of the screen... 89 if (line_value & mask != 0) { 90 @memcpy(self.fb.address[line_offset..][0..self.fb.bypp], fg_bytes[0..]); 91 } else { 92 @memcpy(self.fb.address[line_offset..][0..self.fb.bypp], bg_bytes[0..]); 93 } 94 line_offset += self.fb.bypp; 95 mask >>= 1; 96 } 97 offset += self.fb.pitch; 98 } 99 } 100 101 pub fn putc(self: *Self, ch: u8) void { 102 // input can be \r, \n, or printable. 103 // ignore \r, move down for \n, and print char normally 104 // if \n, check to see if we overrun then scroll 105 // if normal, see if we overrun the end and do newline 106 if (ch == '\r') return; 107 if (ch == '\n') { 108 self.current_x = 0; 109 self.current_y += 1; 110 // go to top if we went below the bottom of the screen 111 if (self.current_y >= self.maxCharsHeight()) { 112 self.scrollUp(1); 113 self.current_y = self.maxCharsHeight() - 1; 114 } 115 return; 116 } 117 self.putchar(ch, self.current_x, self.current_y, self.fg_color, self.bg_color); 118 self.current_x += 1; 119 120 if (self.current_x < self.maxCharsWidth()) return; 121 self.current_x = 0; 122 self.current_y += 1; 123 if (self.current_y >= self.maxCharsHeight()) { 124 self.scrollUp(1); 125 self.current_y = self.maxCharsHeight() - 1; 126 } 127 } 128 129 pub fn puts(self: *Self, msg: []const u8) void { 130 for (msg) |ch| { 131 self.putc(ch); 132 } 133 } 134 135 fn convertColor(self: *const Self, color: u32) u32 { 136 const mult: u32 = blk: { 137 const width: u4 = @truncate(self.fb.red_mask_size); 138 break :blk (@as(u32, 1) << width) - 1; 139 }; 140 const div = 255; 141 const red: u32 = (color >> 16) & 0xFF; 142 const green: u32 = (color >> 8) & 0xFF; 143 const blue: u32 = color & 0xFF; 144 145 const red_shift: u5 = @truncate(self.fb.red_mask_shift); 146 const green_shift: u5 = @truncate(self.fb.green_mask_shift); 147 const blue_shift: u5 = @truncate(self.fb.blue_mask_shift); 148 149 return (((red * mult) / div) << red_shift) | (((green * mult) / div) << green_shift) | (((blue * mult) / div) << blue_shift); 150 } 151 152 pub fn setColor(self: *Self, fg: u32, bg: u32) void { 153 self.fg_color = self.convertColor(fg); 154 self.bg_color = self.convertColor(bg); 155 } 156 157 pub fn writer(self: *Self) Writer { 158 return .{ .context = self }; 159 } 160 161 pub fn write(self: *Self, buffer: []const u8) !usize { 162 self.puts(buffer); 163 return buffer.len; 164 } 165 166 // scroll the lines of text, without doing anything else. 167 // erase the first line of text, and memcpy the second line and on up to the first 168 pub fn scrollUp(self: *Self, amount: u64) void { 169 const num_lines = self.maxCharsHeight(); 170 const h = self.font.hdr.height; 171 if (amount > num_lines) return; // later just clear the entire screen 172 var i: u64 = amount; 173 while (i < num_lines) : (i += 1) { 174 // for each run, erase the previous line and copy the current line up a line. 175 // const curr_line = self.fb.address[i * h * self.fb.pitch .. (i + 1) * h * self.fb.pitch]; 176 const curr_line = self.fb.address[i * h * self.fb.pitch ..][0 .. h * self.fb.pitch]; 177 const prev_line = self.fb.address[(i - amount) * h * self.fb.pitch ..][0 .. h * self.fb.pitch]; 178 179 @memset(prev_line, 0); 180 @memcpy(prev_line, curr_line); 181 } 182 // finally, delete the last line (s) 183 // const last_line = self.fb.address[(num_lines - amount) * h * self.fb.pitch .. (num_lines) * h * self.fb.pitch]; 184 const last_line = self.fb.address[(num_lines - amount) * h * self.fb.pitch ..][0 .. amount * h * self.fb.pitch]; 185 @memset(last_line, 0); 186 } 187 188 fn maxCharsWidth(self: *const Self) u64 { 189 return self.fb.width / (self.font.hdr.width + 0); 190 } 191 192 fn maxCharsHeight(self: *const Self) u64 { 193 return self.fb.height / self.font.hdr.height; 194 } 195};