1 /*********************************************************************
2 *                    SEGGER Microcontroller GmbH                     *
3 *                        The Embedded Experts                        *
4 **********************************************************************
5 *                                                                    *
6 *            (c) 1995 - 2021 SEGGER Microcontroller GmbH             *
7 *                                                                    *
8 *       www.segger.com     Support: support@segger.com               *
9 *                                                                    *
10 **********************************************************************
11 *                                                                    *
12 *       SEGGER SystemView * Real-time application analysis           *
13 *                                                                    *
14 **********************************************************************
15 *                                                                    *
16 * All rights reserved.                                               *
17 *                                                                    *
18 * SEGGER strongly recommends to not make any changes                 *
19 * to or modify the source code of this software in order to stay     *
20 * compatible with the SystemView and RTT protocol, and J-Link.       *
21 *                                                                    *
22 * Redistribution and use in source and binary forms, with or         *
23 * without modification, are permitted provided that the following    *
24 * condition is met:                                                  *
25 *                                                                    *
26 * o Redistributions of source code must retain the above copyright   *
27 *   notice, this condition and the following disclaimer.             *
28 *                                                                    *
29 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND             *
30 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,        *
31 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF           *
32 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE           *
33 * DISCLAIMED. IN NO EVENT SHALL SEGGER Microcontroller BE LIABLE FOR *
34 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR           *
35 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT  *
36 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;    *
37 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF      *
38 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT          *
39 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE  *
40 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH   *
41 * DAMAGE.                                                            *
42 *                                                                    *
43 **********************************************************************
44 *                                                                    *
45 *       SystemView version: 3.40                                    *
46 *                                                                    *
47 **********************************************************************
48 ---------------------------END-OF-HEADER------------------------------
49 File    : SEGGER_RTT_printf.c
50 Purpose : Replacement for printf to write formatted data via RTT
51 Revision: $Rev: 17697 $
52 ----------------------------------------------------------------------
53 */
54 #include "SEGGER_RTT.h"
55 #include "SEGGER_RTT_Conf.h"
56 
57 /*********************************************************************
58 *
59 *       Defines, configurable
60 *
61 **********************************************************************
62 */
63 
64 #ifndef SEGGER_RTT_PRINTF_BUFFER_SIZE
65   #define SEGGER_RTT_PRINTF_BUFFER_SIZE (64)
66 #endif
67 
68 #include <stdlib.h>
69 #include <stdarg.h>
70 
71 
72 #define FORMAT_FLAG_LEFT_JUSTIFY   (1u << 0)
73 #define FORMAT_FLAG_PAD_ZERO       (1u << 1)
74 #define FORMAT_FLAG_PRINT_SIGN     (1u << 2)
75 #define FORMAT_FLAG_ALTERNATE      (1u << 3)
76 
77 /*********************************************************************
78 *
79 *       Types
80 *
81 **********************************************************************
82 */
83 
84 typedef struct {
85   char*     pBuffer;
86   unsigned  BufferSize;
87   unsigned  Cnt;
88 
89   int   ReturnValue;
90 
91   unsigned RTTBufferIndex;
92 } SEGGER_RTT_PRINTF_DESC;
93 
94 /*********************************************************************
95 *
96 *       Function prototypes
97 *
98 **********************************************************************
99 */
100 
101 /*********************************************************************
102 *
103 *       Static code
104 *
105 **********************************************************************
106 */
107 /*********************************************************************
108 *
109 *       _StoreChar
110 */
_StoreChar(SEGGER_RTT_PRINTF_DESC * p,char c)111 static void _StoreChar(SEGGER_RTT_PRINTF_DESC * p, char c) {
112   unsigned Cnt;
113 
114   Cnt = p->Cnt;
115   if ((Cnt + 1u) <= p->BufferSize) {
116     *(p->pBuffer + Cnt) = c;
117     p->Cnt = Cnt + 1u;
118     p->ReturnValue++;
119   }
120   //
121   // Write part of string, when the buffer is full
122   //
123   if (p->Cnt == p->BufferSize) {
124     if (SEGGER_RTT_Write(p->RTTBufferIndex, p->pBuffer, p->Cnt) != p->Cnt) {
125       p->ReturnValue = -1;
126     } else {
127       p->Cnt = 0u;
128     }
129   }
130 }
131 
132 /*********************************************************************
133 *
134 *       _PrintUnsigned
135 */
_PrintUnsigned(SEGGER_RTT_PRINTF_DESC * pBufferDesc,unsigned v,unsigned Base,unsigned NumDigits,unsigned FieldWidth,unsigned FormatFlags)136 static void _PrintUnsigned(SEGGER_RTT_PRINTF_DESC * pBufferDesc, unsigned v, unsigned Base, unsigned NumDigits, unsigned FieldWidth, unsigned FormatFlags) {
137   static const char _aV2C[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
138   unsigned Div;
139   unsigned Digit;
140   unsigned Number;
141   unsigned Width;
142   char c;
143 
144   Number = v;
145   Digit = 1u;
146   //
147   // Get actual field width
148   //
149   Width = 1u;
150   while (Number >= Base) {
151     Number = (Number / Base);
152     Width++;
153   }
154   if (NumDigits > Width) {
155     Width = NumDigits;
156   }
157   //
158   // Print leading chars if necessary
159   //
160   if ((FormatFlags & FORMAT_FLAG_LEFT_JUSTIFY) == 0u) {
161     if (FieldWidth != 0u) {
162       if (((FormatFlags & FORMAT_FLAG_PAD_ZERO) == FORMAT_FLAG_PAD_ZERO) && (NumDigits == 0u)) {
163         c = '0';
164       } else {
165         c = ' ';
166       }
167       while ((FieldWidth != 0u) && (Width < FieldWidth)) {
168         FieldWidth--;
169         _StoreChar(pBufferDesc, c);
170         if (pBufferDesc->ReturnValue < 0) {
171           break;
172         }
173       }
174     }
175   }
176   if (pBufferDesc->ReturnValue >= 0) {
177     //
178     // Compute Digit.
179     // Loop until Digit has the value of the highest digit required.
180     // Example: If the output is 345 (Base 10), loop 2 times until Digit is 100.
181     //
182     while (1) {
183       if (NumDigits > 1u) {       // User specified a min number of digits to print? => Make sure we loop at least that often, before checking anything else (> 1 check avoids problems with NumDigits being signed / unsigned)
184         NumDigits--;
185       } else {
186         Div = v / Digit;
187         if (Div < Base) {        // Is our divider big enough to extract the highest digit from value? => Done
188           break;
189         }
190       }
191       Digit *= Base;
192     }
193     //
194     // Output digits
195     //
196     do {
197       Div = v / Digit;
198       v -= Div * Digit;
199       _StoreChar(pBufferDesc, _aV2C[Div]);
200       if (pBufferDesc->ReturnValue < 0) {
201         break;
202       }
203       Digit /= Base;
204     } while (Digit);
205     //
206     // Print trailing spaces if necessary
207     //
208     if ((FormatFlags & FORMAT_FLAG_LEFT_JUSTIFY) == FORMAT_FLAG_LEFT_JUSTIFY) {
209       if (FieldWidth != 0u) {
210         while ((FieldWidth != 0u) && (Width < FieldWidth)) {
211           FieldWidth--;
212           _StoreChar(pBufferDesc, ' ');
213           if (pBufferDesc->ReturnValue < 0) {
214             break;
215           }
216         }
217       }
218     }
219   }
220 }
221 
222 /*********************************************************************
223 *
224 *       _PrintInt
225 */
_PrintInt(SEGGER_RTT_PRINTF_DESC * pBufferDesc,int v,unsigned Base,unsigned NumDigits,unsigned FieldWidth,unsigned FormatFlags)226 static void _PrintInt(SEGGER_RTT_PRINTF_DESC * pBufferDesc, int v, unsigned Base, unsigned NumDigits, unsigned FieldWidth, unsigned FormatFlags) {
227   unsigned Width;
228   int Number;
229 
230   Number = (v < 0) ? -v : v;
231 
232   //
233   // Get actual field width
234   //
235   Width = 1u;
236   while (Number >= (int)Base) {
237     Number = (Number / (int)Base);
238     Width++;
239   }
240   if (NumDigits > Width) {
241     Width = NumDigits;
242   }
243   if ((FieldWidth > 0u) && ((v < 0) || ((FormatFlags & FORMAT_FLAG_PRINT_SIGN) == FORMAT_FLAG_PRINT_SIGN))) {
244     FieldWidth--;
245   }
246 
247   //
248   // Print leading spaces if necessary
249   //
250   if ((((FormatFlags & FORMAT_FLAG_PAD_ZERO) == 0u) || (NumDigits != 0u)) && ((FormatFlags & FORMAT_FLAG_LEFT_JUSTIFY) == 0u)) {
251     if (FieldWidth != 0u) {
252       while ((FieldWidth != 0u) && (Width < FieldWidth)) {
253         FieldWidth--;
254         _StoreChar(pBufferDesc, ' ');
255         if (pBufferDesc->ReturnValue < 0) {
256           break;
257         }
258       }
259     }
260   }
261   //
262   // Print sign if necessary
263   //
264   if (pBufferDesc->ReturnValue >= 0) {
265     if (v < 0) {
266       v = -v;
267       _StoreChar(pBufferDesc, '-');
268     } else if ((FormatFlags & FORMAT_FLAG_PRINT_SIGN) == FORMAT_FLAG_PRINT_SIGN) {
269       _StoreChar(pBufferDesc, '+');
270     } else {
271 
272     }
273     if (pBufferDesc->ReturnValue >= 0) {
274       //
275       // Print leading zeros if necessary
276       //
277       if (((FormatFlags & FORMAT_FLAG_PAD_ZERO) == FORMAT_FLAG_PAD_ZERO) && ((FormatFlags & FORMAT_FLAG_LEFT_JUSTIFY) == 0u) && (NumDigits == 0u)) {
278         if (FieldWidth != 0u) {
279           while ((FieldWidth != 0u) && (Width < FieldWidth)) {
280             FieldWidth--;
281             _StoreChar(pBufferDesc, '0');
282             if (pBufferDesc->ReturnValue < 0) {
283               break;
284             }
285           }
286         }
287       }
288       if (pBufferDesc->ReturnValue >= 0) {
289         //
290         // Print number without sign
291         //
292         _PrintUnsigned(pBufferDesc, (unsigned)v, Base, NumDigits, FieldWidth, FormatFlags);
293       }
294     }
295   }
296 }
297 
298 /*********************************************************************
299 *
300 *       Public code
301 *
302 **********************************************************************
303 */
304 /*********************************************************************
305 *
306 *       SEGGER_RTT_vprintf
307 *
308 *  Function description
309 *    Stores a formatted string in SEGGER RTT control block.
310 *    This data is read by the host.
311 *
312 *  Parameters
313 *    BufferIndex  Index of "Up"-buffer to be used. (e.g. 0 for "Terminal")
314 *    sFormat      Pointer to format string
315 *    pParamList   Pointer to the list of arguments for the format string
316 *
317 *  Return values
318 *    >= 0:  Number of bytes which have been stored in the "Up"-buffer.
319 *     < 0:  Error
320 */
SEGGER_RTT_vprintf(unsigned BufferIndex,const char * sFormat,va_list * pParamList)321 int SEGGER_RTT_vprintf(unsigned BufferIndex, const char * sFormat, va_list * pParamList) {
322   char c;
323   SEGGER_RTT_PRINTF_DESC BufferDesc;
324   int v;
325   unsigned NumDigits;
326   unsigned FormatFlags;
327   unsigned FieldWidth;
328   char acBuffer[SEGGER_RTT_PRINTF_BUFFER_SIZE];
329 
330   BufferDesc.pBuffer        = acBuffer;
331   BufferDesc.BufferSize     = SEGGER_RTT_PRINTF_BUFFER_SIZE;
332   BufferDesc.Cnt            = 0u;
333   BufferDesc.RTTBufferIndex = BufferIndex;
334   BufferDesc.ReturnValue    = 0;
335 
336   do {
337     c = *sFormat;
338     sFormat++;
339     if (c == 0u) {
340       break;
341     }
342     if (c == '%') {
343       //
344       // Filter out flags
345       //
346       FormatFlags = 0u;
347       v = 1;
348       do {
349         c = *sFormat;
350         switch (c) {
351         case '-': FormatFlags |= FORMAT_FLAG_LEFT_JUSTIFY; sFormat++; break;
352         case '0': FormatFlags |= FORMAT_FLAG_PAD_ZERO;     sFormat++; break;
353         case '+': FormatFlags |= FORMAT_FLAG_PRINT_SIGN;   sFormat++; break;
354         case '#': FormatFlags |= FORMAT_FLAG_ALTERNATE;    sFormat++; break;
355         default:  v = 0; break;
356         }
357       } while (v);
358       //
359       // filter out field with
360       //
361       FieldWidth = 0u;
362       do {
363         c = *sFormat;
364         if ((c < '0') || (c > '9')) {
365           break;
366         }
367         sFormat++;
368         FieldWidth = (FieldWidth * 10u) + ((unsigned)c - '0');
369       } while (1);
370 
371       //
372       // Filter out precision (number of digits to display)
373       //
374       NumDigits = 0u;
375       c = *sFormat;
376       if (c == '.') {
377         sFormat++;
378         do {
379           c = *sFormat;
380           if ((c < '0') || (c > '9')) {
381             break;
382           }
383           sFormat++;
384           NumDigits = NumDigits * 10u + ((unsigned)c - '0');
385         } while (1);
386       }
387       //
388       // Filter out length modifier
389       //
390       c = *sFormat;
391       do {
392         if ((c == 'l') || (c == 'h')) {
393           sFormat++;
394           c = *sFormat;
395         } else {
396           break;
397         }
398       } while (1);
399       //
400       // Handle specifiers
401       //
402       switch (c) {
403       case 'c': {
404         char c0;
405         v = va_arg(*pParamList, int);
406         c0 = (char)v;
407         _StoreChar(&BufferDesc, c0);
408         break;
409       }
410       case 'd':
411         v = va_arg(*pParamList, int);
412         _PrintInt(&BufferDesc, v, 10u, NumDigits, FieldWidth, FormatFlags);
413         break;
414       case 'u':
415         v = va_arg(*pParamList, int);
416         _PrintUnsigned(&BufferDesc, (unsigned)v, 10u, NumDigits, FieldWidth, FormatFlags);
417         break;
418       case 'x':
419       case 'X':
420         v = va_arg(*pParamList, int);
421         _PrintUnsigned(&BufferDesc, (unsigned)v, 16u, NumDigits, FieldWidth, FormatFlags);
422         break;
423       case 's':
424         {
425           const char * s = va_arg(*pParamList, const char *);
426           do {
427             c = *s;
428             s++;
429             if (c == '\0') {
430               break;
431             }
432            _StoreChar(&BufferDesc, c);
433           } while (BufferDesc.ReturnValue >= 0);
434         }
435         break;
436       case 'p':
437         v = va_arg(*pParamList, int);
438         _PrintUnsigned(&BufferDesc, (unsigned)v, 16u, 8u, 8u, 0u);
439         break;
440       case '%':
441         _StoreChar(&BufferDesc, '%');
442         break;
443       default:
444         break;
445       }
446       sFormat++;
447     } else {
448       _StoreChar(&BufferDesc, c);
449     }
450   } while (BufferDesc.ReturnValue >= 0);
451 
452   if (BufferDesc.ReturnValue > 0) {
453     //
454     // Write remaining data, if any
455     //
456     if (BufferDesc.Cnt != 0u) {
457       SEGGER_RTT_Write(BufferIndex, acBuffer, BufferDesc.Cnt);
458     }
459     BufferDesc.ReturnValue += (int)BufferDesc.Cnt;
460   }
461   return BufferDesc.ReturnValue;
462 }
463 
464 /*********************************************************************
465 *
466 *       SEGGER_RTT_printf
467 *
468 *  Function description
469 *    Stores a formatted string in SEGGER RTT control block.
470 *    This data is read by the host.
471 *
472 *  Parameters
473 *    BufferIndex  Index of "Up"-buffer to be used. (e.g. 0 for "Terminal")
474 *    sFormat      Pointer to format string, followed by the arguments for conversion
475 *
476 *  Return values
477 *    >= 0:  Number of bytes which have been stored in the "Up"-buffer.
478 *     < 0:  Error
479 *
480 *  Notes
481 *    (1) Conversion specifications have following syntax:
482 *          %[flags][FieldWidth][.Precision]ConversionSpecifier
483 *    (2) Supported flags:
484 *          -: Left justify within the field width
485 *          +: Always print sign extension for signed conversions
486 *          0: Pad with 0 instead of spaces. Ignored when using '-'-flag or precision
487 *        Supported conversion specifiers:
488 *          c: Print the argument as one char
489 *          d: Print the argument as a signed integer
490 *          u: Print the argument as an unsigned integer
491 *          x: Print the argument as an hexadecimal integer
492 *          s: Print the string pointed to by the argument
493 *          p: Print the argument as an 8-digit hexadecimal integer. (Argument shall be a pointer to void.)
494 */
SEGGER_RTT_printf(unsigned BufferIndex,const char * sFormat,...)495 int SEGGER_RTT_printf(unsigned BufferIndex, const char * sFormat, ...) {
496   int r;
497   va_list ParamList;
498 
499   va_start(ParamList, sFormat);
500   r = SEGGER_RTT_vprintf(BufferIndex, sFormat, &ParamList);
501   va_end(ParamList);
502   return r;
503 }
504 /*************************** End of file ****************************/
505