// Rumpk Layer 0: UART Driver // Minimal serial output for QEMU 'virt' machine // Supports PL011 (ARM64) and 16550A (RISC-V) const std = @import("std"); const builtin = @import("builtin"); // ARM64 PL011 Constants const PL011_BASE: usize = 0x09000000; const PL011_DR: usize = 0x00; const PL011_FR: usize = 0x18; const PL011_TXFF: u32 = 1 << 5; // RISC-V 16550A Constants const NS16550A_BASE: usize = 0x10000000; const NS16550A_THR: usize = 0x00; // Transmitter Holding Register const NS16550A_LSR: usize = 0x05; // Line Status Register const NS16550A_THRE: u8 = 1 << 5; // Transmitter Holding Register Empty pub fn init() void { // QEMU devices are usually pre-initialized by firmware (EDK2/OpenSBI) } pub fn init_riscv() void { // Explicit init if needed, currently empty as we rely on pre-init } fn write_char_arm64(c: u8) void { const dr: *volatile u32 = @ptrFromInt(PL011_BASE + PL011_DR); const fr: *volatile u32 = @ptrFromInt(PL011_BASE + PL011_FR); while ((fr.* & PL011_TXFF) != 0) {} dr.* = c; } fn write_char_riscv64(c: u8) void { const thr: *volatile u8 = @ptrFromInt(NS16550A_BASE + NS16550A_THR); const lsr: *volatile u8 = @ptrFromInt(NS16550A_BASE + NS16550A_LSR); // Wait for THRE (Transmitter Holding Register Empty) while ((lsr.* & NS16550A_THRE) == 0) {} thr.* = c; } fn write_char(c: u8) void { switch (builtin.cpu.arch) { .aarch64 => write_char_arm64(c), .riscv64 => write_char_riscv64(c), else => {}, // Do nothing on others } } pub fn write_bytes(bytes: []const u8) void { for (bytes) |b| { if (b == '\n') { write_char('\r'); } write_char(b); } } pub fn puts(s: []const u8) void { write_bytes(s); } pub fn putc(c: u8) void { if (c == '\n') { write_char('\r'); } write_char(c); } pub fn print(s: []const u8) void { puts(s); } pub const Writer = struct { pub const Error = error{}; pub fn write(self: Writer, bytes: []const u8) Error!usize { _ = self; write_bytes(bytes); return bytes.len; } pub fn print(self: Writer, comptime fmt: []const u8, args: anytype) Error!void { return std.fmt.format(self, fmt, args); } }; pub fn print_hex(value: usize) void { const hex_chars = "0123456789ABCDEF"; write_bytes("0x"); var i: usize = 0; while (i < 16) : (i += 1) { const shift: u6 = @intCast((15 - i) * 4); const nibble = (value >> shift) & 0xF; write_char(hex_chars[nibble]); } }