From 981c224f55321435a1c4e6ee0a547b3b931ff150 Mon Sep 17 00:00:00 2001 From: lordtet Date: Wed, 2 Jul 2025 19:48:33 -0400 Subject: [PATCH] Serial implementation Pt1 Serial testing with COM1 works - need to rework I/O to have it output tho. That's next. --- include/kttools.h | 14 +++++++++++ include/serial.h | 47 +++++++++++++++++++++++++++++++++++++ src/kttools.c | 14 ----------- src/main.c | 50 ++++++++++++++++++++++++++------------- src/serial.c | 60 +++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 155 insertions(+), 30 deletions(-) create mode 100644 include/serial.h create mode 100644 src/serial.c diff --git a/include/kttools.h b/include/kttools.h index d69cfe1..69ae1cb 100644 --- a/include/kttools.h +++ b/include/kttools.h @@ -7,6 +7,20 @@ #define KTTOOLS_H #include +/** + * @brief Convert a hex int to string, up to 32 bits. + * + * Converts int "num" into a null-terminated string, placed into buffer "buf". + * Evaluates from right to left, so left hand digits will be cut off if the buffer is too small + * Should work for non 32-bit integers actually, but is uint32_t for now. + * + * @param num: Value to convert to string + * @param buf: Memory to place the string into + * @param size: Size of the memory buffer + * @param radix: Base counting system to use for output + * @return No return value + * + */ void i_to_str(uint32_t num, char* buf, int size, int radix); #endif diff --git a/include/serial.h b/include/serial.h new file mode 100644 index 0000000..bc3665e --- /dev/null +++ b/include/serial.h @@ -0,0 +1,47 @@ +/* serial.h +* Serial Interface +* +* Send/receive data via serial ports. +*/ +#ifndef SERIAL_H +#define SERIAL_H +#include "asm.h" +#include + +typedef struct SerialState_s { + uint16_t port; +} SerialState_t; + +/** + * @brief Initialize a serial port for communication + * @param port The serial port number to initialize + * @return Status code indicating success or failure + */ +int serial_init(uint16_t port); + +/** + * @brief Check if there is pending data to receive on the serial port + * @return Non-zero if data is pending, 0 otherwise + */ +int serial_recv_pending(); + +/** + * @brief Receive a single byte from the serial port + * @return The received byte as an 8-bit unsigned integer + */ +uint8_t serial_recv8(uint16_t port); + +/** + * @brief Check if the serial port is ready to send data + * @return Non-zero if ready to send, 0 otherwise + */ +int serial_send_pending(uint16_t port); + +/** + * @brief Send a single byte through the serial port + * @param data The 8-bit data byte to send + */ +int serial_send8(uint16_t port, char data); + + +#endif diff --git a/src/kttools.c b/src/kttools.c index 0b3986c..c774308 100644 --- a/src/kttools.c +++ b/src/kttools.c @@ -12,20 +12,6 @@ -/** - * @brief Convert a hex int to string, up to 32 bits. - * - * Converts int "num" into a null-terminated string, placed into buffer "buf". - * Evaluates from right to left, so left hand digits will be cut off if the buffer is too small - * Should work for non 32-bit integers actually, but is uint32_t for now. - * - * @param num: Value to convert to string - * @param buf: Memory to place the string into - * @param size: Size of the memory buffer - * - * @return No return value - * - */ void i_to_str(uint32_t num, char* buf, int size, int radix) { //null terminate the string if(num == 0){ diff --git a/src/main.c b/src/main.c index 4652720..3a63404 100644 --- a/src/main.c +++ b/src/main.c @@ -1,32 +1,50 @@ //Our own code, at this point... -//#include +//Output table: +//0: VGA +//1: COM1 +#define OUTPUT_TYPE 1 #include #include "vga.h" #include "io.h" #include "kttools.h" #include "kmultiboot.h" #include "idt.h" -//finally, main. +#include "serial.h" void kern_main(uint32_t multiboot_magic, mb_info_t* multiboot_info) { //Hello C! Let's get to work in cleaning up our environment a bit and creating some safety. //First interrupts. setup_idt(); - - //wipe the screen - vga_clear(); - - printf(vga_out, "Entry eax:%X\n", multiboot_magic); - - if(multiboot_magic != 0x2BADB002) { - println(vga_out, "Bootloader not multiboot1 compliant! Needed for mmap, etc. Can't work without it, kthxbye!"); - return; - } else { - println(vga_out, "Multiboot detected! Continuing..."); + + //Let's pick an output schema. + int (*outWriter)(char) = 0; + int comstatus = 1234; + switch(OUTPUT_TYPE) { + case 1: + comstatus = serial_init(COM1); + if(comstatus) { + //Fail... + break; + } + outWriter = serial_send8; + break; + } + //VGA is selected or the selected option failed, falling back + if(outWriter == 0){ + outWriter = vga_out; + vga_clear(); } - printf(vga_out, "MEM_LOWER:%X\n", multiboot_info->mem_lower); - printf(vga_out, "MEM_UPPER:%X\n", multiboot_info->mem_upper); - + printf(outWriter, "Entry eax:%X\n", multiboot_magic); + + if(multiboot_magic != 0x2BADB002) { + println(outWriter, writerState, "Bootloader not multiboot1 compliant! Needed for mmap, etc. Can't work without it, kthxbye!"); + return; + } else { + println(outWriter, writerState, "Multiboot detected! Continuing..."); + } + + printf(outWriter, writerState, "MEM_LOWER:%X\n", multiboot_info->mem_lower); + printf(outWriter, writerState"MEM_UPPER:%X\n", multiboot_info->mem_upper); } diff --git a/src/serial.c b/src/serial.c new file mode 100644 index 0000000..b5a5969 --- /dev/null +++ b/src/serial.c @@ -0,0 +1,60 @@ +#include "serial.h" +#include + + +int serial_init(uint16_t port) { + //disable interrupts when messing with it + outb(port + 1, 0x00); + //set the dlab latch in order to make the baud rate accessible. In binary for clarity. + outb(port + 3, 0b10000000); + + //Sets the baud rate. Quick except on baud rate: + //internal clock for the serial controller is 115200 ticks/sec. Or 115200hz. + //This value we set, the "clock divisor", will check it every x ticks. + //So for example, if we wanted to check it every second, we'd want a divisor of 115200. Or, to check it as fast as possible, 1. + //Higher baud rates however equate to more error, so keep the divisor high. + //In ourcase, the osdev wiki uses 3 for the divisor, or 38400 baud, and we will go with this example until further notice. + //(Source:https://wiki.osdev.org/Serial_Ports#Baud_Rate) + + //Baud lo byte + outb(port, 0x03); + //Baud hi byte + outb(port+1, 0x00); + + //Again from OSWiki we're going to use their defaults for now. + //DLAB off + //Parity=0(no Parity) + //One stop bit (set to 0) + //Transmit in units of 8 bits + outb(port+3, 0b00000011); + + //FIFO Control register + //14 bit threshold for interrupt trigger + //flush and enable FIFOs. + outb(port+2, 0b11000111); + + //Enable IRQs + DTR + RTS + //Nicely explained here: https://stackoverflow.com/questions/957337/what-is-the-difference-between-dtr-dsr-and-rts-cts-flow-control + //In the future, this will be referred to 0x0B. + outb(port+4, 0b00001011); + + //done! Let's test our chip. put it in loopback mode. + outb(port+4, 0x1E); + //Lets try sending C4 (boom!) to test it + outb(port, 0xC4); + + if(inb(port) != 0xC4) { + //Boo womp + return 1; + } + + //we're good! All set up. Lets put it back into normal operational mode and gtfo. + outb(port+4, 0x0B); + return 0; + +} + +int serial_send_pending(uint16_t port) { + //Bit 5 of the line status register has our output queue, essentially + return !(inb(port + 5) & 0x20); +}