/*---------------------------------------------------------------------------* Project: CtrBrom - libraries - OS File: os_printf.c Copyright 2008 Nintendo. All rights reserved. These coded instructions, statements, and computer programs contain proprietary information of Nintendo of America Inc. and/or Nintendo Company Ltd., and are protected by Federal copyright law. They may not be disclosed to third parties or copied or duplicated in any form, in whole or in part, without the prior written consent of Nintendo. $Date:: $ $Rev$ $Author$ *---------------------------------------------------------------------------*/ #include #include #include #ifndef SDK_FINALROM static char common_buffer[256]; // thread unsafe, but less use of stack #endif #if !defined(SDK_FINALROM) && defined(SDK_NO_MESSAGE) #undef i_osWarning #undef i_osTWarning #undef i_osPanic #undef i_osTPanic void i_osWarning(const char *file, int line, const char *fmt, ...); void i_osTWarning(const char *file, int line, const char *fmt, ...); void i_osPanic(const char *file, int line, const char *fmt, ...); void i_osTPanic(const char *file, int line, const char *fmt, ...); #endif /* SDK_NO_MESSAGE */ /*---------------------------------------------------------------------------* Name: osPutChar Description: put a letter for debug console Arguments: c : char code , shuild be 0x01-0xff Returns: None. *---------------------------------------------------------------------------*/ #ifndef SDK_FINALROM SDK_WEAK_SYMBOL void osPutChar(char c) { char str[2]; str[0] = c; str[1] = '\0'; // Because of no putchar-type function on ISDebugger library, // use 'putstring' for a single letter outputting osPutString(str); } #endif /*---------------------------------------------------------------------------* Name: osPutString Description: put a string to debug console. console port are automatically switched depends on emu/hw Arguments: str : string Returns: None. *---------------------------------------------------------------------------*/ static void osPutStringInit(const char *str); #ifndef SDK_FINALROM static void osPutStringKMC(const char *str); void osPutStringARM(const char *str); #ifdef SDK_ARM7 static void osPutStringPrnSrv(const char *str); #endif #endif #ifndef SDK_FINALROM void (*osPutString) (const char *str) = osPutStringInit; #endif /*---------------------------------------------------------------------------* Name: osPutStringInit Description: osPutString initializer *---------------------------------------------------------------------------*/ static void osPutStringInit(const char *str) { #ifndef SDK_FINALROM OSIntrMode intr = osDisableInterrupts(); { #ifdef SDK_DEBUGGER_KMC osPutString = osPutStringKMC; #else // SDK_DEBUGGER_ARM osPutString = osPutStringARM; #endif } osPutString(str); (void)osRestoreInterrupts(intr); #else (void)str; // avoiding to unused warning #endif } /*---------------------------------------------------------------------------* Name: osPutStringKMC Description: osPutString for hardware emulator called KMC Debugger *---------------------------------------------------------------------------*/ #ifndef SDK_FINALROM static void osPutStringKMC(const char *str) { vlink_dos_putstring_console((char *)str, strlen(str)); // STD_GetStringLength(str)); } #endif /*---------------------------------------------------------------------------* Name: osPutStringARM Description: osPutString for hardware emulator called ARM Debugger *---------------------------------------------------------------------------*/ #ifndef SDK_FINALROM #include asm void osPutStringARM(const char *str) { mov r1, r0 mov r0, #0x04 swi 0x123456 bx lr } #include #endif /*---------------------------------------------------------------------------* Name: osVPrintf/osTVPrintf Description: print formatted strings (vprintf ver.) Arguments: fmt : format string vlist : parameters Returns: None. *---------------------------------------------------------------------------*/ // If you want to use "vsnprintf()" in ARM7, define SDK_USE_VSNPRINTF. // "vsnprintf()" is able to print float format but the code size become hugely bigger. // #define SDK_USE_VSNPRINTF #ifndef SDK_FINALROM #if defined(SDK_USE_VSNPRINTF) SDK_WEAK_SYMBOL void osVPrintf(const char *fmt, va_list vlist) { (void)vsnprintf(common_buffer, sizeof(common_buffer), fmt, vlist); osPutString(common_buffer); } #else SDK_WEAK_SYMBOL void osVPrintf(const char *fmt, va_list vlist) { osTVPrintf(fmt, vlist); } #endif SDK_WEAK_SYMBOL void osTVPrintf(const char *fmt, va_list vlist) { (void)osVSNPrintf(common_buffer, sizeof(common_buffer), fmt, vlist); osPutString(common_buffer); } #endif /*---------------------------------------------------------------------------* Name: osPrintf/osTPrintf Description: print formatted strings. Arguments: fmt : format string ... and parameters Returns: None. *---------------------------------------------------------------------------*/ #ifndef SDK_FINALROM SDK_WEAK_SYMBOL void osPrintf(const char *fmt, ...) { va_list vlist; va_start(vlist, fmt); osVPrintf(fmt, vlist); va_end(vlist); } SDK_WEAK_SYMBOL void osTPrintf(const char *fmt, ...) { va_list vlist; va_start(vlist, fmt); osTVPrintf(fmt, vlist); va_end(vlist); } #endif /*---------------------------------------------------------------------------* Name: i_osWarning / i_osTWarning Description: print warning message. Arguments: file : filename of warning location line : line number of warning location fmt : format string ... and parameters Returns: None. *---------------------------------------------------------------------------*/ #ifndef SDK_FINALROM SDK_WEAK_SYMBOL void i_osWarning(const char *file, int line, const char *fmt, ...) { va_list vlist; va_start(vlist, fmt); osPrintf("%s:%d Warning:", file, line); osVPrintf(fmt, vlist); osPrintf("\n"); va_end(vlist); } SDK_WEAK_SYMBOL void i_osTWarning(const char *file, int line, const char *fmt, ...) { va_list vlist; va_start(vlist, fmt); osTPrintf("%s:%d Warning:", file, line); osTVPrintf(fmt, vlist); osTPrintf("\n"); va_end(vlist); } #endif /*---------------------------------------------------------------------------* Name: i_osPanic/i_osTPanic Description: print panic message and halt cpu. Arguments: file : filename of panic location line : line number of panic location fmt : format string ... and parameters Returns: None. *---------------------------------------------------------------------------*/ #ifndef SDK_FINALROM void (*i_osFuncTerminate) (void) = osTerminate; SDK_WEAK_SYMBOL void i_osPanic(const char *file, int line, const char *fmt, ...) { va_list vlist; va_start(vlist, fmt); (void)osDisableInterrupts(); osPrintf("%s:%d Panic:", file, line); osVPrintf(fmt, vlist); osPrintf("\n"); i_osFuncTerminate(); // Never Returns } SDK_WEAK_SYMBOL void i_osTPanic(const char *file, int line, const char *fmt, ...) { va_list vlist; va_start(vlist, fmt); (void)osDisableInterrupts(); osTPrintf("%s:%d Panic:", file, line); osTVPrintf(fmt, vlist); osTPrintf("\n"); i_osFuncTerminate(); // Never Returns } #endif #if defined(SDK_CW_WARNOFF_SAFESTRB) #include #endif /* inner function for sized-buffer output */ typedef struct dst_string_tag { size_t len; char *cur; char *base; } dst_string; static void string_put_char(dst_string * p, char c) { if (p->len > 0) *p->cur = c, --p->len; ++p->cur; } static void string_fill_char(dst_string * p, char c, int n) { if (n > 0) { size_t i, k = p->len; if (k > (size_t) n) k = (size_t) n; for (i = 0; i < k; ++i) p->cur[i] = c; p->len -= k; p->cur += n; } } static void string_put_string(dst_string * p, const char *s, int n) { if (n > 0) { size_t i, k = p->len; if (k > (size_t) n) k = (size_t) n; for (i = 0; i < k; ++i) p->cur[i] = s[i]; p->len -= k; p->cur += n; } } /*---------------------------------------------------------------------------* Name: osSPrintf Description: equal to 'osVSPrintf' except argument style. Arguments: dst : destination buffer. fmt : format string. Returns: length of the generated string. *---------------------------------------------------------------------------*/ SDK_WEAK_SYMBOL int osSPrintf(char *dst, const char *fmt, ...) { int ret; va_list va; va_start(va, fmt); ret = osVSPrintf(dst, fmt, va); va_end(va); return ret; } /*---------------------------------------------------------------------------* Name: osVSPrintf Description: equal to 'osVSNPrintf' except buffer size argument. Arguments: dst : destination buffer. fmt : format string. vlist : parameters. Returns: length of the generated string. *---------------------------------------------------------------------------*/ SDK_WEAK_SYMBOL int osVSPrintf(char *dst, const char *fmt, va_list vlist) { return osVSNPrintf(dst, 0x7FFFFFFF, fmt, vlist); } /*---------------------------------------------------------------------------* Name: osSNPrintf Description: equal to 'osVSNPrintf' except argument style. Arguments: dst : destination buffer. len : destination buffer size. fmt : format string. Returns: length of the generated string. (except '\0') if(result < len), put NUL in dst[result]. else if(len > 0), put NUL in dst[len - 1]. else, do nothing. *---------------------------------------------------------------------------*/ SDK_WEAK_SYMBOL int osSNPrintf(char *dst, size_t len, const char *fmt, ...) { int ret; va_list va; va_start(va, fmt); ret = osVSNPrintf(dst, len, fmt, va); va_end(va); return ret; } /*---------------------------------------------------------------------------* Name: osVSNPrintf Description: small-size vsnprintf which is similar to 'vsnprintf' without following supports. * CodeWarrior Extensions (#s) * MSL AltiVec Extensions (v, vh, vl, hv, lv, @) * indexed argments (%m$, *m$) * floating-point * wchar_t Note: '+' and '#' do not work, MSL's sprintf(). to keep same result, they are no implement. { // exsample char buf[5]; sprintf(buf, "%-i\n", 45); // "45" (OK) sprintf(buf, "%0i\n", 45); // "45" (OK) sprintf(buf, "% i\n", 45); // " 45" (OK) sprintf(buf, "%+i\n", 45); // "%+i" ("+45" expected) sprintf(buf, "%#x\n", 45); // "%#x" ("0x2d" expected) // but, this works correctly! sprintf(buf, "% +i\n", 45); // "+45" (OK) } Arguments: dst : destination buffer. len : destination buffer size. fmt : format string. vlist : parameters. Returns: length of the generated string. (except '\0') if(result < len), put NUL in dst[result]. else if(len > 0), put NUL in dst[len - 1]. else, do nothing. *---------------------------------------------------------------------------*/ SDK_WEAK_SYMBOL int osVSNPrintf(char *dst, size_t len, const char *fmt, va_list vlist) { char buf[24]; int n_buf; char prefix[2]; int n_prefix; const char *s = fmt; dst_string str; str.len = len, str.cur = str.base = dst; while (*s) { if ((unsigned int)(((unsigned char)*s ^ 0x20) - 0xA1) < 0x3C) { /* Shift JIS character */ string_put_char(&str, *s++); if (*s) string_put_char(&str, *s++); } else if (*s != '%') { /* normal ASCII character */ string_put_char(&str, *s++); } else { /* output with format */ enum { flag_blank = 000001, /* ' ' */ flag_plus = 000002, /* '+' */ flag_sharp = 000004, /* '#' */ flag_minus = 000010, /* '-' */ flag_zero = 000020, /* '0' */ flag_l1 = 000040, /* "l" */ flag_h1 = 000100, /* "h" */ flag_l2 = 000200, /* "ll" */ flag_h2 = 000400, /* "hh" */ flag_unsigned = 010000, /* 'o', 'u', ... */ flag_end }; int flag = 0, width = 0, precision = -1, radix = 10; char hex_char = 'a' - 10; const char *p_start = s; /* flags */ for (;;) { switch (*++s) { case '+': if (s[-1] != ' ') break; flag |= flag_plus; continue; case ' ': flag |= flag_blank; continue; case '-': flag |= flag_minus; continue; case '0': flag |= flag_zero; continue; } break; } /* width */ if (*s == '*') { ++s, width = va_arg(vlist, int); if (width < 0) width = -width, flag |= flag_minus; } else { while ((*s >= '0') && (*s <= '9')) width = (width * 10) + *s++ - '0'; } /* precision */ if (*s == '.') { ++s, precision = 0; if (*s == '*') { ++s, precision = va_arg(vlist, int); if (precision < 0) precision = -1; } else { while ((*s >= '0') && (*s <= '9')) precision = (precision * 10) + *s++ - '0'; } } /* option */ switch (*s) { case 'h': if (*++s != 'h') flag |= flag_h1; else ++s, flag |= flag_h2; break; case 'l': if (*++s != 'l') flag |= flag_l1; else ++s, flag |= flag_l2; break; } /* type */ switch (*s) { case 'd': /* signed decimal */ case 'i': /* signed decimal */ goto put_integer; case 'o': /* unsigned octal */ radix = 8; flag |= flag_unsigned; goto put_integer; case 'u': /* unsigned decimal */ flag |= flag_unsigned; goto put_integer; case 'X': /* unsigned hexadecimal */ hex_char = 'A' - 10; goto put_hexadecimal; case 'x': /* unsigned hexadecimal */ goto put_hexadecimal; case 'p': /* pointer */ /* equal to code warrior */ flag |= flag_sharp; precision = 8; goto put_hexadecimal; case 'c': /* character */ if (precision >= 0) goto put_invalid; { int c = va_arg(vlist, int); width -= 1; if (flag & flag_minus) { string_put_char(&str, (char)c); string_fill_char(&str, ' ', width); } else { char pad = (char)((flag & flag_zero) ? '0' : ' '); string_fill_char(&str, pad, width); string_put_char(&str, (char)c); } ++s; } break; case 's': /* string */ { int n_buf = 0; const char *p_buf = va_arg(vlist, const char *); if (precision < 0) { while (p_buf[n_buf]) ++n_buf; } else { while ((n_buf < precision) && p_buf[n_buf]) ++n_buf; } width -= n_buf; if (flag & flag_minus) { string_put_string(&str, p_buf, n_buf); string_fill_char(&str, ' ', width); } else { char pad = (char)((flag & flag_zero) ? '0' : ' '); string_fill_char(&str, pad, width); string_put_string(&str, p_buf, n_buf); } ++s; } break; case 'n': /* store the number of output */ { int pos = str.cur - str.base; if (flag & flag_h2) ; else if (flag & flag_h1) *va_arg(vlist, signed short *) = (signed short)pos; else if (flag & flag_l2) *va_arg(vlist, u64 *) = (u64)pos; else *va_arg(vlist, signed int *) = (signed int)pos; } ++s; break; case '%': /* output '%' */ if (p_start + 1 != s) goto put_invalid; string_put_char(&str, *s++); break; default: /* invalid type */ goto put_invalid; put_invalid: string_put_string(&str, p_start, s - p_start); break; put_hexadecimal: radix = 16; flag |= flag_unsigned; put_integer: { u64 val = 0; n_prefix = 0; if (flag & flag_minus) flag &= ~flag_zero; if (precision < 0) precision = 1; else flag &= ~flag_zero; if (flag & flag_unsigned) { if (flag & flag_h2) val = va_arg(vlist, unsigned char); else if (flag & flag_h1) val = va_arg(vlist, unsigned short); else if (flag & flag_l2) val = va_arg(vlist, u64); else val = va_arg(vlist, unsigned long); flag &= ~(flag_plus | flag_blank); if (flag & flag_sharp) { if (radix == 16) { if (val != 0) { prefix[0] = (char)(hex_char + (10 + 'x' - 'a')); prefix[1] = '0'; n_prefix = 2; } } else if (radix == 8) { prefix[0] = '0'; n_prefix = 1; } } } else { if (flag & flag_h2) val = va_arg(vlist, char); else if (flag & flag_h1) val = va_arg(vlist, short); else if (flag & flag_l2) val = va_arg(vlist, u64); else val = va_arg(vlist, long); if ((val >> 32) & 0x80000000) { val = ~val + 1; prefix[0] = '-'; n_prefix = 1; } else { if (val || precision) { if (flag & flag_plus) { prefix[0] = '+'; n_prefix = 1; } else if (flag & flag_blank) { prefix[0] = ' '; n_prefix = 1; } } } } n_buf = 0; switch (radix) { case 8: while (val != 0) { int d = (int)(val & 0x07); val >>= 3; buf[n_buf++] = (char)(d + '0'); } break; case 10: if ((val >> 32) == 0) { #if defined(SDK_CW) || defined(__MWERKS__) #pragma optimize_for_size off #endif u32 v = (u32)val; while (v != 0) { // u32 と定数の除算であれば、コンパイラが自動的に // マジックナンバーを使用した積算に変換する u32 r = v / 10; int d = (int)(v - (r * 10)); v = r; buf[n_buf++] = (char)(d + '0'); } } else { while (val != 0) { u64 r = val / 10; int d = (int)(val - (r * 10)); val = r; buf[n_buf++] = (char)(d + '0'); } } break; case 16: while (val != 0) { int d = (int)(val & 0x0f); val >>= 4; buf[n_buf++] = (char)((d < 10) ? (d + '0') : (d + hex_char)); } break; } if ((n_prefix > 0) && (prefix[0] == '0')) { n_prefix = 0; buf[n_buf++] = '0'; } } goto put_to_stream; put_to_stream: { int n_pad = precision - n_buf; if (flag & flag_zero) { if (n_pad < width - n_buf - n_prefix) n_pad = width - n_buf - n_prefix; } if (n_pad > 0) width -= n_pad; width -= n_prefix + n_buf; if (!(flag & flag_minus)) string_fill_char(&str, ' ', width); while (n_prefix > 0) string_put_char(&str, prefix[--n_prefix]); string_fill_char(&str, '0', n_pad); while (n_buf > 0) string_put_char(&str, buf[--n_buf]); if (flag & flag_minus) string_fill_char(&str, ' ', width); ++s; } break; } } } if (str.len > 0) *str.cur = '\0'; else if (len > 0) str.base[len - 1] = '\0'; return str.cur - str.base; } #if defined(SDK_CW_WARNOFF_SAFESTRB) #include #endif //================================================================================ // DUMMY PRINT (stub for FINALROM) //================================================================================ #ifdef SDK_FINALROM #ifdef osPrintf #undef osPrintf #endif void osPrintf(const char *fmt, ...); void osPrintf(const char *fmt, ...) { //#pragma unused( fmt ) } #endif