summaryrefslogtreecommitdiff
path: root/software/dhrystone/dhry_printf.c
diff options
context:
space:
mode:
Diffstat (limited to 'software/dhrystone/dhry_printf.c')
-rw-r--r--software/dhrystone/dhry_printf.c271
1 files changed, 271 insertions, 0 deletions
diff --git a/software/dhrystone/dhry_printf.c b/software/dhrystone/dhry_printf.c
new file mode 100644
index 0000000..1fdaac4
--- /dev/null
+++ b/software/dhrystone/dhry_printf.c
@@ -0,0 +1,271 @@
+/* The functions in this file are only meant to support Dhrystone on an
+ * embedded RV32 system and are obviously incorrect in general. */
+
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
+
+#undef putchar
+int putchar(int ch)
+{
+ return write(1, &ch, 1) == 1 ? ch : -1;
+}
+
+static void sprintf_putch(int ch, void** data)
+{
+ char** pstr = (char**)data;
+ **pstr = ch;
+ (*pstr)++;
+}
+
+static unsigned long getuint(va_list *ap, int lflag)
+{
+ if (lflag)
+ return va_arg(*ap, unsigned long);
+ else
+ return va_arg(*ap, unsigned int);
+}
+
+static long getint(va_list *ap, int lflag)
+{
+ if (lflag)
+ return va_arg(*ap, long);
+ else
+ return va_arg(*ap, int);
+}
+
+static inline void printnum(void (*putch)(int, void**), void **putdat,
+ unsigned long num, unsigned base, int width, int padc)
+{
+ unsigned digs[sizeof(num)*8];
+ int pos = 0;
+
+ while (1)
+ {
+ digs[pos++] = num % base;
+ if (num < base)
+ break;
+ num /= base;
+ }
+
+ while (width-- > pos)
+ putch(padc, putdat);
+
+ while (pos-- > 0)
+ putch(digs[pos] + (digs[pos] >= 10 ? 'a' - 10 : '0'), putdat);
+}
+
+static inline void print_double(void (*putch)(int, void**), void **putdat,
+ double num, int width, int prec)
+{
+ union {
+ double d;
+ uint64_t u;
+ } u;
+ u.d = num;
+
+ if (u.u & (1ULL << 63)) {
+ putch('-', putdat);
+ u.u &= ~(1ULL << 63);
+ }
+
+ for (int i = 0; i < prec; i++)
+ u.d *= 10;
+
+ char buf[32], *pbuf = buf;
+ printnum(sprintf_putch, (void**)&pbuf, (unsigned long)u.d, 10, 0, 0);
+ if (prec > 0) {
+ for (int i = 0; i < prec; i++) {
+ pbuf[-i] = pbuf[-i-1];
+ }
+ pbuf[-prec] = '.';
+ pbuf++;
+ }
+
+ for (char* p = buf; p < pbuf; p++)
+ putch(*p, putdat);
+}
+
+static void vprintfmt(void (*putch)(int, void**), void **putdat, const char *fmt, va_list ap)
+{
+ register const char* p;
+ const char* last_fmt;
+ register int ch, err;
+ unsigned long num;
+ int base, lflag, width, precision, altflag;
+ char padc;
+
+ while (1) {
+ while ((ch = *(unsigned char *) fmt) != '%') {
+ if (ch == '\0')
+ return;
+ fmt++;
+ putch(ch, putdat);
+ }
+ fmt++;
+
+ // Process a %-escape sequence
+ last_fmt = fmt;
+ padc = ' ';
+ width = -1;
+ precision = -1;
+ lflag = 0;
+ altflag = 0;
+ reswitch:
+ switch (ch = *(unsigned char *) fmt++) {
+
+ // flag to pad on the right
+ case '-':
+ padc = '-';
+ goto reswitch;
+
+ // flag to pad with 0's instead of spaces
+ case '0':
+ padc = '0';
+ goto reswitch;
+
+ // width field
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ for (precision = 0; ; ++fmt) {
+ precision = precision * 10 + ch - '0';
+ ch = *fmt;
+ if (ch < '0' || ch > '9')
+ break;
+ }
+ goto process_precision;
+
+ case '*':
+ precision = va_arg(ap, int);
+ goto process_precision;
+
+ case '.':
+ if (width < 0)
+ width = 0;
+ goto reswitch;
+
+ case '#':
+ altflag = 1;
+ goto reswitch;
+
+ process_precision:
+ if (width < 0)
+ width = precision, precision = -1;
+ goto reswitch;
+
+ // long flag
+ case 'l':
+ if (lflag)
+ goto bad;
+ goto reswitch;
+
+ // character
+ case 'c':
+ putch(va_arg(ap, int), putdat);
+ break;
+
+ // double
+ case 'f':
+ print_double(putch, putdat, va_arg(ap, double), width, precision);
+ break;
+
+ // string
+ case 's':
+ if ((p = va_arg(ap, char *)) == NULL)
+ p = "(null)";
+ if (width > 0 && padc != '-')
+ for (width -= strnlen(p, precision); width > 0; width--)
+ putch(padc, putdat);
+ for (; (ch = *p) != '\0' && (precision < 0 || --precision >= 0); width--) {
+ putch(ch, putdat);
+ p++;
+ }
+ for (; width > 0; width--)
+ putch(' ', putdat);
+ break;
+
+ // (signed) decimal
+ case 'd':
+ num = getint(&ap, lflag);
+ if ((long) num < 0) {
+ putch('-', putdat);
+ num = -(long) num;
+ }
+ base = 10;
+ goto signed_number;
+
+ // unsigned decimal
+ case 'u':
+ base = 10;
+ goto unsigned_number;
+
+ // (unsigned) octal
+ case 'o':
+ // should do something with padding so it's always 3 octits
+ base = 8;
+ goto unsigned_number;
+
+ // pointer
+ case 'p':
+ lflag = 1;
+ putch('0', putdat);
+ putch('x', putdat);
+ /* fall through to 'x' */
+
+ // (unsigned) hexadecimal
+ case 'x':
+ base = 16;
+ unsigned_number:
+ num = getuint(&ap, lflag);
+ signed_number:
+ printnum(putch, putdat, num, base, width, padc);
+ break;
+
+ // escaped '%' character
+ case '%':
+ putch(ch, putdat);
+ break;
+
+ // unrecognized escape sequence - just print it literally
+ default:
+ bad:
+ putch('%', putdat);
+ fmt = last_fmt;
+ break;
+ }
+ }
+}
+
+int printf(const char* fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+
+ vprintfmt((void*)putchar, 0, fmt, ap);
+
+ va_end(ap);
+ return 0; // incorrect return value, but who cares, anyway?
+}
+
+int sprintf(char* str, const char* fmt, ...)
+{
+ va_list ap;
+ char* str0 = str;
+ va_start(ap, fmt);
+
+ vprintfmt(sprintf_putch, (void**)&str, fmt, ap);
+ *str = 0;
+
+ va_end(ap);
+ return str - str0;
+}