1 //*****************************************************************************
2 //
3 //! @file am_util_stdio.c
4 //!
5 //! @brief A few printf-style functions for use with Ambiq products
6 //!
7 //! Functions for performing printf-style operations without dynamic memory
8 //! allocation.
9 //!
10 //! For further information about this module concerning its history, uses,
11 //! and limitations, please see the Ambiq Micro KB article "Q&A: What does
12 //! the AmbiqSuite SDK am_util_stdio_printf() function do?" at:
13 //!
14 //! https://support.ambiqmicro.com/hc/en-us/articles/360040441631
15 //!
16 //! @addtogroup stdio STDIO - Ambiq's Implementation
17 //! @ingroup utils
18 //! @{
19 //
20 //*****************************************************************************
21 
22 //*****************************************************************************
23 //
24 // Copyright (c) 2023, Ambiq Micro, Inc.
25 // All rights reserved.
26 //
27 // Redistribution and use in source and binary forms, with or without
28 // modification, are permitted provided that the following conditions are met:
29 //
30 // 1. Redistributions of source code must retain the above copyright notice,
31 // this list of conditions and the following disclaimer.
32 //
33 // 2. Redistributions in binary form must reproduce the above copyright
34 // notice, this list of conditions and the following disclaimer in the
35 // documentation and/or other materials provided with the distribution.
36 //
37 // 3. Neither the name of the copyright holder nor the names of its
38 // contributors may be used to endorse or promote products derived from this
39 // software without specific prior written permission.
40 //
41 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
42 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
43 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
44 // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
45 // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
46 // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
47 // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
48 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
49 // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
50 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
51 // POSSIBILITY OF SUCH DAMAGE.
52 //
53 // This is part of revision release_sdk_4_4_0-3c5977e664 of the AmbiqSuite Development Package.
54 //
55 //*****************************************************************************
56 
57 #include <stdint.h>
58 #include <stdbool.h>
59 #include <stdarg.h>
60 #include "am_util_stdio.h"
61 
62 //*****************************************************************************
63 //
64 // Global Variables
65 //
66 //*****************************************************************************
67 
68 // function pointer for printf
69 am_util_stdio_print_char_t g_pfnCharPrint;
70 
71 // buffer for printf
72 static char g_prfbuf[AM_PRINTF_BUFSIZE];
73 
74 // Flag to do conversion of '\n' to '\n\r' in sprintf()
75 static bool g_bTxtXlate = false;
76 
77 //*****************************************************************************
78 //
79 // Sets the interface for printf calls.
80 //
81 //*****************************************************************************
82 void
am_util_stdio_printf_init(am_util_stdio_print_char_t pfnCharPrint)83 am_util_stdio_printf_init(am_util_stdio_print_char_t pfnCharPrint)
84 {
85     g_pfnCharPrint = pfnCharPrint;
86 }
87 
88 //*****************************************************************************
89 //
90 // Converts strings to 32-bit unsigned integers.
91 //
92 //*****************************************************************************
93 uint32_t
am_util_stdio_strtoul(const char * str,char ** endptr,int base)94 am_util_stdio_strtoul(const char *str, char **endptr, int base)
95 {
96     char *pos;
97     uint32_t ui32BaseVal;
98     uint32_t ui32RetVal;
99 
100     //
101     // Prepare a pointer to start advancing through the string.
102     //
103     pos = (char *)str;
104 
105     //
106     // Determine what base we are using. Default to '16', but change to other
107     // values as specified by the user. If the user didn't specify anything,
108     // try to guess the base value from looking at the first few characters of
109     // the input
110     //
111     ui32BaseVal = 16;
112 
113     //
114     // Switch to octal for a leading zero
115     //
116     if (*pos == '0')
117     {
118         ui32BaseVal = 8;
119         pos++;
120 
121         //
122         // Switch back to hex for a leading '0x'
123         //
124         if (*pos == 'x')
125         {
126             ui32BaseVal = 16;
127             pos++;
128         }
129     }
130 
131     //
132     // No matter what, if the user specified a base value, use that as the real
133     // base value.
134     //
135     if (base)
136     {
137         ui32BaseVal = base;
138     }
139 
140     //
141     // Start accumulating the converted integer value
142     //
143     ui32RetVal = 0;
144 
145     //
146     // Loop through the digits, one character at a time. End the loop if the
147     // number is out of range
148     //
149     while ((*pos >= 'a' && *pos <= 'f' && ui32BaseVal == 16) ||
150            (*pos >= 'A' && *pos <= 'F' && ui32BaseVal == 16) ||
151            (*pos >= '0' && *pos <= '9'))
152     {
153         //
154         // Make sure to stop if we hit a NULL byte.
155         //
156         if (*pos == 0)
157         {
158             break;
159         }
160 
161         //
162         // Multiply by the base value to move up one 'digit'
163         //
164         ui32RetVal *= ui32BaseVal;
165 
166         //
167         // Add the value of the next character.
168         //
169         if (*pos >= '0' && *pos <= '9')
170         {
171             ui32RetVal += *pos - '0';
172         }
173         else if (*pos >= 'A' && *pos <= 'F')
174         {
175             ui32RetVal += (*pos - 'A') + 10;
176         }
177         else
178         {
179             ui32RetVal += (*pos - 'a') + 10;
180         }
181 
182         //
183         // Grab the next character.
184         //
185         pos++;
186     }
187 
188     //
189     // If we get here, hopefully it means that we have parsed a number
190     // correctly. The 'pos' pointer should already be pointing at the character
191     // right after the last valid number, so set the enptr appropriately, and
192     // return the calculated numerical value of the string.
193     //
194     if (endptr)
195     {
196         *endptr = pos;
197     }
198 
199     return ui32RetVal;
200 }
201 
202 //*****************************************************************************
203 //
204 //  Divide an unsigned 32-bit value by 10.
205 //
206 //  Note: Adapted from Ch10 of Hackers Delight (hackersdelight.org).
207 //
208 //*****************************************************************************
209 static uint64_t
divu64_10(uint64_t ui64Val)210 divu64_10(uint64_t ui64Val)
211 {
212     uint64_t q64, r64;
213     uint32_t q32, r32, ui32Val;
214 
215     //
216     // If a 32-bit value, use the more optimal 32-bit routine.
217     //
218     if ( ui64Val >> 32 )
219     {
220         q64 = (ui64Val>>1) + (ui64Val>>2);
221         q64 += (q64 >> 4);
222         q64 += (q64 >> 8);
223         q64 += (q64 >> 16);
224         q64 += (q64 >> 32);
225         q64 >>= 3;
226         r64 = ui64Val - q64*10;
227         return q64 + ((r64 + 6) >> 4);
228     }
229     else
230     {
231         ui32Val = (uint32_t)(ui64Val & 0xffffffff);
232         q32 = (ui32Val>>1) + (ui32Val>>2);
233         q32 += (q32 >> 4);
234         q32 += (q32 >> 8);
235         q32 += (q32 >> 16);
236         q32 >>= 3;
237         r32 = ui32Val - q32*10;
238         return (uint64_t)(q32 + ((r32 + 6) >> 4));
239     }
240 }
241 
242 //*****************************************************************************
243 //
244 // Return the number of decimal digits in an uint64_t.
245 //
246 // example: 10000 return 5, 123 returns 3.
247 //
248 //*****************************************************************************
249 static int
ndigits_in_u64(uint64_t ui64Val)250 ndigits_in_u64(uint64_t ui64Val)
251 {
252     int iNDigits = ui64Val ? 0 : 1;
253 
254     while ( ui64Val )
255     {
256         //
257         // ui32Val /= 10;
258         //
259         ui64Val = divu64_10(ui64Val);
260         ++iNDigits;
261     }
262 
263     return iNDigits;
264 }
265 
266 //*****************************************************************************
267 //
268 // Return the number of decimal digits in a 64-bit integer.
269 //
270 // Note: Does not include the '-' sign.
271 //
272 // example: -3 returns 1, 3 returns 1, 15 returns 2, -15 returns 2, ...
273 //
274 //*****************************************************************************
275 static int
ndigits_in_i64(int64_t i64Val)276 ndigits_in_i64(int64_t i64Val)
277 {
278     if ( i64Val < 0 )
279     {
280         //
281         // Get absolute value
282         //
283         i64Val = -i64Val;
284     }
285 
286     return ndigits_in_u64((uint64_t) i64Val);
287 }
288 
289 //*****************************************************************************
290 //
291 // Return the number of hex digits in an uint64_t.
292 //
293 //*****************************************************************************
294 static int
ndigits_in_hex(uint64_t ui64Val)295 ndigits_in_hex(uint64_t ui64Val)
296 {
297     int iDigits = ui64Val ? 0 : 1;
298 
299     while ( ui64Val )
300     {
301         ui64Val >>= 4;
302         ++iDigits;
303     }
304 
305     return iDigits;
306 }
307 
308 //*****************************************************************************
309 //
310 // Converts a string representing a decimal value to an int32_t.
311 //
312 // Returns the int32_t integer value.
313 //
314 // Note: If a count of the number of chars is desired, then provide
315 // pui32CharCnt.
316 //
317 //*****************************************************************************
318 static uint32_t
decstr_to_int(const char * pcStr,uint32_t * pui32CharCnt)319 decstr_to_int(const char *pcStr, uint32_t *pui32CharCnt)
320 {
321     bool bNeg = false;
322     uint32_t ui32Val = 0, uCnt = 0;
323 
324     if ( *pcStr == '-')
325     {
326         bNeg = true;
327         pcStr++;
328         uCnt++;
329     }
330 
331     while ( *pcStr >= '0'  &&  *pcStr <= '9' )
332     {
333         ++uCnt;
334 
335         //
336         // Multiply accumulated value by 10.
337         //
338         ui32Val *= 10;
339 
340         //
341         // Add in the new low digit.
342         //
343         ui32Val += (*pcStr - '0');
344         pcStr++;
345     }
346 
347     if ( pui32CharCnt )
348     {
349         *pui32CharCnt = uCnt;
350     }
351 
352     return bNeg ? -ui32Val : ui32Val;
353 }
354 
355 //*****************************************************************************
356 //
357 // Converts ui64Val to a string.
358 // Note: pcBuf[] must be sized for a minimum of 21 characters.
359 //
360 // Returns the number of decimal digits in the string.
361 //
362 // NOTE: If pcBuf is NULL, will compute a return ui64Val only (no chars
363 // written).
364 //
365 //*****************************************************************************
366 static int
uint64_to_str(uint64_t ui64Val,char * pcBuf)367 uint64_to_str(uint64_t ui64Val, char *pcBuf)
368 {
369     char tbuf[25];
370     int ix = 0, iNumDig = 0;
371     unsigned uMod;
372     uint64_t u64Tmp;
373 
374     do
375     {
376         //
377         // Divide by 10
378         //
379         u64Tmp = divu64_10(ui64Val);
380 
381         //
382         // Get modulus
383         //
384         uMod = ui64Val - (u64Tmp * 10);
385 
386         tbuf[ix++] = uMod + '0';
387         ui64Val = u64Tmp;
388     } while ( ui64Val );
389 
390     //
391     // Save the total number of digits
392     //
393     iNumDig = ix;
394 
395     //
396     // Now, reverse the buffer when saving to the caller's buffer.
397     //
398     if ( pcBuf )
399     {
400         while ( ix-- )
401         {
402             *pcBuf++ = tbuf[ix];
403         }
404 
405         //
406         // Terminate the caller's buffer
407         //
408         *pcBuf = 0x00;
409     }
410 
411     return iNumDig;
412 }
413 
414 //*****************************************************************************
415 //
416 // Converts ui64Val to a hex string.  Alpha chars are lower case.
417 // Input:
418 //  ui64Val = Value to be converted.
419 //  pcBuf[] must be sized for a minimum of 17 characters.
420 //
421 // Returns the number of hex digits required for ui64Val (does not
422 //  include the terminating NULL char in the string).
423 //
424 // NOTE: If pcBuf is NULL, will compute a return value only (no chars
425 // written).
426 //
427 //*****************************************************************************
428 static int
uint64_to_hexstr(uint64_t ui64Val,char * pcBuf,bool bLower)429 uint64_to_hexstr(uint64_t ui64Val, char *pcBuf, bool bLower)
430 {
431     int iNumDig, ix = 0;
432     char cCh, tbuf[20];
433 
434     if ( ui64Val == 0 )
435     {
436         tbuf[ix++] = '0';   // Print a '0'
437     }
438 
439     while ( ui64Val )
440     {
441         cCh = ui64Val & 0xf;
442 
443         //
444         // Alpha character
445         //
446         if ( cCh > 9 )
447         {
448             cCh += bLower ? 0x27 : 0x7;
449         }
450 
451         tbuf[ix++] = cCh + '0';
452         ui64Val >>= 4;
453     }
454 
455     //
456     // Save the total number of digits
457     //
458     iNumDig = ix;
459 
460     //
461     // Now, reverse the buffer when saving to the callers buffer.
462     //
463     if (pcBuf)
464     {
465         while (ix--)
466         {
467             *pcBuf++ = tbuf[ix];
468         }
469 
470         //
471         // Terminate the caller's buffer
472         //
473         *pcBuf = 0;
474     }
475 
476     return iNumDig;
477 }
478 
479 //*****************************************************************************
480 //
481 // Return length of the given string.
482 //
483 //*****************************************************************************
484 static uint32_t
simple_strlen(char * pcBuf)485 simple_strlen(char *pcBuf)
486 {
487     uint32_t ui32RetVal = 0;
488     if ( !pcBuf )
489     {
490         return ui32RetVal;
491     }
492 
493     while ( *pcBuf++ )
494     {
495         ui32RetVal++;
496     }
497     return ui32RetVal;
498 }
499 
500 //*****************************************************************************
501 //
502 // Pad a string buffer with pad characters.
503 //
504 //*****************************************************************************
505 static int32_t
padbuffer(char * pcBuf,uint8_t cPadChar,int32_t i32NumChars)506 padbuffer(char *pcBuf, uint8_t cPadChar, int32_t i32NumChars)
507 {
508     int32_t i32Cnt = 0;
509 
510     if ( i32NumChars <= 0 )
511     {
512         return i32Cnt;
513     }
514 
515     while ( i32NumChars-- )
516     {
517         if ( pcBuf )
518         {
519             *pcBuf++ = cPadChar;
520         }
521         i32Cnt++;
522     }
523 
524     return i32Cnt;
525 }
526 
527 //*****************************************************************************
528 //
529 // Text mode translates linefeed (\n) characters to carriage return/
530 // linefeed (CR/LF) combinations in printf() and sprintf() functions.
531 //
532 //*****************************************************************************
533 bool
am_util_stdio_textmode_set(bool bSetTextTranslationMode)534 am_util_stdio_textmode_set(bool bSetTextTranslationMode)
535 {
536     bool bRet = g_bTxtXlate;
537 
538     //
539     // true=cvt LF chars to CR/LF
540     //
541     g_bTxtXlate = bSetTextTranslationMode;
542 
543     //
544     // return previous mode.
545     //
546     return bRet;
547 }
548 
549 //*****************************************************************************
550 //
551 //  Float to ASCII text. A basic implementation for providing support for
552 //  single-precision %f.
553 //
554 //  param
555 //      fValue     = Float value to be converted.
556 //      pcBuf      = Buffer to place string AND input of buffer size.
557 //      iPrecision = Desired number of decimal places.
558 //      IMPORTANT: On entry, the first 32-bit word of pcBuf must
559 //                 contain the size (in bytes) of the buffer!
560 //                 The recommended size is at least 16 bytes.
561 //
562 //  This function performs a basic translation of a floating point single
563 //  precision value to a string.
564 //
565 //  return Number of chars printed to the buffer.
566 //
567 //*****************************************************************************
568 #define AM_FTOA_ERR_VAL_TOO_SMALL   -1
569 #define AM_FTOA_ERR_VAL_TOO_LARGE   -2
570 #define AM_FTOA_ERR_BUFSIZE         -3
571 
572 typedef union
573 {
574     int32_t I32;
575     float F;
576 } i32fl_t;
577 
ftoa(float fValue,char * pcBuf,int iPrecision)578 static int ftoa(float fValue, char *pcBuf, int iPrecision)
579 {
580     i32fl_t unFloatValue;
581     int iExp2, iBufSize;
582     int32_t i32Significand, i32IntPart, i32FracPart;
583     char *pcBufInitial, *pcBuftmp;
584 
585     iBufSize = *(uint32_t*)pcBuf;
586     if (iBufSize < 4)
587     {
588         return AM_FTOA_ERR_BUFSIZE;
589     }
590 
591     if (fValue == 0.0f)
592     {
593         // "0.0"
594         *(uint32_t*)pcBuf = 0x00 << 24 | ('0' << 16) | ('.' << 8) | ('0' << 0);
595         return 3;
596     }
597 
598     pcBufInitial = pcBuf;
599 
600     unFloatValue.F = fValue;
601 
602     iExp2 = ((unFloatValue.I32 >> 23) & 0x000000FF) - 127;
603     i32Significand = (unFloatValue.I32 & 0x00FFFFFF) | 0x00800000;
604     i32FracPart = 0;
605     i32IntPart = 0;
606 
607     if (iExp2 >= 31)
608     {
609         return AM_FTOA_ERR_VAL_TOO_LARGE;
610     }
611     else if (iExp2 < -23)
612     {
613         return AM_FTOA_ERR_VAL_TOO_SMALL;
614     }
615     else if (iExp2 >= 23)
616     {
617         i32IntPart = i32Significand << (iExp2 - 23);
618     }
619     else if (iExp2 >= 0)
620     {
621         i32IntPart = i32Significand >> (23 - iExp2);
622         i32FracPart = (i32Significand << (iExp2 + 1)) & 0x00FFFFFF;
623     }
624     else // if (iExp2 < 0)
625     {
626         i32FracPart = (i32Significand & 0x00FFFFFF) >> -(iExp2 + 1);
627     }
628 
629     if (unFloatValue.I32 < 0)
630     {
631         *pcBuf++ = '-';
632     }
633 
634     if (i32IntPart == 0)
635     {
636         *pcBuf++ = '0';
637     }
638     else
639     {
640         if (i32IntPart > 0)
641         {
642             uint64_to_str(i32IntPart, pcBuf);
643         }
644         else
645         {
646             *pcBuf++ = '-';
647             uint64_to_str(-i32IntPart, pcBuf);
648         }
649         while (*pcBuf)    // Get to end of new string
650         {
651             pcBuf++;
652         }
653     }
654 
655     //
656     // Now, begin the fractional part
657     //
658     *pcBuf++ = '.';
659 
660     if (i32FracPart == 0)
661     {
662         *pcBuf++ = '0';
663     }
664     else
665     {
666         int jx, iMax;
667 
668         iMax = iBufSize - (pcBuf - pcBufInitial) - 1;
669         iMax = (iMax > iPrecision) ? iPrecision : iMax;
670 
671         for (jx = 0; jx < iMax; jx++)
672         {
673             i32FracPart *= 10;
674             *pcBuf++ = (i32FracPart >> 24) + '0';
675             i32FracPart &= 0x00FFFFFF;
676         }
677 
678         //
679         // Per the printf spec, the number of digits printed to the right of the
680         // decimal point (i.e. iPrecision) should be rounded.
681         // Some examples:
682         // Value        iPrecision          Formatted value
683         // 1.36399      Unspecified (6)     1.363990
684         // 1.36399      3                   1.364
685         // 1.36399      4                   1.3640
686         // 1.36399      5                   1.36399
687         // 1.363994     Unspecified (6)     1.363994
688         // 1.363994     3                   1.364
689         // 1.363994     4                   1.3640
690         // 1.363994     5                   1.36399
691         // 1.363995     Unspecified (6)     1.363995
692         // 1.363995     3                   1.364
693         // 1.363995     4                   1.3640
694         // 1.363995     5                   1.36400
695         // 1.996        Unspecified (6)     1.996000
696         // 1.996        2                   2.00
697         // 1.996        3                   1.996
698         // 1.996        4                   1.9960
699         //
700         // To determine whether to round up, we'll look at what the next
701         // decimal value would have been.
702         //
703         if ( ((i32FracPart * 10) >> 24) >= 5 )
704         {
705             //
706             // Yes, we need to round up.
707             // Go back through the string and make adjustments as necessary.
708             //
709             pcBuftmp = pcBuf - 1;
710             while ( pcBuftmp >= pcBufInitial )
711             {
712                 if ( *pcBuftmp == '.' )
713                 {
714                 }
715                 else if ( *pcBuftmp == '9' )
716                 {
717                     *pcBuftmp = '0';
718                 }
719                 else
720                 {
721                     *pcBuftmp += 1;
722                     break;
723                 }
724                 pcBuftmp--;
725             }
726         }
727     }
728 
729     //
730     // Terminate the string and we're done
731     //
732     *pcBuf = 0x00;
733 
734     return (pcBuf - pcBufInitial);
735 } // ftoa()
736 
737 //******************************************************************************
738 //
739 // Format data into string. (va_list implementation)
740 //
741 //******************************************************************************
742 uint32_t
am_util_stdio_vsprintf(char * pcBuf,const char * pcFmt,va_list pArgs)743 am_util_stdio_vsprintf(char *pcBuf, const char *pcFmt, va_list pArgs)
744 {
745     char *pcStr;
746     uint64_t ui64Val;
747     int64_t i64Val;
748     uint32_t ui32NumChars, ui32CharCnt = 0;
749     int iWidth, iVal, iPrecision;
750     uint8_t ui8CharSpecifier, ui8PadChar;
751     bool bLower, bLongLong, bNeg;
752     uint32_t ui32strlen = 0;
753 
754     while ( *pcFmt != 0x0 )
755     {
756         iPrecision = 6;             // printf() default precision for %f is 6
757 
758         if ( *pcFmt != '%' )
759         {
760             //
761             // Accumulate the string portion of the format specification.
762             //
763             if ( pcBuf )
764             {
765                 // If '\n', convert to '\r\n'
766                 if ( *pcFmt == '\n'  &&  g_bTxtXlate )
767                 {
768                     *pcBuf++ = '\r';
769                     ++ui32CharCnt;
770                 }
771                 *pcBuf++ = *pcFmt;
772             }
773 
774             ++pcFmt;
775             ++ui32CharCnt;
776             continue;
777         }
778 
779         //
780         // Handle the specifier.
781         //
782         ++pcFmt;
783         bLower = bLongLong = false;
784 
785         //
786         // Default to space as ui8PadChar
787         //
788         ui8PadChar = ' ';
789 
790         if ( *pcFmt == '0' )
791         {
792             ui8PadChar = '0';
793             ++pcFmt;
794         }
795 
796         //
797         // Width specifier
798         //
799         iWidth = decstr_to_int(pcFmt, &ui32NumChars);
800         pcFmt += ui32NumChars;
801 
802         //
803         // For now, only support a negative width specifier for %s
804         //
805         if ( ( *pcFmt != 's' )  &&  ( iWidth < 0 ) )
806         {
807             iWidth = -iWidth;
808         }
809 
810         //
811         // Check for precision specifier
812         //
813         if (*pcFmt == '.')
814         {
815             ++pcFmt;
816             iPrecision = decstr_to_int(pcFmt, &ui32NumChars);
817             pcFmt += ui32NumChars;
818         }
819 
820         //
821         // Check for the long or long long length field sub-specifiers, 'l' or
822         // 'll', which must be a modifier for either 'd', 'i', 'u', 'x', or 'X'
823         // (or even 'o', which is not currently supported). Other sub-specifiers
824         // like 'hh','h', etc. are not currently handled.
825         // Note - 'l' is used in Coremark, a primary reason it's supported here.
826         //
827         if ( *pcFmt == 'l' )
828         {
829             pcFmt++;
830             if ( *pcFmt == 'l' )    // "ll" (long long)
831             {
832                 pcFmt++;
833                 bLongLong = true;
834             }
835         }
836 
837         switch ( *pcFmt )
838         {
839             case 'c':
840                 ui8CharSpecifier = va_arg(pArgs, uint32_t);
841 
842                 if ( pcBuf )
843                 {
844                     *pcBuf++ = ui8CharSpecifier;
845                 }
846 
847                 ++ui32CharCnt;
848                 break;
849 
850             case 's':
851                 pcStr = va_arg(pArgs, char *);
852 
853                 //
854                 // For %s, we support the width specifier. If iWidth is negative
855                 // the string is left-aligned (padding on the right).  Otherwise
856                 // the string is padded at the beginning with spaces.
857                 //
858                 ui32strlen = simple_strlen(pcStr);
859                 if ( iWidth > 0 )
860                 {
861                     // Pad the beginning of the string (right-aligned).
862                     if ( ui32strlen < iWidth )
863                     {
864                         // String needs some padding.
865                         iWidth -= ui32strlen;
866                         iWidth = padbuffer(pcBuf, ui8PadChar, iWidth);
867                         pcBuf += pcBuf ? iWidth : 0;
868                         ui32CharCnt += iWidth;
869                         iWidth = 0;
870                     }
871                 }
872 
873                 while (*pcStr != 0x0)
874                 {
875                     if ( pcBuf )
876                     {
877                         *pcBuf++ = *pcStr;
878                     }
879 
880                     ++pcStr;
881                     ++ui32CharCnt;
882                 }
883 
884                 if ( iWidth )
885                 {
886                     iWidth = -iWidth;
887 
888                     // Pad the end of the string (left-aligned).
889                     if ( ui32strlen < iWidth )
890                     {
891                         // String needs some padding.
892                         iWidth -= ui32strlen;
893                         iWidth = padbuffer(pcBuf, ui8PadChar, iWidth);
894                         pcBuf += pcBuf ? iWidth : 0;
895                         ui32CharCnt += iWidth;
896                         iWidth = 0;
897                     }
898                 }
899                 break;
900 
901             case 'x':
902                 bLower = true;
903             case 'X':
904                 ui64Val = bLongLong ? va_arg(pArgs, uint64_t) :
905                                       va_arg(pArgs, uint32_t);
906 
907                 if ( iWidth )
908                 {
909                     //
910                     // Compute # of leading chars
911                     //
912                     iWidth -= ndigits_in_hex(ui64Val);
913 
914                     iWidth = padbuffer(pcBuf, ui8PadChar, iWidth);
915                     pcBuf += pcBuf ? iWidth : 0;
916                     ui32CharCnt += iWidth;
917                     iWidth = 0;
918                 }
919 
920                 iVal = uint64_to_hexstr(ui64Val, pcBuf, bLower);
921 
922                 if ( pcBuf )
923                 {
924                     pcBuf += iVal;
925                 }
926 
927                 ui32CharCnt += iVal;
928                 break;
929 
930             case 'u':
931                 ui64Val = bLongLong ? va_arg(pArgs, uint64_t) :
932                                       va_arg(pArgs, uint32_t);
933 
934                 if ( iWidth )
935                 {
936                     //
937                     // We need to pad the beginning of the value.
938                     // Compute # of leading chars
939                     //
940                     iWidth -= ndigits_in_u64(ui64Val);
941 
942                     iWidth = padbuffer(pcBuf, ui8PadChar, iWidth);
943                     pcBuf += pcBuf ? iWidth : 0;
944                     ui32CharCnt += iWidth;
945                     iWidth = 0;
946                 }
947 
948                 iVal = uint64_to_str(ui64Val, pcBuf);
949 
950                 if ( pcBuf )
951                 {
952                     pcBuf += iVal;
953                 }
954 
955                 ui32CharCnt += iVal;
956                 break;
957 
958             case 'd':
959             case 'i':
960                 //
961                 // Output for a negative number, for example, -5:
962                 //   %d:-5
963                 //  %5d:   -5
964                 // %05d:-0005
965                 //
966                 i64Val = bLongLong ? va_arg(pArgs, int64_t) :
967                                      va_arg(pArgs, int32_t);
968 
969                 //
970                 // Get absolute value
971                 //
972                 if ( i64Val < 0 )
973                 {
974                     ui64Val = -i64Val;          // Get absolute value
975                     bNeg = true;
976                 }
977                 else
978                 {
979                     ui64Val = i64Val;
980                     bNeg = false;
981                 }
982 
983                 if ( iWidth )
984                 {
985                     //
986                     // We need to pad the beginning of the value.
987                     // Compute # of leading chars
988                     //
989                     iWidth -= ndigits_in_i64(ui64Val);
990 
991                     if ( bNeg )
992                     {
993                         --iWidth;
994 
995                         //
996                         // Allow for the negative sign
997                         //
998                         if ( ui8PadChar == '0' )
999                         {
1000                             //
1001                             // Print the neg sign BEFORE the leading zeros
1002                             //
1003                             if ( pcBuf )
1004                             {
1005                                 *pcBuf++ = '-';
1006                             }
1007 
1008                             ++ui32CharCnt;
1009                         }
1010                     }
1011 
1012                     iWidth = padbuffer(pcBuf, ui8PadChar, iWidth);
1013                     pcBuf += pcBuf ? iWidth : 0;
1014                     ui32CharCnt += iWidth;
1015                     iWidth = 0;
1016 
1017                     if ( bNeg  &&  (ui8PadChar == ' ') )
1018                     {
1019                         //
1020                         // Print the neg sign AFTER the leading blanks
1021                         //
1022                         if ( pcBuf )
1023                         {
1024                             *pcBuf++ = '-';
1025                         }
1026 
1027                         ++ui32CharCnt;
1028                     }
1029                 }
1030                 else
1031                 {
1032                     if ( bNeg )
1033                     {
1034                         if ( pcBuf )
1035                         {
1036                             *pcBuf++ = '-';
1037                         }
1038                         ++ui32CharCnt;
1039                     }
1040                 }
1041 
1042                 iVal = uint64_to_str(ui64Val, pcBuf);
1043 
1044                 if ( pcBuf )
1045                 {
1046                     pcBuf += iVal;
1047                 }
1048 
1049                 ui32CharCnt += iVal;
1050                 break;
1051 
1052 
1053             case 'f':
1054             case 'F':
1055                 if ( pcBuf )
1056                 {
1057                     float fValue = va_arg(pArgs, double);
1058 
1059                     //
1060                     // pcBuf is an input (size of buffer) and also an output of ftoa()
1061                     //
1062                     *(uint32_t*)pcBuf = 20;
1063 
1064                     iVal = ftoa(fValue, pcBuf, iPrecision);
1065                     if ( iVal < 0 )
1066                     {
1067                         uint32_t u32PrntErrVal;
1068                         if ( iVal == AM_FTOA_ERR_VAL_TOO_SMALL )
1069                         {
1070                             u32PrntErrVal = (0x00 << 24) | ('0' << 16) |
1071                                             ('.' << 8)   | ('0' << 0);  // "0.0"
1072                         }
1073                         else if ( iVal == AM_FTOA_ERR_VAL_TOO_LARGE )
1074                         {
1075                             u32PrntErrVal = (0x00 << 24) | ('#' << 16) |
1076                                             ('.' << 8)   | ('#' << 0);  // "#.#"
1077                         }
1078                         else
1079                         {
1080                             u32PrntErrVal = (0x00 << 24) | ('?' << 16) |
1081                                             ('.' << 8)   | ('?' << 0);  // "?.?"
1082                         }
1083                         *(uint32_t*)pcBuf = u32PrntErrVal;
1084                         iVal = 3;
1085                     }
1086                     ui32CharCnt += iVal;
1087                     pcBuf += iVal;
1088                 }
1089                 break;
1090 
1091             //
1092             // Invalid specifier character
1093             // For non-handled specifiers, we'll just print the character.
1094             // e.g. this will allow the normal printing of a '%' using
1095             // "%%".
1096             //
1097             default:
1098                 if ( pcBuf )
1099                 {
1100                     *pcBuf++ = *pcFmt;
1101                 }
1102 
1103                 ++ui32CharCnt;
1104                 break;
1105 
1106         } // switch ()
1107 
1108         //
1109         // Bump the format specification to the next character
1110         //
1111         ++pcFmt;
1112 
1113     } // while ()
1114 
1115     //
1116     // Terminate the string
1117     //
1118     if ( pcBuf )
1119     {
1120         *pcBuf = 0x0;
1121     }
1122 
1123     return (ui32CharCnt);
1124 }
1125 
1126 //******************************************************************************
1127 //
1128 // Format data into string.
1129 //
1130 //******************************************************************************
1131 uint32_t
am_util_stdio_sprintf(char * pcBuf,const char * pcFmt,...)1132 am_util_stdio_sprintf(char *pcBuf, const char *pcFmt, ...)
1133 {
1134     uint32_t ui32CharCnt;
1135 
1136     va_list pArgs;
1137     va_start(pArgs, pcFmt);
1138     ui32CharCnt = am_util_stdio_vsprintf(pcBuf, pcFmt, pArgs);
1139     va_end(pArgs);
1140 
1141     return ui32CharCnt;
1142 }
1143 
1144 //*****************************************************************************
1145 //
1146 // A lite version of printf()
1147 //
1148 //*****************************************************************************
1149 uint32_t
am_util_stdio_printf(const char * pcFmt,...)1150 am_util_stdio_printf(const char *pcFmt, ...)
1151 {
1152     uint32_t ui32NumChars;
1153 
1154     if (!g_pfnCharPrint)
1155     {
1156         return 0;
1157     }
1158 
1159     //
1160     // Convert to the desired string.
1161     //
1162     va_list pArgs;
1163     va_start(pArgs, pcFmt);
1164     ui32NumChars = am_util_stdio_vsprintf(g_prfbuf, pcFmt, pArgs);
1165     va_end(pArgs);
1166 
1167     //
1168     // This is where we print the buffer to the configured interface.
1169     //
1170     g_pfnCharPrint(g_prfbuf);
1171 
1172     //
1173     // return the number of characters printed.
1174     //
1175     return ui32NumChars;
1176 }
1177 
1178 uint32_t
am_util_stdio_vsnprintf(char * pcBuf,uint32_t n,const char * pcFmt,va_list pArgs)1179 am_util_stdio_vsnprintf(char *pcBuf, uint32_t n, const char *pcFmt,
1180                         va_list pArgs)
1181 {
1182     uint32_t ui32NumChars, i;
1183 
1184     if (n >= AM_PRINTF_BUFSIZE)
1185     {
1186         return 0;
1187     }
1188 
1189     ui32NumChars = am_util_stdio_vsprintf(g_prfbuf, pcFmt, pArgs);
1190 
1191     if (ui32NumChars >= n)
1192     {
1193         return 0;
1194     }
1195 
1196     for (i = 0; i < ui32NumChars; i++)
1197     {
1198         pcBuf[i] = g_prfbuf[i];
1199     }
1200 
1201     return ui32NumChars;
1202 }
1203 
1204 uint32_t
am_util_stdio_snprintf(char * pcBuf,uint32_t n,const char * pcFmt,...)1205 am_util_stdio_snprintf(char *pcBuf, uint32_t n, const char *pcFmt, ...)
1206 {
1207     uint32_t ui32CharCnt;
1208 
1209     va_list pArgs;
1210     va_start(pArgs, pcFmt);
1211     ui32CharCnt = am_util_stdio_vsnprintf(pcBuf, n, pcFmt, pArgs);
1212     va_end(pArgs);
1213 
1214     return ui32CharCnt;
1215 }
1216 
1217 
1218 uint32_t
am_util_stdio_vprintf(const char * pcFmt,va_list pArgs)1219 am_util_stdio_vprintf(const char *pcFmt, va_list pArgs)
1220 {
1221     uint32_t ui32NumChars;
1222 
1223     if (!g_pfnCharPrint)
1224     {
1225         return 0;
1226     }
1227 
1228     ui32NumChars = am_util_stdio_vsprintf(g_prfbuf, pcFmt, pArgs);
1229 
1230     //
1231     // This is where we print the buffer to the configured interface.
1232     //
1233     g_pfnCharPrint(g_prfbuf);
1234 
1235     //
1236     // return the number of characters printed.
1237     //
1238     return ui32NumChars;
1239 }
1240 
1241 //*****************************************************************************
1242 //
1243 // This function clears a standard terminal screen.
1244 //
1245 //*****************************************************************************
1246 void
am_util_stdio_terminal_clear(void)1247 am_util_stdio_terminal_clear(void)
1248 {
1249     //
1250     // Simulate a clear terminal by printing a series of linefeeds.
1251     //
1252     am_util_stdio_printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n");
1253 }
1254 
1255 //*****************************************************************************
1256 //
1257 // End Doxygen group.
1258 //! @}
1259 //
1260 //*****************************************************************************
1261 
1262