From f785bce3ccde87a0bccc8ca7602cf6e5f0c9b53d Mon Sep 17 00:00:00 2001 From: Freya Murphy Date: Sat, 3 Feb 2024 17:05:04 -0500 Subject: [PATCH] rework kprintf --- src/arch/amd64/paging.c | 1 - src/kmain.c | 3 + src/print.c | 720 ++++++++++++++++++++++++---------------- 3 files changed, 439 insertions(+), 285 deletions(-) diff --git a/src/arch/amd64/paging.c b/src/arch/amd64/paging.c index fd24af4..a80737c 100644 --- a/src/arch/amd64/paging.c +++ b/src/arch/amd64/paging.c @@ -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; diff --git a/src/kmain.c b/src/kmain.c index ce8016d..b4ebadb 100644 --- a/src/kmain.c +++ b/src/kmain.c @@ -1,3 +1,4 @@ +#include #include #include #include @@ -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 diff --git a/src/print.c b/src/print.c index fa16bab..8cab6e4 100644 --- a/src/print.c +++ b/src/print.c @@ -1,6 +1,7 @@ #include #include #include +#include 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); }