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 RTT * Real Time Transfer for embedded targets         *
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 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 *       RTT version: 7.22                                           *
46 *                                                                    *
47 **********************************************************************
48 
49 ---------------------------END-OF-HEADER------------------------------
50 File    : SEGGER_RTT_printf.c
51 Purpose : Replacement for printf to write formatted data via RTT
52 Revision: $Rev: 17697 $
53 ----------------------------------------------------------------------
54 */
55 #include "SEGGER_RTT.h"
56 #include "SEGGER_RTT_Conf.h"
57 
58 /*********************************************************************
59 *
60 *       Defines, configurable
61 *
62 **********************************************************************
63 */
64 
65 #ifndef SEGGER_RTT_PRINTF_BUFFER_SIZE
66   #define SEGGER_RTT_PRINTF_BUFFER_SIZE (64)
67 #endif
68 
69 #include <stdlib.h>
70 #include <stdarg.h>
71 
72 
73 #define FORMAT_FLAG_LEFT_JUSTIFY   (1u << 0)
74 #define FORMAT_FLAG_PAD_ZERO       (1u << 1)
75 #define FORMAT_FLAG_PRINT_SIGN     (1u << 2)
76 #define FORMAT_FLAG_ALTERNATE      (1u << 3)
77 
78 /*********************************************************************
79 *
80 *       Types
81 *
82 **********************************************************************
83 */
84 
85 typedef struct {
86   char*     pBuffer;
87   unsigned  BufferSize;
88   unsigned  Cnt;
89 
90   int   ReturnValue;
91 
92   unsigned RTTBufferIndex;
93 } SEGGER_RTT_PRINTF_DESC;
94 
95 /*********************************************************************
96 *
97 *       Function prototypes
98 *
99 **********************************************************************
100 */
101 
102 /*********************************************************************
103 *
104 *       Static code
105 *
106 **********************************************************************
107 */
108 /*********************************************************************
109 *
110 *       _StoreChar
111 */
_StoreChar(SEGGER_RTT_PRINTF_DESC * p,char c)112 static void _StoreChar(SEGGER_RTT_PRINTF_DESC * p, char c) {
113   unsigned Cnt;
114 
115   Cnt = p->Cnt;
116   if ((Cnt + 1u) <= p->BufferSize) {
117     *(p->pBuffer + Cnt) = c;
118     p->Cnt = Cnt + 1u;
119     p->ReturnValue++;
120   }
121   //
122   // Write part of string, when the buffer is full
123   //
124   if (p->Cnt == p->BufferSize) {
125     if (SEGGER_RTT_Write(p->RTTBufferIndex, p->pBuffer, p->Cnt) != p->Cnt) {
126       p->ReturnValue = -1;
127     } else {
128       p->Cnt = 0u;
129     }
130   }
131 }
132 
133 /*********************************************************************
134 *
135 *       _PrintUnsigned
136 */
_PrintUnsigned(SEGGER_RTT_PRINTF_DESC * pBufferDesc,unsigned v,unsigned Base,unsigned NumDigits,unsigned FieldWidth,unsigned FormatFlags)137 static void _PrintUnsigned(SEGGER_RTT_PRINTF_DESC * pBufferDesc, unsigned v, unsigned Base, unsigned NumDigits, unsigned FieldWidth, unsigned FormatFlags) {
138   static const char _aV2C[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
139   unsigned Div;
140   unsigned Digit;
141   unsigned Number;
142   unsigned Width;
143   char c;
144 
145   Number = v;
146   Digit = 1u;
147   //
148   // Get actual field width
149   //
150   Width = 1u;
151   while (Number >= Base) {
152     Number = (Number / Base);
153     Width++;
154   }
155   if (NumDigits > Width) {
156     Width = NumDigits;
157   }
158   //
159   // Print leading chars if necessary
160   //
161   if ((FormatFlags & FORMAT_FLAG_LEFT_JUSTIFY) == 0u) {
162     if (FieldWidth != 0u) {
163       if (((FormatFlags & FORMAT_FLAG_PAD_ZERO) == FORMAT_FLAG_PAD_ZERO) && (NumDigits == 0u)) {
164         c = '0';
165       } else {
166         c = ' ';
167       }
168       while ((FieldWidth != 0u) && (Width < FieldWidth)) {
169         FieldWidth--;
170         _StoreChar(pBufferDesc, c);
171         if (pBufferDesc->ReturnValue < 0) {
172           break;
173         }
174       }
175     }
176   }
177   if (pBufferDesc->ReturnValue >= 0) {
178     //
179     // Compute Digit.
180     // Loop until Digit has the value of the highest digit required.
181     // Example: If the output is 345 (Base 10), loop 2 times until Digit is 100.
182     //
183     while (1) {
184       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)
185         NumDigits--;
186       } else {
187         Div = v / Digit;
188         if (Div < Base) {        // Is our divider big enough to extract the highest digit from value? => Done
189           break;
190         }
191       }
192       Digit *= Base;
193     }
194     //
195     // Output digits
196     //
197     do {
198       Div = v / Digit;
199       v -= Div * Digit;
200       _StoreChar(pBufferDesc, _aV2C[Div]);
201       if (pBufferDesc->ReturnValue < 0) {
202         break;
203       }
204       Digit /= Base;
205     } while (Digit);
206     //
207     // Print trailing spaces if necessary
208     //
209     if ((FormatFlags & FORMAT_FLAG_LEFT_JUSTIFY) == FORMAT_FLAG_LEFT_JUSTIFY) {
210       if (FieldWidth != 0u) {
211         while ((FieldWidth != 0u) && (Width < FieldWidth)) {
212           FieldWidth--;
213           _StoreChar(pBufferDesc, ' ');
214           if (pBufferDesc->ReturnValue < 0) {
215             break;
216           }
217         }
218       }
219     }
220   }
221 }
222 
223 /*********************************************************************
224 *
225 *       _PrintInt
226 */
_PrintInt(SEGGER_RTT_PRINTF_DESC * pBufferDesc,int v,unsigned Base,unsigned NumDigits,unsigned FieldWidth,unsigned FormatFlags)227 static void _PrintInt(SEGGER_RTT_PRINTF_DESC * pBufferDesc, int v, unsigned Base, unsigned NumDigits, unsigned FieldWidth, unsigned FormatFlags) {
228   unsigned Width;
229   int Number;
230 
231   Number = (v < 0) ? -v : v;
232 
233   //
234   // Get actual field width
235   //
236   Width = 1u;
237   while (Number >= (int)Base) {
238     Number = (Number / (int)Base);
239     Width++;
240   }
241   if (NumDigits > Width) {
242     Width = NumDigits;
243   }
244   if ((FieldWidth > 0u) && ((v < 0) || ((FormatFlags & FORMAT_FLAG_PRINT_SIGN) == FORMAT_FLAG_PRINT_SIGN))) {
245     FieldWidth--;
246   }
247 
248   //
249   // Print leading spaces if necessary
250   //
251   if ((((FormatFlags & FORMAT_FLAG_PAD_ZERO) == 0u) || (NumDigits != 0u)) && ((FormatFlags & FORMAT_FLAG_LEFT_JUSTIFY) == 0u)) {
252     if (FieldWidth != 0u) {
253       while ((FieldWidth != 0u) && (Width < FieldWidth)) {
254         FieldWidth--;
255         _StoreChar(pBufferDesc, ' ');
256         if (pBufferDesc->ReturnValue < 0) {
257           break;
258         }
259       }
260     }
261   }
262   //
263   // Print sign if necessary
264   //
265   if (pBufferDesc->ReturnValue >= 0) {
266     if (v < 0) {
267       v = -v;
268       _StoreChar(pBufferDesc, '-');
269     } else if ((FormatFlags & FORMAT_FLAG_PRINT_SIGN) == FORMAT_FLAG_PRINT_SIGN) {
270       _StoreChar(pBufferDesc, '+');
271     } else {
272 
273     }
274     if (pBufferDesc->ReturnValue >= 0) {
275       //
276       // Print leading zeros if necessary
277       //
278       if (((FormatFlags & FORMAT_FLAG_PAD_ZERO) == FORMAT_FLAG_PAD_ZERO) && ((FormatFlags & FORMAT_FLAG_LEFT_JUSTIFY) == 0u) && (NumDigits == 0u)) {
279         if (FieldWidth != 0u) {
280           while ((FieldWidth != 0u) && (Width < FieldWidth)) {
281             FieldWidth--;
282             _StoreChar(pBufferDesc, '0');
283             if (pBufferDesc->ReturnValue < 0) {
284               break;
285             }
286           }
287         }
288       }
289       if (pBufferDesc->ReturnValue >= 0) {
290         //
291         // Print number without sign
292         //
293         _PrintUnsigned(pBufferDesc, (unsigned)v, Base, NumDigits, FieldWidth, FormatFlags);
294       }
295     }
296   }
297 }
298 
299 /*********************************************************************
300 *
301 *       Public code
302 *
303 **********************************************************************
304 */
305 /*********************************************************************
306 *
307 *       SEGGER_RTT_vprintf
308 *
309 *  Function description
310 *    Stores a formatted string in SEGGER RTT control block.
311 *    This data is read by the host.
312 *
313 *  Parameters
314 *    BufferIndex  Index of "Up"-buffer to be used. (e.g. 0 for "Terminal")
315 *    sFormat      Pointer to format string
316 *    pParamList   Pointer to the list of arguments for the format string
317 *
318 *  Return values
319 *    >= 0:  Number of bytes which have been stored in the "Up"-buffer.
320 *     < 0:  Error
321 */
SEGGER_RTT_vprintf(unsigned BufferIndex,const char * sFormat,va_list * pParamList)322 int SEGGER_RTT_vprintf(unsigned BufferIndex, const char * sFormat, va_list * pParamList) {
323   char c;
324   SEGGER_RTT_PRINTF_DESC BufferDesc;
325   int v;
326   unsigned NumDigits;
327   unsigned FormatFlags;
328   unsigned FieldWidth;
329   char acBuffer[SEGGER_RTT_PRINTF_BUFFER_SIZE];
330 
331   BufferDesc.pBuffer        = acBuffer;
332   BufferDesc.BufferSize     = SEGGER_RTT_PRINTF_BUFFER_SIZE;
333   BufferDesc.Cnt            = 0u;
334   BufferDesc.RTTBufferIndex = BufferIndex;
335   BufferDesc.ReturnValue    = 0;
336 
337   do {
338     c = *sFormat;
339     sFormat++;
340     if (c == 0u) {
341       break;
342     }
343     if (c == '%') {
344       //
345       // Filter out flags
346       //
347       FormatFlags = 0u;
348       v = 1;
349       do {
350         c = *sFormat;
351         switch (c) {
352         case '-': FormatFlags |= FORMAT_FLAG_LEFT_JUSTIFY; sFormat++; break;
353         case '0': FormatFlags |= FORMAT_FLAG_PAD_ZERO;     sFormat++; break;
354         case '+': FormatFlags |= FORMAT_FLAG_PRINT_SIGN;   sFormat++; break;
355         case '#': FormatFlags |= FORMAT_FLAG_ALTERNATE;    sFormat++; break;
356         default:  v = 0; break;
357         }
358       } while (v);
359       //
360       // filter out field with
361       //
362       FieldWidth = 0u;
363       do {
364         c = *sFormat;
365         if ((c < '0') || (c > '9')) {
366           break;
367         }
368         sFormat++;
369         FieldWidth = (FieldWidth * 10u) + ((unsigned)c - '0');
370       } while (1);
371 
372       //
373       // Filter out precision (number of digits to display)
374       //
375       NumDigits = 0u;
376       c = *sFormat;
377       if (c == '.') {
378         sFormat++;
379         do {
380           c = *sFormat;
381           if ((c < '0') || (c > '9')) {
382             break;
383           }
384           sFormat++;
385           NumDigits = NumDigits * 10u + ((unsigned)c - '0');
386         } while (1);
387       }
388       //
389       // Filter out length modifier
390       //
391       c = *sFormat;
392       do {
393         if ((c == 'l') || (c == 'h')) {
394           sFormat++;
395           c = *sFormat;
396         } else {
397           break;
398         }
399       } while (1);
400       //
401       // Handle specifiers
402       //
403       switch (c) {
404       case 'c': {
405         char c0;
406         v = va_arg(*pParamList, int);
407         c0 = (char)v;
408         _StoreChar(&BufferDesc, c0);
409         break;
410       }
411       case 'd':
412         v = va_arg(*pParamList, int);
413         _PrintInt(&BufferDesc, v, 10u, NumDigits, FieldWidth, FormatFlags);
414         break;
415       case 'u':
416         v = va_arg(*pParamList, int);
417         _PrintUnsigned(&BufferDesc, (unsigned)v, 10u, NumDigits, FieldWidth, FormatFlags);
418         break;
419       case 'x':
420       case 'X':
421         v = va_arg(*pParamList, int);
422         _PrintUnsigned(&BufferDesc, (unsigned)v, 16u, NumDigits, FieldWidth, FormatFlags);
423         break;
424       case 's':
425         {
426           const char * s = va_arg(*pParamList, const char *);
427           do {
428             c = *s;
429             s++;
430             if (c == '\0') {
431               break;
432             }
433            _StoreChar(&BufferDesc, c);
434           } while (BufferDesc.ReturnValue >= 0);
435         }
436         break;
437       case 'p':
438         v = va_arg(*pParamList, int);
439         _PrintUnsigned(&BufferDesc, (unsigned)v, 16u, 8u, 8u, 0u);
440         break;
441       case '%':
442         _StoreChar(&BufferDesc, '%');
443         break;
444       default:
445         break;
446       }
447       sFormat++;
448     } else {
449       _StoreChar(&BufferDesc, c);
450     }
451   } while (BufferDesc.ReturnValue >= 0);
452 
453   if (BufferDesc.ReturnValue > 0) {
454     //
455     // Write remaining data, if any
456     //
457     if (BufferDesc.Cnt != 0u) {
458       SEGGER_RTT_Write(BufferIndex, acBuffer, BufferDesc.Cnt);
459     }
460     BufferDesc.ReturnValue += (int)BufferDesc.Cnt;
461   }
462   return BufferDesc.ReturnValue;
463 }
464 
465 /*********************************************************************
466 *
467 *       SEGGER_RTT_printf
468 *
469 *  Function description
470 *    Stores a formatted string in SEGGER RTT control block.
471 *    This data is read by the host.
472 *
473 *  Parameters
474 *    BufferIndex  Index of "Up"-buffer to be used. (e.g. 0 for "Terminal")
475 *    sFormat      Pointer to format string, followed by the arguments for conversion
476 *
477 *  Return values
478 *    >= 0:  Number of bytes which have been stored in the "Up"-buffer.
479 *     < 0:  Error
480 *
481 *  Notes
482 *    (1) Conversion specifications have following syntax:
483 *          %[flags][FieldWidth][.Precision]ConversionSpecifier
484 *    (2) Supported flags:
485 *          -: Left justify within the field width
486 *          +: Always print sign extension for signed conversions
487 *          0: Pad with 0 instead of spaces. Ignored when using '-'-flag or precision
488 *        Supported conversion specifiers:
489 *          c: Print the argument as one char
490 *          d: Print the argument as a signed integer
491 *          u: Print the argument as an unsigned integer
492 *          x: Print the argument as an hexadecimal integer
493 *          s: Print the string pointed to by the argument
494 *          p: Print the argument as an 8-digit hexadecimal integer. (Argument shall be a pointer to void.)
495 */
SEGGER_RTT_printf(unsigned BufferIndex,const char * sFormat,...)496 int SEGGER_RTT_printf(unsigned BufferIndex, const char * sFormat, ...) {
497   int r;
498   va_list ParamList;
499 
500   va_start(ParamList, sFormat);
501   r = SEGGER_RTT_vprintf(BufferIndex, sFormat, &ParamList);
502   va_end(ParamList);
503   return r;
504 }
505 /*************************** End of file ****************************/
506