|  |  | 
|  | typedef unsigned long va_list; | 
|  |  | 
|  | #define ACC    4 | 
|  | #define __read(source)                    \ | 
|  | ({ va_list __res;                    \ | 
|  | __asm__ __volatile__(                \ | 
|  | "move\t%0, " #source "\n\t"        \ | 
|  | : "=r" (__res));            \ | 
|  | __res;                        \ | 
|  | }) | 
|  |  | 
|  | enum format_type { | 
|  | FORMAT_TYPE_NONE, | 
|  | FORMAT_TYPE_HEX, | 
|  | FORMAT_TYPE_ULONG, | 
|  | FORMAT_TYPE_FLOAT | 
|  | }; | 
|  |  | 
|  | struct printf_spec { | 
|  | char    type; | 
|  | }; | 
|  |  | 
|  | static int format_decode(char *fmt, struct printf_spec *spec) | 
|  | { | 
|  | char *start = fmt; | 
|  |  | 
|  | for (; *fmt ; ++fmt) { | 
|  | if (*fmt == '%') { | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | switch (*++fmt) { | 
|  | case 'x': | 
|  | spec->type = FORMAT_TYPE_HEX; | 
|  | break; | 
|  |  | 
|  | case 'd': | 
|  | spec->type = FORMAT_TYPE_ULONG; | 
|  | break; | 
|  |  | 
|  | case 'f': | 
|  | spec->type = FORMAT_TYPE_FLOAT; | 
|  | break; | 
|  |  | 
|  | default: | 
|  | spec->type = FORMAT_TYPE_NONE; | 
|  | } | 
|  |  | 
|  | return ++fmt - start; | 
|  | } | 
|  |  | 
|  | void *memcpy(void *dest, void *src, int n) | 
|  | { | 
|  | int i; | 
|  | char *s = src; | 
|  | char *d = dest; | 
|  |  | 
|  | for (i = 0; i < n; i++) { | 
|  | d[i] = s[i]; | 
|  | } | 
|  | return dest; | 
|  | } | 
|  |  | 
|  | char *number(char *buf, va_list num) | 
|  | { | 
|  | int i; | 
|  | char *str = buf; | 
|  | static char digits[16] = "0123456789abcdef"; | 
|  | str = str + sizeof(num) * 2; | 
|  |  | 
|  | for (i = 0; i < sizeof(num) * 2; i++) { | 
|  | *--str = digits[num & 15]; | 
|  | num >>= 4; | 
|  | } | 
|  |  | 
|  | return buf + sizeof(num) * 2; | 
|  | } | 
|  |  | 
|  | char *__number(char *buf, va_list num) | 
|  | { | 
|  | int i; | 
|  | va_list mm = num; | 
|  | char *str = buf; | 
|  |  | 
|  | if (!num) { | 
|  | *str++ = '0'; | 
|  | return str; | 
|  | } | 
|  |  | 
|  | for (i = 0; mm; mm = mm/10, i++) { | 
|  | /* Do nothing. */ | 
|  | } | 
|  |  | 
|  | str = str + i; | 
|  |  | 
|  | while (num) { | 
|  | *--str = num % 10 + 48; | 
|  | num = num / 10; | 
|  | } | 
|  |  | 
|  | return str + i; | 
|  | } | 
|  |  | 
|  | va_list modf(va_list args, va_list *integer, va_list *num) | 
|  | { | 
|  | int i; | 
|  | double dot_v = 0; | 
|  | va_list E, DOT, DOT_V; | 
|  |  | 
|  | if (!args) { | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | for (i = 0, args = args << 1 >> 1; i < 52; i++) { | 
|  | if ((args >> i) & 0x1) { | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | *integer = 0; | 
|  |  | 
|  | if ((args >> 56 != 0x3f) || (args >> 52 == 0x3ff)) { | 
|  | E = (args >> 52) - 1023; | 
|  | DOT = 52 - E - i; | 
|  | DOT_V = args << (12 + E) >> (12 + E) >> i; | 
|  | *integer = ((args << 12 >> 12) >> (i + DOT)) | (1 << E); | 
|  | } else { | 
|  | E = ~((args >> 52) - 1023) + 1; | 
|  | DOT_V = args << 12 >> 12; | 
|  |  | 
|  | dot_v += 1.0 / (1 << E); | 
|  |  | 
|  | for (i = 1; i <= 16; i++) { | 
|  | if ((DOT_V >> (52 - i)) & 0x1) { | 
|  | dot_v += 1.0 / (1 << E + i); | 
|  | } | 
|  | } | 
|  |  | 
|  | for (i = 1, E = 0; i <= ACC; i++) { | 
|  | dot_v *= 10; | 
|  | if (!(va_list)dot_v) { | 
|  | E++; | 
|  | } | 
|  | } | 
|  |  | 
|  | *num = E; | 
|  |  | 
|  | return dot_v; | 
|  | } | 
|  |  | 
|  | if (args & 0xf) { | 
|  | for (i = 1; i <= 16; i++) { | 
|  | if ((DOT_V >> (DOT - i)) & 0x1) { | 
|  | dot_v += 1.0 / (1 << i); | 
|  | } | 
|  | } | 
|  |  | 
|  | for (i = 1, E = 0; i <= ACC; i++) { | 
|  | dot_v *= 10; | 
|  | if (!(va_list)dot_v) { | 
|  | E++; | 
|  | } | 
|  | } | 
|  |  | 
|  | *num = E; | 
|  |  | 
|  | return dot_v; | 
|  | } else if (DOT) { | 
|  | for (i = 1; i <= DOT; i++) { | 
|  | if ((DOT_V >> (DOT - i)) & 0x1) { | 
|  | dot_v += 1.0 / (1 << i); | 
|  | } | 
|  | } | 
|  |  | 
|  | for (i = 1; i <= ACC; i++) { | 
|  | dot_v = dot_v * 10; | 
|  | } | 
|  |  | 
|  | return dot_v; | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | int vsnprintf(char *buf, int size, char *fmt, va_list args) | 
|  | { | 
|  | char *str, *mm; | 
|  | struct printf_spec spec = {0}; | 
|  |  | 
|  | str = mm = buf; | 
|  |  | 
|  | while (*fmt) { | 
|  | char *old_fmt = fmt; | 
|  | int read = format_decode(fmt, &spec); | 
|  |  | 
|  | fmt += read; | 
|  |  | 
|  | switch (spec.type) { | 
|  | case FORMAT_TYPE_NONE: { | 
|  | memcpy(str, old_fmt, read); | 
|  | str += read; | 
|  | break; | 
|  | } | 
|  | case FORMAT_TYPE_HEX: { | 
|  | memcpy(str, old_fmt, read); | 
|  | str = number(str + read, args); | 
|  | for (; *mm ; ++mm) { | 
|  | if (*mm == '%') { | 
|  | *mm = '0'; | 
|  | break; | 
|  | } | 
|  | } | 
|  | break; | 
|  | } | 
|  | case FORMAT_TYPE_ULONG: { | 
|  | memcpy(str, old_fmt, read - 2); | 
|  | str = __number(str + read - 2, args); | 
|  | break; | 
|  | } | 
|  | case FORMAT_TYPE_FLOAT: { | 
|  | va_list integer, dot_v, num; | 
|  | dot_v = modf(args, &integer, &num); | 
|  | memcpy(str, old_fmt, read - 2); | 
|  | str += read - 2; | 
|  | if ((args >> 63 & 0x1)) { | 
|  | *str++ = '-'; | 
|  | } | 
|  | str = __number(str, integer); | 
|  | if (dot_v) { | 
|  | *str++ = '.'; | 
|  | while (num--) { | 
|  | *str++ = '0'; | 
|  | } | 
|  | str = __number(str, dot_v); | 
|  | } | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  | *str = '\0'; | 
|  |  | 
|  | return str - buf; | 
|  | } | 
|  |  | 
|  | static void serial_out(char *str) | 
|  | { | 
|  | while (*str) { | 
|  | *(char *)0xffffffffb80003f8 = *str++; | 
|  | } | 
|  | } | 
|  |  | 
|  | int vprintf(char *fmt, va_list args) | 
|  | { | 
|  | int printed_len = 0; | 
|  | static char printf_buf[512]; | 
|  | printed_len = vsnprintf(printf_buf, sizeof(printf_buf), fmt, args); | 
|  | serial_out(printf_buf); | 
|  | return printed_len; | 
|  | } | 
|  |  | 
|  | int printf(char *fmt, ...) | 
|  | { | 
|  | return vprintf(fmt, __read($5)); | 
|  | } |