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