Initial commit.
diff --git a/src/dlog.c b/src/dlog.c
new file mode 100644
index 0000000..c4d49ce
--- /dev/null
+++ b/src/dlog.c
@@ -0,0 +1,272 @@
+#include "dlog.h"
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdarg.h>
+
+#include "arch.h"
+#include "spinlock.h"
+#include "std.h"
+
+#define FLAG_SPACE 0x01
+#define FLAG_ZERO 0x02
+#define FLAG_MINUS 0x04
+#define FLAG_PLUS 0x08
+#define FLAG_ALT 0x10
+#define FLAG_UPPER 0x20
+#define FLAG_NEG 0x40
+
+/*
+ * Prints a raw string to the debug log and returns its length.
+ */
+static size_t print_raw_string(const char *str)
+{
+ const char *c = str;
+ while (*c != '\0')
+ arch_putchar(*c++);
+ return c - str;
+}
+
+/*
+ * Prints a formatted string to the debug log. The format includes a minimum
+ * width, the fill character, and flags (whether to align to left or right).
+ *
+ * str is the full string, while suffix is a pointer within str that indicates
+ * where the suffix begins. This is used when printing right-aligned numbers
+ * with a zero fill; for example, -10 with width 4 should be padded to -010,
+ * so suffix would point to index one of the "-10" string .
+ */
+static void print_string(const char *str, const char *suffix, size_t width,
+ int flags, char fill)
+{
+ size_t len = suffix - str;
+
+ /* Print the string up to the beginning of the suffix. */
+ while (str != suffix)
+ arch_putchar(*str++);
+
+ if (flags & FLAG_MINUS) {
+ /* Left-aligned. Print suffix, then print padding if needed. */
+ len += print_raw_string(suffix);
+ while (len < width) {
+ arch_putchar(' ');
+ len++;
+ }
+ return;
+ }
+
+ /* Fill until we reach the desired length. */
+ len += strlen(suffix);
+ while (len < width) {
+ arch_putchar(fill);
+ len++;
+ }
+
+ /* Now print the rest of the string. */
+ print_raw_string(suffix);
+}
+
+/*
+ * Prints a number to the debug log. The caller specifies the base, its minimum
+ * width and printf-style flags.
+ */
+static void print_num(size_t v, size_t base, size_t width, int flags)
+{
+ static const char *digits_lower = "0123456789abcdefx";
+ static const char *digits_upper = "0123456789ABCDEFX";
+ const char *d = (flags & FLAG_UPPER) ? digits_upper : digits_lower;
+ char buf[51];
+ char *ptr = buf + sizeof(buf) - 1;
+ char *num;
+ *ptr = '\0';
+ do {
+ --ptr;
+ *ptr = d[v % base];
+ v /= base;
+ } while (v);
+
+ /* Num stores where the actual number begins. */
+ num = ptr;
+
+ /* Add prefix if requested. */
+ if (flags & FLAG_ALT) {
+ switch (base) {
+ case 16:
+ ptr -= 2;
+ ptr[0] = '0';
+ ptr[1] = d[16];
+ break;
+
+ case 8:
+ ptr--;
+ *ptr = '0';
+ break;
+ }
+ }
+
+ /* Add sign if requested. */
+ if (flags & FLAG_NEG)
+ *--ptr = '-';
+ else if (flags & FLAG_PLUS)
+ *--ptr = '+';
+ else if (flags & FLAG_SPACE)
+ *--ptr = ' ';
+
+ if (flags & FLAG_ZERO)
+ print_string(ptr, num, width, flags, '0');
+ else
+ print_string(ptr, ptr, width, flags, ' ');
+}
+
+/*
+ * Parses the optional flags field of a printf-style format. It returns the spot
+ * on the string where a non-flag character was found.
+ */
+static const char *parse_flags(const char *p, int *flags)
+{
+ for (;;) {
+ switch (*p) {
+ case ' ':
+ *flags |= FLAG_SPACE;
+ break;
+
+ case '0':
+ *flags |= FLAG_ZERO;
+ break;
+
+ case '-':
+ *flags |= FLAG_MINUS;
+ break;
+
+ case '+':
+ *flags |= FLAG_PLUS;
+
+ case '#':
+ *flags |= FLAG_ALT;
+ break;
+
+ default:
+ return p;
+ }
+ p++;
+ }
+}
+
+/*
+ * Prints the given format string to the debug log.
+ */
+void dlog(const char *str, ...)
+{
+ static struct spinlock sl = SPINLOCK_INIT;
+ const char *p;
+ va_list args;
+ size_t w;
+ int flags;
+ char buf[2];
+
+ va_start(args, str);
+
+ sl_lock(&sl);
+
+ for (p = str; *p; p++) {
+ switch (*p) {
+ default:
+ arch_putchar(*p);
+ break;
+
+ case '%':
+ /* Read optional flags. */
+ flags = 0;
+ p = parse_flags(p + 1, &flags) - 1;
+
+ /* Read the minimum width, if one is specified. */
+ w = 0;
+ while (p[1] >= '0' && p[1] <= '9') {
+ w = (w * 10) + (p[1] - '0');
+ p++;
+ }
+
+ /* Read minimum width from arguments. */
+ if (w == 0 && p[1] == '*') {
+ int v = va_arg(args, int);
+ if (v >= 0) {
+ w = v;
+ } else {
+ w = -v;
+ flags |= FLAG_MINUS;
+ }
+ p++;
+ }
+
+ /* Handle the format specifier. */
+ switch (p[1]) {
+ case 's':
+ {
+ char *str = va_arg(args, char *);
+ print_string(str, str, w, flags, ' ');
+ }
+ p++;
+ break;
+
+ case 'd':
+ case 'i':
+ {
+ int v = va_arg(args, int);
+ if (v < 0) {
+ flags |= FLAG_NEG;
+ v = -v;
+ }
+
+ print_num((size_t)v, 10, w, flags);
+ }
+ p++;
+ break;
+
+ case 'X':
+ flags |= FLAG_UPPER;
+ print_num(va_arg(args, size_t), 16, w, flags);
+ break;
+
+ case 'p':
+ print_num(va_arg(args, size_t), 16,
+ sizeof(size_t) * 2, FLAG_ZERO);
+ p++;
+ break;
+
+ case 'x':
+ print_num(va_arg(args, size_t), 16, w, flags);
+ p++;
+ break;
+
+ case 'u':
+ print_num(va_arg(args, size_t), 10, w, flags);
+ p++;
+ break;
+
+ case 'o':
+ print_num(va_arg(args, size_t), 8, w, flags);
+ p++;
+ break;
+
+ case 'c':
+ buf[1] = 0;
+ buf[0] = va_arg(args, int);
+ print_string(buf, buf, w, flags, ' ');
+ p++;
+ break;
+
+ case '%':
+ break;
+
+ default:
+ arch_putchar('%');
+ }
+
+ break;
+ }
+ }
+
+ sl_unlock(&sl);
+
+ va_end(args);
+}