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:
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