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)
return -1;
void *phys = alloc_phys_page();
kprintf("0x%p\n", phys);
if (phys == NULL)
return -2;
page->loaded = 1;

View file

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

View file

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