Much better (and more compliant) printk.

This commit is contained in:
L. Bradley LaBoon 2014-08-26 14:11:53 -04:00
parent 905a8a05a1
commit 1ef6bb95b3
4 changed files with 550 additions and 188 deletions

View File

@ -3,7 +3,6 @@
#include <stddef.h>
int strindexof(char c, const char *str);
size_t strlen(const char *str);
#endif

View File

@ -40,11 +40,11 @@ void brados_main(uint32_t multiMagic, uint32_t multiAddr)
uint64_t lastAddr = 0;
for (int i = 0; i < numEntries; i++) {
if (memmap[i].base_addr != lastAddr) {
printk(&term, "%llx - %llx: Unknown\n", lastAddr, memmap[i].base_addr - 1);
printk(&term, "%#llx - %#llx: Unknown\n", lastAddr, memmap[i].base_addr - 1);
lastAddr = memmap[i].base_addr;
}
printk(&term, "%llx - %llx: ", memmap[i].base_addr, memmap[i].base_addr + memmap[i].length - 1);
printk(&term, "%#llx - %#llx: ", memmap[i].base_addr, memmap[i].base_addr + memmap[i].length - 1);
if (memmap[i].type == 1)
printk(&term, "Available\n");
else
@ -55,5 +55,5 @@ void brados_main(uint32_t multiMagic, uint32_t multiAddr)
// Done
term_writeStr(&term, "\nDone.");
printk(&term, "\nDone.");
}

View File

