text: // See LICENSE for license details.
  
  #include <stdint.h>
  #include <string.h>
  #include <stdarg.h>
  #include <stdio.h>
  #include <limits.h>
  #include <sys/signal.h>
  #include "util.h"
  
  #define SYS_write 64
  
  #undef strcmp
  
  extern volatile uint64_t tohost;
  extern volatile uint64_t fromhost;
  
  static uintptr_t syscall(uintptr_t which, uint64_t arg0, uint64_t arg1, uint64_t arg2)
  {
    volatile uint64_t magic_mem[8] __attribute__((aligned(64)));
    magic_mem[0] = which;
    magic_mem[1] = arg0;
    magic_mem[2] = arg1;
    magic_mem[3] = arg2;
    __sync_synchronize();
  
    tohost = (uintptr_t)magic_mem;
    while (fromhost == 0)
      ;
  //  fromhost = 0;
  
  //  __sync_synchronize();
    return magic_mem[0];
  }
  
  #define NUM_COUNTERS 31
  static uintptr_t counters[NUM_COUNTERS];
  static char* counter_names[NUM_COUNTERS];
  
  void setStats(int enable)
  {
    if(!enable) {
      asm volatile ("not t0,x0; csrw 0x320, t0");
    }
    int i = 0;
  #define READ_CTR(name,event_name) do { \
      while (i >= NUM_COUNTERS) ; \
      uintptr_t csr = read_csr(name); \
      if (!enable) { csr -= counters[i]; counter_names[i] = #event_name; } \
      counters[i++] = csr; \
    } while (0)
    
    READ_CTR(mcycle, mycycle);
    READ_CTR(minstret, minstret);
    READ_CTR(mhpmcounter3,misprediction);
    READ_CTR(mhpmcounter4,exceptions);
    READ_CTR(mhpmcounter5,interrupts);
    READ_CTR(mhpmcounter6,csrops);
    READ_CTR(mhpmcounter7,jumps);
    READ_CTR(mhpmcounter8,branches);
    READ_CTR(mhpmcounter9,floats);
    READ_CTR(mhpmcounter10,muldiv);
    READ_CTR(mhpmcounter11,rawstalls);
    READ_CTR(mhpmcounter12,exetalls);
    READ_CTR(mhpmcounter13,icache_access);
    READ_CTR(mhpmcounter14,icache_miss);
    READ_CTR(mhpmcounter15,icache_fbhit);
    READ_CTR(mhpmcounter16,icache_ncaccess);
    READ_CTR(mhpmcounter17,icache_fbrelease);
    READ_CTR(mhpmcounter18,dcache_read_access		);
    READ_CTR(mhpmcounter19,dcache_write_access		);
    READ_CTR(mhpmcounter20,dcache_atomic_access		);
    READ_CTR(mhpmcounter21,dcache_nc_read_access		);
    READ_CTR(mhpmcounter22,dcache_nc_write_access);
    READ_CTR(mhpmcounter23,dcache_read_miss		);
    READ_CTR(mhpmcounter24,dcache_write_miss		);
    READ_CTR(mhpmcounter25,dcache_atomic_miss		);
    READ_CTR(mhpmcounter26,dcache_read_fb_hits		);
    READ_CTR(mhpmcounter27,dcache_write_fb_hits		);
    READ_CTR(mhpmcounter28,dcache_atomic_fb_hits		);
    READ_CTR(mhpmcounter29,dcache_fb_releases		);
    READ_CTR(mhpmcounter30,dcache_line_evictions		);
    READ_CTR(mhpmcounter31,itlb_misses);
  #undef READ_CTR
  }
  
  void __attribute__((noreturn)) tohost_exit(uintptr_t code)
  {
          asm volatile (
              "uart_end: li t1, 0x11300;" "\n\t"
              "lb a1, 12(t1);" "\n\t"
              "andi a1, a1, 0x1;" "\n\t"
              "beqz a1, uart_end;" "\n\t"
              "li t6, 0x20000;" "\n\t"
              "sw t5, 12(t6);" "\n\t"
  				:
  				:
  				:"x0","t1","t2", "cc", "memory");
    tohost = (code << 1) | 1;
    while (1);
  }
  
  uintptr_t __attribute__((weak)) handle_trap(uintptr_t cause, uintptr_t epc, uintptr_t regs[32])
  {
    tohost_exit(1337);
  }
  
  void exit(int code)
  {
    tohost_exit(code);
  }
  
  void abort()
  {
    exit(128 + SIGABRT);
  }
  
  void printstr(const char* s)
  {
    syscall(SYS_write, 1, (uintptr_t)s, strlen(s));
  }
  
  void __attribute__((weak)) thread_entry(int cid, int nc)
  {
    // multi-threaded programs override this function.
    // for the case of single-threaded programs, only let core 0 proceed.
    while (cid != 0);
  }
  
  int __attribute__((weak)) main(int argc, char** argv)
  {
    // single-threaded programs override this function.
    printstr("Implement main(), foo!\n");
    return -1;
  }
  
  static void init_tls()
  {
    register void* thread_pointer asm("tp");
    extern char _tls_data;
    extern __thread char _tdata_begin, _tdata_end, _tbss_end;
    size_t tdata_size = &_tdata_end - &_tdata_begin;
    memcpy(thread_pointer, &_tls_data, tdata_size);
    size_t tbss_size = &_tbss_end - &_tdata_end;
    memset(thread_pointer + tdata_size, 0, tbss_size);
  }
  
  void _init(int cid, int nc)
  {
    init_tls();
    thread_entry(cid, nc);
  
    // only single-threaded programs should ever get here.
    int ret = main(0, 0);
    for (int i = 0; i < NUM_COUNTERS; i++)
  			printf("%s = %d\n",counter_names[i],counters[i]);
    exit(ret);
  }
  
  
  #undef putchar
  int putchar(int ch)
  {
    register char a0 asm("a0") = ch;
    asm volatile ("li t1, 0x11300" "\n\t"	//The base address of UART config registers
          "uart_status_simple: lb a1, 12(t1)" "\n\t"
          "andi a1,a1,0x2" "\n\t"
          "beqz a1, uart_status_simple" "\n\t"
  				"sb a0, 4(t1)"  "\n\t"
  				:
  				:
  				:"a0","t1","cc","memory");
    return 0;
  }
  
  /*
  #undef getchar
  int getchar()                                                              
  {
   register char a0 asm("a0");
         asm volatile ("li t1, 0x11200" "\n\t" //The base address of UART config registers
             		  	"uart_statusr: lb t2, 40(t1)" "\n\t"
      				"andi t2, t2, 0x1" "\n\t"
  	    			"beqz t2, uart_statusr" "\n\t"
                      "lb a0, 0(t1)"  "\n\t"      //The base address of UART data register
                      :  
                      :  
                      :"a0","t1","t2","cc","memory");
     return a0;
  }
  */
  
  
  
  
  
  void printhex(uint64_t x)
  {
    char str[17];
    int i;
    for (i = 0; i < 16; i++)
    {
      str[15-i] = (x & 0xF) + ((x & 0xF) < 10 ? '0' : 'a'-10);
      x >>= 4;
    }
    str[16] = 0;
  
    printstr(str);
  }
  
  static inline void printnum(void (*putch)(int, void**), void **putdat,
                      unsigned long long num, unsigned base, int width, int padc)
  {
    unsigned digs[sizeof(num)*CHAR_BIT];
    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 unsigned long long getuint(va_list *ap, int lflag)
  {
    if (lflag >= 2)
      return va_arg(*ap, unsigned long long);
    else if (lflag)
      return va_arg(*ap, unsigned long);
    else
      return va_arg(*ap, unsigned int);
  }
  
  static long long getint(va_list *ap, int lflag)
  {
    if (lflag >= 2)
      return va_arg(*ap, long long);
    else if (lflag)
      return va_arg(*ap, long);
    else
      return va_arg(*ap, int);
  }
  
  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 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 (doubled for long long)
      case 'l':
        lflag++;
        goto reswitch;
  
      // character
      case 'c':
        putch(va_arg(ap, int), putdat);
        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 long) num < 0) {
          putch('-', putdat);
          num = -(long 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':
        static_assert(sizeof(long) == sizeof(void*));
        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:
        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);
  
    void sprintf_putch(int ch, void** data)
    {
      char** pstr = (char**)data;
      **pstr = ch;
      (*pstr)++;
    }
  
    vprintfmt(sprintf_putch, (void**)&str, fmt, ap);
    *str = 0;
  
    va_end(ap);
    return str - str0;
  }
  
  void* memcpy(void* dest, const void* src, size_t len)
  {
    if ((((uintptr_t)dest | (uintptr_t)src | len) & (sizeof(uintptr_t)-1)) == 0) {
      const uintptr_t* s = src;
      uintptr_t *d = dest;
      while (d < (uintptr_t*)(dest + len))
        *d++ = *s++;
    } else {
      const char* s = src;
      char *d = dest;
      while (d < (char*)(dest + len))
        *d++ = *s++;
    }
    return dest;
  }
  
  void* memset(void* dest, int byte, size_t len)
  {
    if ((((uintptr_t)dest | len) & (sizeof(uintptr_t)-1)) == 0) {
      uintptr_t word = byte & 0xFF;
      word |= word << 8;
      word |= word << 16;
      word |= word << 16 << 16;
  
      uintptr_t *d = dest;
      while (d < (uintptr_t*)(dest + len))
        *d++ = word;
    } else {
      char *d = dest;
      while (d < (char*)(dest + len))
        *d++ = byte;
    }
    return dest;
  }
  
  size_t strlen(const char *s)
  {
    const char *p = s;
    while (*p)
      p++;
    return p - s;
  }
  
  size_t strnlen(const char *s, size_t n)
  {
    const char *p = s;
    while (n-- && *p)
      p++;
    return p - s;
  }
  
  int strcmp(const char* s1, const char* s2)
  {
    unsigned char c1, c2;
  
    do {
      c1 = *s1++;
      c2 = *s2++;
    } while (c1 != 0 && c1 == c2);
  
    return c1 - c2;
  }
  
  char* strcpy(char* dest, const char* src)
  {
    char* d = dest;
    while ((*d++ = *src++))
      ;
    return dest;
  }
  
  long atol(const char* str)
  {
    long res = 0;
    int sign = 0;
  
    while (*str == ' ')
      str++;
  
    if (*str == '-' || *str == '+') {
      sign = *str == '-';
      str++;
    }
  
    while (*str) {
      res *= 10;
      res += *str++ - '0';
    }
  
    return sign ? -res : res;
  }
  