826 lines
23 KiB
C
826 lines
23 KiB
C
#include <stdarg.h>
|
|
#include <stdint.h>
|
|
#include <stddef.h>
|
|
#include <stdbool.h>
|
|
#include <string.h>
|
|
#include <math.h>
|
|
#include "../../include/io/ports.h"
|
|
#include "../../include/io/serial.h"
|
|
|
|
static volatile uint8_t serial_lock = 0;
|
|
static uint16_t default_serial_port = 0;
|
|
|
|
void serial_initialize(uint16_t port, uint32_t baud_rate) {
|
|
outb(port + 1, 0x00);
|
|
|
|
uint16_t divisor = 115200 / baud_rate;
|
|
outb(port + 3, 0x80);
|
|
outb(port + 0, divisor & 0xFF);
|
|
outb(port + 1, (divisor >> 8) & 0xFF);
|
|
|
|
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;
|
|
}
|
|
|
|
outb(port + 4, 0x0F);
|
|
|
|
default_serial_port = port;
|
|
}
|
|
|
|
uint16_t serial_get_default_port(void) {
|
|
return default_serial_port;
|
|
}
|
|
|
|
void serial_set_default_port(uint16_t port) {
|
|
default_serial_port = port;
|
|
}
|
|
|
|
void serial_force_unlock(void) {
|
|
__sync_lock_release(&serial_lock);
|
|
asm volatile("sti");
|
|
}
|
|
|
|
int serial_received_port(uint16_t port) {
|
|
return inb(port + 5) & 1;
|
|
}
|
|
|
|
int serial_received(void) {
|
|
if (default_serial_port == 0) return 0;
|
|
return serial_received_port(default_serial_port);
|
|
}
|
|
|
|
char serial_read_port(uint16_t port) {
|
|
while (serial_received_port(port) == 0);
|
|
return inb(port);
|
|
}
|
|
|
|
char serial_read(void) {
|
|
if (default_serial_port == 0) return 0;
|
|
return serial_read_port(default_serial_port);
|
|
}
|
|
|
|
int serial_is_transmit_empty_port(uint16_t port) {
|
|
return inb(port + 5) & 0x20;
|
|
}
|
|
|
|
int serial_is_transmit_empty(void) {
|
|
if (default_serial_port == 0) return 1;
|
|
return serial_is_transmit_empty_port(default_serial_port);
|
|
}
|
|
|
|
void serial_write_port(uint16_t port, char c) {
|
|
while (serial_is_transmit_empty_port(port) == 0);
|
|
outb(port, c);
|
|
}
|
|
|
|
void serial_write(char c) {
|
|
if (default_serial_port == 0) return;
|
|
serial_write_port(default_serial_port, c);
|
|
}
|
|
|
|
void serial_writestring_port(uint16_t port, const char* str) {
|
|
while (*str) {
|
|
serial_write_port(port, *str++);
|
|
}
|
|
}
|
|
|
|
void serial_writestring(const char* str) {
|
|
if (default_serial_port == 0) return;
|
|
uint64_t flags;
|
|
asm volatile("pushfq; pop %0; cli" : "=r"(flags) :: "memory");
|
|
while (__sync_lock_test_and_set(&serial_lock, 1))
|
|
__asm__ volatile("pause" ::: "memory");
|
|
serial_writestring_port(default_serial_port, str);
|
|
__sync_lock_release(&serial_lock);
|
|
asm volatile("push %0; popfq" :: "r"(flags) : "memory", "cc");
|
|
}
|
|
|
|
|
|
void serial_writebuf(const char* buf, size_t len) {
|
|
if (default_serial_port == 0 || !buf || len == 0) return;
|
|
uint64_t flags;
|
|
asm volatile("pushfq; pop %0; cli" : "=r"(flags) :: "memory");
|
|
while (__sync_lock_test_and_set(&serial_lock, 1))
|
|
__asm__ volatile("pause" ::: "memory");
|
|
for (size_t i = 0; i < len; i++)
|
|
serial_write_port(default_serial_port, buf[i]);
|
|
__sync_lock_release(&serial_lock);
|
|
asm volatile("push %0; popfq" :: "r"(flags) : "memory", "cc");
|
|
}
|
|
|
|
|
|
|
|
static void reverse_string(char* str, int length) {
|
|
int start = 0;
|
|
int end = length - 1;
|
|
while (start < end) {
|
|
char temp = str[start];
|
|
str[start] = str[end];
|
|
str[end] = temp;
|
|
start++;
|
|
end--;
|
|
}
|
|
}
|
|
|
|
static void uint_to_str(uint64_t value, char* buffer, int base, bool uppercase) {
|
|
char* ptr = buffer;
|
|
|
|
if (base < 2 || base > 36) {
|
|
*ptr = '\0';
|
|
return;
|
|
}
|
|
|
|
const char* digits = uppercase ? "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" :
|
|
"0123456789abcdefghijklmnopqrstuvwxyz";
|
|
|
|
if (value == 0) {
|
|
*ptr++ = '0';
|
|
*ptr = '\0';
|
|
return;
|
|
}
|
|
|
|
while (value > 0) {
|
|
int digit = value % base;
|
|
*ptr++ = digits[digit];
|
|
value /= base;
|
|
}
|
|
|
|
*ptr = '\0';
|
|
reverse_string(buffer, ptr - buffer);
|
|
}
|
|
|
|
static void int_to_str(int64_t value, char* buffer, int base, bool uppercase) {
|
|
if (value < 0 && (base == 10 || base == 8)) {
|
|
buffer[0] = '-';
|
|
uint_to_str(-value, buffer + 1, base, uppercase);
|
|
} else {
|
|
uint_to_str(value, buffer, base, uppercase);
|
|
}
|
|
}
|
|
|
|
static uint64_t pow10_u64(int n) {
|
|
uint64_t r = 1;
|
|
while (n-- > 0) r *= 10;
|
|
return r;
|
|
}
|
|
|
|
static int double_to_string(double value, char* buffer, int precision) {
|
|
if (isnan(value)) {
|
|
strcpy(buffer, "nan");
|
|
return 3;
|
|
}
|
|
|
|
if (isinf(value)) {
|
|
if (value < 0) {
|
|
strcpy(buffer, "-inf");
|
|
return 4;
|
|
} else {
|
|
strcpy(buffer, "inf");
|
|
return 3;
|
|
}
|
|
}
|
|
|
|
if (precision < 0) precision = 6;
|
|
if (precision > 16) precision = 16;
|
|
|
|
bool negative = (value < 0);
|
|
double abs_val = negative ? -value : value;
|
|
|
|
uint64_t mult = pow10_u64(precision);
|
|
double scaled = abs_val * mult + 0.5;
|
|
uint64_t int_scaled = (uint64_t)scaled;
|
|
|
|
uint64_t int_part = int_scaled / mult;
|
|
uint64_t frac_part = int_scaled % mult;
|
|
|
|
char* ptr = buffer;
|
|
if (negative) *ptr++ = '-';
|
|
|
|
char int_buf[32];
|
|
char* p = int_buf + sizeof(int_buf) - 1;
|
|
*p = '\0';
|
|
|
|
if (int_part == 0) {
|
|
*--p = '0';
|
|
} else {
|
|
uint64_t n = int_part;
|
|
while (n > 0) {
|
|
*--p = '0' + (n % 10);
|
|
n /= 10;
|
|
}
|
|
}
|
|
|
|
size_t int_len = (int_buf + sizeof(int_buf) - 1) - p;
|
|
memcpy(ptr, p, int_len);
|
|
ptr += int_len;
|
|
|
|
if (precision > 0) {
|
|
*ptr++ = '.';
|
|
|
|
char frac_buf[32];
|
|
char* f = frac_buf + sizeof(frac_buf) - 1;
|
|
*f = '\0';
|
|
|
|
if (frac_part == 0) {
|
|
for (int i = 0; i < precision; i++) {
|
|
*--f = '0';
|
|
}
|
|
} else {
|
|
uint64_t n = frac_part;
|
|
int count = 0;
|
|
while (n > 0) {
|
|
*--f = '0' + (n % 10);
|
|
n /= 10;
|
|
count++;
|
|
}
|
|
while (count < precision) {
|
|
*--f = '0';
|
|
count++;
|
|
}
|
|
}
|
|
|
|
size_t frac_len = (frac_buf + sizeof(frac_buf) - 1) - f;
|
|
memcpy(ptr, f, frac_len);
|
|
ptr += frac_len;
|
|
}
|
|
|
|
*ptr = '\0';
|
|
return ptr - buffer;
|
|
}
|
|
|
|
static int double_to_scientific(double value, char* buffer, int precision, bool uppercase) {
|
|
if (isnan(value)) {
|
|
strcpy(buffer, "nan");
|
|
return 3;
|
|
}
|
|
|
|
if (isinf(value)) {
|
|
if (value < 0) {
|
|
strcpy(buffer, "-inf");
|
|
return 4;
|
|
} else {
|
|
strcpy(buffer, "inf");
|
|
return 3;
|
|
}
|
|
}
|
|
|
|
if (value == 0.0) {
|
|
if (precision < 0) precision = 6;
|
|
buffer[0] = '0';
|
|
buffer[1] = '.';
|
|
for (int i = 0; i < precision; i++) {
|
|
buffer[2 + i] = '0';
|
|
}
|
|
buffer[2 + precision] = uppercase ? 'E' : 'e';
|
|
buffer[3 + precision] = '+';
|
|
buffer[4 + precision] = '0';
|
|
buffer[5 + precision] = '0';
|
|
buffer[6 + precision] = '\0';
|
|
return 6 + precision;
|
|
}
|
|
|
|
if (precision < 0) precision = 6;
|
|
if (precision > 16) precision = 16;
|
|
|
|
double abs_val = value < 0 ? -value : value;
|
|
int exponent = 0;
|
|
|
|
if (abs_val >= 10.0) {
|
|
while (abs_val >= 10.0) {
|
|
abs_val /= 10.0;
|
|
exponent++;
|
|
}
|
|
} else if (abs_val < 1.0 && abs_val > 0.0) {
|
|
while (abs_val < 1.0) {
|
|
abs_val *= 10.0;
|
|
exponent--;
|
|
}
|
|
}
|
|
|
|
if (value < 0) abs_val = -abs_val;
|
|
|
|
int len = double_to_string(abs_val, buffer, precision);
|
|
|
|
char e_char = uppercase ? 'E' : 'e';
|
|
buffer[len++] = e_char;
|
|
|
|
if (exponent >= 0) {
|
|
buffer[len++] = '+';
|
|
} else {
|
|
buffer[len++] = '-';
|
|
exponent = -exponent;
|
|
}
|
|
|
|
if (exponent < 10) {
|
|
buffer[len++] = '0';
|
|
buffer[len++] = '0' + exponent;
|
|
} else {
|
|
buffer[len++] = '0' + (exponent / 10);
|
|
buffer[len++] = '0' + (exponent % 10);
|
|
}
|
|
|
|
buffer[len] = '\0';
|
|
return len;
|
|
}
|
|
|
|
static int double_to_general(double value, char* buffer, int precision, bool uppercase) {
|
|
double abs_val = value < 0 ? -value : value;
|
|
|
|
if (abs_val == 0.0) {
|
|
strcpy(buffer, "0");
|
|
return 1;
|
|
}
|
|
|
|
if (precision < 0) precision = 6;
|
|
if (precision == 0) precision = 1;
|
|
|
|
bool use_scientific = (abs_val >= 1e6 || (abs_val < 1e-4 && abs_val > 0));
|
|
|
|
if (use_scientific) {
|
|
return double_to_scientific(value, buffer, precision - 1, uppercase);
|
|
}
|
|
|
|
int digits_before = 0;
|
|
double temp = abs_val;
|
|
while (temp >= 1.0) {
|
|
temp /= 10.0;
|
|
digits_before++;
|
|
}
|
|
if (digits_before == 0) digits_before = 1;
|
|
|
|
int decimal_places = precision - digits_before;
|
|
if (decimal_places < 0) decimal_places = 0;
|
|
|
|
return double_to_string(value, buffer, decimal_places);
|
|
}
|
|
|
|
void serial_printf_port(uint16_t port, const char* format, ...) {
|
|
uint64_t flags;
|
|
asm volatile("pushfq; pop %0; cli" : "=r"(flags) :: "memory");
|
|
while (__sync_lock_test_and_set(&serial_lock, 1)) {
|
|
__asm__ volatile("pause" ::: "memory");
|
|
}
|
|
|
|
va_list args;
|
|
va_start(args, format);
|
|
|
|
char buffer[256];
|
|
const char* ptr = format;
|
|
|
|
while (*ptr) {
|
|
if (*ptr != '%') {
|
|
serial_write_port(port, *ptr);
|
|
ptr++;
|
|
continue;
|
|
}
|
|
|
|
const char* percent_start = ptr++;
|
|
|
|
while (*ptr == '0' || *ptr == '-' || *ptr == '+' || *ptr == ' ' || *ptr == '#') {
|
|
ptr++;
|
|
}
|
|
|
|
int width = 0;
|
|
while (*ptr >= '0' && *ptr <= '9') {
|
|
width = width * 10 + (*ptr - '0');
|
|
ptr++;
|
|
}
|
|
|
|
int precision = -1;
|
|
if (*ptr == '.') {
|
|
ptr++;
|
|
precision = 0;
|
|
while (*ptr >= '0' && *ptr <= '9') {
|
|
precision = precision * 10 + (*ptr - '0');
|
|
ptr++;
|
|
}
|
|
}
|
|
|
|
bool has_ll = false;
|
|
bool has_l = false;
|
|
bool has_size_t = false;
|
|
|
|
if (*ptr == 'z') {
|
|
ptr++;
|
|
has_size_t = true;
|
|
} else if (*ptr == 'l') {
|
|
ptr++;
|
|
if (*ptr == 'l') {
|
|
ptr++;
|
|
has_ll = true;
|
|
} else {
|
|
has_l = true;
|
|
}
|
|
} else if (*ptr == 'h') {
|
|
ptr++;
|
|
if (*ptr == 'h') ptr++;
|
|
} else if (*ptr == 'L' || *ptr == 'j' || *ptr == 't') {
|
|
ptr++;
|
|
}
|
|
|
|
switch (*ptr) {
|
|
case 'c': {
|
|
char c = (char)va_arg(args, int);
|
|
serial_write_port(port, c);
|
|
break;
|
|
}
|
|
|
|
case 's': {
|
|
const char* str = va_arg(args, const char*);
|
|
if (!str) {
|
|
str = "(null)";
|
|
}
|
|
serial_writestring_port(port, str);
|
|
break;
|
|
}
|
|
|
|
case 'd':
|
|
case 'i': {
|
|
int64_t num;
|
|
if (has_size_t) {
|
|
num = (int64_t)va_arg(args, size_t);
|
|
} else if (has_ll) {
|
|
num = va_arg(args, int64_t);
|
|
} else if (has_l) {
|
|
num = (int64_t)va_arg(args, long);
|
|
} else {
|
|
num = (int64_t)va_arg(args, int);
|
|
}
|
|
int_to_str(num, buffer, 10, false);
|
|
serial_writestring_port(port, buffer);
|
|
break;
|
|
}
|
|
|
|
case 'u': {
|
|
uint64_t num;
|
|
if (has_size_t) {
|
|
num = (uint64_t)va_arg(args, size_t);
|
|
} else if (has_ll) {
|
|
num = va_arg(args, uint64_t);
|
|
} else if (has_l) {
|
|
num = (uint64_t)va_arg(args, unsigned long);
|
|
} else {
|
|
num = (uint64_t)va_arg(args, unsigned int);
|
|
}
|
|
uint_to_str(num, buffer, 10, false);
|
|
serial_writestring_port(port, buffer);
|
|
break;
|
|
}
|
|
|
|
case 'x': {
|
|
uint64_t num;
|
|
if (has_size_t) {
|
|
num = (uint64_t)va_arg(args, size_t);
|
|
} else if (has_ll) {
|
|
num = va_arg(args, uint64_t);
|
|
} else if (has_l) {
|
|
num = (uint64_t)va_arg(args, unsigned long);
|
|
} else {
|
|
num = (uint64_t)va_arg(args, unsigned int);
|
|
}
|
|
uint_to_str(num, buffer, 16, false);
|
|
serial_writestring_port(port, buffer);
|
|
break;
|
|
}
|
|
|
|
case 'X': {
|
|
uint64_t num;
|
|
if (has_size_t) {
|
|
num = (uint64_t)va_arg(args, size_t);
|
|
} else if (has_ll) {
|
|
num = va_arg(args, uint64_t);
|
|
} else if (has_l) {
|
|
num = (uint64_t)va_arg(args, unsigned long);
|
|
} else {
|
|
num = (uint64_t)va_arg(args, unsigned int);
|
|
}
|
|
uint_to_str(num, buffer, 16, true);
|
|
serial_writestring_port(port, buffer);
|
|
break;
|
|
}
|
|
|
|
case 'o': {
|
|
uint64_t num;
|
|
if (has_size_t) {
|
|
num = (uint64_t)va_arg(args, size_t);
|
|
} else if (has_ll) {
|
|
num = va_arg(args, uint64_t);
|
|
} else if (has_l) {
|
|
num = (uint64_t)va_arg(args, unsigned long);
|
|
} else {
|
|
num = (uint64_t)va_arg(args, unsigned int);
|
|
}
|
|
uint_to_str(num, buffer, 8, false);
|
|
serial_writestring_port(port, buffer);
|
|
break;
|
|
}
|
|
|
|
case 'p': {
|
|
void* ptr_val = va_arg(args, void*);
|
|
serial_writestring_port(port, "0x");
|
|
uint_to_str((uintptr_t)ptr_val, buffer, 16, false);
|
|
serial_writestring_port(port, buffer);
|
|
break;
|
|
}
|
|
|
|
case 'f':
|
|
case 'F': {
|
|
double num = va_arg(args, double);
|
|
double_to_string(num, buffer, precision);
|
|
serial_writestring_port(port, buffer);
|
|
break;
|
|
}
|
|
|
|
case 'e':
|
|
case 'E': {
|
|
double num = va_arg(args, double);
|
|
double_to_scientific(num, buffer, precision, (*ptr == 'E'));
|
|
serial_writestring_port(port, buffer);
|
|
break;
|
|
}
|
|
|
|
case 'g':
|
|
case 'G': {
|
|
double num = va_arg(args, double);
|
|
double_to_general(num, buffer, precision, (*ptr == 'G'));
|
|
serial_writestring_port(port, buffer);
|
|
break;
|
|
}
|
|
|
|
case 'a':
|
|
case 'A': {
|
|
double num = va_arg(args, double);
|
|
double_to_scientific(num, buffer, precision, (*ptr == 'A'));
|
|
buffer[0] = '0';
|
|
buffer[1] = 'x';
|
|
serial_writestring_port(port, buffer);
|
|
break;
|
|
}
|
|
|
|
case 'n': {
|
|
int* count_ptr = va_arg(args, int*);
|
|
*count_ptr = 0;
|
|
break;
|
|
}
|
|
|
|
case '%': {
|
|
serial_write_port(port, '%');
|
|
break;
|
|
}
|
|
|
|
default: {
|
|
for (const char* p = percent_start; p <= ptr; p++) {
|
|
serial_write_port(port, *p);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (*ptr != '\0') {
|
|
ptr++;
|
|
}
|
|
}
|
|
|
|
va_end(args);
|
|
__sync_lock_release(&serial_lock);
|
|
asm volatile("push %0; popfq" :: "r"(flags) : "memory", "cc");
|
|
}
|
|
|
|
void serial_printf(const char* format, ...) {
|
|
if (default_serial_port == 0) return;
|
|
uint64_t flags;
|
|
asm volatile("pushfq; pop %0; cli" : "=r"(flags) :: "memory");
|
|
while (__sync_lock_test_and_set(&serial_lock, 1)) {
|
|
__asm__ volatile("pause" ::: "memory");
|
|
}
|
|
|
|
va_list args;
|
|
va_start(args, format);
|
|
|
|
char buffer[256];
|
|
const char* ptr = format;
|
|
|
|
while (*ptr) {
|
|
if (*ptr != '%') {
|
|
serial_write_port(default_serial_port, *ptr);
|
|
ptr++;
|
|
continue;
|
|
}
|
|
|
|
const char* percent_start = ptr++;
|
|
|
|
while (*ptr == '0' || *ptr == '-' || *ptr == '+' || *ptr == ' ' || *ptr == '#') {
|
|
ptr++;
|
|
}
|
|
|
|
int width = 0;
|
|
while (*ptr >= '0' && *ptr <= '9') {
|
|
width = width * 10 + (*ptr - '0');
|
|
ptr++;
|
|
}
|
|
|
|
int precision = -1;
|
|
if (*ptr == '.') {
|
|
ptr++;
|
|
precision = 0;
|
|
while (*ptr >= '0' && *ptr <= '9') {
|
|
precision = precision * 10 + (*ptr - '0');
|
|
ptr++;
|
|
}
|
|
}
|
|
|
|
bool has_ll = false;
|
|
bool has_l = false;
|
|
bool has_size_t = false;
|
|
|
|
if (*ptr == 'z') {
|
|
ptr++;
|
|
has_size_t = true;
|
|
} else if (*ptr == 'l') {
|
|
ptr++;
|
|
if (*ptr == 'l') {
|
|
ptr++;
|
|
has_ll = true;
|
|
} else {
|
|
has_l = true;
|
|
}
|
|
} else if (*ptr == 'h') {
|
|
ptr++;
|
|
if (*ptr == 'h') ptr++;
|
|
} else if (*ptr == 'L' || *ptr == 'j' || *ptr == 't') {
|
|
ptr++;
|
|
}
|
|
|
|
switch (*ptr) {
|
|
case 'c': {
|
|
char c = (char)va_arg(args, int);
|
|
serial_write_port(default_serial_port, c);
|
|
break;
|
|
}
|
|
|
|
case 's': {
|
|
const char* str = va_arg(args, const char*);
|
|
if (!str) {
|
|
str = "(null)";
|
|
}
|
|
serial_writestring_port(default_serial_port, str);
|
|
break;
|
|
}
|
|
|
|
case 'd':
|
|
case 'i': {
|
|
int64_t num;
|
|
if (has_size_t) {
|
|
num = (int64_t)va_arg(args, size_t);
|
|
} else if (has_ll) {
|
|
num = va_arg(args, int64_t);
|
|
} else if (has_l) {
|
|
num = (int64_t)va_arg(args, long);
|
|
} else {
|
|
num = (int64_t)va_arg(args, int);
|
|
}
|
|
int_to_str(num, buffer, 10, false);
|
|
serial_writestring_port(default_serial_port, buffer);
|
|
break;
|
|
}
|
|
|
|
case 'u': {
|
|
uint64_t num;
|
|
if (has_size_t) {
|
|
num = (uint64_t)va_arg(args, size_t);
|
|
} else if (has_ll) {
|
|
num = va_arg(args, uint64_t);
|
|
} else if (has_l) {
|
|
num = (uint64_t)va_arg(args, unsigned long);
|
|
} else {
|
|
num = (uint64_t)va_arg(args, unsigned int);
|
|
}
|
|
uint_to_str(num, buffer, 10, false);
|
|
serial_writestring_port(default_serial_port, buffer);
|
|
break;
|
|
}
|
|
|
|
case 'x': {
|
|
uint64_t num;
|
|
if (has_size_t) {
|
|
num = (uint64_t)va_arg(args, size_t);
|
|
} else if (has_ll) {
|
|
num = va_arg(args, uint64_t);
|
|
} else if (has_l) {
|
|
num = (uint64_t)va_arg(args, unsigned long);
|
|
} else {
|
|
num = (uint64_t)va_arg(args, unsigned int);
|
|
}
|
|
uint_to_str(num, buffer, 16, false);
|
|
serial_writestring_port(default_serial_port, buffer);
|
|
break;
|
|
}
|
|
|
|
case 'X': {
|
|
uint64_t num;
|
|
if (has_size_t) {
|
|
num = (uint64_t)va_arg(args, size_t);
|
|
} else if (has_ll) {
|
|
num = va_arg(args, uint64_t);
|
|
} else if (has_l) {
|
|
num = (uint64_t)va_arg(args, unsigned long);
|
|
} else {
|
|
num = (uint64_t)va_arg(args, unsigned int);
|
|
}
|
|
uint_to_str(num, buffer, 16, true);
|
|
serial_writestring_port(default_serial_port, buffer);
|
|
break;
|
|
}
|
|
|
|
case 'o': {
|
|
uint64_t num;
|
|
if (has_size_t) {
|
|
num = (uint64_t)va_arg(args, size_t);
|
|
} else if (has_ll) {
|
|
num = va_arg(args, uint64_t);
|
|
} else if (has_l) {
|
|
num = (uint64_t)va_arg(args, unsigned long);
|
|
} else {
|
|
num = (uint64_t)va_arg(args, unsigned int);
|
|
}
|
|
uint_to_str(num, buffer, 8, false);
|
|
serial_writestring_port(default_serial_port, buffer);
|
|
break;
|
|
}
|
|
|
|
case 'p': {
|
|
void* ptr_val = va_arg(args, void*);
|
|
serial_writestring_port(default_serial_port, "0x");
|
|
uint_to_str((uintptr_t)ptr_val, buffer, 16, false);
|
|
serial_writestring_port(default_serial_port, buffer);
|
|
break;
|
|
}
|
|
|
|
case 'f':
|
|
case 'F': {
|
|
double num = va_arg(args, double);
|
|
double_to_string(num, buffer, precision);
|
|
serial_writestring_port(default_serial_port, buffer);
|
|
break;
|
|
}
|
|
|
|
case 'e':
|
|
case 'E': {
|
|
double num = va_arg(args, double);
|
|
double_to_scientific(num, buffer, precision, (*ptr == 'E'));
|
|
serial_writestring_port(default_serial_port, buffer);
|
|
break;
|
|
}
|
|
|
|
case 'g':
|
|
case 'G': {
|
|
double num = va_arg(args, double);
|
|
double_to_general(num, buffer, precision, (*ptr == 'G'));
|
|
serial_writestring_port(default_serial_port, buffer);
|
|
break;
|
|
}
|
|
|
|
case 'a':
|
|
case 'A': {
|
|
double num = va_arg(args, double);
|
|
double_to_scientific(num, buffer, precision, (*ptr == 'A'));
|
|
buffer[0] = '0';
|
|
buffer[1] = 'x';
|
|
serial_writestring_port(default_serial_port, buffer);
|
|
break;
|
|
}
|
|
|
|
case 'n': {
|
|
int* count_ptr = va_arg(args, int*);
|
|
*count_ptr = 0;
|
|
break;
|
|
}
|
|
|
|
case '%': {
|
|
serial_write_port(default_serial_port, '%');
|
|
break;
|
|
}
|
|
|
|
default: {
|
|
for (const char* p = percent_start; p <= ptr; p++) {
|
|
serial_write_port(default_serial_port, *p);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (*ptr != '\0') {
|
|
ptr++;
|
|
}
|
|
}
|
|
|
|
va_end(args);
|
|
__sync_lock_release(&serial_lock);
|
|
asm volatile("push %0; popfq" :: "r"(flags) : "memory", "cc");
|
|
} |