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. */
37 #if defined(SDCC_mcs51)
38 #if defined(SDCC_STACK_AUTO)
39 #if defined(SDCC_USE_XSTACK)
42 //strange enough "idata" doesn't work
45 #elif defined(SDCC_MODEL_LARGE)
67 #define NULL_STRING "<NULL>"
68 #define NULL_STRING_LENGTH 6
71 /****************************************************************************/
73 //typedef char * ptr_t;
80 //#define toupper(c) ((c)&=~0x20)
81 #define toupper(c) ((c)&=0xDF)
85 unsigned char byte[5];
92 static const char memory_id[] = "IXCP-";
94 #ifndef SDCC_STACK_AUTO
95 static BOOL lower_case;
96 static pfn_outputchar output_char;
102 /* this one NEEDS to be in data */
103 static data value_t value;
105 static value_t value;
109 /****************************************************************************/
111 #ifdef SDCC_STACK_AUTO
112 static void output_digit( unsigned char n, BOOL lower_case, pfn_outputchar output_char, void* p )
114 static void output_digit( unsigned char n )
117 output_char( n <= 9 ? '0'+n :
118 (lower_case ? n+(char)('a'-10) : n+(char)('A'-10)), p );
121 /*--------------------------------------------------------------------------*/
123 #ifdef SDCC_STACK_AUTO
124 #define OUTPUT_2DIGITS( B ) output_2digits( B, lower_case, output_char, p )
125 static void output_2digits( unsigned char b, BOOL lower_case, pfn_outputchar output_char, void* p )
127 output_digit( b>>4, lower_case, output_char, p );
128 output_digit( b&0x0F, lower_case, output_char, p );
131 #define OUTPUT_2DIGITS( B ) output_2digits( B )
132 static void output_2digits( unsigned char b )
134 output_digit( b>>4 );
135 output_digit( b&0x0F );
139 /*--------------------------------------------------------------------------*/
141 #if defined ASM_ALLOWED
142 static void calculate_digit( unsigned char radix )
146 value.byte[4] = value.ul & 0x07;
149 else if (radix == 16)
151 value.byte[4] = value.ul & 0x0F;
158 for( i = 32; i != 0; i-- )
178 if (radix <= value.byte[4] )
180 value.byte[4] -= radix;
186 #elif defined SDCC_STACK_AUTO
187 static void calculate_digit( value_t* value, unsigned char radix )
191 value->byte[4] = value->ul & 0x07;
194 else if (radix == 16)
196 value->byte[4] = value->ul & 0x0F;
203 for( i = 32; i != 0; i-- )
205 value->byte[4] = (value->byte[4] << 1) | ((value->ul >> 31) & 0x01);
208 if (radix <= value->byte[4] )
210 value->byte[4] -= radix;
217 static void calculate_digit( unsigned char radix )
221 value.byte[4] = value.ul & 0x07;
224 else if (radix == 16)
226 value.byte[4] = value.ul & 0x0F;
233 for( i = 32; i != 0; i-- )
235 value.byte[4] = (value.byte[4] << 1) | ((value.ul >> 31) & 0x01);
238 if (radix <= value.byte[4] )
240 value.byte[4] -= radix;
250 /* This is a very inefficient but direct approach, since we have no math
251 library yet (e.g. log()).
252 It does most of the modifiers, but has some restrictions. E.g. the
253 abs(float) shouldn't be bigger than an unsigned long (that's
254 about 4294967295), but still makes it usefull for most real-life
258 #define DEFAULT_FLOAT_PRECISION 6
260 #ifdef SDCC_STACK_AUTO
261 #define OUTPUT_FLOAT(F, W, D, L, Z, S, P) output_float(F, W, D, L, Z, S, P, output_char, p)
262 static int output_float (float f, unsigned char reqWidth,
263 signed char reqDecimals,
264 BOOL left, BOOL zero, BOOL sign, BOOL space,
265 pfn_outputchar output_char, void* p)
267 #define OUTPUT_FLOAT(F, W, D, L, Z, S, P) output_float(F, W, D, L, Z, S, P)
268 static int output_float (float f, unsigned char reqWidth,
269 signed char reqDecimals,
270 BOOL left, BOOL zero, BOOL sign, BOOL space)
274 unsigned long integerPart;
278 unsigned char minWidth, i;
279 int charsOutputted=0;
288 // this part is from Frank van der Hulst
291 for (exp = 0; f >= 10.0; exp++) f /=10.0;
292 for ( ; f < 1.0; exp--) f *=10.0;
295 output_char ('-', p);
299 output_char ('+', p);
303 charsOutputted += OUTPUT_FLOAT(f, 0, reqDecimals, 0, 0, 0, 0);
304 output_char ('e', p);
307 output_char ('-', p);
311 output_char ('0'+exp/10, p);
312 output_char ('0'+exp%10, p);
314 return charsOutputted;
319 decimalPart=f-integerPart;
321 // fill the buffer with the integerPart (in reversed order!)
322 while (integerPart) {
323 fpBuffer[fpBI++]='0' + integerPart%10;
327 // we need at least a 0
328 fpBuffer[fpBI++]='0';
331 // display some decimals as default
333 reqDecimals=DEFAULT_FLOAT_PRECISION;
335 // fill buffer with the decimalPart (in normal order)
337 if (i=reqDecimals /* that's an assignment */) {
340 // truncate the float
341 integerPart=decimalPart;
342 fpBuffer[fpBD++]='0' + integerPart;
343 decimalPart-=integerPart;
347 minWidth=fpBI; // we need at least these
348 minWidth+=reqDecimals?reqDecimals+1:0; // maybe these
349 if (negative || sign || space)
350 minWidth++; // and maybe even this :)
352 if (!left && reqWidth>i) {
369 while (reqWidth-->minWidth)
375 while (reqWidth-->minWidth)
414 // output the integer part
417 output_char (fpBuffer[i], p);
421 // ouput the decimal part
423 output_char ('.', p);
426 while (reqDecimals--)
428 output_char (fpBuffer[i++], p);
433 if (left && reqWidth>minWidth) {
434 while (reqWidth-->minWidth)
440 return charsOutputted;
444 int _print_format (pfn_outputchar pfn, void* pvoid, const char *format, va_list ap)
450 BOOL signed_argument;
454 #ifdef SDCC_STACK_AUTO
465 signed char decimals;
466 unsigned char length;
469 #ifdef SDCC_STACK_AUTO
470 #define output_char pfn
477 // reset output chars
514 width = 10*width + (c - '0');
516 /* first character of width is a zero */
520 decimals = 10*decimals + (c-'0');
522 goto get_conversion_spec;
526 if (decimals=-1) decimals=0;
528 ; // duplicate, ignore
529 goto get_conversion_spec;
532 lower_case = islower(c);
542 goto get_conversion_spec;
545 goto get_conversion_spec;
548 goto get_conversion_spec;
551 goto get_conversion_spec;
554 goto get_conversion_spec;
557 output_char( va_arg(ap,int), p );
562 PTR = va_arg(ap,ptr_t);
567 length=NULL_STRING_LENGTH;
569 length = strlen(PTR);
572 length = strlen(PTR);
574 if ( ( !left_justify ) && (length < width) )
577 while( width-- != 0 )
579 output_char( ' ', p );
586 output_char( *PTR++, p );
590 if ( left_justify && (length < width))
593 while( width-- != 0 )
595 output_char( ' ', p );
602 PTR = va_arg(ap,ptr_t);
605 output_char(memory_id[(value.byte[3] > 3) ? 4 : value.byte[3]], p );
609 OUTPUT_2DIGITS( value.byte[2] );
610 OUTPUT_2DIGITS( value.byte[1] );
611 OUTPUT_2DIGITS( value.byte[0] );
612 charsOutputted += 10;
614 output_char( memory_id[(value.byte[2] > 3) ? 4 : value.byte[2]], p );
618 if ((value.byte[2] != 0x00 /* DSEG */) &&
619 (value.byte[2] != 0x03 /* SSEG */))
621 OUTPUT_2DIGITS( value.byte[1] );
624 OUTPUT_2DIGITS( value.byte[0] );
652 // nothing special, just output the character
658 if (float_argument) {
659 value.f=va_arg(ap,float);
673 // ignore b and l conversion spec for now
674 charsOutputted += OUTPUT_FLOAT(value.f, width, decimals, left_justify,
675 zero_padding, prefix_sign, prefix_space);
677 } else if (radix != 0)
679 // Apperently we have to output an integral type
680 // with radix "radix"
682 unsigned char store[6];
683 unsigned char NEAR *pstore = &store[5];
686 // store value in byte[0] (LSB) ... byte[3] (MSB)
689 value.l = va_arg(ap,char);
690 if (!signed_argument)
695 else if (long_argument)
697 value.l = va_arg(ap,long);
701 value.l = va_arg(ap,int);
702 if (!signed_argument)
708 if ( signed_argument )
721 #if defined SDCC_STACK_AUTO
722 calculate_digit(&value, radix);
724 calculate_digit(radix);
726 #if defined ASM_ALLOWED
730 mov a,_value+4 ; a = <msd>
732 orl b,a ; b = <msd><lsd>
736 mov a,_value+4 ; a = <lsd>
743 *pstore = (value.byte[4] << 4) | (value.byte[4] >> 4) | *pstore;
748 *pstore = value.byte[4];
753 } while( (value.byte[0] != 0) || (value.byte[1] != 0) ||
754 (value.byte[2] != 0) || (value.byte[3] != 0) );
758 // default width. We set it to 1 to output
759 // at least one character in case the value itself
760 // is zero (i.e. length==0)
764 /* prepend spaces if needed */
765 if (!zero_padding && !left_justify)
767 while ( width > (unsigned char) (length+1) )
769 output_char( ' ', p );
775 if (signed_argument) // this now means the original value was negative
777 output_char( '-', p );
779 // adjust width to compensate for this character
782 else if (length != 0)
787 output_char( '+', p );
789 // adjust width to compensate for this character
792 else if (prefix_space)
794 output_char( ' ', p );
796 // adjust width to compensate for this character
801 /* prepend zeroes/spaces if needed */
803 while ( width-- > length )
805 output_char( zero_padding ? '0' : ' ', p );
810 /* spaces are appended after the digits */
817 /* output the digits */
824 pop acc ; a = <msd><lsd>
825 nop ; to disable the "optimizer"
828 anl a,#0x0F ; a = <msd>
832 anl a,#0x0F ; a = <lsd>
840 value.byte[4] = *pstore >> 4;
844 value.byte[4] = *pstore & 0x0F;
847 #ifdef SDCC_STACK_AUTO
848 output_digit( value.byte[4], lower_case, output_char, p );
850 output_digit( value.byte[4] );
864 // nothing special, just output the character
870 return charsOutputted;
873 /****************************************************************************/