@ -1,5 +1,6 @@
#include <stdarg.h>
#include <stddef.h>
#include <stdint.h>
#include <brados/printk.h>
#include <brados/string.h>
@ -9,7 +10,7 @@ const char *BASE16 = "0123456789ABCDEF";
const char *BASE16L = "0123456789abcdef";
// Print an unsigned number as a string; return the number of characters printed
static int printNum(struct vgastate *term, unsigned long long num, unsigned int base, const char *digits)
static int printNum(struct vgastate *term, uintmax_t num, unsigned int base, const char *digits)
{
if (base > strlen(digits))
return 0;
@ -23,6 +24,18 @@ static int printNum(struct vgastate *term, unsigned long long num, unsigned int
return numPrinted;
}
// Determines the printed length of a number
static unsigned int numLen(uintmax_t num, unsigned int base)
{
unsigned int len = 1;
while (num >= base) {
len++;
num /= base;
}
return len;
}
// Print a formatted string to the given terminal
int printk(struct vgastate *term, const char *fmt, ...)
{
@ -30,189 +43,551 @@ int printk(struct vgastate *term, const char *fmt, ...)
va_list args;
va_start(args, fmt);
size_t i = 0;
while (i < strlen(fmt)) {
if (fmt[i] == '%') {
i++;
int end = 0, lng = 0;
// The bits of 'mode' explained:
// 0: Whether we are printing characters normally or parsing the format
// 1: Whether we are still parsing flags
// 2: Whether we are looking at width or precision (have we seen a .)
// 3: + flag
// 4: space flag
// 5: - flag
// 6: # flag
// 7: 0 flag
uint8_t mode = 0;
int length = 0;
unsigned int width = 0, precision = 0;
while (end == 0) {
switch (fmt[i]) {
// Print a percent sign
case '%':
term_putChar(term, '%');
numPrinted++;
end = 1;
for (size_t i = 0; i < strlen(fmt); i++) {
if (mode % 2 == 0) {
// Begin parsing format if we reach a '%'
if (fmt[i] == '%') {
mode |= 0x01;
// Or just print the character if we're not parsing
} else {
term_putChar(term, fmt[i]);
numPrinted++;
}
continue;
}
switch (fmt[i]) {
case '%':
// Print a percent sign
term_putChar(term, '%');
numPrinted++;
mode = 0;
break;
case '+':
// Set the plus flag
if ((mode >> 1) % 2 == 0)
mode |= 0x08;
break;
case ' ':
// Set the space flag
if ((mode >> 1) % 2 == 0)
mode |= 0x10;
break;
case '-':
// Set the left-align flag
if ((mode >> 1) % 2 == 0)
mode |= 0x20;
break;
case '#':
// Set the alternate flag
if ((mode >> 1) % 2 == 0)
mode |= 0x40;
break;
case '0':
// Set the zero-pad flag
if ((mode >> 1) % 2 == 0)
mode |= 0x80;
// Next digit in width specifier
else if ((mode >> 2) % 2 == 0)
width *= 10;
// Next digit in precision specifier
else
precision *= 10;
break;
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
// Next digit in width specifier
if ((mode >> 2) % 2 == 0) {
mode |= 0x02;
width *= 10;
width += (fmt[i] - 48); // 0 = ascii 48
// Next digit in precision specifier
} else {
precision *= 10;
precision += (fmt[i] - 48);
}
break;
case '.':
// Delimit width and precision
if ((mode >> 2) % 2 == 0)
mode |= 0x06;
break;
case 'h':
// Denotes a short or char argument
mode |= 0x06;
length--;
break;
case 'l':
// Denotes a long or long long argument
mode |= 0x06;
length++;
break;
case 'z':
// Denotes a size_t argument
mode |= 0x06;
length = 3;
break;
case 'j':
// Denotes a intmax_t argument
mode |= 0x06;
length = 4;
break;
case 't':
// Denotes a ptrdiff_t argument
mode |= 0x06;
length = 5;
break;
case 'd':
// Print a signed integer
case 'i': {
intmax_t num;
uintmax_t unum;
// Pull the argument from the list
switch (length) {
case 1:
num = va_arg(args, long);
break;
// Print a single character
case 'c':
term_putChar(term, (char) va_arg(args, int));
numPrinted++;
end = 1;
case 2:
num = va_arg(args, long long);
break;
// Print an integer number
case 'd':
case 'i':
switch (lng) {
case 0: {
int d;
d = va_arg(args, int);
if (d < 0) {
term_putChar(term, '-');
numPrinted++;
d *= -1;
}
numPrinted += printNum(term, (unsigned int) d, 10, BASE16);
break;
} case 1: {
long ld;
ld = va_arg(args, long);
if (ld < 0) {
term_putChar(term, '-');
numPrinted++;
ld *= -1;
}
numPrinted += printNum(term, (unsigned long) ld, 10, BASE16);
break;
} default: {
long long lld;
lld = va_arg(args, long long);
if (lld < 0) {
term_putChar(term, '-');
numPrinted++;
lld *= -1;
}
numPrinted += printNum(term, (unsigned long long) lld, 10, BASE16);
break;
}}
end = 1;
case 3:
num = va_arg(args, size_t);
break;
// Expect a long (long) value
case 'l':
lng++;
case 4:
num = va_arg(args, intmax_t);
break;
// Store the number of printed characters
case 'n': {
int *n;
n = va_arg(args, int *);
*n = numPrinted;
end = 1;
case 5:
num = va_arg(args, ptrdiff_t);
break;
// Print an unsigned integer in octal form
} case 'o':
default:
num = va_arg(args, int);
break;
}
// Get unsigned version
if (num < 0)
unum = num * -1;
else
unum = num;
// Get the length of the printed number
unsigned int len = numLen(unum, 10);
// Get the length of the printed number with formatting
unsigned int flen = len;
if (precision > len)
flen += (precision - len);
if (num < 0)
flen++;
if (num >= 0 && ((mode >> 3) % 2 == 1 || (mode >> 4) % 2 == 1))
flen++;
// If specified, print preceding space padding
if ((mode >> 5) % 2 == 0) {
if (precision > 0 || (mode >> 7) % 2 == 0) {
for (int j = width - flen; j > 0; j--) {
term_putChar(term, ' ');
numPrinted++;
}
}
}
// If specified print the sign
if (num < 0) {
term_putChar(term, '-');
numPrinted++;
} else if ((mode >> 3) % 2 == 1) {
term_putChar(term, '+');
numPrinted++;
} else if ((mode >> 4) % 2 == 1) {
term_putChar(term, ' ');
numPrinted++;
}
// If specified, print the precision 0s
if (precision > 0) {
for (int j = precision - len; j > 0; j--) {
term_putChar(term, '0');
numPrinted++;
}
} else if ((mode >> 7) % 2 == 1) {
for (int j = width - flen; j > 0; j--) {
term_putChar(term, '0');
numPrinted++;
switch (lng) {
case 0: {
unsigned int o;
o = va_arg(args, unsigned int);
numPrinted += printNum(term, o, 8, BASE16);
break;
} case 1: {
unsigned long lo;
lo = va_arg(args, unsigned long);
numPrinted += printNum(term, lo, 8, BASE16);
break;
} default: {
unsigned long long llo;
llo = va_arg(args, unsigned long long);
numPrinted += printNum(term, llo, 8, BASE16);
break;
}}
end = 1;
break;
// Print a null-terminated string
case 's': {
char *s;
s = va_arg(args, char *);
term_writeStr(term, s);
numPrinted += strlen(s);
end = 1;
break;
// Print an unsigned integer value
} case 'u':
switch (lng) {
case 0: {
unsigned int u;
u = va_arg(args, unsigned int);
numPrinted += printNum(term, u, 10, BASE16);
break;
} case 1: {
unsigned long lu;
lu = va_arg(args, unsigned long);
numPrinted += printNum(term, lu, 10, BASE16);
break;
} default: {
unsigned long long llu;
llu = va_arg(args, unsigned long long);
numPrinted += printNum(term, llu, 10, BASE16);
break;
}}
end = 1;
break;
// Print an unsigned integer in lower hex form
case 'x':
term_writeStr(term, "0x");
numPrinted += 2;
switch (lng) {
case 0: {
unsigned int x;
x = va_arg(args, unsigned int);
numPrinted += printNum(term, x, 16, BASE16L);
break;
} case 1: {
unsigned long lx;
lx = va_arg(args, unsigned long);
numPrinted += printNum(term, lx, 16, BASE16L);
break;
} default: {
unsigned long long llx;
llx = va_arg(args, unsigned long long);
numPrinted += printNum(term, llx, 16, BASE16L);
break;
}}
end = 1;
break;
// Print an unsigned integer in upper hex form
case 'X':
term_writeStr(term, "0X");
numPrinted += 2;
switch (lng) {
case 0: {
unsigned int x;
x = va_arg(args, unsigned int);
numPrinted += printNum(term, x, 16, BASE16);
break;
} case 1: {
unsigned long lx;
lx = va_arg(args, unsigned long);
numPrinted += printNum(term, lx, 16, BASE16);
break;
} default: {
unsigned long long llx;
llx = va_arg(args, unsigned long long);
numPrinted += printNum(term, llx, 16, BASE16);
break;
}}
end = 1;
break;
// Exit if we reach the end of the string
case '\0':
end = 1;
break;
// Unknown character; just print the character
default:
term_putChar(term, fmt[i]);
numPrinted++;
end = 1;
break;
}
i++;
}
} else {
// Print the number
numPrinted += printNum(term, unum, 10, BASE16);
// If specified, print succeeding spaces
if ((mode >> 5) % 2 == 1) {
for (int j = width - flen; j > 0; j--) {
term_putChar(term, ' ');
numPrinted++;
}
}
mode = 0;
break;
// Print an unsigned integer
} case 'u': {
uintmax_t num;
// Pull the argument from the list
switch (length) {
case 1:
num = va_arg(args, unsigned long);
break;
case 2:
num = va_arg(args, unsigned long long);
break;
case 3:
num = va_arg(args, size_t);
break;
case 4:
num = va_arg(args, intmax_t);
break;
case 5:
num = va_arg(args, ptrdiff_t);
break;
default:
num = va_arg(args, unsigned int);
break;
}
// Get the length of the printed number
unsigned int len = numLen(num, 10);
// Get the length of the printed number with formatting
unsigned int flen = len;
if (precision > len)
flen += (precision - len);
if ((mode >> 3) % 2 == 1 || (mode >> 4) % 2 == 1)
flen++;
// If specified, print preceding space padding
if ((mode >> 5) % 2 == 0) {
if (precision > 0 || (mode >> 7) % 2 == 0) {
for (int j = width - flen; j > 0; j--) {
term_putChar(term, ' ');
numPrinted++;
}
}
}
// If specified print the sign
if ((mode >> 3) % 2 == 1) {
term_putChar(term, '+');
numPrinted++;
} else if ((mode >> 4) % 2 == 1) {
term_putChar(term, ' ');
numPrinted++;
}
// If specified, print the precision 0s
if (precision > 0) {
for (int j = precision - len; j > 0; j--) {
term_putChar(term, '0');
numPrinted++;
}
} else if ((mode >> 7) % 2 == 1) {
for (int j = width - flen; j > 0; j--) {
term_putChar(term, '0');
numPrinted++;
}
}
// Print the number
numPrinted += printNum(term, num, 10, BASE16);
// If specified, print succeeding spaces
if ((mode >> 5) % 2 == 1) {
for (int j = width - flen; j > 0; j--) {
term_putChar(term, ' ');
numPrinted++;
}
}
mode = 0;
break;
// Print an unsigned integer in hexadecimal
} case 'x':
case 'X': {
uintmax_t num;
// Pull the argument from the list
switch (length) {
case 1:
num = va_arg(args, unsigned long);
break;
case 2:
num = va_arg(args, unsigned long long);
break;
case 3:
num = va_arg(args, size_t);
break;
case 4:
num = va_arg(args, intmax_t);
break;
case 5:
num = va_arg(args, ptrdiff_t);
break;
default:
num = va_arg(args, unsigned int);
break;
}
// Get the length of the printed number
unsigned int len = numLen(num, 10);
// Get the length of the printed number with formatting
unsigned int flen = len;
if (precision > len)
flen += (precision - len);
if ((mode >> 3) % 2 == 1 || (mode >> 4) % 2 == 1)
flen++;
if ((mode >> 6) % 2 == 1)
flen += 2;
// If specified, print preceding space padding
if ((mode >> 5) % 2 == 0) {
if (precision > 0 || (mode >> 7) % 2 == 0) {
for (int j = width - flen; j > 0; j--) {
term_putChar(term, ' ');
numPrinted++;
}
}
}
// If specified print the sign
if ((mode >> 3) % 2 == 1) {
term_putChar(term, '+');
numPrinted++;
} else if ((mode >> 4) % 2 == 1) {
term_putChar(term, ' ');
numPrinted++;
}
// If specified, print the 0x
if ((mode >> 6) % 2 == 1) {
term_putChar(term, '0');
if (fmt[i] == 'x')
term_putChar(term, 'x');
else
term_putChar(term, 'X');
numPrinted += 2;
}
// If specified, print the precision 0s
if (precision > 0) {
for (int j = precision - len; j > 0; j--) {
term_putChar(term, '0');
numPrinted++;
}
} else if ((mode >> 7) % 2 == 1) {
for (int j = width - flen; j > 0; j--) {
term_putChar(term, '0');
numPrinted++;
}
}
// Print the number
if (fmt[i] == 'x')
numPrinted += printNum(term, num, 16, BASE16L);
else
numPrinted += printNum(term, num, 16, BASE16);
// If specified, print succeeding spaces
if ((mode >> 5) % 2 == 1) {
for (int j = width - flen; j > 0; j--) {
term_putChar(term, ' ');
numPrinted++;
}
}
mode = 0;
break;
// Print an unsigned integer in octal
} case 'o': {
uintmax_t num;
// Pull the argument from the list
switch (length) {
case 1:
num = va_arg(args, unsigned long);
break;
case 2:
num = va_arg(args, unsigned long long);
break;
case 3:
num = va_arg(args, size_t);
break;
case 4:
num = va_arg(args, intmax_t);
break;
case 5:
num = va_arg(args, ptrdiff_t);
break;
default:
num = va_arg(args, unsigned int);
break;
}
// Get the length of the printed number
unsigned int len = numLen(num, 10);
// Get the length of the printed number with formatting
unsigned int flen = len;
if (precision > len)
flen += (precision - len);
if ((mode >> 3) % 2 == 1 || (mode >> 4) % 2 == 1)
flen++;
if ((mode >> 6) % 2 == 1)
flen++;
// If specified, print preceding space padding
if ((mode >> 5) % 2 == 0) {
if (precision > 0 || (mode >> 7) % 2 == 0) {
for (int j = width - flen; j > 0; j--) {
term_putChar(term, ' ');
numPrinted++;
}
}
}
// If specified print the sign
if ((mode >> 3) % 2 == 1) {
term_putChar(term, '+');
numPrinted++;
} else if ((mode >> 4) % 2 == 1) {
term_putChar(term, ' ');
numPrinted++;
}
// If specified, print the 0x
if ((mode >> 6) % 2 == 1) {
term_putChar(term, '0');
numPrinted++;
}
// If specified, print the precision 0s
if (precision > 0) {
for (int j = precision - len; j > 0; j--) {
term_putChar(term, '0');
numPrinted++;
}
} else if ((mode >> 7) % 2 == 1) {
for (int j = width - flen; j > 0; j--) {
term_putChar(term, '0');
numPrinted++;
}
}
// Print the number
numPrinted += printNum(term, num, 8, BASE16);
// If specified, print succeeding spaces
if ((mode >> 5) % 2 == 1) {
for (int j = width - flen; j > 0; j--) {
term_putChar(term, ' ');
numPrinted++;
}
}
mode = 0;
break;
// Print a string
} case 's': {
char *s = va_arg(args, char *);
term_writeStr(term, s);
numPrinted += strlen(s);
mode = 0;
break;
// Print a character
} case 'c':
if (mode % 2 == 0)
term_putChar(term, 'c');
else
term_putChar(term, (char) va_arg(args, int));
numPrinted++;
mode = 0;
break;
// Print a pointer
case 'p': {
uintmax_t num = va_arg(args, void *);
// Get the length of the printed number
unsigned int len = numLen(num, 16);
unsigned int flen = len + 2;
precision = sizeof(void *) * 2; // A byte is 2 hex chars
// If specified, print preceding space padding
if ((mode >> 5) % 2 == 0) {
for (int j = width - flen; j > 0; j--) {
term_putChar(term, ' ');
numPrinted++;
}
}
term_putChar(term, '0');
term_putChar(term, 'x');
numPrinted += 2;
// If specified, print the precision 0s
for (int j = precision - len; j > 0; j--) {
term_putChar(term, '0');
numPrinted++;
}
// Print the number
numPrinted += printNum(term, num, 16, BASE16);
// If specified, print succeeding spaces
if ((mode >> 5) % 2 == 1) {
for (int j = width - flen; j > 0; j--) {
term_putChar(term, ' ');
numPrinted++;
}
}
mode = 0;
break;
} default:
term_putChar(term, fmt[i]);
numPrinted++;
i++;
mode = 0;
break;
}
}

View File

@ -2,18 +2,6 @@
#include <brados/string.h>
// Find the first instance of c in str
// Returns -1 if c is not found
int strindexof(char c, const char *str)
{
for (size_t i = 0; i < strlen(str); i++) {
if (str[i] == c)
return i;
}
return -1;
}
// Find the length of a string
size_t strlen(const char *str)
{