/****************************************************************************** * Copyright (c) 1995 - 2022 Xilinx, Inc. All rights reserved. * SPDX-License-Identifier: MIT *******************************************************************************/ /*---------------------------------------------------*/ /* Modified from : */ /* Public Domain version of printf */ /* Rud Merriam, Compsult, Inc. Houston, Tx. */ /* For Embedded Systems Programming, 1991 */ /* */ /*---------------------------------------------------*/ #include "xil_printf.h" #include "xil_types.h" #include "xil_assert.h" #include #include #include static void padding( const s32 l_flag,const struct params_s *par); static void outs(const charptr lp, struct params_s *par); static s32 getnum( charptr* linep); typedef struct params_s { s32 len; s32 num1; s32 num2; char8 pad_character; s32 do_padding; s32 left_flag; s32 unsigned_flag; } params_t; /*---------------------------------------------------*/ /* The purpose of this routine is to output data the */ /* same as the standard printf function without the */ /* overhead most run-time libraries involve. Usually */ /* the printf brings in many kilobytes of code and */ /* that is unacceptable in most embedded systems. */ /*---------------------------------------------------*/ /*---------------------------------------------------*/ /* */ /* This routine puts pad characters into the output */ /* buffer. */ /* */ static void padding( const s32 l_flag, const struct params_s *par) { s32 i; if ((par->do_padding != 0) && (l_flag != 0) && (par->len < par->num1)) { i=(par->len); for (; i<(par->num1); i++) { #if defined(STDOUT_BASEADDRESS) || defined(VERSAL_PLM) outbyte( par->pad_character); #endif } } } /*---------------------------------------------------*/ /* */ /* This routine moves a string to the output buffer */ /* as directed by the padding and positioning flags. */ /* */ static void outs(const charptr lp, struct params_s *par) { charptr LocalPtr; LocalPtr = lp; /* pad on left if needed */ if(LocalPtr != NULL) { par->len = (s32)strlen( LocalPtr); padding( !(par->left_flag), par); /* Move string to the buffer */ while (((*LocalPtr) != (char8)0) && ((par->num2) != 0)) { (par->num2)--; #if defined(STDOUT_BASEADDRESS) || defined(VERSAL_PLM) outbyte(*LocalPtr); #endif LocalPtr += 1; } } /* Pad on right if needed */ /* CR 439175 - elided next stmt. Seemed bogus. */ padding( par->left_flag, par); } /*---------------------------------------------------*/ /* */ /* This routine moves a number to the output buffer */ /* as directed by the padding and positioning flags. */ /* */ static void outnum( const s32 n, const s32 base, struct params_s *par) { s32 negative; s32 i; char8 outbuf[32]; const char8 digits[] = "0123456789ABCDEF"; u32 num; for(i = 0; i<32; i++) { outbuf[i] = '0'; } /* Check if number is negative */ if ((par->unsigned_flag == 0) && (base == 10) && (n < 0L)) { negative = 1; num =(-(n)); } else{ num = n; negative = 0; } /* Build number (backwards) in outbuf */ i = 0; do { outbuf[i] = digits[(num % (u32)base)]; i++; num /= base; } while (num > 0U); if (negative != 0) { outbuf[i] = '-'; i++; } outbuf[i] = '\0'; i--; /* Move the converted number to the buffer and */ /* add in the padding where needed. */ par->len = (s32)strlen(outbuf); padding( !(par->left_flag), par); while (&outbuf[i] >= outbuf) { #if defined(STDOUT_BASEADDRESS) || defined(VERSAL_PLM) outbyte( outbuf[i] ); #endif i--; } padding( par->left_flag, par); } /*---------------------------------------------------*/ /* */ /* This routine moves a 64-bit number to the output */ /* buffer as directed by the padding and positioning */ /* flags. */ /* */ #if defined (__aarch64__) || defined (__arch64__) static void outnum1( const s64 n, const s32 base, params_t *par) { s32 negative; s32 i; char8 outbuf[64]; const char8 digits[] = "0123456789ABCDEF"; u64 num; for(i = 0; i<64; i++) { outbuf[i] = '0'; } /* Check if number is negative */ if ((par->unsigned_flag == 0) && (base == 10) && (n < 0L)) { negative = 1; num =(-(n)); } else{ num = (n); negative = 0; } /* Build number (backwards) in outbuf */ i = 0; do { outbuf[i] = digits[(num % base)]; i++; num /= base; } while (num > 0); if (negative != 0) { outbuf[i] = '-'; i++; } outbuf[i] = '\0'; i--; /* Move the converted number to the buffer and */ /* add in the padding where needed. */ par->len = (s32)strlen(outbuf); padding( !(par->left_flag), par); while (&outbuf[i] >= outbuf) { outbyte( outbuf[i] ); i--; } padding( par->left_flag, par); } #endif /*---------------------------------------------------*/ /* */ /* This routine gets a number from the format */ /* string. */ /* */ static s32 getnum(charptr* linep) { s32 n = 0; s32 ResultIsDigit = 0; charptr cptr = *linep; while (cptr != NULL) { ResultIsDigit = isdigit(((u8)*cptr)); if (ResultIsDigit == 0) { break; } n = ((n*10) + (((s32)*cptr) - (s32)'0')); cptr += 1; } *linep = ((charptr)(cptr)); return(n); } /*---------------------------------------------------*/ /* */ /* This routine operates just like a printf/sprintf */ /* routine. It outputs a set of data under the */ /* control of a formatting string. Not all of the */ /* standard C format control are supported. The ones */ /* provided are primarily those needed for embedded */ /* systems work. Primarily the floating point */ /* routines are omitted. Other formats could be */ /* added easily by following the examples shown for */ /* the supported formats. */ /* */ /* void esp_printf( const func_ptr f_ptr, const charptr ctrl1, ...) */ #if defined (__aarch64__) && HYP_GUEST && EL1_NONSECURE && XEN_USE_PV_CONSOLE void xil_printf( const char8 *ctrl1, ...){ XPVXenConsole_Printf(ctrl1); } #else void xil_printf( const char8 *ctrl1, ...) { va_list argp; va_start(argp, ctrl1); xil_vprintf(ctrl1, argp); va_end(argp); } #endif /* This routine is equivalent to vprintf routine */ void xil_vprintf(const char8 *ctrl1, va_list argp) { s32 Check; #if defined (__aarch64__) || defined (__arch64__) s32 long_flag; #endif s32 dot_flag; params_t par; u8 ch; char8 *ctrl = (char8 *)ctrl1; while ((ctrl != NULL) && (*ctrl != (char8)0)) { /* move format string chars to buffer until a */ /* format control is found. */ if (*ctrl != '%') { #if defined(STDOUT_BASEADDRESS) || defined(VERSAL_PLM) outbyte(*ctrl); #endif ctrl += 1; continue; } /* initialize all the flags for this format. */ dot_flag = 0; #if defined (__aarch64__) || defined (__arch64__) long_flag = 0; #endif par.unsigned_flag = 0; par.left_flag = 0; par.do_padding = 0; par.pad_character = ' '; par.num2=32767; par.num1=0; par.len=0; try_next: if(ctrl != NULL) { ctrl += 1; } if(ctrl != NULL) { ch = (u8)*ctrl; } else { break; } if (isdigit(ch) != 0) { if (dot_flag != 0) { par.num2 = getnum(&ctrl); } else { if (ch == (u8)'0') { par.pad_character = '0'; } if(ctrl != NULL) { par.num1 = getnum(&ctrl); } par.do_padding = 1; } if(ctrl != NULL) { ctrl -= 1; } goto try_next; } switch (tolower(ch)) { case '%': #if defined(STDOUT_BASEADDRESS) || defined(VERSAL_PLM) outbyte( '%'); #endif Check = 1; break; case '-': par.left_flag = 1; Check = 0; break; case '.': dot_flag = 1; Check = 0; break; case 'l': #if defined (__aarch64__) || defined (__arch64__) long_flag = 1; #endif Check = 0; break; case 'u': par.unsigned_flag = 1; /* fall through */ case 'i': case 'd': #if defined (__aarch64__) || defined (__arch64__) if (long_flag != 0){ outnum1((s64)va_arg(argp, s64), 10L, &par); } else { outnum( va_arg(argp, s32), 10L, &par); } #else outnum( va_arg(argp, s32), 10L, &par); #endif Check = 1; break; case 'p': #if defined (__aarch64__) || defined (__arch64__) par.unsigned_flag = 1; outnum1((s64)va_arg(argp, s64), 16L, &par); Check = 1; break; #endif case 'X': case 'x': par.unsigned_flag = 1; #if defined (__aarch64__) || defined (__arch64__) if (long_flag != 0) { outnum1((s64)va_arg(argp, s64), 16L, &par); } else { outnum((s32)va_arg(argp, s32), 16L, &par); } #else outnum((s32)va_arg(argp, s32), 16L, &par); #endif Check = 1; break; case 's': outs( va_arg( argp, char *), &par); Check = 1; break; case 'c': #if defined(STDOUT_BASEADDRESS) || defined(VERSAL_PLM) outbyte( (char8)va_arg( argp, s32)); #endif Check = 1; break; case '\\': switch (*ctrl) { case 'a': #if defined(STDOUT_BASEADDRESS) || defined(VERSAL_PLM) outbyte( ((char8)0x07)); #endif break; case 'h': #if defined(STDOUT_BASEADDRESS) || defined(VERSAL_PLM) outbyte( ((char8)0x08)); #endif break; case 'r': #if defined(STDOUT_BASEADDRESS) || defined(VERSAL_PLM) outbyte( ((char8)0x0D)); #endif break; case 'n': #if defined(STDOUT_BASEADDRESS) || defined(VERSAL_PLM) outbyte( ((char8)0x0D)); outbyte( ((char8)0x0A)); #endif break; default: #if defined(STDOUT_BASEADDRESS) || defined(VERSAL_PLM) outbyte( *ctrl); #endif break; } ctrl += 1; Check = 0; break; default: Check = 1; break; } if(Check == 1) { if(ctrl != NULL) { ctrl += 1; } continue; } goto try_next; } } /*---------------------------------------------------*/