sylveos

Toy Operating System
Log | Files | Refs

commit e2ea44793d4e394e5fcee53b5692b4cee3296fea
parent 9f3a50f47df68a52adf217b63253305298d91460
Author: Sylvia Ivory <git@sivory.net>
Date:   Thu, 22 Jan 2026 10:24:09 -0800

Use proper Writer and Reader interfaces

Diffstat:
Msrc/devices/mini-uart.zig | 42+++++++++++++++++++++++++++++++++++++-----
Msrc/main.zig | 4+++-
Msrc/root.zig | 21+++++++++------------
3 files changed, 49 insertions(+), 18 deletions(-)

diff --git a/src/devices/mini-uart.zig b/src/devices/mini-uart.zig @@ -4,7 +4,7 @@ const mem = @import("pi").mem; const gpio = @import("./gpio.zig"); const timer = @import("./timer.zig"); -pub const Error = error{AlreadyInitialized} || gpio.Error; +pub const Error = error{ AlreadyInitialized, NotInitialized, InvalidReadLimit } || gpio.Error; // Page 8: Auxiliary peripherals Register Map const BASE_ADDRESS: usize = mem.BASE_ADDRESS + 0x0021_5000; @@ -52,6 +52,9 @@ pub fn initialize(baud: u32, tx_pin: TxPin, rx_pin: RxPin) Error!void { .Gpio37 => .alt_fn_2, }; + // The error case is technically unreachable due to hardcoding gpio pin values + // But at the same time this function body may change with time so it's important + // to keep the Error try gpio.fn_sel(@intFromEnum(tx_pin), tx_fn); try gpio.fn_sel(@intFromEnum(rx_pin), rx_fn); @@ -121,7 +124,7 @@ fn can_read() bool { return (mem.get_u32(@ptrFromInt(AUX_MU_LSR_REG)) & 1) == 1; } -pub fn write_byte(byte: u8) void { +fn write_byte(byte: u8) void { // TODO; support timeout while (!can_write()) {} @@ -131,7 +134,7 @@ pub fn write_byte(byte: u8) void { mem.put_u32(@ptrFromInt(AUX_MU_IO_REG), @as(u32, byte) & 0xFF); } -pub fn read_byte() u8 { +fn read_byte() u8 { // TODO; support timeout while (!can_read()) {} @@ -141,8 +144,37 @@ pub fn read_byte() u8 { return @truncate(mem.get_u32(AUX_MU_IO_REG)); } -pub fn write_slice(slice: []const u8) void { - for (slice) |b| { +fn drain(io_w: *std.Io.Writer, data: []const []const u8, splat: usize) !usize { + if (!initialized) return std.Io.Writer.Error.WriteFailed; + + // We don't care about getting the "parent" + _ = io_w; + _ = splat; + + for (data[0]) |b| { write_byte(b); } + + return data[0].len; +} + +const writer_vtable: std.Io.Writer.VTable = .{ .drain = drain }; +pub var writer = std.Io.Writer{ .buffer = undefined, .vtable = &writer_vtable }; + +fn stream(io_r: *std.Io.Reader, io_w: *std.Io.Writer, limit: std.Io.Limit) !usize { + // We don't care about getting the "parent" + _ = io_r; + + if (limit.toInt()) |*max| { + for (0..max) |_| { + try io_w.writeByte(read_byte()); + } + + return max; + } else { + return std.Io.Reader.StreamError.ReadFailed; + } } + +const reader_vtable: std.Io.Reader.VTable = .{ .stream = stream }; +pub var reader = std.Io.Reader{ .buffer = undefined, .vtable = &reader_vtable }; diff --git a/src/main.zig b/src/main.zig @@ -1,3 +1,5 @@ +const uart = @import("devices/mini-uart.zig"); + pub fn main() !void { - return error.Oops; + try uart.writer.print("Hello {s}", .{"World!"}); } diff --git a/src/root.zig b/src/root.zig @@ -20,14 +20,7 @@ noinline fn kmain() void { uart.initialize(115200, .Gpio14, .Gpio15) catch {}; user_main() catch |e| { - if (uart.is_initialized()) { - var buffer: [1024]u8 = undefined; - const slice = std.fmt.bufPrint(&buffer, "main: {t}\n", .{e}) catch { - uart.write_slice("failed to print error\n"); - abort(); - }; - uart.write_slice(slice); - } + uart.writer.print("main returned error: {t}\n", .{e}) catch {}; }; } @@ -49,12 +42,16 @@ noinline fn abort() noreturn { fn panic_handler(msg: []const u8, trace_addr: ?usize) noreturn { @branchHint(.cold); - _ = trace_addr; if (uart.is_initialized()) { - uart.write_slice("Panic: "); - uart.write_slice(msg); - uart.write_byte('\n'); + uart.writer.print("kernel panic: {s}\n", .{msg}) catch {}; + + var it = std.debug.StackIterator.init(trace_addr, null); + var ix: usize = 0; + + while (it.next()) |frame| : (ix += 1) { + uart.writer.print("| #{d:0>2}: 0x{X:0>16}\n", .{ ix, frame }) catch {}; + } } // TODO; proper abort