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. */
51 #define NULL_STRING "<NULL>"
52 #define NULL_STRING_LENGTH 6
55 /****************************************************************************/
57 //typedef char * ptr_t;
64 //#define toupper(c) ((c)&=~0x20)
65 #define toupper(c) ((c)&=0xDF)
69 unsigned char byte[5];
76 static const char memory_id[] = "IXCP-";
78 #ifndef SDCC_STACK_AUTO
79 static BOOL lower_case;
80 static pfn_outputchar output_char;
86 /* this one NEEDS to be in data */
87 static data value_t value;
93 /****************************************************************************/
95 #ifdef SDCC_STACK_AUTO
96 static void output_digit( unsigned char n, BOOL lower_case, pfn_outputchar output_char, void* p )
98 static void output_digit( unsigned char n )
101 output_char( n <= 9 ? '0'+n :
102 (lower_case ? n+(char)('a'-10) : n+(char)('A'-10)), p );
105 /*--------------------------------------------------------------------------*/
107 #ifdef SDCC_STACK_AUTO
108 #define OUTPUT_2DIGITS( B ) output_2digits( B, lower_case, output_char, p )
109 static void output_2digits( unsigned char b, BOOL lower_case, pfn_outputchar output_char, void* p )
111 output_digit( b>>4, lower_case, output_char, p );
112 output_digit( b&0x0F, lower_case, output_char, p );
115 #define OUTPUT_2DIGITS( B ) output_2digits( B )
116 static void output_2digits( unsigned char b )
118 output_digit( b>>4 );
119 output_digit( b&0x0F );
123 /*--------------------------------------------------------------------------*/
125 #if defined ASM_ALLOWED
126 static void calculate_digit( unsigned char radix )
130 for( i = 32; i != 0; i-- )
150 if (radix <= value.byte[4] )
152 value.byte[4] -= radix;
157 #elif defined SDCC_STACK_AUTO
158 static void calculate_digit( value_t* value, unsigned char radix )
162 for( i = 32; i != 0; i-- )
164 value->byte[4] = (value->byte[4] << 1) | ((value->ul >> 31) & 0x01);
167 if (radix <= value->byte[4] )
169 value->byte[4] -= radix;
175 static void calculate_digit( unsigned char radix )
179 for( i = 32; i != 0; i-- )
181 value.byte[4] = (value.byte[4] << 1) | ((value.ul >> 31) & 0x01);
184 if (radix <= value.byte[4] )
186 value.byte[4] -= radix;
195 /* This is a very inefficient but direct approach, since we have no math
196 library yet (e.g. log()).
197 It does most of the modifiers, but has some restrictions. E.g. the
198 abs(float) shouldn't be bigger than an unsigned long (that's
199 about 4294967295), but still makes it usefull for most real-life
203 #define DEFAULT_FLOAT_PRECISION 6
205 #ifdef SDCC_STACK_AUTO
206 #define OUTPUT_FLOAT(F, W, D, L, Z, S, P) output_float(F, W, D, L, Z, S, P, output_char, p)
207 static int output_float (float f, unsigned char reqWidth,
208 signed char reqDecimals,
209 BOOL left, BOOL zero, BOOL sign, BOOL space,
210 pfn_outputchar output_char, void* p)
212 #define OUTPUT_FLOAT(F, W, D, L, Z, S, P) output_float(F, W, D, L, Z, S, P)
213 static int output_float (float f, unsigned char reqWidth,
214 signed char reqDecimals,
215 BOOL left, BOOL zero, BOOL sign, BOOL space)
219 unsigned long integerPart;
223 unsigned char minWidth, i;
224 int charsOutputted=0;
233 // this part is from Frank van der Hulst
236 for (exp = 0; f >= 10.0; exp++) f /=10.0;
237 for ( ; f < 1.0; exp--) f *=10.0;
240 output_char ('-', p);
244 output_char ('+', p);
248 charsOutputted += OUTPUT_FLOAT(f, 0, reqDecimals, 0, 0, 0, 0);
249 output_char ('e', p);
252 output_char ('-', p);
256 output_char ('0'+exp/10, p);
257 output_char ('0'+exp%10, p);
259 return charsOutputted;
264 decimalPart=f-integerPart;
266 // fill the buffer with the integerPart (in reversed order!)
267 while (integerPart) {
268 fpBuffer[fpBI++]='0' + integerPart%10;
272 // we need at least a 0
273 fpBuffer[fpBI++]='0';
276 // display some decimals as default
278 reqDecimals=DEFAULT_FLOAT_PRECISION;
280 // fill buffer with the decimalPart (in normal order)
282 if (i=reqDecimals /* that's an assignment */) {
285 // truncate the float
286 integerPart=decimalPart;
287 fpBuffer[fpBD++]='0' + integerPart;
288 decimalPart-=integerPart;
292 minWidth=fpBI; // we need at least these
293 minWidth+=reqDecimals?reqDecimals+1:0; // maybe these
294 if (negative || sign || space)
295 minWidth++; // and maybe even this :)
297 if (!left && reqWidth>i) {
314 while (reqWidth-->minWidth)
320 while (reqWidth-->minWidth)
359 // output the integer part
362 output_char (fpBuffer[i], p);
366 // ouput the decimal part
368 output_char ('.', p);
371 while (reqDecimals--)
373 output_char (fpBuffer[i++], p);
378 if (left && reqWidth>minWidth) {
379 while (reqWidth-->minWidth)
385 return charsOutputted;
389 int _print_format (pfn_outputchar pfn, void* pvoid, const char *format, va_list ap)
395 BOOL signed_argument;
399 #ifdef SDCC_STACK_AUTO
410 signed char decimals;
411 unsigned char length;
414 #ifdef SDCC_STACK_AUTO
415 #define output_char pfn
422 // reset output chars
459 width = 10*width + (c - '0');
461 /* first character of width is a zero */
465 decimals = 10*decimals + (c-'0');
467 goto get_conversion_spec;
471 if (decimals=-1) decimals=0;
473 ; // duplicate, ignore
474 goto get_conversion_spec;
477 lower_case = islower(c);
487 goto get_conversion_spec;
490 goto get_conversion_spec;
493 goto get_conversion_spec;
496 goto get_conversion_spec;
499 goto get_conversion_spec;
502 output_char( va_arg(ap,int), p );
507 PTR = va_arg(ap,ptr_t);
512 length=NULL_STRING_LENGTH;
514 length = strlen(PTR);
517 length = strlen(PTR);
519 if ( ( !left_justify ) && (length < width) )
522 while( width-- != 0 )
524 output_char( ' ', p );
531 output_char( *PTR++, p );
535 if ( left_justify && (length < width))
538 while( width-- != 0 )
540 output_char( ' ', p );
547 PTR = va_arg(ap,ptr_t);
550 output_char(memory_id[(value.byte[3] > 3) ? 4 : value.byte[3]], p );
554 OUTPUT_2DIGITS( value.byte[2] );
555 OUTPUT_2DIGITS( value.byte[1] );
556 OUTPUT_2DIGITS( value.byte[0] );
557 charsOutputted += 10;
559 output_char( memory_id[(value.byte[2] > 3) ? 4 : value.byte[2]], p );
563 if ((value.byte[2] != 0x00 /* DSEG */) &&
564 (value.byte[2] != 0x03 /* SSEG */))
566 OUTPUT_2DIGITS( value.byte[1] );
569 OUTPUT_2DIGITS( value.byte[0] );
597 // nothing special, just output the character
603 if (float_argument) {
604 value.f=va_arg(ap,float);
618 // ignore b and l conversion spec for now
619 charsOutputted += OUTPUT_FLOAT(value.f, width, decimals, left_justify,
620 zero_padding, prefix_sign, prefix_space);
622 } else if (radix != 0)
624 // Apperently we have to output an integral type
625 // with radix "radix"
627 unsigned char store[6];
628 unsigned char _AUTOMEM *pstore = &store[5];
631 // store value in byte[0] (LSB) ... byte[3] (MSB)
634 value.l = va_arg(ap,char);
635 if (!signed_argument)
640 else if (long_argument)
642 value.l = va_arg(ap,long);
646 value.l = va_arg(ap,int);
647 if (!signed_argument)
653 if ( signed_argument )
666 #if defined SDCC_STACK_AUTO
667 calculate_digit(&value, radix);
669 calculate_digit(radix);
671 #if defined ASM_ALLOWED
675 mov a,_value+4 ; a = <msd>
677 orl b,a ; b = <msd><lsd>
681 mov a,_value+4 ; a = <lsd>
688 *pstore = (value.byte[4] << 4) | (value.byte[4] >> 4) | *pstore;
693 *pstore = value.byte[4];
702 // default width. We set it to 1 to output
703 // at least one character in case the value itself
704 // is zero (i.e. length==0)
708 /* prepend spaces if needed */
709 if (!zero_padding && !left_justify)
711 while ( width > (unsigned char) (length+1) )
713 output_char( ' ', p );
719 if (signed_argument) // this now means the original value was negative
721 output_char( '-', p );
723 // adjust width to compensate for this character
726 else if (length != 0)
731 output_char( '+', p );
733 // adjust width to compensate for this character
736 else if (prefix_space)
738 output_char( ' ', p );
740 // adjust width to compensate for this character
745 /* prepend zeroes/spaces if needed */
747 while ( width-- > length )
749 output_char( zero_padding ? '0' : ' ', p );
754 /* spaces are appended after the digits */
761 /* output the digits */
768 pop acc ; a = <msd><lsd>
769 nop ; to disable the "optimizer"
772 anl a,#0x0F ; a = <msd>
776 anl a,#0x0F ; a = <lsd>
784 value.byte[4] = *pstore >> 4;
788 value.byte[4] = *pstore & 0x0F;
791 #ifdef SDCC_STACK_AUTO
792 output_digit( value.byte[4], lower_case, output_char, p );
794 output_digit( value.byte[4] );
808 // nothing special, just output the character
814 return charsOutputted;
817 /****************************************************************************/