/* Copyright 2018 Embedded Microprocessor Benchmark Consortium (EEMBC) Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include #include #define ZEROPAD (1 << 0) /* Pad with zero */ #define SIGN (1 << 1) /* Unsigned/signed long */ #define PLUS (1 << 2) /* Show plus */ #define SPACE (1 << 3) /* Spacer */ #define LEFT (1 << 4) /* Left justified */ #define HEX_PREP (1 << 5) /* 0x */ #define UPPERCASE (1 << 6) /* 'ABCDEF' */ #define is_digit(c) ((c) >= '0' && (c) <= '9') static char * digits = "0123456789abcdefghijklmnopqrstuvwxyz"; static char * upper_digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; static ee_size_t strnlen(const char *s, ee_size_t count); static ee_size_t strnlen(const char *s, ee_size_t count) { const char *sc; for (sc = s; *sc != '\0' && count--; ++sc) ; return sc - s; } static int skip_atoi(const char **s) { int i = 0; while (is_digit(**s)) i = i * 10 + *((*s)++) - '0'; return i; } static char * number(char *str, long num, int base, int size, int precision, int type) { char c, sign, tmp[66]; char *dig = digits; int i; if (type & UPPERCASE) dig = upper_digits; if (type & LEFT) type &= ~ZEROPAD; if (base < 2 || base > 36) return 0; c = (type & ZEROPAD) ? '0' : ' '; sign = 0; if (type & SIGN) { if (num < 0) { sign = '-'; num = -num; size--; } else if (type & PLUS) { sign = '+'; size--; } else if (type & SPACE) { sign = ' '; size--; } } if (type & HEX_PREP) { if (base == 16) size -= 2; else if (base == 8) size--; } i = 0; if (num == 0) tmp[i++] = '0'; else { while (num != 0) { tmp[i++] = dig[((unsigned long)num) % (unsigned)base]; num = ((unsigned long)num) / (unsigned)base; } } if (i > precision) precision = i; size -= precision; if (!(type & (ZEROPAD | LEFT))) while (size-- > 0) *str++ = ' '; if (sign) *str++ = sign; if (type & HEX_PREP) { if (base == 8) *str++ = '0'; else if (base == 16) { *str++ = '0'; *str++ = digits[33]; } } if (!(type & LEFT)) while (size-- > 0) *str++ = c; while (i < precision--) *str++ = '0'; while (i-- > 0) *str++ = tmp[i]; while (size-- > 0) *str++ = ' '; return str; } static char * eaddr(char *str, unsigned char *addr, int size, int precision, int type) { char tmp[24]; char *dig = digits; int i, len; if (type & UPPERCASE) dig = upper_digits; len = 0; for (i = 0; i < 6; i++) { if (i != 0) tmp[len++] = ':'; tmp[len++] = dig[addr[i] >> 4]; tmp[len++] = dig[addr[i] & 0x0F]; } if (!(type & LEFT)) while (len < size--) *str++ = ' '; for (i = 0; i < len; ++i) *str++ = tmp[i]; while (len < size--) *str++ = ' '; return str; } static char * iaddr(char *str, unsigned char *addr, int size, int precision, int type) { char tmp[24]; int i, n, len; len = 0; for (i = 0; i < 4; i++) { if (i != 0) tmp[len++] = '.'; n = addr[i]; if (n == 0) tmp[len++] = digits[0]; else { if (n >= 100) { tmp[len++] = digits[n / 100]; n = n % 100; tmp[len++] = digits[n / 10]; n = n % 10; } else if (n >= 10) { tmp[len++] = digits[n / 10]; n = n % 10; } tmp[len++] = digits[n]; } } if (!(type & LEFT)) while (len < size--) *str++ = ' '; for (i = 0; i < len; ++i) *str++ = tmp[i]; while (len < size--) *str++ = ' '; return str; } #if HAS_FLOAT char * ecvtbuf(double arg, int ndigits, int *decpt, int *sign, char *buf); char * fcvtbuf(double arg, int ndigits, int *decpt, int *sign, char *buf); static void ee_bufcpy(char *d, char *s, int count); void ee_bufcpy(char *pd, char *ps, int count) { char *pe = ps + count; while (ps != pe) *pd++ = *ps++; } static void parse_float(double value, char *buffer, char fmt, int precision) { int decpt, sign, exp, pos; char *digits = NULL; char cvtbuf[80]; int capexp = 0; int magnitude; if (fmt == 'G' || fmt == 'E') { capexp = 1; fmt += 'a' - 'A'; } if (fmt == 'g') { digits = ecvtbuf(value, precision, &decpt, &sign, cvtbuf); magnitude = decpt - 1; if (magnitude < -4 || magnitude > precision - 1) { fmt = 'e'; precision -= 1; } else { fmt = 'f'; precision -= decpt; } } if (fmt == 'e') { digits = ecvtbuf(value, precision + 1, &decpt, &sign, cvtbuf); if (sign) *buffer++ = '-'; *buffer++ = *digits; if (precision > 0) *buffer++ = '.'; ee_bufcpy(buffer, digits + 1, precision); buffer += precision; *buffer++ = capexp ? 'E' : 'e'; if (decpt == 0) { if (value == 0.0) exp = 0; else exp = -1; } else exp = decpt - 1; if (exp < 0) { *buffer++ = '-'; exp = -exp; } else *buffer++ = '+'; buffer[2] = (exp % 10) + '0'; exp = exp / 10; buffer[1] = (exp % 10) + '0'; exp = exp / 10; buffer[0] = (exp % 10) + '0'; buffer += 3; } else if (fmt == 'f') { digits = fcvtbuf(value, precision, &decpt, &sign, cvtbuf); if (sign) *buffer++ = '-'; if (*digits) { if (decpt <= 0) { *buffer++ = '0'; *buffer++ = '.'; for (pos = 0; pos < -decpt; pos++) *buffer++ = '0'; while (*digits) *buffer++ = *digits++; } else { pos = 0; while (*digits) { if (pos++ == decpt) *buffer++ = '.'; *buffer++ = *digits++; } } } else { *buffer++ = '0'; if (precision > 0) { *buffer++ = '.'; for (pos = 0; pos < precision; pos++) *buffer++ = '0'; } } } *buffer = '\0'; } static void decimal_point(char *buffer) { while (*buffer) { if (*buffer == '.') return; if (*buffer == 'e' || *buffer == 'E') break; buffer++; } if (*buffer) { int n = strnlen(buffer, 256); while (n > 0) { buffer[n + 1] = buffer[n]; n--; } *buffer = '.'; } else { *buffer++ = '.'; *buffer = '\0'; } } static void cropzeros(char *buffer) { char *stop; while (*buffer && *buffer != '.') buffer++; if (*buffer++) { while (*buffer && *buffer != 'e' && *buffer != 'E') buffer++; stop = buffer--; while (*buffer == '0') buffer--; if (*buffer == '.') buffer--; while (buffer != stop) *++buffer = 0; } } static char * flt(char *str, double num, int size, int precision, char fmt, int flags) { char tmp[80]; char c, sign; int n, i; // Left align means no zero padding if (flags & LEFT) flags &= ~ZEROPAD; // Determine padding and sign char c = (flags & ZEROPAD) ? '0' : ' '; sign = 0; if (flags & SIGN) { if (num < 0.0) { sign = '-'; num = -num; size--; } else if (flags & PLUS) { sign = '+'; size--; } else if (flags & SPACE) { sign = ' '; size--; } } // Compute the precision value if (precision < 0) precision = 6; // Default precision: 6 // Convert floating point number to text parse_float(num, tmp, fmt, precision); if ((flags & HEX_PREP) && precision == 0) decimal_point(tmp); if (fmt == 'g' && !(flags & HEX_PREP)) cropzeros(tmp); n = strnlen(tmp, 256); // Output number with alignment and padding size -= n; if (!(flags & (ZEROPAD | LEFT))) while (size-- > 0) *str++ = ' '; if (sign) *str++ = sign; if (!(flags & LEFT)) while (size-- > 0) *str++ = c; for (i = 0; i < n; i++) *str++ = tmp[i]; while (size-- > 0) *str++ = ' '; return str; } #endif static int ee_vsprintf(char *buf, const char *fmt, va_list args) { int len; unsigned long num; int i, base; char * str; char * s; int flags; // Flags to number() int field_width; // Width of output field int precision; // Min. # of digits for integers; max number of chars for // from string int qualifier; // 'h', 'l', or 'L' for integer fields for (str = buf; *fmt; fmt++) { if (*fmt != '%') { *str++ = *fmt; continue; } // Process flags flags = 0; repeat: fmt++; // This also skips first '%' switch (*fmt) { case '-': flags |= LEFT; goto repeat; case '+': flags |= PLUS; goto repeat; case ' ': flags |= SPACE; goto repeat; case '#': flags |= HEX_PREP; goto repeat; case '0': flags |= ZEROPAD; goto repeat; } // Get field width field_width = -1; if (is_digit(*fmt)) field_width = skip_atoi(&fmt); else if (*fmt == '*') { fmt++; field_width = va_arg(args, int); if (field_width < 0) { field_width = -field_width; flags |= LEFT; } } // Get the precision precision = -1; if (*fmt == '.') { ++fmt; if (is_digit(*fmt)) precision = skip_atoi(&fmt); else if (*fmt == '*') { ++fmt; precision = va_arg(args, int); } if (precision < 0) precision = 0; } // Get the conversion qualifier qualifier = -1; if (*fmt == 'l' || *fmt == 'L') { qualifier = *fmt; fmt++; } // Default base base = 10; switch (*fmt) { case 'c': if (!(flags & LEFT)) while (--field_width > 0) *str++ = ' '; *str++ = (unsigned char)va_arg(args, int); while (--field_width > 0) *str++ = ' '; continue; case 's': s = va_arg(args, char *); if (!s) s = ""; len = strnlen(s, precision); if (!(flags & LEFT)) while (len < field_width--) *str++ = ' '; for (i = 0; i < len; ++i) *str++ = *s++; while (len < field_width--) *str++ = ' '; continue; case 'p': if (field_width == -1) { field_width = 2 * sizeof(void *); flags |= ZEROPAD; } str = number(str, (unsigned long)va_arg(args, void *), 16, field_width, precision, flags); continue; case 'A': flags |= UPPERCASE; case 'a': if (qualifier == 'l') str = eaddr(str, va_arg(args, unsigned char *), field_width, precision, flags); else str = iaddr(str, va_arg(args, unsigned char *), field_width, precision, flags); continue; // Integer number formats - set up the flags and "break" case 'o': base = 8; break; case 'X': flags |= UPPERCASE; case 'x': base = 16; break; case 'd': case 'i': flags |= SIGN; case 'u': break; #if HAS_FLOAT case 'f': str = flt(str, va_arg(args, double), field_width, precision, *fmt, flags | SIGN); continue; #endif default: if (*fmt != '%') *str++ = '%'; if (*fmt) *str++ = *fmt; else --fmt; continue; } if (qualifier == 'l') num = va_arg(args, unsigned long); else if (flags & SIGN) num = va_arg(args, int); else num = va_arg(args, unsigned int); str = number(str, num, base, field_width, precision, flags); } *str = '\0'; return str - buf; } #include void uart_send_char(char c) { #if defined(BOARD_ehrenberg) || defined(BOARD_tgc_vp) while (get_uart_rx_tx_reg_tx_free(uart)==0) ; uart_write(uart, c); if (c == '\n') { while (get_uart_rx_tx_reg_tx_free(uart)==0) ; uart_write(uart, '\r'); } #elif defined(BOARD_iss) *((uint32_t*) 0xFFFF0000) = c; #else while (UART0_REG(UART_REG_TXFIFO) & 0x80000000) ; UART0_REG(UART_REG_TXFIFO) = c; if (c == '\n') { while (UART0_REG(UART_REG_TXFIFO) & 0x80000000) ; UART0_REG(UART_REG_TXFIFO) = '\r'; } #endif } int ee_printf(const char *fmt, ...) { char buf[1024], *p; va_list args; int n = 0; va_start(args, fmt); ee_vsprintf(buf, fmt, args); va_end(args); p = buf; while (*p) { uart_send_char(*p); n++; p++; } return n; }