4 * Copyright 2003, 2004, 2005 Develer S.r.l. (http://www.develer.com/)
5 * This file is part of DevLib - See README.devlib for information.
10 * \brief Basic "printf", "sprintf" and "fprintf" formatter.
12 * This module is 100% reentrant and can be adapted to user-defined routines
13 * that needs formatters with special properties like different output
14 * channels or new format specifiers.
16 * To reduce size in applications not using real numbers or long integers
17 * the formatter may be compiled to exclude certain parts. This is
18 * controlled by giving a -D option a compilation time:
21 * -D CONFIG_PRINTF=PRINTF_FULL Full ANSI printf formatter
22 * -D CONFIG_PRINTF=PRINTF_NOFLOAT Exclude support for floats
23 * -D CONFIG_PRINTF=PRINTF_REDUCED Simplified formatter (see below)
24 * -D CONFIG_PRINTF=PRINTF_NOMODIFIERS Exclude 'l' and 'h' modifiers in reduced version
25 * -D CONFIG_PRINTF=PRINTF_DISABLED No formatter at all
28 * Code size on AVR4 with GCC 3.4.1 (-O2):
29 * PRINTF_FULL 2912byte (0xB60)
30 * PRINTF_NOFLOAT 1684byte (0x694)
31 * PRINTF_REDUCED 924byte (0x39C)
32 * PRINTF_NOMODIFIERS 416byte (0x1A0)
34 * Code/data size in words on DSP56K with CodeWarrior 6.0:
36 * PRINTF_NOFLOAT 795/45
37 * PRINTF_REDUCED 482/0
38 * PRINTF_NOMODIFIERS 301/0
40 * The reduced version of formatter is suitable when program size is critical
41 * rather than formatting power. This routine uses less than 20 bytes of
42 * stack space which makes it practical even in systems with less than 256
45 * The only formatting specifiers supported by the reduced formatter are:
47 * %% %c %s %d %o %x %X and %hd %ho %hx %hX %ld %lo %lx %lX
50 * It means that real variables are not supported as well as field
51 * width and precision arguments.
56 *#* Revision 1.15 2005/04/11 19:10:28 bernie
57 *#* Include top-level headers from cfg/ subdir.
59 *#* Revision 1.14 2005/03/01 23:26:22 bernie
60 *#* Use shared hextab.
62 *#* Revision 1.13 2005/02/18 12:33:25 bernie
65 *#* Revision 1.12 2005/02/16 20:28:03 bernie
68 *#* Revision 1.11 2005/02/16 16:51:29 bernie
69 *#* Simplify float code.
71 *#* Revision 1.10 2004/10/26 09:01:35 bernie
74 *#* Revision 1.9 2004/09/14 21:06:23 bernie
77 *#* Revision 1.8 2004/08/25 14:12:09 rasky
78 *#* Aggiornato il comment block dei log RCS
82 #include <mware/pgm.h>
83 #include <mware/hex.h>
84 #include <cfg/config.h> /* CONFIG_ macros */
85 #include <cfg/debug.h> /* ASSERT */
87 #ifndef CONFIG_PRINTF_N_FORMATTER
88 /*! Disable the arcane %n formatter. */
89 #define CONFIG_PRINTF_N_FORMATTER 0
92 #ifndef CONFIG_PRINTF_OCTAL_FORMATTER
93 /*! Disable the %o formatter. */
94 #define CONFIG_PRINTF_OCTAL_FORMATTER 0
97 /* True if we must keep a count of the number of characters we print. */
98 #define CONFIG_PRINTF_COUNT_CHARS (CONFIG_PRINTF_RETURN_COUNT || CONFIG_PRINTF_N_FORMATTER)
102 #if CONFIG_PRINTF > PRINTF_NOFLOAT
105 /* Maximum precision for floating point values */
106 typedef long double max_float_t;
108 /*bernie: save some memory, who cares about floats with lots of decimals? */
109 #define FRMWRI_BUFSIZE 134
110 #warning 134 is too much, the code must be fixed to have a lower precision limit
113 * Conservative estimate. Should be (probably) 12 (which is the size necessary
114 * to represent (2^32-1) in octal plus the sign bit.
116 #define FRMWRI_BUFSIZE 16
119 /* Probably useful for fancy microcontrollers such as the PIC, nobody knows. */
120 #ifndef MEM_ATTRIBUTE
121 #define MEM_ATTRIBUTE
124 #if CONFIG_PRINTF > PRINTF_NOMODIFIERS
125 #define IS_SHORT (h_modifier || (sizeof(int) == 2 && !l_modifier))
127 #define IS_SHORT (sizeof(int) == 2)
128 #endif /* CONFIG_PRINTF > PRINTF_NOMODIFIERS */
131 #if CONFIG_PRINTF > PRINTF_NOFLOAT
133 static char *float_conversion(MEM_ATTRIBUTE max_float_t value,
134 MEM_ATTRIBUTE short nr_of_digits,
135 MEM_ATTRIBUTE char *buf,
136 MEM_ATTRIBUTE char format_flag,
137 MEM_ATTRIBUTE char g_flag,
138 MEM_ATTRIBUTE bool alternate_flag)
140 MEM_ATTRIBUTE char *cp;
141 MEM_ATTRIBUTE char *buf_pointer;
142 MEM_ATTRIBUTE short n, i, dec_point_pos, integral_10_log;
149 while (value >= 1e11) /* To speed up things a bit */
152 integral_10_log += 10;
160 else if (value) /* Not just 0.0 */
162 while (value <= 1e-10) /* To speed up things a bit */
165 integral_10_log -= 10;
175 if (integral_10_log < nr_of_digits && integral_10_log >= -4)
178 nr_of_digits -= integral_10_log;
182 /* %#G - No removal of trailing zeros */
185 /* %G - Removal of trailing zeros */
186 alternate_flag = true;
196 /* Less than one... */
197 if (integral_10_log < 0)
199 *buf_pointer++ = '0';
200 if ((n = nr_of_digits) || alternate_flag)
201 *buf_pointer++ = '.';
203 while (--i > integral_10_log && nr_of_digits)
205 *buf_pointer++ = '0';
208 if (integral_10_log < (-n - 1))
209 /* Nothing more to do */
215 dec_point_pos = - integral_10_log;
220 while (i <= nr_of_digits )
222 value -= (max_float_t)(n = (short)value); /* n=Digit value=Remainder */
223 value *= 10; /* Prepare for next shot */
224 *buf_pointer++ = n + '0';
225 if ( ! i++ && (nr_of_digits || alternate_flag))
226 *buf_pointer++ = '.';
229 /* Rounding possible */
233 cp = buf_pointer - 1;
238 if ( (*cp += n) == ('9' + 1) )
246 } while (cp-- > buf);
255 if (*(cp - 1) == '.')
280 /* %G - Remove trailing zeros */
283 while (*(buf_pointer - 1) == '0')
285 if (*(buf_pointer - 1) == '.')
292 *buf_pointer++ = format_flag;
293 if (integral_10_log < 0)
295 *buf_pointer++ = '-';
296 integral_10_log = -integral_10_log;
299 *buf_pointer++ = '+';
305 *buf_pointer++ = (integral_10_log % 10) + '0';
306 integral_10_log /= 10;
307 } while ( integral_10_log || n < 2 );
308 for ( i = n ; n > 0 ; n-- )
309 *(buf_pointer - 11 - i + n) = *(buf_pointer - n);
312 return (buf_pointer);
315 #endif /* CONFIG_PRINTF > PRINTF_NOFLOAT */
318 * This routine forms the core and entry of the formatter.
320 * The conversion performed conforms to the ANSI specification for "printf".
323 PGM_FUNC(_formatted_write)(const char * PGM_ATTR format,
324 void put_one_char(char, void *),
325 void *secret_pointer,
328 #if CONFIG_PRINTF > PRINTF_REDUCED
329 MEM_ATTRIBUTE static char bad_conversion[] = "???";
330 MEM_ATTRIBUTE static char null_pointer[] = "<NULL>";
332 MEM_ATTRIBUTE int precision;
334 #if CONFIG_PRINTF_COUNT_CHARS
335 MEM_ATTRIBUTE int nr_of_chars;
337 MEM_ATTRIBUTE int field_width;
338 MEM_ATTRIBUTE char format_flag;
339 enum PLUS_SPACE_FLAGS {
340 PSF_NONE, PSF_PLUS, PSF_MINUS
344 #if CONFIG_PRINTF_OCTAL_FORMATTER
348 MEM_ATTRIBUTE struct {
349 enum PLUS_SPACE_FLAGS plus_space_flag : 2;
350 #if CONFIG_PRINTF_OCTAL_FORMATTER
351 enum DIV_FACTOR div_factor : 2;
353 enum DIV_FACTOR div_factor : 1;
355 bool left_adjust : 1;
356 bool l_L_modifier : 1;
358 bool alternate_flag : 1;
359 bool nonzero_value : 1;
365 MEM_ATTRIBUTE unsigned long ulong;
367 #if CONFIG_PRINTF > PRINTF_NOFLOAT
368 MEM_ATTRIBUTE max_float_t fvalue;
371 MEM_ATTRIBUTE char *buf_pointer;
372 MEM_ATTRIBUTE char *ptr;
373 MEM_ATTRIBUTE const char *hex;
374 MEM_ATTRIBUTE char buf[FRMWRI_BUFSIZE];
376 #if CONFIG_PRINTF_COUNT_CHARS
379 for (;;) /* Until full format string read */
381 while ((format_flag = PGM_READ_CHAR(format++)) != '%') /* Until '%' or '\0' */
384 #if CONFIG_PRINTF_RETURN_COUNT
385 return (nr_of_chars);
389 put_one_char(format_flag, secret_pointer);
390 #if CONFIG_PRINTF_COUNT_CHARS
394 if (PGM_READ_CHAR(format) == '%') /* %% prints as % */
397 put_one_char('%', secret_pointer);
398 #if CONFIG_PRINTF_COUNT_CHARS
404 flags.left_adjust = false;
405 flags.alternate_flag = false;
406 flags.plus_space_flag = PSF_NONE;
407 flags.zeropad = false;
409 flags.progmem = false;
411 ptr = buf_pointer = &buf[0];
414 /* check for leading '-', '+', ' ','#' or '0' flags */
417 switch (PGM_READ_CHAR(format))
420 if (flags.plus_space_flag)
423 flags.plus_space_flag = PSF_PLUS;
426 flags.left_adjust = true;
429 flags.alternate_flag = true;
432 flags.zeropad = true;
440 /* Optional field width (may be '*') */
441 if (PGM_READ_CHAR(format) == '*')
443 field_width = va_arg(ap, int);
446 field_width = -field_width;
447 flags.left_adjust = true;
454 while (PGM_READ_CHAR(format) >= '0' && PGM_READ_CHAR(format) <= '9')
455 field_width = field_width * 10 + (PGM_READ_CHAR(format++) - '0');
458 if (flags.left_adjust)
459 flags.zeropad = false;
461 /* Optional precision (or '*') */
462 if (PGM_READ_CHAR(format) == '.')
464 if (PGM_READ_CHAR(++format) == '*')
466 precision = va_arg(ap, int);
472 while (PGM_READ_CHAR(format) >= '0' && PGM_READ_CHAR(format) <= '9')
473 precision = precision * 10 + (PGM_READ_CHAR(format++) - '0');
479 /* At this point, "left_adjust" is nonzero if there was
480 * a sign, "zeropad" is 1 if there was a leading zero
481 * and 0 otherwise, "field_width" and "precision"
482 * contain numbers corresponding to the digit strings
483 * before and after the decimal point, respectively,
484 * and "plus_space_flag" is either 0 (no flag) or
485 * contains a plus or space character. If there was no
486 * decimal point, "precision" will be -1.
489 flags.l_L_modifier = false;
490 flags.h_modifier = false;
492 /* Optional 'l','L' r 'h' modifier? */
493 switch (PGM_READ_CHAR(format))
497 flags.l_L_modifier = true;
501 flags.h_modifier = true;
507 * At exit from the following switch, we will emit
508 * the characters starting at "buf_pointer" and
511 switch (format_flag = PGM_READ_CHAR(format++))
513 #if CONFIG_PRINTF_N_FORMATTER
515 if (sizeof(short) != sizeof(int))
517 if (sizeof(int) != sizeof(long))
520 *va_arg(ap, short *) = nr_of_chars;
521 else if (flags.l_L_modifier)
522 *va_arg(ap, long *) = nr_of_chars;
524 *va_arg(ap, int *) = nr_of_chars;
529 *va_arg(ap, short *) = nr_of_chars;
531 *va_arg(ap, int *) = nr_of_chars;
536 if (flags.l_L_modifier)
537 *va_arg(ap, long *) = nr_of_chars;
539 *va_arg(ap, int *) = nr_of_chars;
544 buf[0] = va_arg(ap, int);
548 /* Custom formatter for strings in program memory. */
551 flags.progmem = true;
556 if ( !(buf_pointer = va_arg(ap, char *)) )
557 buf_pointer = null_pointer;
562 * Move `ptr' to the last character of the
563 * string that will be actually printed.
569 for (n=0; pgm_read_char(ptr) && n < precision; n++)
574 for (n=0; *ptr && n < precision; n++)
578 #if CONFIG_PRINTF_OCTAL_FORMATTER
580 if (flags.alternate_flag && !precision)
588 if (format_flag == 'p')
589 #if defined(__AVR__) || defined(__I196__) /* 16bit pointers */
590 ulong = (unsigned long)(unsigned short)va_arg(ap, char *);
591 #else /* 32bit pointers */
592 ulong = (unsigned long)va_arg(ap, char *);
593 #endif /* 32bit pointers */
594 else if (sizeof(short) == sizeof(int))
595 ulong = flags.l_L_modifier ?
596 va_arg(ap, unsigned long) : (unsigned long)va_arg(ap, unsigned int);
598 ulong = flags.h_modifier ?
599 (unsigned long)(unsigned short) va_arg(ap, int)
600 : (unsigned long)va_arg(ap, int);
602 #if CONFIG_PRINTF_OCTAL_FORMATTER
603 (format_flag == 'o') ? DIV_OCT :
605 (format_flag == 'u') ? DIV_DEC : DIV_HEX;
606 flags.plus_space_flag = PSF_NONE;
607 goto INTEGRAL_CONVERSION;
611 if (sizeof(short) == sizeof(long))
613 if ( (long)(ulong = va_arg(ap, unsigned long)) < 0)
615 flags.plus_space_flag = PSF_MINUS;
616 ulong = (unsigned long)(-((signed long)ulong));
619 else if (sizeof(short) == sizeof(int))
621 if ( (long)(ulong = flags.l_L_modifier ?
622 va_arg(ap,unsigned long) : (unsigned long)va_arg(ap,int)) < 0)
624 flags.plus_space_flag = PSF_MINUS;
625 ulong = (unsigned long)(-((signed long)ulong));
630 if ( (signed long)(ulong = (unsigned long) (flags.h_modifier ?
631 (short) va_arg(ap, int) : va_arg(ap,int))) < 0)
633 flags.plus_space_flag = PSF_MINUS;
634 ulong = (unsigned long)(-((signed long)ulong));
637 flags.div_factor = DIV_DEC;
639 /* Now convert to digits */
641 ptr = buf_pointer = &buf[FRMWRI_BUFSIZE - 1];
642 flags.nonzero_value = (ulong != 0);
644 /* No char if zero and zero precision */
645 if (precision != 0 || flags.nonzero_value)
647 switch (flags.div_factor)
651 *--buf_pointer = hex[ulong % 10];
657 *--buf_pointer = hex[ulong % 16];
660 #if CONFIG_PRINTF_OCTAL_FORMATTER
663 *--buf_pointer = hex[ulong % 8];
670 /* "precision" takes precedence */
673 precision = field_width - (flags.plus_space_flag != PSF_NONE);
674 while (precision > (int)(ptr - buf_pointer))
675 *--buf_pointer = '0';
677 if (flags.alternate_flag && flags.nonzero_value)
679 if (format_flag == 'x' || format_flag == 'X')
681 *--buf_pointer = format_flag;
682 *--buf_pointer = '0';
684 #if CONFIG_PRINTF_OCTAL_FORMATTER
685 else if ((format_flag == 'o') && (*buf_pointer != '0'))
687 *--buf_pointer = '0';
691 ASSERT(buf_pointer >= buf);
694 #if CONFIG_PRINTF > PRINTF_NOFLOAT
703 goto FLOATING_CONVERSION;
715 if (sizeof(double) != sizeof(max_float_t))
717 fvalue = flags.l_L_modifier ?
718 va_arg(ap,max_float_t) : va_arg(ap,double);
721 fvalue = va_arg(ap,max_float_t);
725 flags.plus_space_flag = PSF_MINUS;
728 ptr = float_conversion (fvalue,
730 buf_pointer += field_width,
733 flags.alternate_flag);
736 precision = field_width - (flags.plus_space_flag != PSF_NONE);
737 while (precision > ptr - buf_pointer)
738 *--buf_pointer = '0';
742 #endif /* CONFIG_PRINTF <= PRINTF_NOFLOAT */
744 case '\0': /* Really bad place to find NUL in */
748 /* Undefined conversion! */
749 ptr = buf_pointer = bad_conversion;
750 ptr += sizeof(bad_conversion) - 1;
756 * This part emittes the formatted string to "put_one_char".
759 /* If field_width == 0 then nothing should be written. */
760 precision = ptr - buf_pointer;
762 if ( precision > field_width)
768 n = field_width - precision - (flags.plus_space_flag != PSF_NONE);
771 /* emit any leading pad characters */
772 if (!flags.left_adjust)
775 put_one_char(' ', secret_pointer);
776 #if CONFIG_PRINTF_COUNT_CHARS
781 /* emit flag characters (if any) */
782 if (flags.plus_space_flag)
784 put_one_char(flags.plus_space_flag == PSF_PLUS ? '+' : '-', secret_pointer);
785 #if CONFIG_PRINTF_COUNT_CHARS
793 while (--precision >= 0)
795 put_one_char(pgm_read_char(buf_pointer++), secret_pointer);
796 #if CONFIG_PRINTF_COUNT_CHARS
802 #endif /* CPU_HARVARD */
804 /* emit the string itself */
805 while (--precision >= 0)
807 put_one_char(*buf_pointer++, secret_pointer);
808 #if CONFIG_PRINTF_COUNT_CHARS
814 /* emit trailing space characters */
815 if (flags.left_adjust)
818 put_one_char(' ', secret_pointer);
819 #if CONFIG_PRINTF_COUNT_CHARS
825 #else /* PRINTF_REDUCED starts here */
827 #if CONFIG_PRINTF > PRINTF_NOMODIFIERS
828 char l_modifier, h_modifier;
829 unsigned long u_val, div_val;
831 unsigned int u_val, div_val;
832 #endif /* CONFIG_PRINTF > PRINTF_NOMODIFIERS */
835 unsigned int nr_of_chars, base;
840 for (;;) /* Until full format string read */
842 while ((format_flag = PGM_READ_CHAR(format++)) != '%') /* Until '%' or '\0' */
845 return (nr_of_chars);
846 put_one_char(format_flag, secret_pointer);
850 #if CONFIG_PRINTF > PRINTF_NOMODIFIERS
851 /*=================================*/
852 /* Optional 'l' or 'h' modifiers ? */
853 /*=================================*/
854 l_modifier = h_modifier = 0;
855 switch (PGM_READ_CHAR(format))
867 #endif /* CONFIG_PRINTF > PRINTF_NOMODIFIERS */
869 switch (format_flag = PGM_READ_CHAR(format++))
872 format_flag = va_arg(ap, int);
874 put_one_char(format_flag, secret_pointer);
879 ptr = va_arg(ap, char *);
880 while ((format_flag = *ptr++))
882 put_one_char(format_flag, secret_pointer);
892 div_val = 0x40000000;
893 goto CONVERSION_LOOP;
900 div_val = 1000000000;
901 goto CONVERSION_LOOP;
909 div_val = 0x10000000;
912 #if CONFIG_PRINTF > PRINTF_NOMODIFIERS
914 u_val = (format_flag == 'd') ?
915 (short)va_arg(ap, int) : (unsigned short)va_arg(ap, int);
917 u_val = va_arg(ap, long);
919 u_val = (format_flag == 'd') ?
920 va_arg(ap,int) : va_arg(ap,unsigned int);
921 #else /* CONFIG_PRINTF > PRINTF_NOMODIFIERS */
922 u_val = va_arg(ap,int);
923 #endif /* CONFIG_PRINTF > PRINTF_NOMODIFIERS */
924 if (format_flag == 'd')
926 if (((int)u_val) < 0)
929 put_one_char('-', secret_pointer);
933 while (div_val > 1 && div_val > u_val)
939 outChar = (u_val / div_val) + '0';
942 if (format_flag == 'x')
943 outChar += 'a'-'9'-1;
945 outChar += 'A'-'9'-1;
947 put_one_char(outChar, secret_pointer);
954 } /* end switch(format_flag...) */
956 #endif /* CONFIG_PRINTF > PRINTF_REDUCED */
959 #endif /* CONFIG_PRINTF */