/* A reduced version of the printf function. Copyright (C) 2011 Richard Henderson This file is part of QEMU PALcode. 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 Free Software Foundation; either version 2 of the License or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the text of the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not see . */ #include "console.h" #include "protos.h" static int print_buf_pad(char *buf, int buflen, char *p, int width, int pad) { int len = buf + buflen - p; int r = 0; if (width > len) { *--p = pad; len++; while (width > buflen) { crb_puts(0, p, 1); width--; r++; } while (width > len) *--p = pad, len++; } crb_puts(0, p, len); return r + len; } static int print_decimal(unsigned long val, int width, int pad) { char buf[32]; char *p = buf + sizeof(buf); if (val == 0) *--p = '0'; else { do { unsigned long d, r; /* Compiling with -Os results in a call to the division routine. Do what the compiler ought to have done. */ d = __builtin_alpha_umulh(val, 0xcccccccccccccccd); d >>= 3; r = val - (d * 10); *--p = r + '0'; val = d; } while (val); } return print_buf_pad(buf, sizeof(buf), p, width, pad); } static int print_hex(unsigned long val, int width, char pad) { char buf[32]; char *p = buf + sizeof(buf); if (val == 0) *--p = '0'; else { do { int d = val % 16; *--p = (d < 10 ? '0' : 'a' - 10) + d; val /= 16; } while (val); } return print_buf_pad(buf, sizeof(buf), p, width, pad); } int printf(const char *fmt, ...) { va_list args; unsigned long val; int r = 0; va_start(args, fmt); for (; *fmt ; fmt++) if (*fmt != '%') { crb_puts(0, fmt, 1); r++; } else { const char *percent = fmt; bool is_long = false; char pad = ' '; int width = 0; restart: switch (*++fmt) { case '%': crb_puts(0, "%", 1); r++; break; case 'l': is_long = true; goto restart; case 'd': if (is_long) { long d = va_arg (args, long); if (d < 0) { crb_puts(0, "-", 1); d = -d; } val = d; } else { int d = va_arg (args, int); if (d < 0) { crb_puts(0, "-", 1); d = -d; r++; } val = d; } goto do_unsigned; case 'u': if (is_long) val = va_arg (args, unsigned long); else val = va_arg (args, unsigned int); do_unsigned: r += print_decimal (val, width, pad); break; case 'x': if (is_long) val = va_arg (args, unsigned long); else val = va_arg (args, unsigned int); r += print_hex (val, width, pad); break; case 's': { const char *s = va_arg (args, const char *); int len = strlen(s); crb_puts(0, s, len); r += len; } break; case '0': pad = '0'; case '1' ... '9': width = *fmt - '0'; while (fmt[1] >= '0' && fmt[1] <= '9') width = width * 10 + *++fmt - '0'; goto restart; default: { int len = fmt - percent; crb_puts(0, percent, len); r += len; } break; } } va_end(args); return r; }