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. */
50 #define NULL_STRING "<NULL>"
51 #define NULL_STRING_LENGTH 6
54 /****************************************************************************/
56 //typedef char * ptr_t;
63 //#define toupper(c) ((c)&=~0x20)
64 #define toupper(c) ((c)&=0xDF)
68 unsigned char byte[5];
75 static const char memory_id[] = "IXCP-";
77 #ifndef SDCC_STACK_AUTO
78 static BOOL lower_case;
79 static pfn_outputchar output_char;
85 /* this one NEEDS to be in data */
86 static data value_t value;
92 /****************************************************************************/
94 #ifdef SDCC_STACK_AUTO
95 static void output_digit( unsigned char n, BOOL lower_case, pfn_outputchar output_char, void* p )
97 static void output_digit( unsigned char n )
100 output_char( n <= 9 ? '0'+n :
101 (lower_case ? n+(char)('a'-10) : n+(char)('A'-10)), p );
104 /*--------------------------------------------------------------------------*/
106 #ifdef SDCC_STACK_AUTO
107 #define OUTPUT_2DIGITS( B ) output_2digits( B, lower_case, output_char, p )
108 static void output_2digits( unsigned char b, BOOL lower_case, pfn_outputchar output_char, void* p )
110 output_digit( b>>4, lower_case, output_char, p );
111 output_digit( b&0x0F, lower_case, output_char, p );
114 #define OUTPUT_2DIGITS( B ) output_2digits( B )
115 static void output_2digits( unsigned char b )
117 output_digit( b>>4 );
118 output_digit( b&0x0F );
122 /*--------------------------------------------------------------------------*/
124 #if defined ASM_ALLOWED
125 static void calculate_digit( unsigned char radix )
129 for( i = 32; i != 0; i-- )
149 if (radix <= value.byte[4] )
151 value.byte[4] -= radix;
156 #elif defined SDCC_STACK_AUTO
157 static void calculate_digit( value_t* value, unsigned char radix )
161 for( i = 32; i != 0; i-- )
163 value->byte[4] = (value->byte[4] << 1) | (value->ul >> 31) & 0x01;
166 if (radix <= value->byte[4] )
168 value->byte[4] -= radix;
174 static void calculate_digit( unsigned char radix )
178 for( i = 32; i != 0; i-- )
180 value.byte[4] = (value.byte[4] << 1) | (value.ul >> 31) & 0x01;
183 if (radix <= value.byte[4] )
185 value.byte[4] -= radix;
194 /* This is a very inefficient but direct approach, since we have no math
195 library yet (e.g. log()).
196 It does most of the modifiers, but has some restrictions. E.g. the
197 abs(float) shouldn't be bigger than an unsigned long (that's
198 about 4294967295), but still makes it usefull for most real-life
202 #define DEFAULT_FLOAT_PRECISION 6
204 #ifdef SDCC_STACK_AUTO
205 #define OUTPUT_FLOAT(F, W, D, L, Z, S, P) output_float(F, W, D, L, Z, S, P, output_char, p)
206 static int output_float (float f, unsigned char reqWidth,
207 signed char reqDecimals,
208 BOOL left, BOOL zero, BOOL sign, BOOL space,
209 pfn_outputchar output_char, void* p)
211 #define OUTPUT_FLOAT(F, W, D, L, Z, S, P) output_float(F, W, D, L, Z, S, P)
212 static int output_float (float f, unsigned char reqWidth,
213 signed char reqDecimals,
214 BOOL left, BOOL zero, BOOL sign, BOOL space)
218 unsigned long integerPart;
222 unsigned char minWidth, i;
223 int charsOutputted=0;
232 // this part is from Frank van der Hulst
235 for (exp = 0; f >= 10.0; exp++) f /=10.0;
236 for ( ; f < 1.0; exp--) f *=10.0;
239 output_char ('-', p);
243 output_char ('+', p);
247 charsOutputted += OUTPUT_FLOAT(f, 0, reqDecimals, 0, 0, 0, 0);
248 output_char ('e', p);
251 output_char ('-', p);
255 output_char ('0'+exp/10, p);
256 output_char ('0'+exp%10, p);
258 return charsOutputted;
263 decimalPart=f-integerPart;
265 // fill the buffer with the integerPart (in reversed order!)
266 while (integerPart) {
267 fpBuffer[fpBI++]='0' + integerPart%10;
271 // we need at least a 0
272 fpBuffer[fpBI++]='0';
275 // display some decimals as default
277 reqDecimals=DEFAULT_FLOAT_PRECISION;
279 // fill buffer with the decimalPart (in normal order)
281 if (i=reqDecimals /* that's an assignment */) {
284 // truncate the float
285 integerPart=decimalPart;
286 fpBuffer[fpBD++]='0' + integerPart;
287 decimalPart-=integerPart;
291 minWidth=fpBI; // we need at least these
292 minWidth+=reqDecimals?reqDecimals+1:0; // maybe these
293 if (negative || sign || space)
294 minWidth++; // and maybe even this :)
296 if (!left && reqWidth>i) {
313 while (reqWidth-->minWidth)
319 while (reqWidth-->minWidth)
358 // output the integer part
361 output_char (fpBuffer[i], p);
365 // ouput the decimal part
367 output_char ('.', p);
370 while (reqDecimals--)
372 output_char (fpBuffer[i++], p);
377 if (left && reqWidth>minWidth) {
378 while (reqWidth-->minWidth)
384 return charsOutputted;
388 int _print_format (pfn_outputchar pfn, void* pvoid, const char *format, va_list ap)
394 BOOL signed_argument;
398 #ifdef SDCC_STACK_AUTO
409 signed char decimals;
410 unsigned char length;
413 #ifdef SDCC_STACK_AUTO
414 #define output_char pfn
421 // reset output chars
458 width = 10*width + (c - '0');
460 /* first character of width is a zero */
464 decimals = 10*decimals + (c-'0');
466 goto get_conversion_spec;
470 if (decimals=-1) decimals=0;
472 ; // duplicate, ignore
473 goto get_conversion_spec;
476 lower_case = islower(c);
486 goto get_conversion_spec;
489 goto get_conversion_spec;
492 goto get_conversion_spec;
495 goto get_conversion_spec;
498 goto get_conversion_spec;
501 output_char( va_arg(ap,int), p );
506 PTR = va_arg(ap,ptr_t);
511 length=NULL_STRING_LENGTH;
513 length = strlen(PTR);
516 length = strlen(PTR);
518 if ( ( !left_justify ) && (length < width) )
521 while( width-- != 0 )
523 output_char( ' ', p );
530 output_char( *PTR++, p );
534 if ( left_justify && (length < width))
537 while( width-- != 0 )
539 output_char( ' ', p );
546 PTR = va_arg(ap,ptr_t);
549 output_char(memory_id[(value.byte[3] > 3) ? 4 : value.byte[3]], p );
553 OUTPUT_2DIGITS( value.byte[2] );
554 OUTPUT_2DIGITS( value.byte[1] );
555 OUTPUT_2DIGITS( value.byte[0] );
556 charsOutputted += 10;
558 output_char( memory_id[(value.byte[2] > 3) ? 4 : value.byte[2]], p );
562 if ((value.byte[2] != 0x00 /* DSEG */) &&
563 (value.byte[2] != 0x03 /* SSEG */))
565 OUTPUT_2DIGITS( value.byte[1] );
568 OUTPUT_2DIGITS( value.byte[0] );
596 // nothing special, just output the character
602 if (float_argument) {
603 value.f=va_arg(ap,float);
617 // ignore b and l conversion spec for now
618 charsOutputted += OUTPUT_FLOAT(value.f, width, decimals, left_justify,
619 zero_padding, prefix_sign, prefix_space);
621 } else if (radix != 0)
623 // Apperently we have to output an integral type
624 // with radix "radix"
625 unsigned char store = 0;
627 // store value in byte[0] (LSB) ... byte[3] (MSB)
630 value.l = va_arg(ap,char);
631 if (!signed_argument)
633 value.byte[1] = 0x00;
634 value.byte[2] = 0x00;
635 value.byte[3] = 0x00;
638 else if (long_argument)
640 value.l = va_arg(ap,long);
644 value.l = va_arg(ap,int);
645 if (!signed_argument)
647 value.byte[2] = 0x00;
648 value.byte[3] = 0x00;
652 if ( signed_argument )
665 #if defined SDCC_STACK_AUTO
666 calculate_digit(&value, radix);
668 calculate_digit(radix);
670 #if defined ASM_ALLOWED
674 mov a,_value+4 ; a = <msd>
676 orl b,a ; b = <msd><lsd>
680 mov a,_value+4 ; a = <lsd>
687 store = (value.byte[4] << 4) | (value.byte[4] >> 4) | store;
691 store = value.byte[4];
696 } while( (value.byte[0] != 0) || (value.byte[1] != 0) ||
697 (value.byte[2] != 0) || (value.byte[3] != 0) );
701 // default width. We set it to 1 to output
702 // at least one character in case the value itself
703 // is zero (i.e. length==0)
707 /* prepend spaces if needed */
708 if (!zero_padding && !left_justify)
710 while ( width > (unsigned char) (length+1) )
712 output_char( ' ', p );
718 if (signed_argument) // this now means the original value was negative
720 output_char( '-', p );
722 // adjust width to compensate for this character
725 else if (length != 0)
730 output_char( '+', p );
732 // adjust width to compensate for this character
735 else if (prefix_space)
737 output_char( ' ', p );
739 // adjust width to compensate for this character
744 /* prepend zeroes/spaces if needed */
746 while ( width-- > length )
748 output_char( zero_padding ? '0' : ' ', p );
753 /* spaces are appended after the digits */
760 /* output the digits */
767 pop acc ; a = <msd><lsd>
768 nop ; to disable the "optimizer"
771 anl a,#0x0F ; a = <msd>
775 anl a,#0x0F ; a = <lsd>
782 value.byte[4] = store >> 4;
786 value.byte[4] = store & 0x0F;
789 #ifdef SDCC_STACK_AUTO
790 output_digit( value.byte[4], lower_case, output_char, p );
792 output_digit( value.byte[4] );
806 // nothing special, just output the character
812 return charsOutputted;
815 /****************************************************************************/