vprintf.c - formatted output conversion
Written By - Martijn van Balen aed@iae.nl (1999)
+ Added %f By - johan.knol@iduna.nl (2000)
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
You are forbidden to forbid anyone else to use, share and improve
what you give them. Help stamp out software-hoarding!
-------------------------------------------------------------------------*/
+#ifdef __ds390
+#define USE_FLOATS 1
+#endif
#include <stdarg.h>
#include <string.h>
#include <ctype.h>
#include <stdio.h>
-extern void putchar(const char);
-
#define PTR value.p
+#ifdef SDCC_ds390
+#define NULL_STRING "<NULL>"
+#define NULL_STRING_LENGTH 6
+#endif
+
+/* XSPEC is defined in stdio.h and used here to place
+ auto variables in XSEG */
+
/****************************************************************************/
typedef char _generic *ptr_t;
unsigned char byte[5];
long l;
unsigned long ul;
+ float f;
char _generic *p;
} value_t;
static code char memory_id[] = "IXCP-";
-ptr_t output_ptr;
-bit output_to_string;
-bit lower_case;
-bit lsd;
+static ptr_t output_ptr;
+static bit output_to_string;
+static bit lower_case;
+static bit lsd;
+
+/* this one NEEDS to be in data */
+static data value_t value;
-data value_t value;
+static unsigned char radix;
- unsigned short radix;
+// jwk: TODO: this makes the whole dammed thing nonreentrent
+static int charsOutputted;
/****************************************************************************/
{
putchar( c );
}
+ charsOutputted++;
}
/*--------------------------------------------------------------------------*/
static void output_digit( unsigned char n ) reentrant
{
- output_char( n <= 9 ? '0'+n : (lower_case ? n+(char)('a'-10) : n+(char)('A'-10)) );
+ output_char( n <= 9 ? '0'+n :
+ (lower_case ? n+(char)('a'-10) : n+(char)('A'-10)) );
}
/*--------------------------------------------------------------------------*/
{
_asm
clr c
- mov a,_value+0
+ mov a,_value+0
rlc a
mov _value+0,a
mov a,_value+1
}
}
+#if USE_FLOATS
+
+/* This is a very inefficient but direct approach, since we have no math
+ library yet (e.g. log()).
+ It does most of the modifiers, but has some restrictions. E.g. the
+ abs(float) shouldn't be bigger than an unsigned long (that's
+ about 4294967295), but still makes it usefull for most real-life
+ applications.
+*/
+
+#define DEFAULT_FLOAT_PRECISION 6
+
+static void output_float (float f, unsigned char reqWidth,
+ signed char reqDecimals,
+ bit left, bit zero, bit sign, bit space)
+{
+ char negative=0;
+ long integerPart;
+ float decimalPart;
+ char fpBuffer[128];
+ char fpBI=0, fpBD;
+ unsigned char minWidth, i;
+
+ // save the sign
+ if (f<0) {
+ negative=1;
+ f=-f;
+ }
+
+ // split the float
+ integerPart=f;
+ decimalPart=f-integerPart;
+
+ // fill the buffer with the integerPart (in reversed order!)
+ while (integerPart) {
+ fpBuffer[fpBI++]='0' + integerPart%10;
+ integerPart /= 10;
+ }
+ if (!fpBI) {
+ // we need at least a 0
+ fpBuffer[fpBI++]='0';
+ }
+
+ // display some decimals as default
+ if (reqDecimals==-1)
+ reqDecimals=DEFAULT_FLOAT_PRECISION;
+
+ // fill buffer with the decimalPart (in normal order)
+ fpBD=fpBI;
+ if (i=reqDecimals /* that's an assignment */) {
+ do {
+ decimalPart *= 10.0;
+ // truncate the float
+ integerPart=decimalPart;
+ fpBuffer[fpBD++]='0' + integerPart;
+ decimalPart-=integerPart;
+ } while (--i);
+ }
+
+ minWidth=fpBI; // we need at least these
+ minWidth+=reqDecimals?reqDecimals+1:0; // maybe these
+ if (negative || sign || space)
+ minWidth++; // and maybe even this :)
+
+ if (!left && reqWidth>i) {
+ if (zero) {
+ if (negative) output_char('-');
+ else if (sign) output_char('+');
+ else if (space) output_char(' ');
+ while (reqWidth-->minWidth)
+ output_char ('0');
+ } else {
+ while (reqWidth-->minWidth)
+ output_char (' ');
+ if (negative) output_char('-');
+ else if (sign) output_char('+');
+ else if (space) output_char (' ');
+ }
+ } else {
+ if (negative) output_char('-');
+ else if (sign) output_char('+');
+ else if (space) output_char(' ');
+ }
+
+ // output the integer part
+ i=fpBI-1;
+ do {
+ output_char (fpBuffer[i]);
+ } while (i--);
+
+ // ouput the decimal part
+ if (reqDecimals) {
+ output_char ('.');
+ i=fpBI;
+ while (reqDecimals--)
+ output_char (fpBuffer[i++]);
+ }
+
+ if (left && reqWidth>minWidth) {
+ while (reqWidth-->minWidth)
+ output_char(' ');
+ }
+}
+#endif
+
/*--------------------------------------------------------------------------*/
int vsprintf (const char *buf, const char *format, va_list ap)
bit signed_argument;
bit char_argument;
bit long_argument;
+ bit float_argument;
unsigned char width;
+ signed char decimals;
unsigned char length;
char c;
+ // reset output chars
+ charsOutputted=0;
+
output_ptr = buf;
if ( !buf )
{
output_to_string = 1;
}
+#ifdef SDCC_ds390
+ if (format==0) {
+ format=NULL_STRING;
+ }
+#endif
+
while( c=*format++ )
{
- if ( c == '%' )
+ if ( c=='%' )
{
left_justify = 0;
zero_padding = 0;
radix = 0;
char_argument = 0;
long_argument = 0;
+ float_argument = 0;
width = 0;
+ decimals = -1;
get_conversion_spec:
c = *format++;
- if (isdigit(c))
- {
- width = 10*width + (c - '0');
+ if (c=='%') {
+ output_char(c);
+ continue;
+ }
- if (width == 0)
- {
- /* first character of width is a zero */
- zero_padding = 1;
+ if (isdigit(c)) {
+ if (decimals==-1) {
+ width = 10*width + (c - '0');
+ if (width == 0) {
+ /* first character of width is a zero */
+ zero_padding = 1;
+ }
+ } else {
+ decimals = 10*decimals + (c-'0');
}
- goto get_conversion_spec;
+ goto get_conversion_spec;
+ }
+
+ if (c=='.') {
+ if (decimals=-1) decimals=0;
+ else
+ ; // duplicate, ignore
+ goto get_conversion_spec;
}
lower_case = islower(c);
goto get_conversion_spec;
case 'C':
- output_char( va_arg(ap,unsigned char) );
+ output_char( va_arg(ap,int) );
break;
case 'S':
PTR = va_arg(ap,ptr_t);
+#ifdef SDCC_ds390
+ if (PTR==0) {
+ PTR=NULL_STRING;
+ length=NULL_STRING_LENGTH;
+ } else {
+ length = strlen(PTR);
+ }
+#else
length = strlen(PTR);
+#endif
if ( ( !left_justify ) && (length < width) )
{
width -= length;
case 'P':
PTR = va_arg(ap,ptr_t);
-#ifdef SDCC_MODEL_FLAT24
+#ifdef SDCC_ds390
output_char(memory_id[(value.byte[3] > 3) ? 4 : value.byte[3]] );
output_char(':');
output_char('0');
output_char(':');
output_char('0');
output_char('x');
- if ((value.byte[2] != 0x00 /* DSEG */) && (value.byte[2] != 0x03 /* SSEG */))
+ if ((value.byte[2] != 0x00 /* DSEG */) &&
+ (value.byte[2] != 0x03 /* SSEG */))
output_2digits( value.byte[1] );
output_2digits( value.byte[0] );
#endif
radix = 16;
break;
+ case 'F':
+ float_argument=1;
+ break;
+
default:
// nothing special, just output the character
output_char( c );
break;
}
- if (radix != 0)
+ if (float_argument) {
+ value.f=va_arg(ap,float);
+#if !USE_FLOATS
+ PTR="<NO FLOAT>";
+ while (c=*PTR++)
+ output_char (c);
+ // treat as long hex
+ //radix=16;
+ //long_argument=1;
+ //zero_padding=1;
+ //width=8;
+#else
+ // ignore b and l conversion spec for now
+ output_float(value.f, width, decimals, left_justify, zero_padding,
+ prefix_sign, prefix_space);
+#endif
+ } else if (radix != 0)
{
// Apperently we have to output an integral type
// with radix "radix"
{
value.l = va_arg(ap,long);
}
- else
+ else // must be int
{
value.l = va_arg(ap,int);
if (!signed_argument)
// Copy \0 to the end of buf
// Modified by JB 17/12/99
- if (output_to_string) output_char(0);
+ if (output_to_string) {
+ output_char(0);
+ return charsOutputted-1;
+ } else {
+ return charsOutputted;
+ }
}
/*--------------------------------------------------------------------------*/