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