| ////////////////////////////////////////////////////////////////////////////// |
| // SPDX-FileCopyrightText: 2021, Dinesh Annayya //// |
| // //// |
| // 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. //// |
| // SPDX-License-Identifier: Apache-2.0 //// |
| // SPDX-FileContributor: Dinesh Annayya <dinesha@opencores.org> //// |
| ////////////////////////////////////////////////////////////////////////////// |
| |
| #include <string.h> |
| #include <stdarg.h> |
| #include "sc_print.h" |
| |
| #define SC_SIM_OUTPORT (0xf0000000) |
| #define CHAR_BIT (8) |
| |
| static void |
| sc_puts(long str, long strlen) { |
| volatile char *out_ptr = (volatile char*)SC_SIM_OUTPORT; |
| const char *in_ptr = (const char*)str; |
| for (long len = strlen; len > 0; --len) |
| *out_ptr = *in_ptr++; |
| } |
| |
| #undef putchar |
| int |
| putchar(int ch) { |
| static __thread char buf[64] __attribute__((aligned(64))); |
| static __thread int buflen = 0; |
| |
| buf[buflen++] = ch; |
| |
| if ( ch == '\n' || buflen == sizeof(buf) ) { |
| sc_puts((long)buf, buflen); |
| buflen = 0; |
| } |
| |
| return 0; |
| } |
| |
| static void |
| printf_putch(int ch, void** data) |
| { |
| putchar(ch); |
| } |
| |
| static void |
| print(const char *str) |
| { |
| sc_puts((long)str, strlen(str)); |
| } |
| |
| |
| 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 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 inline void |
| printnum(void(*putch)(int, void**), |
| void **putdat, |
| unsigned long long num, |
| unsigned base, |
| int width, |
| int padc, |
| int hex_A) |
| { |
| unsigned digs[sizeof(num) * CHAR_BIT]; |
| int pos = 0; |
| |
| for ( ;; ) { |
| 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 ? hex_A - 10 : '0'), 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; |
| int err; |
| unsigned long long num; |
| int base; |
| int lflag; |
| int width; |
| int precision; |
| int altflag; |
| char padc; |
| int hex_A = 'a'; |
| for ( ;; ) { |
| 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; |
| |
| case 'f': |
| { |
| // #ifndef nopfloat |
| // double num = getdouble(&ap, lflag); |
| // printdoubleF(putch, putdat, num, width, precision, padc); |
| // #endif |
| } |
| break; |
| |
| // 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': |
| hex_A = 'a'; |
| base = 16; |
| goto unsigned_number; |
| |
| case 'X': |
| hex_A = 'A'; |
| base = 16; |
| unsigned_number: |
| num = getuint(&ap, lflag); |
| signed_number: |
| printnum(putch, putdat, num, base, width, padc, hex_A); |
| break; |
| |
| // escaped '%' character |
| case '%': |
| putch(ch, putdat); |
| break; |
| |
| // unrecognized escape sequence - just print it literally |
| default: |
| putch('%', putdat); |
| fmt = last_fmt; |
| break; |
| } |
| } |
| } |
| |
| int |
| sc_printf(const char* fmt, ...) |
| { |
| va_list ap; |
| va_start(ap, fmt); |
| |
| vprintfmt(printf_putch, NULL, fmt, ap); |
| |
| va_end(ap); |
| return 0; // incorrect return value, but who cares, anyway? |
| } |