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;
 | 
						|
}
 |