274 lines
		
	
	
		
			5.3 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
		
		
			
		
	
	
			274 lines
		
	
	
		
			5.3 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
|  | /* 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 <stdint.h>
 | ||
|  | #include <string.h>
 | ||
|  | #include <unistd.h>
 | ||
|  | 
 | ||
|  | #include "os_types.h"
 | ||
|  | #include "dbg_io.h"
 | ||
|  | 
 | ||
|  | #undef putchar
 | ||
|  | int putchar(int ch) | ||
|  | { | ||
|  |     //return write(1, &ch, 1) == 1 ? ch : -1;
 | ||
|  |     dbg_putchar(ch); | ||
|  |     return ch; | ||
|  | } | ||
|  | 
 | ||
|  | 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; | ||
|  |   unsigned long num; | ||
|  |   int base, lflag, width, precision; | ||
|  |   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; | ||
|  |   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 '#': | ||
|  |       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; | ||
|  | } |