From 2aa443bf0b6dc8ce5c50e67f99dbd15b46ba321a Mon Sep 17 00:00:00 2001 From: Eryn Wells Date: Sun, 13 Mar 2016 13:35:50 -0400 Subject: [PATCH] Some small functions for dealing with ASCII --- src/Console.hh | 6 + src/ConsolePrintFormat.cc | 404 ++++++++++++++++++++++++++++++++++++++ src/SConscript | 1 + src/kstd/ascii.hh | 82 ++++++++ 4 files changed, 493 insertions(+) create mode 100644 src/ConsolePrintFormat.cc create mode 100644 src/kstd/ascii.hh diff --git a/src/Console.hh b/src/Console.hh index 82cf444..f50c83d 100644 --- a/src/Console.hh +++ b/src/Console.hh @@ -58,6 +58,12 @@ struct Console /** Write a string to the terminal at the current cursor position. */ void printString(const char *str); + /** + * Write a format string to the current cursor position. Returns the number + * of characters printed. + */ + int printFormat(const char *format, ...); + void setColor(Color fg, Color bg); private: diff --git a/src/ConsolePrintFormat.cc b/src/ConsolePrintFormat.cc new file mode 100644 index 0000000..022cf54 --- /dev/null +++ b/src/ConsolePrintFormat.cc @@ -0,0 +1,404 @@ +/* ConsolePrintFormat.cc + * vim: set tw=80: + * Eryn Wells + */ +/** + * Implementation of Console::printFormat, a printf style method. + */ + +#include + +void itoa(int value, char* buffer, int base); + + +struct PrintfSpec +{ + enum Size { + SizeNormal, + SizeDoubleShort, + SizeShort, + SizeLong, + SizeDoubleLong, + }; + + enum Type { + TypeInt, + TypeHex, + TypeChar, + TypeString + }; + + union Value { + int d; + char hhd; + short int hd; + long int ld; + long long int lld; + unsigned int x; + unsigned char hhx; + unsigned short int hx; + unsigned long int lx; + unsigned long long int llx; + char c; + char* s; + }; + + bool zero; + int width; + Size size; + Type type; + Value value; + + void clear(); + int print(Console& console); +}; + + +void +PrintfSpec::clear() +{ + zero = false; + width = 0; + size = PrintfSpec::SizeNormal; + type = PrintfSpec::TypeInt; +} + + +int +PrintfSpec::print(Console& console) +{ + int nchars = 0; + int length = 0; + char buf[32]; + char pad = ' '; + char *str; + + /* + * TypeChar is a special case because it will always be a single character + * in length and there is no \0 to terminate it. + */ + if (type == TypeChar) { + if (width == 0) { + width = 1; + } + for (int i = 1; i < width; i++) { + putchar(' '); + } + putchar(value.c); + return width; + } + + if (type == TypeInt || type == TypeHex) { + if (zero) { + pad = '0'; + } + if (type == TypeInt) { + switch (size) { + case SizeNormal: + itoa(value.d, buf, 10); + break; + case SizeDoubleShort: + itoa(value.hhd, buf, 10); + break; + case SizeShort: + itoa(value.hd, buf, 10); + break; + case SizeLong: + itoa(value.ld, buf, 10); + break; + case SizeDoubleLong: + itoa(value.lld, buf, 10); + break; + } + } else { + switch (size) { + case SizeNormal: + itoa(value.x, buf, 16); + break; + case SizeDoubleShort: + itoa(value.hhx, buf, 16); + break; + case SizeShort: + itoa(value.hx, buf, 16); + break; + case SizeLong: + itoa(value.lx, buf, 16); + break; + case SizeDoubleLong: + itoa(value.llx, buf, 16); + break; + } + } + length = strlen(buf); + if (width < length) { + width = length; + } + str = buf; + } else if (type == TypeString) { + length = strlen(value.s); + if (width < length) { + width = length; + } + str = value.s; + } else { + // Don't know how to print this. + return 0; + } + + for (int i = width; i > 0; i--) { + if (i <= length) { + console.printChar(str[length - i]); + } + else if (i <= width) { + console.printChar(pad); + } + } + + return nchars; +} + + +void +itoa(int value, + char* buffer, + int base) +{ + const bool neg = base == 10 && value < 0; + + if (neg) { + value *= -1; + } + + char* p = buffer; + int place; + do { + place = value % base; + value /= base; + *p++ = place < 10 ? place + '0' : (place - 10) + 'a'; + } while (value != 0); + + if (neg) { + *p++ = '-'; + } + + *p-- = '\0'; + + // Reverse the string. + char* t = buffer; + char c; + while (p > t) { + c = *p; + *p-- = *t; + *t++ = c; + } +} + + +void +to_upper(char* buffer) +{ + char* p = buffer; + for (char* p = buffer; *p != 0; p++) { + if (*p >= 'a' && *p <= 'z') { + *p = (*p - 'a') + 'A'; + } + } +} + + +inline bool +is_digit(char c) +{ + return c >= '0' && c <= '9'; +} + + +inline bool +is_hex(char c) +{ + return is_digit(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'); +} + + +inline bool +is_lower(char c) +{ + return c >= 'a' && c <= 'z'; +} + + +inline bool +is_upper(char c) +{ + return c >= 'A' && c <= 'Z'; +} + + +inline bool +is_size(char c) +{ + return c == 'h' || c == 'l'; +} + + +inline bool +is_specifier(char c) +{ + return c == 'd' || c == 'x' || c == 'X' || c == 'c' || c == 's'; +} + + +inline bool +is_letter(char c) +{ + return is_lower(c) || is_upper(c); +} + +namespace kernel { + +int +Console::printFormat(const char* fmt, + ...) +{ + enum { + StateDefault = 0, + StatePercent, + StateWidth, + StateSize + } state = StateDefault; + + va_list vl; + va_start(vl, fmt); + + int nchars = 0; + PrintfSpec spec; + + for (const char* p = fmt; *p != 0; p++) { + switch (state) { + case StateDefault: + if (*p == '%') { + state = StatePercent; + spec.clear(); + } else { + printChar(*p); + nchars++; + } + break; + case StatePercent: + if (*p == '%') { + state = StateDefault; + printChar(*p); + nchars++; + } else if (is_digit(*p)) { + if (*p == '0' && !spec.zero) { + spec.zero = true; + } else { + state = StateWidth; + spec.width = *p - '0'; + } + } else if (is_size(*p)) { + goto state_size; + } else if (is_specifier(*p)) { + goto state_specifier; + } + break; + case StateWidth: + if (is_digit(*p)) { + spec.width = 10 * spec.width + (*p - '0'); + } else if (is_size(*p)) { + state = StateSize; + goto state_size; + } else if (is_specifier(*p)) { + goto state_specifier; + } + break; + case StateSize: + if (is_size(*p)) { + goto state_size; + } else if (is_specifier(*p)) { + goto state_specifier; + } + break; + default: + printChar(*p); + nchars++; + break; + } + continue; + + state_size: + if (*p == 'h') { + spec.size = (spec.size == PrintfSpec::SizeShort) + ? PrintfSpec::SizeDoubleShort + : PrintfSpec::SizeShort; + } else if (*p == 'l') { + spec.size = (spec.size == PrintfSpec::SizeLong) + ? PrintfSpec::SizeDoubleLong + : PrintfSpec::SizeLong; + } + continue; + + state_specifier: + state = StateDefault; + switch (*p) { + case 'd': + case 'i': + switch (spec.size) { + case PrintfSpec::SizeNormal: + spec.value.d = va_arg(vl, int); + break; + case PrintfSpec::SizeDoubleShort: + spec.value.hhd = (signed char)va_arg(vl, int); + break; + case PrintfSpec::SizeShort: + spec.value.hd = (short int)va_arg(vl, int); + break; + case PrintfSpec::SizeLong: + spec.value.ld = va_arg(vl, long int); + break; + case PrintfSpec::SizeDoubleLong: + spec.value.lld = va_arg(vl, long long int); + break; + } + spec.type = PrintfSpec::TypeInt; + break; + case 'x': + case 'X': + switch (spec.size) { + case PrintfSpec::SizeNormal: + spec.value.x = va_arg(vl, unsigned int); + break; + case PrintfSpec::SizeDoubleShort: + spec.value.hhx = (unsigned char)va_arg(vl, unsigned int); + break; + case PrintfSpec::SizeShort: + spec.value.hx = (unsigned short int)va_arg(vl, unsigned int); + break; + case PrintfSpec::SizeLong: + spec.value.lx = va_arg(vl, unsigned long int); + break; + case PrintfSpec::SizeDoubleLong: + spec.value.llx = va_arg(vl, unsigned long long int); + break; + } + spec.type = PrintfSpec::TypeHex; + break; + case 'c': + spec.value.c = va_arg(vl, int); + spec.type = PrintfSpec::TypeChar; + break; + case 's': + spec.value.s = va_arg(vl, char*); + spec.type = PrintfSpec::TypeString; + break; + } + nchars += spec.print(*this); + continue; + } + + va_end(vl); + + return nchars; +} + +} /* namespace kernel */ diff --git a/src/SConscript b/src/SConscript index 7d703d1..83ab222 100644 --- a/src/SConscript +++ b/src/SConscript @@ -11,6 +11,7 @@ files = [ 'boot.s', 'Main.cc', 'Console.cc', + 'ConsolePrintFormat.cc', 'Descriptors.cc', 'Interrupts.cc', 'PIC.cc', diff --git a/src/kstd/ascii.hh b/src/kstd/ascii.hh new file mode 100644 index 0000000..7dd60d7 --- /dev/null +++ b/src/kstd/ascii.hh @@ -0,0 +1,82 @@ +/* ascii.hh + * vim: set tw=80: + * Eryn Wells + */ +/** + * A few useful utility functions for dealing with ASCII values. + */ + +namespace kernel { + +struct Char +{ + /* + * Categorization + */ + + static inline bool + isDigit(char c) + { + return c >= '0' && c <='9'; + } + + static inline bool + isHexDigit(char c) + { + return isDigit(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'); + } + + static inline bool + isLower(char c) + { + return c >= 'a' && c <= 'z'; + } + + static inline bool + isUpper(char c) + { + return c >= 'A' && c <= 'Z'; + } + + static inline bool + isLetter(char c) + { + return isLower(c) || isUpper(c); + } + + static inline bool + isAlphanumeric(char c) + { + return isLetter(c) || isDigit(c); + } + + /* + * Conversion + */ + + static inline char + toLower(char c) + { + if (isUpper(c)) { + return 'a' + (c - 'A'); + } + return c; + } + + static inline char + toUpper(char c) + { + if (isLower(c)) { + return 'A' + (c - 'a'); + } + return c; + } +}; + + +struct Int +{ + static void toString(int value, char *buffer, int base = 10); +}; + +} /* namespace kernel */