Files
blankos/src/drivers/serial.c
2024-08-09 14:36:01 +02:00

282 lines
5.6 KiB
C

#include "../kernel/io.h"
#include "serial.h"
#include "../libc/stdio.h"
int init_serial()
{
outb(PORT+1, 0x00);
outb(PORT+3, 0x80);
outb(PORT+0, 0x03);
outb(PORT+1, 0x00);
outb(PORT+3, 0x03);
outb(PORT+2, 0xC7);
outb(PORT+4, 0x0B);
outb(PORT+4, 0x1E);
outb(PORT+0, 0xAE);
if (inb(PORT+0) != 0xAE) {
return 1;
}
outb(PORT+4, 0x0F);
return 0;
}
int is_transmit_empty()
{
return inb(PORT+5) & 0x20;
}
void write_serial(const char a)
{
while (is_transmit_empty() == 0);
outb(PORT, a);
}
void serial_puts(const char* str)
{
unsigned int i = 0;
write_serial(str[0]); // Transmit first byte 2 times
while (*str++)
{
write_serial(str[i]);
}
}
void log(const char* str, const int errlevel)
{
switch (errlevel)
{
case 0:
serial_puts("[ERROR] ");
break;
case 1:
serial_puts("[WARNING] ");
break;
case 2:
serial_puts("[INFO] ");
break;
case 3:
serial_puts("[DEBUG] ");
break;
}
serial_puts(str);
}
const char serial_charset[] = "0123456789abcdef";
int* serial_printf_number(int* argp, int length, bool sign, int radix)
{
char buffer[32];
unsigned long long number;
int number_sign = 1;
int pos = 0;
switch(length)
{
case PRINTF_LENGTH_SHORT_SHORT:
case PRINTF_LENGTH_SHORT:
case PRINTF_LENGTH_START:
if (sign)
{
int n = *argp;
if (n < 0)
{
n = -n;
number_sign = -1;
}
number = (unsigned long long) n;
}
else {
number = *(unsigned int*) argp;
}
argp++;
break;
case PRINTF_LENGTH_LONG:
if (sign)
{
long int n = *(long int*)argp;
if (n < 0)
{
n = -n;
number_sign = -1;
}
number = (unsigned long long) n;
}
else {
number = *(unsigned long int*) argp;
}
argp += 2;
break;
case PRINTF_LENGTH_LONG_LONG:
if (sign)
{
long long int n = *(long long int*)argp;
if (n < 0)
{
n = -n;
number_sign = -1;
}
number = (unsigned long long) n;
}
else {
number = *(unsigned long long int*) argp;
}
argp += 4;
break;
}
do {
uint32_t rem;
x86_div64_32(number, radix, &number, &rem);
buffer[pos++] = serial_charset[rem];
} while (number > 0);
if (sign && number_sign < 0)
{
buffer[pos++] = '-';
}
while (--pos >= 0)
{
write_serial(buffer[pos]);
}
return argp;
}
void serial_printf(int errlevel, const char* fmt, ...)
{
switch (errlevel)
{
case 0:
serial_puts("[ERROR] ");
break;
case 1:
serial_puts("[WARNING] ");
break;
case 2:
serial_puts("[INFO] ");
break;
case 3:
serial_puts("[DEBUG] ");
break;
}
int* argp = (int*) &fmt;
int state = PRINTF_STATE_START;
int length = PRINTF_LENGTH_START;
int radix = 10;
bool sign = false;
argp++;
while (*fmt)
{
switch(state) {
case PRINTF_STATE_START:
if (*fmt == '%')
{
state = PRINTF_STATE_LENGTH;
}
else {
write_serial(*fmt);
}
break;
case PRINTF_STATE_LENGTH:
if (*fmt == 'h')
{
length = PRINTF_LENGTH_SHORT;
state = PRINTF_STATE_SHORT;
}
else if (*fmt == 'l')
{
length = PRINTF_LENGTH_LONG;
state = PRINTF_STATE_LONG;
}
else {
goto PRINTF_STATE_SPEC_;
}
break;
case PRINTF_STATE_SHORT:
if (*fmt == 'h')
{
length = PRINTF_LENGTH_SHORT_SHORT;
state = PRINTF_STATE_SPEC;
}
else {
goto PRINTF_STATE_SPEC_;
}
break;
case PRINTF_STATE_LONG:
if (*fmt == 'l')
{
length = PRINTF_LENGTH_LONG_LONG;
state = PRINTF_STATE_SPEC;
}
else {
goto PRINTF_STATE_SPEC_;
}
break;
case PRINTF_STATE_SPEC:
PRINTF_STATE_SPEC_:
switch(*fmt)
{
case 'c':
serial_puts(*(const char **)argp);
argp++;
break;
case 's':
serial_puts(*(const char **)argp);
argp++;
break;
case '%':
putc('%');
break;
case 'd':
case 'i':
radix = 10;
sign = true;
argp = serial_printf_number(argp, length, sign, radix);
break;
case 'u':
radix = 10;
sign = false;
argp = serial_printf_number(argp, length, sign, radix);
break;
case 'X':
case 'x':
case 'p':
radix = 16;
sign = false;
argp = serial_printf_number(argp, length, sign, radix);
break;
case 'o':
radix = 8;
sign = false;
argp = serial_printf_number(argp, length, sign, radix);
break;
case 'f': {
// Handle floating-point numbers
double* dargp = (double*)argp;
double d = *(double*)dargp;
char buffer[64];
dtostrf(d, buffer, 6); // Default precision: 6
serial_puts(buffer);
argp += 2; // Incrementing by 2 to move past the double argument
break;
}
default:
break;
}
state = PRINTF_STATE_START;
length = PRINTF_LENGTH_START;
radix = 10;
sign = false;
break;
}
fmt++;
}
serial_puts("\n");
}