1 /*-------------------------------------------------------------------------
2 printf_large.c - formatted output conversion
4 Written By - Martijn van Balen aed@iae.nl (1999)
5 Added %f By - johan.knol@iduna.nl (2000)
6 Refactored by - Maarten Brock (2004)
8 This library is free software; you can redistribute it and/or
9 modify it under the terms of the GNU Lesser General Public
10 License as published by the Free Software Foundation; either
11 version 2.1 of the License, or (at your option) any later version.
13 This library is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public
19 License along with this library; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 In other words, you are welcome to use, share and improve this program.
23 You are forbidden to forbid anyone else to use, share and improve
24 what you give them. Help stamp out software-hoarding!
25 -------------------------------------------------------------------------*/
27 #ifdef SDCC_STACK_AUTO
29 /* Eventually we should get rid of ASM_ALLOWED completely as it */
30 /* prevents portability, clobbers this source and brings only 2% */
31 /* optimization. A better alternative is a completely handcrafted */
32 /* asm version if needed. */
49 #define NULL_STRING "<NULL>"
50 #define NULL_STRING_LENGTH 6
53 /****************************************************************************/
55 //typedef char * ptr_t;
62 //#define toupper(c) ((c)&=~0x20)
63 #define toupper(c) ((c)&=0xDF)
65 #if defined (SDCC_STACK_AUTO) || defined (SDCC_hc08)
66 //as long as bit/bool cannot be used reentrant
74 unsigned char byte[5];
81 static const char memory_id[] = "IXCP-";
83 #ifndef SDCC_STACK_AUTO
84 static BOOL lower_case;
85 static pfn_outputchar output_char;
91 /* this one NEEDS to be in data */
92 static data value_t value;
98 /****************************************************************************/
100 #ifdef SDCC_STACK_AUTO
101 static void output_digit( unsigned char n, BOOL lower_case, pfn_outputchar output_char, void* p )
103 static void output_digit( unsigned char n )
106 output_char( n <= 9 ? '0'+n :
107 (lower_case ? n+(char)('a'-10) : n+(char)('A'-10)), p );
110 /*--------------------------------------------------------------------------*/
112 #ifdef SDCC_STACK_AUTO
113 #define OUTPUT_2DIGITS( B ) output_2digits( B, lower_case, output_char, p )
114 static void output_2digits( unsigned char b, BOOL lower_case, pfn_outputchar output_char, void* p )
116 output_digit( b>>4, lower_case, output_char, p );
117 output_digit( b&0x0F, lower_case, output_char, p );
120 #define OUTPUT_2DIGITS( B ) output_2digits( B )
121 static void output_2digits( unsigned char b )
123 output_digit( b>>4 );
124 output_digit( b&0x0F );
128 /*--------------------------------------------------------------------------*/
130 #if defined ASM_ALLOWED
131 static void calculate_digit( unsigned char radix )
135 for( i = 32; i != 0; i-- )
155 if (radix <= value.byte[4] )
157 value.byte[4] -= radix;
162 #elif defined SDCC_STACK_AUTO
163 static void calculate_digit( value_t* value, unsigned char radix )
167 for( i = 32; i != 0; i-- )
169 value->byte[4] = (value->byte[4] << 1) | (value->ul >> 31) & 0x01;
172 if (radix <= value->byte[4] )
174 value->byte[4] -= radix;
180 static void calculate_digit( unsigned char radix )
184 for( i = 32; i != 0; i-- )
186 value.byte[4] = (value.byte[4] << 1) | (value.ul >> 31) & 0x01;
189 if (radix <= value.byte[4] )
191 value.byte[4] -= radix;
200 /* This is a very inefficient but direct approach, since we have no math
201 library yet (e.g. log()).
202 It does most of the modifiers, but has some restrictions. E.g. the
203 abs(float) shouldn't be bigger than an unsigned long (that's
204 about 4294967295), but still makes it usefull for most real-life
208 #define DEFAULT_FLOAT_PRECISION 6
210 #ifdef SDCC_STACK_AUTO
211 #define OUTPUT_FLOAT(F, W, D, L, Z, S, P) output_float(F, W, D, L, Z, S, P, output_char, p)
212 static int output_float (float f, unsigned char reqWidth,
213 signed char reqDecimals,
214 BOOL left, BOOL zero, BOOL sign, BOOL space,
215 pfn_outputchar output_char, void* p)
217 #define OUTPUT_FLOAT(F, W, D, L, Z, S, P) output_float(F, W, D, L, Z, S, P)
218 static int output_float (float f, unsigned char reqWidth,
219 signed char reqDecimals,
220 BOOL left, BOOL zero, BOOL sign, BOOL space)
224 unsigned long integerPart;
228 unsigned char minWidth, i;
229 int charsOutputted=0;
238 // this part is from Frank van der Hulst
241 for (exp = 0; f >= 10.0; exp++) f /=10.0;
242 for ( ; f < 1.0; exp--) f *=10.0;
245 output_char ('-', p);
249 output_char ('+', p);
253 charsOutputted += OUTPUT_FLOAT(f, 0, reqDecimals, 0, 0, 0, 0);
254 output_char ('e', p);
257 output_char ('-', p);
261 output_char ('0'+exp/10, p);
262 output_char ('0'+exp%10, p);
264 return charsOutputted;
269 decimalPart=f-integerPart;
271 // fill the buffer with the integerPart (in reversed order!)
272 while (integerPart) {
273 fpBuffer[fpBI++]='0' + integerPart%10;
277 // we need at least a 0
278 fpBuffer[fpBI++]='0';
281 // display some decimals as default
283 reqDecimals=DEFAULT_FLOAT_PRECISION;
285 // fill buffer with the decimalPart (in normal order)
287 if (i=reqDecimals /* that's an assignment */) {
290 // truncate the float
291 integerPart=decimalPart;
292 fpBuffer[fpBD++]='0' + integerPart;
293 decimalPart-=integerPart;
297 minWidth=fpBI; // we need at least these
298 minWidth+=reqDecimals?reqDecimals+1:0; // maybe these
299 if (negative || sign || space)
300 minWidth++; // and maybe even this :)
302 if (!left && reqWidth>i) {
319 while (reqWidth-->minWidth)
325 while (reqWidth-->minWidth)
364 // output the integer part
367 output_char (fpBuffer[i], p);
371 // ouput the decimal part
373 output_char ('.', p);
376 while (reqDecimals--)
378 output_char (fpBuffer[i++], p);
383 if (left && reqWidth>minWidth) {
384 while (reqWidth-->minWidth)
390 return charsOutputted;
394 int _print_format (pfn_outputchar pfn, void* pvoid, const char *format, va_list ap)
400 BOOL signed_argument;
404 #ifdef SDCC_STACK_AUTO
415 signed char decimals;
416 unsigned char length;
419 #ifdef SDCC_STACK_AUTO
420 #define output_char pfn
427 // reset output chars
464 width = 10*width + (c - '0');
466 /* first character of width is a zero */
470 decimals = 10*decimals + (c-'0');
472 goto get_conversion_spec;
476 if (decimals=-1) decimals=0;
478 ; // duplicate, ignore
479 goto get_conversion_spec;
482 lower_case = islower(c);
492 goto get_conversion_spec;
495 goto get_conversion_spec;
498 goto get_conversion_spec;
501 goto get_conversion_spec;
504 goto get_conversion_spec;
507 output_char( va_arg(ap,int), p );
512 PTR = va_arg(ap,ptr_t);
517 length=NULL_STRING_LENGTH;
519 length = strlen(PTR);
522 length = strlen(PTR);
524 if ( ( !left_justify ) && (length < width) )
527 while( width-- != 0 )
529 output_char( ' ', p );
536 output_char( *PTR++, p );
540 if ( left_justify && (length < width))
543 while( width-- != 0 )
545 output_char( ' ', p );
552 PTR = va_arg(ap,ptr_t);
555 output_char(memory_id[(value.byte[3] > 3) ? 4 : value.byte[3]], p );
559 OUTPUT_2DIGITS( value.byte[2] );
560 OUTPUT_2DIGITS( value.byte[1] );
561 OUTPUT_2DIGITS( value.byte[0] );
562 charsOutputted += 10;
564 output_char( memory_id[(value.byte[2] > 3) ? 4 : value.byte[2]], p );
568 if ((value.byte[2] != 0x00 /* DSEG */) &&
569 (value.byte[2] != 0x03 /* SSEG */))
571 OUTPUT_2DIGITS( value.byte[1] );
574 OUTPUT_2DIGITS( value.byte[0] );
602 // nothing special, just output the character
608 if (float_argument) {
609 value.f=va_arg(ap,float);
623 // ignore b and l conversion spec for now
624 charsOutputted += OUTPUT_FLOAT(value.f, width, decimals, left_justify,
625 zero_padding, prefix_sign, prefix_space);
627 } else if (radix != 0)
629 // Apperently we have to output an integral type
630 // with radix "radix"
631 unsigned char store = 0;
633 // store value in byte[0] (LSB) ... byte[3] (MSB)
636 value.l = va_arg(ap,char);
637 if (!signed_argument)
639 value.byte[1] = 0x00;
640 value.byte[2] = 0x00;
641 value.byte[3] = 0x00;
644 else if (long_argument)
646 value.l = va_arg(ap,long);
650 value.l = va_arg(ap,int);
651 if (!signed_argument)
653 value.byte[2] = 0x00;
654 value.byte[3] = 0x00;
658 if ( signed_argument )
671 #if defined SDCC_STACK_AUTO
672 calculate_digit(&value, radix);
674 calculate_digit(radix);
676 #if defined ASM_ALLOWED
680 mov a,_value+4 ; a = <msd>
682 orl b,a ; b = <msd><lsd>
686 mov a,_value+4 ; a = <lsd>
693 store = (value.byte[4] << 4) | (value.byte[4] >> 4) | store;
697 store = value.byte[4];
702 } while( (value.byte[0] != 0) || (value.byte[1] != 0) ||
703 (value.byte[2] != 0) || (value.byte[3] != 0) );
707 // default width. We set it to 1 to output
708 // at least one character in case the value itself
709 // is zero (i.e. length==0)
713 /* prepend spaces if needed */
714 if (!zero_padding && !left_justify)
716 while ( width > (unsigned char) (length+1) )
718 output_char( ' ', p );
724 if (signed_argument) // this now means the original value was negative
726 output_char( '-', p );
728 // adjust width to compensate for this character
731 else if (length != 0)
736 output_char( '+', p );
738 // adjust width to compensate for this character
741 else if (prefix_space)
743 output_char( ' ', p );
745 // adjust width to compensate for this character
750 /* prepend zeroes/spaces if needed */
752 while ( width-- > length )
754 output_char( zero_padding ? '0' : ' ', p );
759 /* spaces are appended after the digits */
766 /* output the digits */
773 pop acc ; a = <msd><lsd>
774 nop ; to disable the "optimizer"
777 anl a,#0x0F ; a = <msd>
781 anl a,#0x0F ; a = <lsd>
788 value.byte[4] = store >> 4;
792 value.byte[4] = store & 0x0F;
795 #ifdef SDCC_STACK_AUTO
796 output_digit( value.byte[4], lower_case, output_char, p );
798 output_digit( value.byte[4] );
812 // nothing special, just output the character
818 return charsOutputted;
821 /****************************************************************************/