summaryrefslogtreecommitdiff
path: root/src/print.c
diff options
context:
space:
mode:
authorFreya Murphy <freya@freyacat.org>2024-02-01 12:48:55 -0500
committerFreya Murphy <freya@freyacat.org>2024-02-01 12:49:44 -0500
commit192a4ccd6bbc2239f047f782a05e888990011e51 (patch)
tree2b3d8a194d3dd33a5d344b4ec6376dbdfdb3eb0c /src/print.c
parentundo bad decisions (diff)
downloadcorn-192a4ccd6bbc2239f047f782a05e888990011e51.tar.gz
corn-192a4ccd6bbc2239f047f782a05e888990011e51.tar.bz2
corn-192a4ccd6bbc2239f047f782a05e888990011e51.zip
acpi, fix mboot memory map, fix kalloc, fix virtalloc node allocator, add kprintf, other changes
Diffstat (limited to 'src/print.c')
-rw-r--r--src/print.c564
1 files changed, 564 insertions, 0 deletions
diff --git a/src/print.c b/src/print.c
new file mode 100644
index 0000000..f158985
--- /dev/null
+++ b/src/print.c
@@ -0,0 +1,564 @@
+#include <lib.h>
+#include <serial.h>
+#include <stdarg.h>
+
+void kputc(char c) {
+ serial_out(c);
+}
+
+void kputs(const char *s) {
+ serial_out_str(s);
+}
+
+enum format_flag {
+ FLG_NONE = 0x00,
+ FLG_ALTERNATE = 0x01,
+ FLG_ZERO = 0x02,
+ FLG_LEFT_ALIGN = 0x04,
+ FLG_ADD_SIGN = 0x08
+};
+
+struct format_width {
+ bool defined;
+ bool varys;
+ int value;
+};
+
+struct format_precision {
+ bool defined;
+ bool varys;
+ int value;
+};
+
+enum format_modifier {
+ MOD_NONE,
+ MOD_INVALID,
+ MOD_HALF_HALF,
+ MOD_HALF,
+ MOD_LONG_LONG,
+ MOD_LONG,
+};
+
+enum format_conversion {
+ FMT_INT,
+ FMT_UINT,
+ FMT_OCT,
+ FMT_HEX,
+ FMT_HEX_UPPER,
+ FMT_CHAR,
+ FMT_STR,
+ FMT_PTR,
+ FMT_PTR_UPPER,
+ FMT_PERCENT,
+ FMT_INVALID
+};
+
+static enum format_flag read_flag(const char *format, const char **end) {
+
+ enum format_flag flag = FLG_NONE;
+
+ for (; *format != '\0'; format++) {
+ switch (*format) {
+ case '#':
+ flag |= FLG_ALTERNATE;
+ break;
+ case '0':
+ flag |= FLG_ZERO;
+ break;
+ case '-':
+ flag |= FLG_LEFT_ALIGN;
+ break;
+ case '+':
+ flag |= FLG_ADD_SIGN;
+ break;
+ default:
+ *end = format;
+ return flag;
+ }
+ }
+
+ *end = format;
+ return flag;
+}
+
+static struct format_width read_width(const char *format, const char **end) {
+
+ struct format_width width;
+ width.defined = false;
+ width.varys = false;
+ width.value = 0;
+
+ int value = 0;
+
+ for (; *format != '\0'; format++) {
+ char c = *format;
+
+ if (c == '*' && width.defined == false) {
+ width.defined = true;
+ width.varys = true;
+ break;
+ }
+
+ if (!isdigit(c))
+ break;
+
+ int i = c - '0';
+ value *= 10;
+ value += i;
+
+ width.value = value;
+ width.defined = true;
+ width.varys = false;
+ }
+
+ *end = format;
+ return width;
+
+}
+
+static struct format_precision read_precision(const char *format, const char **end) {
+
+ struct format_precision precision;
+ precision.varys = false;
+ precision.defined = false;
+ precision.value = 0;
+
+ if (*format != '.') {
+ *end = format;
+ return precision;
+ }
+
+ format++;
+
+ int value = 0;
+
+ for (; *format != '\0'; format++) {
+ char c = *format;
+
+ if (c == '*' && precision.defined == false) {
+ precision.defined = true;
+ precision.varys = true;
+ break;
+ }
+
+ if (!isdigit(c))
+ break;
+
+ int i = c - '0';
+ value *= 10;
+ value += i;
+
+ precision.value = value;
+ precision.defined = true;
+ precision.varys = false;
+ }
+
+ *end = format;
+ return precision;
+}
+
+static enum format_modifier read_modifier(const char *format, const char **end) {
+
+ enum format_modifier mod = MOD_NONE;
+
+ for (; *format != '\0'; format++) {
+ *end = format;
+ switch (*format) {
+ case 'l':
+ if (mod == MOD_NONE)
+ mod = MOD_LONG;
+ else if (mod == MOD_LONG)
+ return MOD_LONG_LONG;
+ else
+ return MOD_INVALID;
+ break;
+ case 'L':
+ if (mod == MOD_NONE)
+ return MOD_LONG_LONG;
+ else
+ return MOD_INVALID;
+ break;
+ case 'h':
+ if (mod == MOD_NONE)
+ mod = MOD_HALF;
+ else if (mod == MOD_HALF)
+ return MOD_HALF_HALF;
+ else
+ return MOD_INVALID;
+ break;
+ case 'H':
+ if (mod == MOD_NONE)
+ return MOD_HALF_HALF;
+ else
+ return MOD_INVALID;
+ break;
+ default:
+ return mod;
+ }
+ }
+
+ return MOD_INVALID;
+}
+
+static enum format_conversion read_conversion(const char *format, const char **end) {
+ *end = format + 1;
+ switch (*format) {
+ case 'd':
+ case 'i':
+ return FMT_INT;
+ case 'o':
+ return FMT_OCT;
+ case 'u':
+ return FMT_UINT;
+ case 'x':
+ return FMT_HEX;
+ case 'X':
+ return FMT_HEX_UPPER;
+ case 'c':
+ return FMT_CHAR;
+ case 's':
+ 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
+) {
+ if (c >= 0 && c <= 9)
+ return c + '0';
+ c -= 10;
+ return c + base;
+}
+
+static char *get_decimal(
+ long long num,
+ char *buf,
+ char sign,
+ int radix,
+ char base
+) {
+
+ *buf = '\0';
+ buf--;
+
+ if (num == 0) {
+ *buf = '0';
+ buf--;
+ }
+
+ while (num != 0) {
+ char i = num % radix;
+ char c = get_letter(i, base);
+ *buf = c;
+ buf--;
+ num /= radix;
+ }
+
+ if (sign) {
+ *buf = sign;
+ buf--;
+ }
+
+ buf++;
+
+ 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
+) {
+
+ 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 = '+';
+
+ char buf[1024];
+ char *str = get_decimal(
+ num,
+ buf,
+ sign,
+ radix,
+ base
+ );
+
+ 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 (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);
+ }
+
+ 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;
+ }
+
+ 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(
+ long long num,
+ enum format_flag flag,
+ struct format_width width,
+ struct format_precision precision,
+ int radix,
+ char base
+) {
+ bool isneg = false;
+
+ if (num < 0) {
+ num = ~num;
+ isneg = true;
+ }
+
+ print_unum(
+ num,
+ flag,
+ width,
+ precision,
+ isneg,
+ radix,
+ base
+ );
+}
+
+void kvprintf(const char *format, va_list args) {
+ for (; *format != '\0'; format++) {
+ char c = *format;
+ if (c == '%') {
+ enum format_flag flag;
+ struct format_width width;
+ struct format_precision precision;
+ enum format_modifier modifier;
+ enum format_conversion conversion;
+
+ const char *ptr = format + 1;
+
+ flag = read_flag(ptr, &ptr);
+ width = read_width(ptr, &ptr);
+ precision = read_precision(ptr, &ptr);
+ modifier = read_modifier(ptr, &ptr);
+
+ if (modifier == MOD_INVALID) {
+ kputc('%');
+ continue;
+ }
+
+ 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;
+
+ int radix = 0;
+ char base = 0;
+
+ 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;
+ goto printnum;
+ 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;
+ 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;
+ printnum:
+ print_num(
+ data.l,
+ flag,
+ width,
+ precision,
+ radix,
+ base
+ );
+ 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;
+ }
+ format = ptr - 1;
+ } else {
+ kputc(c);
+ }
+ }
+}
+
+void kprintf(const char *format, ...) {
+ va_list args;
+ va_start(args, format);
+ kvprintf(format, args);
+ va_end(args);
+}