/* Driver for serial output. Mostly useful for debug output because QEMU allows the redirection of serial output to either stdio or a file on the host computer. */ #include #include #include #include #define COM1_IO_ADDR 0x3F8 #define COM2_IO_ADDR 0x2F8 #define COM1_IRQ 0x4 #define COM2_IRQ 0x3 #define COM1_LCR (COM1_IO_ADDR + 3) #define COM2_LCR (COM2_IO_ADDR + 3) #define BAUD 57600 #define DIVISOR 2 int serial_init(void) { /* The following is taken from https://wiki.osdev.org/Serial_Ports */ outb(COM1_IO_ADDR + 1, 0x00); // Disable all interrupts outb(COM1_IO_ADDR + 3, 0x80); // Enable DLAB (set baud rate divisor) outb(COM1_IO_ADDR + 0, 0x03); // Set divisor to 3 (lo byte) 38400 baud outb(COM1_IO_ADDR + 1, 0x00); // (hi byte) outb(COM1_IO_ADDR + 3, 0x03); // 8 bits, no parity, one stop bit outb(COM1_IO_ADDR + 2, 0xC7); // Enable FIFO, clear them, with 14-byte threshold outb(COM1_IO_ADDR + 4, 0x0B); // IRQs enabled, RTS/DSR set outb(COM1_IO_ADDR + 4, 0x1E); // Set in loopback mode, test the serial chip outb(COM1_IO_ADDR + 0, 0xAE); // Test serial chip (send byte 0xAE and check if serial returns same byte) // Check if serial is faulty (i.e: not same byte as sent) if(inb(COM1_IO_ADDR + 0) != 0xAE) { return 1; } // If serial is not faulty set it in normal operation mode // (not-loopback with IRQs enabled and OUT#1 and OUT#2 bits enabled) outb(COM1_IO_ADDR + 4, 0x0F); return 0; } static int is_transmit_empty(void) { return inb(COM1_IO_ADDR + 5) & 0x20; } static int serial_received(void) { return inb(COM1_IO_ADDR + 5) & 1; } void serial_write(char a) { while (is_transmit_empty() == 0); outb(COM1_IO_ADDR, a); } void serial_puts(const char* s) { for (int i = 0; i < (int) strlen(s); i++) { serial_write(s[i]); } } char serial_read(void) { while (serial_received() == 0); return inb(COM1_IO_ADDR); } bool use_serial(void) { return true; }