#if !defined(__AVR_TINY__)
#include <avr/pgmspace.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "sectionname.h"
#include "stdio_private.h"
#include "ftoa_engine.h"
#include "ntz.h"
#include "xtoa_fast.h"
#ifndef PRINTF_LEVEL
# define PRINTF_LEVEL PRINTF_STD
#endif
#if PRINTF_LEVEL == PRINTF_MIN || PRINTF_LEVEL == PRINTF_STD \
|| PRINTF_LEVEL == PRINTF_FLT
#else
# error "Not a known printf level."
#endif
#ifndef __AVR_HAVE_LPMX__
# if defined(__AVR_ENHANCED__) && __AVR_ENHANCED__
# define __AVR_HAVE_LPMX__ 1
# endif
#endif
#if defined(__AVR_HAVE_LPMX__) && __AVR_HAVE_LPMX__
# define GETBYTE(flag, mask, pnt) ({ \
unsigned char __c; \
asm ( \
"sbrc %2,%3 \n\t" \
"lpm %0,Z+ \n\t" \
"sbrs %2,%3 \n\t" \
"ld %0,Z+ " \
: "=r" (__c), \
"+z" (pnt) \
: "r" (flag), \
"I" (ntz(mask)) \
); \
__c; \
})
#elif 1
# define GETBYTE(flag, mask, pnt) ({ \
unsigned char __c; \
asm ( \
"sbrc %2,%3 \n\t" \
"lpm \n\t" \
"sbrs %2,%3 \n\t" \
"ld r0,Z \n\t" \
"adiw r30,1 \n\t" \
"mov %0,r0 " \
: "=r" (__c), \
"+z" (pnt) \
: "r" (flag), \
"I" (ntz(mask)) \
: "r0" \
); \
__c; \
})
#else
# define GETBYTE(flag, mask, pnt) ({ \
unsigned char __c; \
__c = ((flag) & (mask)) \
? pgm_read_byte(pnt) : *pnt; \
pnt++; \
__c; \
})
#endif
#if PRINTF_LEVEL <= PRINTF_MIN
#define FL_PGMSTRING 0x01
#define FL_ALTHEX 0x04
#define FL_ALT 0x10
#define FL_ALTLWR 0x20
#define FL_NEGATIVE 0x40
#define FL_LONG 0x80
ATTRIBUTE_CLIB_SECTION
int
vfprintf (FILE * stream, const char *fmt, va_list ap)
{
unsigned char c;
unsigned char flags;
unsigned char buf[11];
stream->len = 0;
if ((stream->flags & __SWR) == 0)
return EOF;
for (;;) {
for (;;) {
c = GETBYTE (stream->flags, __SPGM, fmt);
if (!c) goto ret;
if (c == '%') {
c = GETBYTE (stream->flags, __SPGM, fmt);
if (c != '%') break;
}
putc (c, stream);
}
for (flags = 0;
!(flags & FL_LONG);
c = GETBYTE (stream->flags, __SPGM, fmt))
{
if (c && strchr_P (PSTR(" +-.0123456789h"), c))
continue;
if (c == '#') {
flags |= FL_ALT;
continue;
}
if (c == 'l') {
flags |= FL_LONG;
continue;
}
break;
}
if (c && strchr_P (PSTR("EFGefg"), c)) {
(void) va_arg (ap, double);
putc ('?', stream);
continue;
}
{
const char * pnt;
switch (c) {
case 'c':
putc (va_arg (ap, int), stream);
continue;
case 'S':
flags |= FL_PGMSTRING;
case 's':
pnt = va_arg (ap, char *);
while ( (c = GETBYTE (flags, FL_PGMSTRING, pnt)) != 0)
putc (c, stream);
continue;
}
}
if (c == 'd' || c == 'i') {
long x = (flags & FL_LONG) ? va_arg(ap,long) : va_arg(ap,int);
flags &= ~FL_ALT;
if (x < 0) {
x = -x;
flags |= FL_NEGATIVE;
}
c = __ultoa_invert (x, (char *)buf, 10) - (char *)buf;
} else {
int base;
switch (c) {
case 'u':
flags &= ~FL_ALT;
base = 10;
goto ultoa;
case 'o':
base = 8;
goto ultoa;
case 'p':
flags |= FL_ALT;
case 'x':
flags |= (FL_ALTHEX | FL_ALTLWR);
base = 16;
goto ultoa;
case 'X':
flags |= FL_ALTHEX;
base = 16 | XTOA_UPPER;
ultoa:
c = __ultoa_invert ((flags & FL_LONG)
? va_arg(ap, unsigned long)
: va_arg(ap, unsigned int),
(char *)buf, base) - (char *)buf;
break;
default:
goto ret;
}
}
if (flags & FL_NEGATIVE)
putc ('-', stream);
if ((flags & FL_ALT) && (buf[c-1] != '0')) {
putc ('0', stream);
if (flags & FL_ALTHEX)
#if FL_ALTLWR != 'x' - 'X'
# error
#endif
putc ('X' + (flags & FL_ALTLWR), stream);
}
do {
putc (buf[--c], stream);
} while (c);
}
ret:
return stream->len;
}
#else
#define FL_ZFILL 0x01
#define FL_PLUS 0x02
#define FL_SPACE 0x04
#define FL_LPAD 0x08
#define FL_ALT 0x10
#define FL_WIDTH 0x20
#define FL_PREC 0x40
#define FL_LONG 0x80
#define FL_PGMSTRING FL_LONG
#define FL_NEGATIVE FL_LONG
#define FL_ALTUPP FL_PLUS
#define FL_ALTHEX FL_SPACE
#define FL_FLTUPP FL_ALT
#define FL_FLTEXP FL_PREC
#define FL_FLTFIX FL_LONG
ATTRIBUTE_CLIB_SECTION
int vfprintf (FILE * stream, const char *fmt, va_list ap)
{
unsigned char c;
unsigned char flags;
unsigned char width;
unsigned char prec;
unsigned char buf[11];
stream->len = 0;
if ((stream->flags & __SWR) == 0)
return EOF;
for (;;) {
for (;;) {
c = GETBYTE (stream->flags, __SPGM, fmt);
if (!c) goto ret;
if (c == '%') {
c = GETBYTE (stream->flags, __SPGM, fmt);
if (c != '%') break;
}
putc (c, stream);
}
flags = 0;
width = 0;
prec = 0;
do {
if (flags < FL_WIDTH) {
switch (c) {
case '0':
flags |= FL_ZFILL;
continue;
case '+':
flags |= FL_PLUS;
case ' ':
flags |= FL_SPACE;
continue;
case '-':
flags |= FL_LPAD;
continue;
case '#':
flags |= FL_ALT;
continue;
}
}
if (flags < FL_LONG) {
if (c >= '0' && c <= '9') {
c -= '0';
if (flags & FL_PREC) {
prec = 10*prec + c;
continue;
}
width = 10*width + c;
flags |= FL_WIDTH;
continue;
}
if (c == '.') {
if (flags & FL_PREC)
goto ret;
flags |= FL_PREC;
continue;
}
if (c == 'l') {
flags |= FL_LONG;
continue;
}
if (c == 'h')
continue;
}
break;
} while ( (c = GETBYTE (stream->flags, __SPGM, fmt)) != 0);
#if 'F' != 'E'+1 || 'G' != 'F'+1 || 'f' != 'e'+1 || 'g' != 'f'+1
# error
#endif
#if PRINTF_LEVEL >= PRINTF_FLT
if (c >= 'E' && c <= 'G') {
flags |= FL_FLTUPP;
c += 'e' - 'E';
goto flt_oper;
} else if (c >= 'e' && c <= 'g') {
int exp;
int n;
unsigned char vtype;
unsigned char sign;
# define ndigs c
flags &= ~FL_FLTUPP;
flt_oper:
if (!(flags & FL_PREC))
prec = 6;
flags &= ~(FL_FLTEXP | FL_FLTFIX);
if (c == 'e')
flags |= FL_FLTEXP;
else if (c == 'f')
flags |= FL_FLTFIX;
else if (prec > 0)
prec -= 1;
if (flags & FL_FLTFIX) {
vtype = 7;
ndigs = prec < 60 ? prec + 1 : 60;
} else {
if (prec > 7) prec = 7;
vtype = prec;
ndigs = 0;
}
exp = __ftoa_engine (va_arg(ap,double), (char *)buf, vtype, ndigs);
vtype = buf[0];
sign = 0;
if ((vtype & FTOA_MINUS) && !(vtype & FTOA_NAN))
sign = '-';
else if (flags & FL_PLUS)
sign = '+';
else if (flags & FL_SPACE)
sign = ' ';
if (vtype & (FTOA_NAN | FTOA_INF)) {
const char *p;
ndigs = sign ? 4 : 3;
if (width > ndigs) {
width -= ndigs;
if (!(flags & FL_LPAD)) {
do {
putc (' ', stream);
} while (--width);
}
} else {
width = 0;
}
if (sign)
putc (sign, stream);
p = PSTR("inf");
if (vtype & FTOA_NAN)
p = PSTR("nan");
# if ('I'-'i' != 'N'-'n') || ('I'-'i' != 'F'-'f') || ('I'-'i' != 'A'-'a')
# error
# endif
while ( (ndigs = pgm_read_byte(p)) != 0) {
if (flags & FL_FLTUPP)
ndigs += 'I' - 'i';
putc (ndigs, stream);
p++;
}
goto tail;
}
if (flags & FL_FLTFIX) {
ndigs += exp;
if ((vtype & FTOA_CARRY) && buf[1] == '1')
ndigs -= 1;
if ((signed char)ndigs < 1)
ndigs = 1;
else if (ndigs > 8)
ndigs = 8;
} else if (!(flags & FL_FLTEXP)) {
if (exp <= prec && exp >= -4)
flags |= FL_FLTFIX;
while (prec && buf[1+prec] == '0')
prec--;
if (flags & FL_FLTFIX) {
ndigs = prec + 1;
prec = prec > exp
? prec - exp : 0;
}
}
if (flags & FL_FLTFIX)
n = (exp>0 ? exp+1 : 1);
else
n = 5;
if (sign) n += 1;
if (prec) n += prec + 1;
width = width > n ? width - n : 0;
if (!(flags & (FL_LPAD | FL_ZFILL))) {
while (width) {
putc (' ', stream);
width--;
}
}
if (sign) putc (sign, stream);
if (!(flags & FL_LPAD)) {
while (width) {
putc ('0', stream);
width--;
}
}
if (flags & FL_FLTFIX) {
n = exp > 0 ? exp : 0;
do {
if (n == -1)
putc ('.', stream);
flags = (n <= exp && n > exp - ndigs)
? buf[exp - n + 1] : '0';
if (--n < -prec)
break;
putc (flags, stream);
} while (1);
if (n == exp
&& (buf[1] > '5'
|| (buf[1] == '5' && !(vtype & FTOA_CARRY))) )
{
flags = '1';
}
putc (flags, stream);
} else {
if (buf[1] != '1')
vtype &= ~FTOA_CARRY;
putc (buf[1], stream);
if (prec) {
putc ('.', stream);
sign = 2;
do {
putc (buf[sign++], stream);
} while (--prec);
}
putc (flags & FL_FLTUPP ? 'E' : 'e', stream);
ndigs = '+';
if (exp < 0 || (exp == 0 && (vtype & FTOA_CARRY) != 0)) {
exp = -exp;
ndigs = '-';
}
putc (ndigs, stream);
for (ndigs = '0'; exp >= 10; exp -= 10)
ndigs += 1;
putc (ndigs, stream);
putc ('0' + exp, stream);
}
goto tail;
# undef ndigs
}
#else
if ((c >= 'E' && c <= 'G') || (c >= 'e' && c <= 'g')) {
(void) va_arg (ap, double);
buf[0] = '?';
goto buf_addr;
}
#endif
{
const char * pnt;
size_t size;
switch (c) {
case 'c':
buf[0] = va_arg (ap, int);
#if PRINTF_LEVEL < PRINTF_FLT
buf_addr:
#endif
pnt = (char *)buf;
size = 1;
goto no_pgmstring;
case 's':
pnt = va_arg (ap, char *);
size = strnlen (pnt, (flags & FL_PREC) ? prec : ~0);
no_pgmstring:
flags &= ~FL_PGMSTRING;
goto str_lpad;
case 'S':
pnt = va_arg (ap, char *);
size = strnlen_P (pnt, (flags & FL_PREC) ? prec : ~0);
flags |= FL_PGMSTRING;
str_lpad:
if (!(flags & FL_LPAD)) {
while (size < width) {
putc (' ', stream);
width--;
}
}
while (size) {
putc (GETBYTE (flags, FL_PGMSTRING, pnt), stream);
if (width) width -= 1;
size -= 1;
}
goto tail;
}
}
if (c == 'd' || c == 'i') {
long x = (flags & FL_LONG) ? va_arg(ap,long) : va_arg(ap,int);
flags &= ~(FL_NEGATIVE | FL_ALT);
if (x < 0) {
x = -x;
flags |= FL_NEGATIVE;
}
c = __ultoa_invert (x, (char *)buf, 10) - (char *)buf;
} else {
int base;
if (c == 'u') {
flags &= ~FL_ALT;
base = 10;
goto ultoa;
}
flags &= ~(FL_PLUS | FL_SPACE);
switch (c) {
case 'o':
base = 8;
goto ultoa;
case 'p':
flags |= FL_ALT;
case 'x':
if (flags & FL_ALT)
flags |= FL_ALTHEX;
base = 16;
goto ultoa;
case 'X':
if (flags & FL_ALT)
flags |= (FL_ALTHEX | FL_ALTUPP);
base = 16 | XTOA_UPPER;
ultoa:
c = __ultoa_invert ((flags & FL_LONG)
? va_arg(ap, unsigned long)
: va_arg(ap, unsigned int),
(char *)buf, base) - (char *)buf;
flags &= ~FL_NEGATIVE;
break;
default:
goto ret;
}
}
{
unsigned char len;
len = c;
if (flags & FL_PREC) {
flags &= ~FL_ZFILL;
if (len < prec) {
len = prec;
if ((flags & FL_ALT) && !(flags & FL_ALTHEX))
flags &= ~FL_ALT;
}
}
if (flags & FL_ALT) {
if (buf[c-1] == '0') {
flags &= ~(FL_ALT | FL_ALTHEX | FL_ALTUPP);
} else {
len += 1;
if (flags & FL_ALTHEX)
len += 1;
}
} else if (flags & (FL_NEGATIVE | FL_PLUS | FL_SPACE)) {
len += 1;
}
if (!(flags & FL_LPAD)) {
if (flags & FL_ZFILL) {
prec = c;
if (len < width) {
prec += width - len;
len = width;
}
}
while (len < width) {
putc (' ', stream);
len++;
}
}
width = (len < width) ? width - len : 0;
if (flags & FL_ALT) {
putc ('0', stream);
if (flags & FL_ALTHEX)
putc (flags & FL_ALTUPP ? 'X' : 'x', stream);
} else if (flags & (FL_NEGATIVE | FL_PLUS | FL_SPACE)) {
unsigned char z = ' ';
if (flags & FL_PLUS) z = '+';
if (flags & FL_NEGATIVE) z = '-';
putc (z, stream);
}
while (prec > c) {
putc ('0', stream);
prec--;
}
do {
putc (buf[--c], stream);
} while (c);
}
tail:
while (width) {
putc (' ', stream);
width--;
}
}
ret:
return stream->len;
}
#endif
#endif