blob: 302ed6fb78f1112dc85213377c528ecc341dc919 [file] [log] [blame]
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0
#include "sw/device/lib/print_log.h"
#include <stdarg.h>
// Identifiers for string format type specifiers.
static const char kFormatSpecifier = '%';
static const char kBinary = 'b'; // Print binary [SystemVerilog style].
static const char kDecimalI = 'i'; // Signed decimal.
static const char kDecimal = 'd'; // Signed decimal.
static const char kUnsigned = 'u'; // Unsigned decimal.
static const char kOctal = 'o'; // Octal.
static const char kAsciiChar = 'c'; // Single character byte.
static const char kHexUpper = 'X'; // Upper case hex.
static const char kHexAltUpper = 'H'; // Upper case hex [SystemVerilog style].
static const char kHexLower = 'x'; // Lower case hex.
static const char kHexAltLower = 'h'; // Lower case hex [SystemVerilog style].
static const char kPercent = '%'; // Escape '%'.
static inline void print_digit(print_char_func print_char, unsigned int digit) {
print_char("0123456789ABCDEF"[digit]);
}
static inline void print_num(print_char_func print_char, int width,
unsigned int n, unsigned int base) {
// TODO: Consider changing this to for loop.
if (--width > 0 || n >= base) {
print_num(print_char, width, n / base, base);
}
print_digit(print_char, n % base);
}
void print_log(print_char_func print_char, const char *fmt, ...) {
va_list va;
va_start(va, fmt);
while (*fmt != '\0') {
char ch = *fmt++;
if (ch != kFormatSpecifier) {
// Add CR to new line automatically (if not added already).
if (ch == '\n' && !(*(fmt - 1) == '\r' || *(fmt + 1) == '\r')) {
print_char('\r');
}
print_char(ch);
} else {
int w = 0;
// TODO: Refactor this into a separate function.
while (*fmt != '\0') {
ch = *fmt++;
// Parse width field.
if (ch >= '0' && ch <= '9') {
w = w * 10 + (ch - '0');
continue;
} else {
switch (ch) {
case '\0': {
return;
}
case kBinary: {
unsigned int n = va_arg(va, unsigned int);
print_num(print_char, w, n, 2);
break;
}
case kDecimalI:
case kDecimal: {
unsigned int n = va_arg(va, unsigned int);
if (((int)n) < 0) {
print_char('-');
n = -n;
}
print_num(print_char, w, n, 10);
break;
}
case kUnsigned: {
unsigned int n = va_arg(va, unsigned int);
print_num(print_char, w, n, 10);
break;
}
case kOctal: {
unsigned int n = va_arg(va, unsigned int);
print_num(print_char, w, n, 8);
break;
}
case kHexLower:
case kHexAltLower:
case kHexUpper:
case kHexAltUpper: {
// TODO: This will still print in upper case.
unsigned int n = va_arg(va, unsigned int);
print_num(print_char, w, n, 16);
break;
}
case kAsciiChar: {
char c = va_arg(va, int);
print_char(c);
break;
}
case kPercent: {
print_char('%');
break;
}
default: {
// Unknown format - this error message is printed inline within
// the message.
print_log(print_char, "[INVALID SPECIFIER: %%%c]", ch);
break;
}
}
break;
}
}
}
}
va_end(va);
}