Merge pull request #41 from SamTebbs33/feature/serial-output

Add serial output
This commit is contained in:
Sam Tebbs 2019-06-24 22:56:24 +01:00 committed by GitHub
commit 1b8244adfa
2 changed files with 83 additions and 0 deletions

View file

@ -5,6 +5,7 @@ const arch = if (builtin.is_test) @import("../../test/kernel/arch_mock.zig") els
const multiboot = @import("multiboot.zig"); const multiboot = @import("multiboot.zig");
const tty = @import("tty.zig"); const tty = @import("tty.zig");
const vga = @import("vga.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 // 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 // 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(); arch.init();
vga.init(); vga.init();
tty.init(); tty.init();
serial.init(serial.DEFAULT_BAUDRATE, serial.Port.COM1) catch unreachable;
tty.print("Hello Pluto from kernel :)\n"); tty.print("Hello Pluto from kernel :)\n");
// Enable interrupts // Enable interrupts

80
src/kernel/serial.zig Normal file
View 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);
}
}