rework kprintf

This commit is contained in:
Freya Murphy 2024-02-03 17:05:04 -05:00
parent ac431b0050
commit f785bce3cc
Signed by: freya
GPG key ID: 744AB800E383AE52
3 changed files with 439 additions and 285 deletions

View file

@ -633,7 +633,6 @@ int kload_page(void *virt_addr) {
if (page->loaded) if (page->loaded)
return -1; return -1;
void *phys = alloc_phys_page(); void *phys = alloc_phys_page();
kprintf("0x%p\n", phys);
if (phys == NULL) if (phys == NULL)
return -2; return -2;
page->loaded = 1; page->loaded = 1;

View file

@ -1,3 +1,4 @@
#include <backtrace.h>
#include <fpu.h> #include <fpu.h>
#include <acpi.h> #include <acpi.h>
#include <memory.h> #include <memory.h>
@ -18,6 +19,8 @@ void kmain(struct boot_info *info) {
char *test = kalloc(5); char *test = kalloc(5);
*test = 1; *test = 1;
//log_backtrace();
while (1) { while (1) {
screen_redraw(); screen_redraw();
// loop so we dont halt // loop so we dont halt

View file

@ -1,6 +1,7 @@
#include <lib.h> #include <lib.h>
#include <serial.h> #include <serial.h>
#include <stdarg.h> #include <stdarg.h>
#include <stdint.h>
void kputc(char c) { void kputc(char c) {
serial_out(c); serial_out(c);
@ -31,54 +32,110 @@ struct format_precision {
}; };
enum format_modifier { enum format_modifier {
MOD_NONE, MOD_NONE = 0,
MOD_INVALID, MOD_HALF = 1,
MOD_HALF_HALF, MOD_HALF_HALF = 3,
MOD_HALF, MOD_LONG = 3,
MOD_LONG_LONG, MOD_LONG_LONG = 4,
MOD_LONG, MOD_INVALID = 5,
}; };
enum format_conversion { enum format_conversion {
FMT_INT, // decimal numbers
FMT_UINT, FMT_CHAR = 0,
FMT_INT = 4,
FMT_UINT = 5,
// otehr numbers
FMT_OCT, FMT_OCT,
FMT_HEX, FMT_HEX,
FMT_HEX_UPPER, FMT_HEX_UPPER,
FMT_CHAR, // text
FMT_STR, FMT_STR,
FMT_PTR, FMT_PTR,
FMT_PTR_UPPER, // misc
FMT_PERCENT,
FMT_INVALID FMT_INVALID
}; };
static enum format_flag read_flag(const char *format, const char **end) { enum format_length {
LEN_UCHAR,
LEN_CHAR,
LEN_USHORT,
LEN_SHORT,
LEN_UINT,
LEN_INT,
LEN_ULONG,
LEN_LONG,
LEN_ULONGLONG,
LEN_LONGLONG,
LEN_SIZET,
LEN_INVALID
};
enum format_flag flag = FLG_NONE; struct spacing {
unsigned left;
unsigned length;
unsigned right;
bool zero;
};
enum charcase {
UPPERCASE,
LOWERCASE
};
enum printtype {
NONE,
OCTAL,
HEX
};
static enum printtype conversion_to_printtype(enum format_conversion conversion) {
switch (conversion) {
case FMT_OCT:
return OCTAL;
case FMT_HEX:
case FMT_HEX_UPPER:
return HEX;
default:
return NONE;
}
}
static enum charcase conversion_to_charcase(enum format_conversion conversion) {
switch (conversion) {
case FMT_HEX_UPPER:
return UPPERCASE;
default:
return LOWERCASE;
}
}
static enum format_flag read_flags(const char *format, const char **end) {
enum format_flag flags = FLG_NONE;
for (; *format != '\0'; format++) { for (; *format != '\0'; format++) {
switch (*format) { switch (*format) {
case '#': case '#':
flag |= FLG_ALTERNATE; flags |= FLG_ALTERNATE;
break; break;
case '0': case '0':
flag |= FLG_ZERO; flags |= FLG_ZERO;
break; break;
case '-': case '-':
flag |= FLG_LEFT_ALIGN; flags |= FLG_LEFT_ALIGN;
break; break;
case '+': case '+':
flag |= FLG_ADD_SIGN; flags |= FLG_ADD_SIGN;
break; break;
default: default:
*end = format; *end = format;
return flag; return flags;
} }
} }
*end = format; *end = format;
return flag; return flags;
} }
static struct format_width read_width(const char *format, const char **end) { static struct format_width read_width(const char *format, const char **end) {
@ -221,213 +278,344 @@ static enum format_conversion read_conversion(const char *format, const char **e
return FMT_STR; return FMT_STR;
case 'p': case 'p':
return FMT_PTR; return FMT_PTR;
case 'P':
return FMT_PTR_UPPER;
case '%':
return FMT_PERCENT;
default: default:
return FMT_INVALID; return FMT_INVALID;
} }
} }
static void print_string( static char ctoa_print(
const char *str, char c, // the single digit to convert to ascii
enum format_flag flag, enum charcase cc // if this digit should be uppercase or lowercase
struct format_width width,
struct format_precision precision
) {
size_t max_len = 0;
size_t min_len = 0;
size_t len = 0;
if (width.defined)
min_len = width.value;
if (precision.defined) {
max_len = precision.value;
len = max_len;
if (max_len < min_len)
min_len = max_len;
} else {
len = strlen(str);
}
if (!(flag & FLG_LEFT_ALIGN) && len < min_len) {
for (size_t i = 0; i < (min_len - len); i++) {
kputc(' ');
}
}
for (size_t i = 0; i < len; i++) {
kputc(str[i]);
}
if ((flag & FLG_LEFT_ALIGN) && len < min_len) {
for (size_t i = 0; i < (min_len - len); i++) {
kputc(' ');
}
}
}
static char get_letter(
char c,
char base
) { ) {
char ascii_base = cc == UPPERCASE ? 'A' : 'a';
if (c >= 0 && c <= 9) if (c >= 0 && c <= 9)
return c + '0'; return c + '0';
c -= 10; c -= 10;
return c + base; return c + ascii_base;
} }
static char *get_decimal( static char *ulltoa_print(
long long num, unsigned long long num, // the number to convert to ascii
char *buf, unsigned radix, // the divisor for the number (base 10, octal, hex)
char sign, char *buf, // the buffer to write to
int radix, enum charcase cc, // if any a-z ascii should be uppercase or lowercase
char base bool addsign // add a sign even if its positive
) { ) {
// we are printing the string backwards, add null byte
*buf = '\0'; *buf = '\0';
buf--; buf--;
// if the number is zero, the while loop will never run
// so we have to add the zero digit ourselves
if (num == 0) { if (num == 0) {
*buf = '0'; *buf = '0';
buf--; buf--;
} }
// for each digit, convert to ascii and add to buffer
while (num != 0) { while (num != 0) {
char i = num % radix; char i = num % radix;
char c = get_letter(i, base); char c = ctoa_print(i, cc);
*buf = c; *buf = c;
buf--; buf--;
num /= radix; num /= radix;
} }
if (sign) { // since this is the unsigned function, add a plus sign if
*buf = sign; // requested
if (addsign) {
*buf = '+';
buf--; buf--;
} }
buf++; buf++; // move forward one to be at the start of the string
return buf; return buf;
} }
static void print_unum( static char *lltoa_print(
unsigned long long num, signed long long num, // the number to convert to ascii
enum format_flag flag, unsigned radix, // the divistor for the number (base 10, octal, hex)
struct format_width width, char *buf, // the buffer to write to
struct format_precision precision, enum charcase cc, // if any a-z ascii should be uppercase or lowercase
bool isneg, bool addsign // add a sign even if its positive
int radix,
char base
) { ) {
bool isneg = num < 0;
size_t max_len = 0;
size_t min_len = 0;
size_t len = 0;
char sign = 0;
if (isneg) if (isneg)
sign = '-'; num = -num;
else if (flag & FLG_ADD_SIGN)
sign = '+';
char buf[1024]; buf = ulltoa_print(num, radix, buf, cc, !isneg && addsign);
char *str = get_decimal(
num,
buf + 1023,
sign,
radix,
base
);
bool space_pre = (flag & FLG_LEFT_ALIGN) || !(flag & FLG_ZERO); if (isneg && num < 0) {
buf--;
if (!space_pre && radix == 16 && (flag & FLG_ALTERNATE)) { *buf = '-';
char x = base + ('x' - 'a');
serial_out('0');
serial_out(x);
} }
if (width.defined) return buf;
min_len = width.value; }
if (precision.defined) {
max_len = precision.value; static void print_string_buffer(
len = max_len; const char *buf, // buffer containing the text we want to print
if (max_len < min_len) struct spacing spacing // the spacing on the left right and middle
min_len = max_len; ) {
} else { for (unsigned i = 0; i < spacing.left; i++) {
len = strlen(str); kputc(' ');
} }
bool zero_padded = false; for (unsigned i = 0; i < spacing.length; i++) {
kputc(*buf++);
if (!(flag & FLG_LEFT_ALIGN) && len < min_len) {
for (size_t i = 0; i < (min_len - len); i++) {
(flag & FLG_ZERO) ? kputc('0') : kputc(' ');
}
if (flag & FLG_ZERO)
zero_padded = true;
} }
if (space_pre && radix == 16 && (flag & FLG_ALTERNATE)) { for (unsigned i = 0; i < spacing.right; i++) {
char x = base + ('x' - 'a'); kputc(' ');
serial_out('0');
serial_out(x);
}
if (space_pre && radix == 16 && flag & FLG_ALTERNATE) {
char x = base + ('x' - 'a');
serial_out('0');
serial_out(x);
}
kputs(str);
if (!zero_padded && (flag & FLG_ALTERNATE) && radix == 8)
kputc('0');
if ((flag & FLG_LEFT_ALIGN) && len < min_len) {
for (size_t i = 0; i < (min_len - len); i++) {
(flag & FLG_ZERO) ? kputc('0') : kputc(' ');
}
} }
} }
static void print_num( static void print_number_buffer(
long long num, const char *buf, // buffer containing the text we want to print
enum format_flag flag, struct spacing spacing, // the spacing on the left right and middle
struct format_width width, enum printtype type,
struct format_precision precision, enum charcase cc
int radix,
char base
) { ) {
bool isneg = false;
if (num < 0) { // put the 0x at the start of the string
num = ~num; if (spacing.left && spacing.zero && type == HEX) {
isneg = true; if (cc == UPPERCASE) {
kputs("0X");
} else {
kputs("0x");
}
} }
print_unum( if ((!spacing.left || !spacing.zero) && type == OCTAL) {
num, kputc('0');
flag, }
width,
precision, for (unsigned i = 0; i < spacing.left; i++) {
isneg, spacing.zero ? kputc('0') : kputc(' ');
radix, }
base
); if ((!spacing.left || !spacing.zero) && type == HEX) {
if (cc == UPPERCASE) {
kputs("0X");
} else {
kputs("0x");
}
}
for (unsigned i = 0; i < spacing.length; i++) {
kputc(*buf++);
}
for (unsigned i = 0; i < spacing.right; i++) {
kputc(' ');
}
}
static unsigned get_string_len(
const char *str,
struct format_precision precision
) {
if (!precision.defined)
return strlen(str);
unsigned max = precision.value;
for (unsigned i = 0; i < max; i++) {
if (str[i] == '\0') {
// we broke early
// precision is greater then strlen
return i;
}
}
return max;
}
static struct spacing get_spacing(
struct format_width width,
struct format_precision precision,
enum format_flag flags,
unsigned length
) {
struct spacing spacing;
unsigned min = 0,
max = 0;
if (width.defined) {
min = width.value;
}
if (precision.defined) {
max = precision.value;
if (length > max) {
length = max;
}
if (min > max) {
min = max;
}
}
unsigned gap = 0;
if (max > length) {
gap = max - length;
}
spacing.length = length;
if (flags & FLG_LEFT_ALIGN) {
spacing.left = gap;
spacing.right = 0;
} else {
spacing.left = 0;
spacing.right = gap;
}
spacing.zero = (flags & FLG_ZERO);
return spacing;
}
static struct spacing get_string_spacing(
const char *str,
struct format_width width,
struct format_precision precision,
enum format_flag flags
) {
unsigned length = get_string_len(str, precision);
return get_spacing(width, precision, flags, length);
}
static void print_string(
const char *str,
enum format_flag flags,
struct format_width width,
struct format_precision precision
) {
struct spacing spacing =
get_string_spacing(str, width, precision, flags);
print_string_buffer(str, spacing);
}
static enum format_length apply_modifier(
enum format_conversion conversion,
enum format_modifier modifier
) {
switch (conversion) {
case FMT_CHAR:
if (modifier == MOD_NONE)
return LEN_CHAR;
else
return LEN_INVALID;
case FMT_PTR:
if (modifier == MOD_NONE)
return LEN_SIZET;
else
return LEN_INVALID;
case FMT_INT:
if (modifier == MOD_LONG)
return LEN_LONG;
else if (modifier == MOD_LONG_LONG)
return LEN_LONGLONG;
else if (modifier == MOD_HALF)
return LEN_SHORT;
else if (modifier == MOD_HALF_HALF)
return LEN_CHAR;
else
return LEN_INT;
case FMT_UINT:
case FMT_OCT:
case FMT_HEX:
case FMT_HEX_UPPER:
if (modifier == MOD_LONG)
return LEN_ULONG;
else if (modifier == MOD_LONG_LONG)
return LEN_ULONGLONG;
else if (modifier == MOD_HALF)
return LEN_USHORT;
else if (modifier == MOD_HALF_HALF)
return LEN_UCHAR;
else
return LEN_UINT;
default:
return LEN_INVALID;
}
}
static unsigned printtype_to_radix(
enum printtype type
) {
switch (type) {
case OCTAL:
return 8;
case HEX:
return 16;
case NONE:
default:
return 10;
}
}
static void print_signed_number(
signed long long num,
enum format_flag flags,
struct format_width width,
struct format_precision precision,
enum printtype type,
enum charcase cc
) {
unsigned radix = printtype_to_radix(type);
char buf[256];
char *ptr = lltoa_print(num, radix, buf + 255, cc, flags & FLG_ADD_SIGN);
unsigned length = strlen(ptr);
struct spacing spacing =
get_spacing(width, precision, flags, length);
print_number_buffer(ptr, spacing, type, cc);
}
static void print_unsigned_number(
unsigned long long num,
enum format_flag flags,
struct format_width width,
struct format_precision precision,
enum printtype type,
enum charcase cc
) {
unsigned radix = printtype_to_radix(type);
char buf[256];
char *ptr = ulltoa_print(num, radix, buf + 255, cc, flags & FLG_ADD_SIGN);
unsigned length = strlen(ptr);
struct spacing spacing =
get_spacing(width, precision, flags, length);
print_number_buffer(ptr, spacing, type, cc);
}
static bool is_conversion_number(enum format_conversion conversion) {
switch (conversion) {
case FMT_INT:
case FMT_UINT:
case FMT_OCT:
case FMT_HEX:
case FMT_HEX_UPPER:
case FMT_PTR:
return true;
case FMT_CHAR:
case FMT_STR:
case FMT_INVALID:
default:
return false;
}
} }
void kvprintf(const char *format, va_list args) { void kvprintf(const char *format, va_list args) {
for (; *format != '\0'; format++) { for (; *format != '\0'; format++) {
char c = *format; char c = *format;
if (c == '%') { if (c == '%') {
enum format_flag flag; enum format_flag flags;
struct format_width width; struct format_width width;
struct format_precision precision; struct format_precision precision;
enum format_modifier modifier; enum format_modifier modifier;
@ -435,34 +623,24 @@ void kvprintf(const char *format, va_list args) {
const char *ptr = format + 1; const char *ptr = format + 1;
flag = read_flag(ptr, &ptr); if (*(format + 1) == '%') {
kputc('%');
format++;
continue;
}
flags = read_flags(ptr, &ptr);
width = read_width(ptr, &ptr); width = read_width(ptr, &ptr);
precision = read_precision(ptr, &ptr); precision = read_precision(ptr, &ptr);
modifier = read_modifier(ptr, &ptr); modifier = read_modifier(ptr, &ptr);
if (modifier == MOD_INVALID) { if (modifier == MOD_INVALID) {
kputc('%'); goto error;
continue;
} }
conversion = read_conversion(ptr, &ptr); conversion = read_conversion(ptr, &ptr);
if (conversion == FMT_INVALID) {
kputc('%');
continue;
}
union {
unsigned long long u;
long long l;
char c;
const char *str;
void *ptr;
} data = {0};
int radix = 0;
char base = 0;
if (width.varys) { if (width.varys) {
int len = va_arg(args, int); int len = va_arg(args, int);
width.value = len; width.value = len;
@ -473,106 +651,80 @@ void kvprintf(const char *format, va_list args) {
precision.value = len; precision.value = len;
} }
switch (conversion) { if (conversion == FMT_INVALID) {
case FMT_INT: goto error;
if (modifier == MOD_NONE) }
data.l = va_arg(args, int);
else if (modifier == MOD_HALF)
data.l = (short) va_arg(args, int);
else if (modifier == MOD_HALF_HALF)
data.l = (char) va_arg(args, int);
else if (modifier == MOD_LONG)
data.l = va_arg(args, long);
else if (modifier == MOD_LONG_LONG)
data.l = va_arg(args, long long);
radix = 10;
print_num(
data.l,
flag,
width,
precision,
radix,
base
);
break;
case FMT_UINT:
case FMT_OCT:
case FMT_HEX_UPPER:
case FMT_HEX:
if (modifier == MOD_NONE)
data.u = va_arg(args, unsigned int);
else if (modifier == MOD_HALF)
data.u = (unsigned short) va_arg(args, unsigned int);
else if (modifier == MOD_HALF_HALF)
data.u = (unsigned char) va_arg(args, unsigned int);
else if (modifier == MOD_LONG)
data.u = va_arg(args, unsigned long);
else if (modifier == MOD_LONG_LONG)
data.u = va_arg(args, unsigned long long);
if (conversion == FMT_UINT) { if (conversion == FMT_INT) {
radix = 10; enum format_length length =
} else if (conversion == FMT_OCT) { apply_modifier(conversion, modifier);
radix = 8; enum printtype type = NONE;
} else if (conversion == FMT_HEX) { enum charcase cc = LOWERCASE;
radix = 16;
base = 'a'; signed long long num = 0;
} else if (conversion == FMT_HEX_UPPER) { switch (length) {
radix = 16; case LEN_CHAR:
base = 'A'; case LEN_SHORT:
case LEN_INT:
num = va_arg(args, signed int);
break;
case LEN_LONG:
num = va_arg(args, signed long);
break;
case LEN_LONGLONG:
num = va_arg(args, signed long long);
break;
default:
goto error;
} }
goto printunum;
print_signed_number(num, flags, width, precision, type, cc);
} else if (is_conversion_number(conversion)) {
enum format_length length =
apply_modifier(conversion, modifier);
enum printtype type =
conversion_to_printtype(conversion);
enum charcase cc =
conversion_to_charcase(conversion);
unsigned long long num = 0;
switch (length) {
case LEN_UCHAR:
case LEN_USHORT:
case LEN_UINT:
num = va_arg(args, unsigned int);
break; break;
case FMT_PTR: case LEN_ULONG:
case FMT_PTR_UPPER: num = va_arg(args, unsigned long);
flag |= FLG_ZERO;
data.u = va_arg(args, size_t);
radix = 16;
if (conversion == FMT_PTR)
base = 'a';
else
base = 'A';
goto printunum;
break; break;
printunum: case LEN_ULONGLONG:
print_unum( num = va_arg(args, unsigned long long);
data.u,
flag,
width,
precision,
false,
radix,
base
);
break; break;
case FMT_CHAR: { case LEN_SIZET:
char buf[2]; num = va_arg(args, size_t);
buf[0] = (char) va_arg(args, int);
buf[1] = '\0';
print_string(
buf,
flag,
width,
precision
);
break; break;
default:
goto error;
} }
case FMT_STR:
data.str = va_arg(args, const char*); print_unsigned_number(num, flags, width, precision, type, cc);
print_string( } else if (conversion == FMT_STR) {
data.str, char *str = va_arg(args, char *);
flag, print_string(str, flags, width, precision);
width, } else if (conversion == FMT_CHAR) {
precision char temp[2];
); temp[0] = va_arg(args, int);
break; temp[1] = '\0';
case FMT_PERCENT: print_string(temp, flags, width, precision);
kputc('%');
break;
case FMT_INVALID:
break;
} }
format = ptr - 1; format = ptr - 1;
continue;
error:
kputc('%');
format++;
continue;
} else { } else {
kputc(c); kputc(c);
} }