Merge pull request #41 from SamTebbs33/feature/serial-output
Add serial output
This commit is contained in:
commit
1b8244adfa
2 changed files with 83 additions and 0 deletions
|
@ -5,6 +5,7 @@ const arch = if (builtin.is_test) @import("../../test/kernel/arch_mock.zig") els
|
|||
const multiboot = @import("multiboot.zig");
|
||||
const tty = @import("tty.zig");
|
||||
const vga = @import("vga.zig");
|
||||
const serial = @import("serial.zig");
|
||||
|
||||
// Need to import this as we need the panic to be in the root source file, or zig will just use the
|
||||
// builtin panic and just loop, which is what we don't want
|
||||
|
@ -23,6 +24,8 @@ pub export fn kmain(mb_info: *multiboot.multiboot_info_t, mb_magic: u32) void {
|
|||
arch.init();
|
||||
vga.init();
|
||||
tty.init();
|
||||
serial.init(serial.DEFAULT_BAUDRATE, serial.Port.COM1) catch unreachable;
|
||||
|
||||
tty.print("Hello Pluto from kernel :)\n");
|
||||
|
||||
// Enable interrupts
|
||||
|
|
80
src/kernel/serial.zig
Normal file
80
src/kernel/serial.zig
Normal file
|
@ -0,0 +1,80 @@
|
|||
const arch = @import("arch.zig").internals;
|
||||
|
||||
// The LCR is the line control register
|
||||
const LCR = 3;
|
||||
// Maximum baudrate
|
||||
const BAUD_MAX = 115200;
|
||||
// 8 bits per serial character
|
||||
const CHAR_LEN = 8;
|
||||
// One stop bit per transmission
|
||||
const SINGLE_STOP_BIT = true;
|
||||
// No parity bit
|
||||
const PARITY_BIT = false;
|
||||
|
||||
pub const DEFAULT_BAUDRATE = 38400;
|
||||
|
||||
const SerialError = error {
|
||||
InvalidBaud
|
||||
};
|
||||
|
||||
pub const Port = enum(u16) {
|
||||
COM1 = 0x3F8,
|
||||
COM2 = 0x2F8,
|
||||
COM3 = 0x3E8,
|
||||
COM4 = 0x2E8
|
||||
};
|
||||
|
||||
// Compute a value that encodes the serial properties
|
||||
// This is fed into the LCR (line control register)
|
||||
fn lcrValue(char_len: u8, comptime stop_bit: bool, comptime parity_bit: bool, msb: u8) u8 {
|
||||
var val = char_len & 0x3;
|
||||
val |= (if (stop_bit) 0 else 1) << 2;
|
||||
val |= (if (parity_bit) 1 else 0) << 3;
|
||||
val |= (msb & 0x1) << 7;
|
||||
return val;
|
||||
}
|
||||
|
||||
fn baudDivisor(baud: u32) SerialError!u16 {
|
||||
if (baud > BAUD_MAX or baud <= 0) return SerialError.InvalidBaud;
|
||||
return @intCast(u16, BAUD_MAX / baud);
|
||||
}
|
||||
|
||||
fn transmitIsEmpty(port: Port) bool {
|
||||
return arch.inb(@enumToInt(port) + 5) & 0x20 > 0;
|
||||
}
|
||||
|
||||
///
|
||||
/// Initialise a serial port to a certain baudrate
|
||||
///
|
||||
/// Arguments
|
||||
/// IN baud: u32 - The baudrate to use. Cannot be more than MAX_BAUDRATE
|
||||
/// IN port: Port - The port to initialise
|
||||
///
|
||||
/// Throws a SerialError if the baudrate is invalid
|
||||
///
|
||||
pub fn init(baud: u32, port: Port) SerialError!void {
|
||||
// The baudrate is sent as a divisor of the max baud rate
|
||||
const divisor: u16 = try baudDivisor(baud);
|
||||
const port_int = @enumToInt(port);
|
||||
// Send a byte to start setting the baudrate
|
||||
arch.outb(port_int + LCR, lcrValue(0, false, false, 1));
|
||||
// Send the divisor's lsb
|
||||
arch.outb(port_int, @truncate(u8, divisor));
|
||||
// Send the divisor's msb
|
||||
arch.outb(port_int + 1, @truncate(u8, divisor >> 8));
|
||||
// Send the properties to use
|
||||
arch.outb(port_int + LCR, lcrValue(CHAR_LEN, SINGLE_STOP_BIT, PARITY_BIT, 0));
|
||||
// Stop initialisation
|
||||
arch.outb(port_int + 1, 0);
|
||||
}
|
||||
|
||||
pub fn write(char: u8, port: Port) void {
|
||||
while (!transmitIsEmpty(port)) { arch.halt(); }
|
||||
arch.outb(@enumToInt(port), char);
|
||||
}
|
||||
|
||||
pub fn writeString(str: []const u8, port: Port) void {
|
||||
for (str) |char| {
|
||||
write(char, port);
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue