/************************ *** Team Kitty, 2019 *** *** Sync *** ***********************/ /* This file provides an interface to the serial port. * Most emulators allow the serial port to be saved into * a text buffer, or a file, but this code should work on * hardware with an actual serial port and monitor. */ /* After the development of Syncboot, this becomes a little bit harder. * Some testing will be needed to know for certain whether this will work. * Until then, this is to be put on hold. 21/07/19 - Curle */ #include #define SERIAL_DATA_PORT(base) (base) #define SERIAL_FIFO_COMMAND_PORT(base) (base + 2) #define SERIAL_LINE_COMMAND_PORT(base) (base + 3) #define SERIAL_MODEM_COMMAND_PORT(base) (base + 4) #define SERIAL_LINE_STATUS_PORT(base) (base + 5) #define SERIAL_COM1_BASE 0x3F8 #define SERIAL_LINE_ENABLE_DLAB 0x80 /** serial_set_baud_rate: * Sets the speed that data is sent via the serial port. * The default speed is 115200 bits/s. * The argument given is the divisor of the number. * Hence, the new speed becomes (115200/divisor) bits/s. * * @param com The COM port to configure. * @param divisor The new divisor for the baud rate. */ void serial_set_baud_rate(uint16_t com, uint16_t divisor) { WritePort(SERIAL_LINE_COMMAND_PORT(com), SERIAL_LINE_ENABLE_DLAB, 8); WritePort(SERIAL_DATA_PORT(com), (divisor >> 8) & 0x00FF, 8); WritePort(SERIAL_DATA_PORT(com), divisor & 0x00FF, 8); } /** serial_configure_line: * Sets a specific data byteset in the Serial hardware. * Required for expected operation. * * @param com The COM port to configure. */ void serial_configure_line(uint16_t com) { /* Bit: | 7 | 6 | 5 4 3 | 2 | 1 0 | * Content: | d | b | parity| s | dl | * Value: | 0 | 0 | 0 0 0 | 0 | 1 1 | = 0x03 */ WritePort(SERIAL_LINE_COMMAND_PORT(com), 0x0B, 8); } /** serial_configure_buffers: * Enables FIFO (First In First Out) on the Serial line. * It clears both the receive and transmit queues. * It uses a 14 byte wide queue. * * @param com The COM port to configure. */ void serial_configure_buffers(uint16_t com) { /* Bit: | 7 6 | 5 | 4 | 3 | 2 | 1 | 0 | * Content: | 1v1 | bs | r | dma | clt | clr | e | * Value: | 1 1 | 0 | 0 | 0 | 1 | 1 | 1 | = 0xC7 */ WritePort(SERIAL_FIFO_COMMAND_PORT(com), 0xC7, 8); } /** serial_configure_modem * @param com The COM port to configure. */ void serial_configure_modem(uint16_t com) { /* Bit: | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | * Content: | r | r | af | lb | ao2 | ao1 | rts | dtr | * Value: | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | = 0x03 */ WritePort(SERIAL_MODEM_COMMAND_PORT(com), 0x3, 8); } /** serial_check_tqueue: * Checks whether the Transmit FIFO Queue is empty on the given port. * * @param com The COM port to check. * @return 0 If the queue is not empty. * 1 If the queue is empty. */ int serial_check_tqueue(uint16_t com) { return ReadPort(SERIAL_LINE_STATUS_PORT(com), 8) & 0x20; } /** serial_write: * Prints a single character to the serial port. * Does nothing fancy. * @param com The COM port to write to. * @param data The character to write. */ void serial_write(uint16_t com, const char chr) { //Hang until we have access to the COM port. while(serial_check_tqueue(com) == 0); WritePort(com, chr, 0); } /** serial_print: * Prints a string to the serial port. * Does not support ANSI codes, or color. * * @param com The COM port to write to. * @param data The string to write. */ void serial_print(uint16_t com, const char* data) { for(size_t i = 0; i < strlen(data); i++) { serial_write(com, data[i]); } } /** serial_printf: * A printf-style function for the serial port. * * @param format The base string - used to substitute the succeding values. * @param ... The substitutions. */ void serial_printf(uint16_t com, const char* format, ...) { uint32_t storage; //To hold temporary variables char stringstore[10] = {0}; //To convert ints to strings. va_list list; va_start(list, format); for(size_t i = 0; i < strlen(format); i++) { //Check for the format specifier if(format[i] == '%') { //Check for the other specs if(format[i+1] == 'd') { storage = va_arg(list, int); int_to_ascii(storage, stringstore); serial_print(com, stringstore); empty_string(stringstore); i += 2; } else if(format[i+1] == 'x') { storage = va_arg(list, int); int_to_hex(storage, stringstore); serial_print(com, stringstore); empty_string(stringstore); i += 2; } else if(format[i+1] == 's') { serial_print(com, va_arg(list, char*)); i += 2; } else { serial_print(com, "ERROR: Attempting to parse unknown format string."); return; } } else { serial_write(com, format[i]); i++; } } va_end(list); //Free the list } void init_serial() { // Disable interrupts WritePort(SERIAL_COM1_BASE + 1, 0x00, 0); // Set baud rate divisor. serial_set_baud_rate(SERIAL_COM1_BASE, 3); // 8 bits, no parity, one stop bit. serial_configure_line(SERIAL_COM1_BASE); // Enable FIFO and clear buffers. serial_configure_buffers(SERIAL_COM1_BASE); // Enable IRQs, RTS/DSR set. serial_configure_modem(SERIAL_COM1_BASE); }