/*---------------------------------------------------------------------------*
  Project:  TwlSDK - libraries - OS
  File:     os_printf.c

  Copyright 2003-2009 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:: 2009-09-17#$
  $Rev: 11062 $
  $Author: yada $
 *---------------------------------------------------------------------------*/
#include        <nitro.h>
#include        <nitro/dbghost.h>

//---------------- for IS-NITRO-DEBUGGER
#ifdef          SDK_LINK_ISD
# pragma warn_extracomma off
# include       <isdbglib.h>           // has extracomma in enum
# pragma warn_extracomma reset
#else
void    ISDPrint(const char *);
#endif

//---------------- for IS-TWL-DEBUGGER
#ifdef          SDK_LINK_ISTD
# pragma warn_extracomma off
# include       <istdbglib.h>          // has extracomma in enum
# pragma warn_extracomma reset
#else
void    ISTDPrintEx(int console, const char *);
#endif
//---- print output
#ifdef SDK_ARM9
#define OSi_PRINT_OUTPUT        (*(u8*)HW_PRINT_OUTPUT_ARM9)
#define OSi_PRINT_ERR_OUTPUT    (*(u8*)HW_PRINT_OUTPUT_ARM9ERR)
#else
#define OSi_PRINT_OUTPUT        (*(u8*)HW_PRINT_OUTPUT_ARM7)
#define OSi_PRINT_ERR_OUTPUT    (*(u8*)HW_PRINT_OUTPUT_ARM7ERR)
#endif

#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 OSi_Warning
#undef OSi_TWarning
#undef OSi_Panic
#undef OSi_TPanic
void    OSi_Warning(const char *file, int line, const char *fmt, ...);
void    OSi_TWarning(const char *file, int line, const char *fmt, ...);
void    OSi_Panic(const char *file, int line, const char *fmt, ...);
void    OSi_TPanic(const char *file, int line, const char *fmt, ...);
#endif /* SDK_NO_MESSAGE */


/*---------------------------------------------------------------------------*
  Name:         OS_PutChar
                OS_FPutChar

  Description:  put a letter for debug console

  Arguments:    console : output window
                c       : char code , shuild be 0x01-0xff

  Returns:      None.
 *---------------------------------------------------------------------------*/
#ifndef SDK_FINALROM
SDK_WEAK_SYMBOL void OS_PutChar(char c)
{
	OS_FPutChar((int)OSi_PRINT_OUTPUT, c);
}

SDK_WEAK_SYMBOL void OS_FPutChar(int console, 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
    OS_FPutString(console, str);
}
#endif

/*---------------------------------------------------------------------------*
  Name:         OS_FPutString

  Description:  put a string to debug console.
                console port are automatically switched depends on emu/hw

  Arguments:    console : output window
                str     : string

  Returns:      None.
 *---------------------------------------------------------------------------*/
static void OS_PutStringDefault(const char* str);
static void OS_FPutStringInit(int console, const char *str);

#ifndef SDK_FINALROM
static void OS_FPutStringDummy(int console, const char *str);
static void OS_FPutStringAris(int console, const char *str);
static void OS_FPutStringISD(int console, const char *str);
static void OS_FPutStringISTD(int console, const char *str);
#ifdef  SDK_ARM7
static void OS_FPutStringPrnSrv(int console, const char *str);
#endif
#endif

#ifndef SDK_FINALROM
void    (*OS_PutString)  (const char *str) = OS_PutStringDefault;
void    (*OS_FPutString) (int console, const char *str) = OS_FPutStringInit;
void    (*OSi_FuncTerminate) (void) = OS_Terminate;
//----  hook function
static OSFPutStringHookType  OSi_FPutStringHook = NULL;
#endif


/*---------------------------------------------------------------------------*
  Name:         OS_PutStringDefault
  Description:  OS_PutString initializer
 *---------------------------------------------------------------------------*/
static void OS_PutStringDefault(const char* str)
{
#pragma unused(str)
#ifndef SDK_FINALROM
    OS_FPutString(OS_PRINT_OUTPUT_CURRENT, str);
#endif
}

/*---------------------------------------------------------------------------*
  Name:         OS_FPutStringInit
  Description:  OS_FPutString initializer
 *---------------------------------------------------------------------------*/
static void OS_FPutStringInit(int console, const char *str)
{
#ifndef SDK_FINALROM
    OSIntrMode intr = OS_DisableInterrupts();
	u32 consoleType = OS_GetConsoleType();

    OS_FPutString = OS_FPutStringDummy;

#ifdef  SDK_ARM9
	//---- arm9
    if ( consoleType & OS_CONSOLE_ENSATA )
    {
        OS_FPutString = OS_FPutStringAris;
    }
    else if ( consoleType & OS_CONSOLE_TWLDEBUGGER )
	{
		OS_FPutString = OS_FPutStringISTD;
	}
	else if ( consoleType & OS_CONSOLE_ISDEBUGGER )
	{
#ifndef SDK_TWLLTD
		OS_FPutString = OS_FPutStringISD;
#endif
	}
#else
	//---- arm7
    if ( (OSPrintServerBuffer *)PXI_GetComponentParam() != NULL )
    {
        OS_FPutString = OS_FPutStringPrnSrv;
    }
    else
    {
        if ( consoleType & OS_CONSOLE_TWLDEBUGGER )
        {
            OS_FPutString = OS_FPutStringISTD;
        }
    }
#endif

    OS_FPutString(console, str);

    (void)OS_RestoreInterrupts(intr);

#else
#pragma unused(console, str)
#endif
}

/*---------------------------------------------------------------------------*
  Name:         OS_FPutStringDummy
  Description:  dummy OS_FPutString. do nothing.
 *---------------------------------------------------------------------------*/
static void OS_FPutStringDummy(int console, const char *str)
{
#pragma unused(console, str)
#ifndef SDK_FINALROM
	if ( OSi_FPutStringHook )
	{
		OSi_FPutStringHook(OS_FPUTSTRING_DUMMY, console, str);
	}
#endif
}

/*---------------------------------------------------------------------------*
  Name:         OS_FPutStringAris
  Description:  OS_FPutString for software simulator called ARIS
 *---------------------------------------------------------------------------*/
#ifndef SDK_FINALROM
#ifdef  SDK_CW_WARNOFF_SAFESTRB
#include <nitro/code32.h>
#endif
static void OS_FPutStringAris(int console, const char *str)
{
    char    c;

	if ( console == OS_PRINT_OUTPUT_CURRENT )
	{
		console = (int)OSi_PRINT_OUTPUT;
	}
	else if ( console == OS_PRINT_OUTPUT_ERROR )
	{
		console = (int)OSi_PRINT_ERR_OUTPUT;
	}

	if ( console != OS_PRINT_OUTPUT_NONE )
	{
		while ('\0' != (c = *str))
		{
			reg_OS_EMU_CONSOLE_OUT = (u8)c; // Console out
			str++;
		}
		if ( OSi_FPutStringHook )
		{
			OSi_FPutStringHook(OS_FPUTSTRING_ARIS, console, str);
		}
	}
}
#ifdef  SDK_CW_WARNOFF_SAFESTRB
#include <nitro/codereset.h>
#endif
#endif

/*---------------------------------------------------------------------------*
  Name:         OS_FPutStringISD
  Description:  OS_FPutString for hardware emulator called IS Debugger
 *---------------------------------------------------------------------------*/
#ifndef SDK_FINALROM
static void OS_FPutStringISD(int console, const char *str)
{
    // ISDPrint の初期化処理
    OS_InitLock();

	if ( console == OS_PRINT_OUTPUT_CURRENT )
	{
		console = (int)OSi_PRINT_OUTPUT;
	}
	else if ( console == OS_PRINT_OUTPUT_ERROR )
	{
		console = (int)OSi_PRINT_ERR_OUTPUT;
	}

	if ( console != OS_PRINT_OUTPUT_NONE )
	{
		ISDPrint(str);
		if ( OSi_FPutStringHook )
		{
			OSi_FPutStringHook(OS_FPUTSTRING_ISD, console, str);
		}
	}
}
#endif

/*---------------------------------------------------------------------------*
  Name:         OS_FPutStringISTD
  Description:  OS_FPutString for hardware emulator called IS TWL Debugger
 *---------------------------------------------------------------------------*/
#ifndef SDK_FINALROM
static void OS_FPutStringISTD(int console, const char *str)
{
    // ISTDPrintEx の初期化処理
    OS_InitLock();

	if ( console == OS_PRINT_OUTPUT_CURRENT )
	{
		console = (int)OSi_PRINT_OUTPUT;
	}
	else if ( console == OS_PRINT_OUTPUT_ERROR )
	{
		console = (int)OSi_PRINT_ERR_OUTPUT;
	}

	if ( console != OS_PRINT_OUTPUT_NONE )
	{
		ISTDPrintEx(console, str);
		if ( OSi_FPutStringHook )
		{
			OSi_FPutStringHook(OS_FPUTSTRING_ISTD, console, str);
		}
	}
}
#endif


/*---------------------------------------------------------------------------*
  Name:         OS_VPrintf/OS_TVPrintf

  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_ARM9) || defined(SDK_USE_VSNPRINTF)
SDK_WEAK_SYMBOL void OS_VPrintf(const char *fmt, va_list vlist)
{
	OS_VFPrintf(OS_PRINT_OUTPUT_CURRENT, fmt, vlist);
}
SDK_WEAK_SYMBOL void OS_VFPrintf(int console, const char *fmt, va_list vlist)
{
    (void)vsnprintf(common_buffer, sizeof(common_buffer), fmt, vlist);
    OS_FPutString(console, common_buffer);
}
#else
SDK_WEAK_SYMBOL void OS_VPrintf(const char *fmt, va_list vlist)
{
    OS_TVFPrintf(OS_PRINT_OUTPUT_CURRENT, fmt, vlist);
}
SDK_WEAK_SYMBOL void OS_VFPrintf(int console, const char *fmt, va_list vlist)
{
    OS_TVFPrintf(console, fmt, vlist);
}
#endif
SDK_WEAK_SYMBOL void OS_TVPrintf(const char *fmt, va_list vlist)
{
	OS_TVFPrintf(OS_PRINT_OUTPUT_CURRENT, fmt, vlist);
}
SDK_WEAK_SYMBOL void OS_TVFPrintf(int console, const char *fmt, va_list vlist)
{
    (void)OS_VSNPrintf(common_buffer, sizeof(common_buffer), fmt, vlist);
    OS_FPutString(console, common_buffer);
}
SDK_WEAK_SYMBOL void OS_TVPrintfEx(const char *fmt, va_list vlist)
{
	OS_TVFPrintfEx(OS_PRINT_OUTPUT_CURRENT, fmt, vlist);
}
SDK_WEAK_SYMBOL void OS_TVFPrintfEx(int console, const char *fmt, va_list vlist)
{
    (void)OS_VSNPrintfEx(common_buffer, sizeof(common_buffer), fmt, vlist);
    OS_FPutString(console, common_buffer);
}
#endif

/*---------------------------------------------------------------------------*
  Name:         OS_Printf/OS_TPrintf/OS_TPrintfEx
                OS_FPrintf/OS_TFPrintf/OS_TFPrintfEx

  Description:  print formatted strings.

  Arguments:    console : output window
                fmt : format string
                ...   and parameters

  Returns:      None.
 *---------------------------------------------------------------------------*/
#ifndef SDK_FINALROM
SDK_WEAK_SYMBOL void OS_Printf(const char *fmt, ...)
{
    va_list vlist;

    va_start(vlist, fmt);
    OS_VFPrintf(OS_PRINT_OUTPUT_CURRENT, fmt, vlist);
    va_end(vlist);
}

SDK_WEAK_SYMBOL void OS_FPrintf(int console, const char *fmt, ...)
{
    va_list vlist;

    va_start(vlist, fmt);
    OS_VFPrintf(console, fmt, vlist);
    va_end(vlist);
}

SDK_WEAK_SYMBOL void OS_TPrintf(const char *fmt, ...)
{
    va_list vlist;

    va_start(vlist, fmt);
    OS_TVFPrintf(OS_PRINT_OUTPUT_CURRENT, fmt, vlist);
    va_end(vlist);
}

SDK_WEAK_SYMBOL void OS_TFPrintf(int console, const char *fmt, ...)
{
    va_list vlist;

    va_start(vlist, fmt);
    OS_TVFPrintf(console, fmt, vlist);
    va_end(vlist);
}

SDK_WEAK_SYMBOL void OS_TPrintfEx(const char *fmt, ...)
{
    va_list vlist;

    va_start(vlist, fmt);
    OS_TVFPrintfEx(OS_PRINT_OUTPUT_CURRENT, fmt, vlist);
    va_end(vlist);
}

SDK_WEAK_SYMBOL void OS_TFPrintfEx(int console, const char *fmt, ...)
{
    va_list vlist;

    va_start(vlist, fmt);
    OS_TVFPrintfEx(console, fmt, vlist);
    va_end(vlist);
}
#endif


/*---------------------------------------------------------------------------*
  Name:         OSi_Warning / OSi_TWarning

  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 OSi_Warning(const char *file, int line, const char *fmt, ...)
{
    va_list vlist;

    va_start(vlist, fmt);
    OS_FPrintf(OS_PRINT_OUTPUT_ERROR, "%s:%d Warning:", file, line);
    OS_VFPrintf(OS_PRINT_OUTPUT_ERROR, fmt, vlist);
    OS_FPrintf(OS_PRINT_OUTPUT_ERROR, "\n");
    va_end(vlist);
}


SDK_WEAK_SYMBOL void OSi_TWarning(const char *file, int line, const char *fmt, ...)
{
    va_list vlist;

    va_start(vlist, fmt);
    OS_TPrintf("%s:%d Warning:", file, line);
    OS_TVPrintf(fmt, vlist);
    OS_TPrintf("\n");
    va_end(vlist);
}
#endif

/*---------------------------------------------------------------------------*
  Name:         OSi_Panic/OSi_TPanic

  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
SDK_WEAK_SYMBOL void OSi_Panic(const char *file, int line, const char *fmt, ...)
{
    va_list vlist;

    va_start(vlist, fmt);
    (void)OS_DisableInterrupts();
    OS_FPrintf(OS_PRINT_OUTPUT_ERROR, "%s:%d Panic:", file, line);
    OS_VFPrintf(OS_PRINT_OUTPUT_ERROR, fmt, vlist);
    OS_FPrintf(OS_PRINT_OUTPUT_ERROR, "\n");
    OSi_FuncTerminate();               // Never Returns
}

SDK_WEAK_SYMBOL void OSi_TPanic(const char *file, int line, const char *fmt, ...)
{
    va_list vlist;

    va_start(vlist, fmt);
    (void)OS_DisableInterrupts();
    OS_TPrintf("%s:%d Panic:", file, line);
    OS_TVPrintf(fmt, vlist);
    OS_TPrintf("\n");
    OSi_FuncTerminate();               // Never Returns
}
#endif

/*---------------------------------------------------------------------------*
  Name:         OSi_FWarning / OSi_TFWarning

  Description:  print warning message.

  Arguments:    console : output window
                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 OSi_FWarning(int console, const char *file, int line, const char *fmt, ...)
{
    va_list vlist;

    va_start(vlist, fmt);
    OS_FPrintf(console, "%s:%d Warning:", file, line);
    OS_VFPrintf(console, fmt, vlist);
    OS_FPrintf(console, "\n");
    va_end(vlist);
}


SDK_WEAK_SYMBOL void OSi_TFWarning(int console, const char *file, int line, const char *fmt, ...)
{
    va_list vlist;

    va_start(vlist, fmt);
    OS_TFPrintf(console, "%s:%d Warning:", file, line);
    OS_TVFPrintf(console, fmt, vlist);
    OS_TFPrintf(console, "\n");
    va_end(vlist);
}
#endif

/*---------------------------------------------------------------------------*
  Name:         OSi_FPanic/OSi_TFPanic

  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
SDK_WEAK_SYMBOL void OSi_FPanic(int console, const char *file, int line, const char *fmt, ...)
{
    va_list vlist;

    va_start(vlist, fmt);
    (void)OS_DisableInterrupts();
    OS_FPrintf(console, "%s:%d Panic:", file, line);
    OS_VFPrintf(console, fmt, vlist);
    OS_FPrintf(console, "\n");
    OSi_FuncTerminate();               // Never Returns
}

SDK_WEAK_SYMBOL void OSi_TFPanic(int console, const char *file, int line, const char *fmt, ...)
{
    va_list vlist;

    va_start(vlist, fmt);
    (void)OS_DisableInterrupts();
    OS_TFPrintf(console, "%s:%d Panic:", file, line);
    OS_TVFPrintf(console, fmt, vlist);
    OS_TFPrintf(console, "\n");
    OSi_FuncTerminate();               // Never Returns
}
#endif

/*---------------------------------------------------------------------------*
  Name:         OS_InitPrintServer

  Description:  initialize print server
                - this code should be called before OS_InitLock()
                - OS_InitLock() are also called from OS_Init()

  Arguments:    None

  Returns:      None
 *---------------------------------------------------------------------------*/
#ifdef   SDK_ARM9
#ifndef  SDK_FINALROM
void OS_InitPrintServer(void)
{
    OSPrintServerBuffer *p;

    // first, allocate buffer and register it.
    OS_InitArena();
    p = OS_AllocFromSharedArenaHi(sizeof(OSPrintServerBuffer), 4);
    p->in = p->out = 0UL;
    PXI_SetComponentParam((u32)p);
}
#endif //SDK_FINALROM
#endif //SDK_ARM9


/*---------------------------------------------------------------------------*
  Name:         OS_PrintServer

  Description:  print a string comes from ARM7 via shared memory

  Arguments:    str  :   string

  Returns:      None.
 *---------------------------------------------------------------------------*/
#ifdef   SDK_ARM9
#ifndef  SDK_FINALROM
#include <nitro/code32.h>              // to access 'common_buffer' via cache
void OS_PrintServer(void)
{
    OSPrintServerBuffer *p;
    register OSPrintWChar word;

    u32     in, out;
    int     i;

    p = (OSPrintServerBuffer *)PXI_GetComponentParam();

    //---- If print buffer isn't set up, do nothing.
    if (!p)
    {
        return;
    }

    out = p->out;
    in = p->in;

    while (in != out)
    {
        i = 0;

        while (in != out && i < sizeof(common_buffer) - 3)
        {
            word.s = p->buffer[out].s;
            if (word.c[0])
            {
                common_buffer[i++] = word.c[0]; // store via cache

                if (word.c[1])
                {
                    common_buffer[i++] = word.c[1];     // store via cache
                }
            }
            out++;
            if (out >= OS_PRINTSRV_WCHARSIZE)
                out = 0;
        }
        common_buffer[i] = '\0';       // store via cache
        OS_FPutString(OS_PRINT_OUTPUT_CURRENT, common_buffer);
    }

    // tell finished
    p->out = out;
}

#include <nitro/codereset.h>
#endif //SDK_FINALROM
#endif //SDK_ARM9


/*---------------------------------------------------------------------------*
  Name:         OS_FPutStringPrnSrv

  Description:  sends string to ARM9 via shared memory

  Arguments:    console : output window
                str     : string to be sent

  Returns:      None
 *---------------------------------------------------------------------------*/
#ifdef  SDK_ARM7
static void OS_FPutStringPrnSrv(int console, const char *str)
{
#pragma unused(console, str)
#ifndef  SDK_FINALROM
    OSPrintServerBuffer *p;
    register OSPrintWChar word;

    u32     in, in_tmp, out;
    u32     isOdd;

	if ( console == OS_PRINT_OUTPUT_NONE )
	{
		return;
	}

    p = (OSPrintServerBuffer *)PXI_GetComponentParam();
    in = p->in;
    out = p->out;
    isOdd = ((u32)str) & 1;

    while (1)
    {
        in_tmp = in + 1;
        if (in_tmp >= OS_PRINTSRV_WCHARSIZE)
            in_tmp = 0;
        if (out == in_tmp)
            break;                     // Buffer full, then exit

        if (isOdd)
        {
            p->buffer[in].s = word.s = (u16)((*(u16 *)(str - 1)) & 0xff00);
            str++;
            isOdd = 0UL;
            if (!word.c[1])
                break;
            in = in_tmp;
        }
        else
        {
            p->buffer[in].s = word.s = *(u16 *)str;
            str += 2;
            if (!word.c[0])
                break;
            in = in_tmp;
            if (!word.c[1])
                break;
        }
    }
    p->in = in;
#endif //SDK_FINALROM
}
#endif //SDK_ARM7


#if defined(SDK_CW_WARNOFF_SAFESTRB)
#include <nitro/code32.h>
#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:         OS_SPrintf

  Description:  equal to 'OS_VSPrintf' except argument style.

  Arguments:    dst   : destination buffer.
                fmt   : format string.

  Returns:      length of the generated string.
 *---------------------------------------------------------------------------*/
SDK_WEAK_SYMBOL int OS_SPrintf(char *dst, const char *fmt, ...)
{
    int     ret;
    va_list va;
    va_start(va, fmt);
    ret = OS_VSPrintf(dst, fmt, va);
    va_end(va);
    return ret;
}

/*---------------------------------------------------------------------------*
  Name:         OS_VSPrintf

  Description:  equal to 'OS_VSNPrintf' except buffer size argument.

  Arguments:    dst   : destination buffer.
                fmt   : format string.
                vlist : parameters.

  Returns:      length of the generated string.
 *---------------------------------------------------------------------------*/
SDK_WEAK_SYMBOL int OS_VSPrintf(char *dst, const char *fmt, va_list vlist)
{
    return OS_VSNPrintf(dst, 0x7FFFFFFF, fmt, vlist);
}

/*---------------------------------------------------------------------------*
  Name:         OS_SNPrintf

  Description:  equal to 'OS_VSNPrintf' 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 OS_SNPrintf(char *dst, size_t len, const char *fmt, ...)
{
    int     ret;
    va_list va;
    va_start(va, fmt);
    ret = OS_VSNPrintf(dst, len, fmt, va);
    va_end(va);
    return ret;
}

/*---------------------------------------------------------------------------*
  Name:         OS_VSNPrintf

  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 OS_VSNPrintf(char *dst, size_t len, const char *fmt, va_list vlist)
{
    return STD_TVSNPrintf(dst, len, fmt, vlist);
}

/*---------------------------------------------------------------------------*
  Name:         OS_VSNPrintfEx

  Description:  OS_VSNPrintfEx is an addition of binary output to OS_VSNPrintfEx.

  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 OS_VSNPrintfEx(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_binary = 020000,  /* 'b' */
                flag_colon = 040000,   /* ':' */
                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;
                case ':':
                    flag |= flag_colon;
                    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':                 /* unicode-character */
                flag |= flag_l1;
            case 'c':                 /* character */
                if (precision >= 0)
                    goto put_invalid;
                {
                    int     c = va_arg(vlist, int);
                    width -= 1;
                    if (flag & flag_minus)
                    {
                        if (flag & flag_l1)
                        {
                            char    dst[2];

                            if (STD_ConvertCharUnicodeToSjis(dst, (u16)c) == 1)
                            {
                                string_put_char(&str, dst[0]);
                            }
                            else
                            {
                                string_put_string(&str, dst, 2);
                            }
                        }
                        else
                        {
                            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);

                        if (flag & flag_l1)
                        {
                            char    dst[2];

                            if (STD_ConvertCharUnicodeToSjis(dst, (u16)c) == 1)
                            {
                                string_put_char(&str, dst[0]);
                            }
                            else
                            {
                                string_put_string(&str, dst, 2);
                            }
                        }
                        else
                        {
                            string_put_char(&str, (char)c);
                        }
                    }
                    ++s;
                }
                break;

            case 'S':                 /* unicode-string */
                flag |= flag_l1;
            case 's':                 /* string */
                {
                    int     n_buf = 0;
                    const char *p_buf = va_arg(vlist, const char *);

                    if (precision < 0)
                    {
                        while (p_buf[n_buf])
                        {
                            if (flag & flag_l1)
                            {
                                n_buf += 2;
                            }
                            else
                            {
                                ++n_buf;
                            }
                        }
                    }
                    else
                    {
                        while ((n_buf < precision) && p_buf[n_buf])
                        {
                            if (flag & flag_l1)
                            {
                                n_buf += 2;
                            }
                            else
                            {
                                ++n_buf;
                            }
                        }
                    }
                    if (flag & flag_l1)
                    {
                        n_buf /= 2;
                    }
                    width -= n_buf;
                    if (flag & flag_minus)
                    {
                        if (flag & flag_l1)
                        {
                            u16    *w_buf = (u16 *)p_buf;
                            char    dst[2];

                            while (n_buf--)
                            {
                                if (STD_ConvertCharUnicodeToSjis(dst, *w_buf) == 1)
                                {
                                    string_put_char(&str, dst[0]);
                                }
                                else
                                {
                                    string_put_string(&str, dst, 2);
                                }
                                w_buf++;
                            }
                        }
                        else
                        {
                            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);

                        if (flag & flag_l1)
                        {
                            u16    *w_buf = (u16 *)p_buf;
                            char    dst[2];

                            while (n_buf--)
                            {
                                if (STD_ConvertCharUnicodeToSjis(dst, *w_buf) == 1)
                                {
                                    string_put_char(&str, dst[0]);
                                }
                                else
                                {
                                    string_put_string(&str, dst, 2);
                                }
                                w_buf++;
                            }
                        }
                        else
                        {
                            string_put_string(&str, p_buf, n_buf);
                        }
                    }
                    ++s;
                }
                break;

            case 'B':                 /* binary */
                hex_char = 'A' - 10;
            case 'b':                 /* binary */
                {
                    int     n_buf = width;
                    int     count = 0;
                    char   *p_buf = va_arg(vlist, char *);
                    int     column_count;

                    if (n_buf <= 0)
                    {
                        n_buf = 1;
                    }
                    if (precision == 0)
                    {
                        precision = -1;
                    }
                    column_count = precision;

                    while (1)
                    {
                        char    left_char, right_char;
                        int     left = ((p_buf[count] & 0xF0) >> 4);
                        int     right = (p_buf[count] & 0x0F);

                        left_char = (char)((left < 10) ? (left + '0') : (left + hex_char));
                        right_char = (char)((right < 10) ? (right + '0') : (right + hex_char));

                        string_put_char(&str, left_char);
                        string_put_char(&str, right_char);

                        count++;

                        if (count >= n_buf)
                            break;

                        if (column_count > 0)
                        {
                            --column_count;
                        }

                        if (column_count == 0)
                        {
                            string_put_char(&str, '\n');
                            if ((flag & flag_blank) && !(flag & flag_minus))
                            {
                                string_put_char(&str, ' ');
                            }
                            column_count = precision;
                        }
                        else if (flag & flag_colon)
                        {
                            string_put_char(&str, ':');
                        }
                        else if (flag & flag_blank)
                        {
                            string_put_char(&str, ' ');
                        }
                    }
                    ++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(SDK_RX) || 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 <nitro/codereset.h>
#endif

//================================================================================
//              Set output of print (for IS-TWL-DEBUGGER)
//================================================================================
#ifndef SDK_FINALROM
#ifdef  SDK_LINK_ISTD
/*---------------------------------------------------------------------------*
  Name:         OS_SetPrintOutput

  Description:  Specify the output window OS_Printf uses.
                This is only for IS-TWL-DEBUGGER.

  Arguments:    proc : processor
                         OS_PRINT_OUTPUT_PROC_ARM9     : ARM9
                         OS_PRINT_OUTPUT_PROC_ARM7     : ARM7
                         OS_PRINT_OUTPUT_PROC_ARM9ERR  : ARM9 for error
                         OS_PRINT_OUTPUT_PROC_ARM7ERR  : ARM7 for error
                num  : window number (0-3)

  Returns:      None.
 *---------------------------------------------------------------------------*/
void OS_SetPrintOutput( OSPrintOutputProc proc, int num )
{
    SDK_ASSERT( (0<=num && num<=OS_PRINT_OUTPUT_WINDOW_MAX) || num==OS_PRINT_OUTPUT_NONE );
    SDK_ASSERT( 0<=(int)proc && (int)proc<=OS_PRINT_OUTPUT_PROC_ARM7ERR );
	*(u8*)(HW_PRINT_OUTPUT_ARM9 + proc) = (u8)num;
}
#endif
#endif

//================================================================================
//              FPutString hook
//================================================================================
#ifndef SDK_FINALROM
/*---------------------------------------------------------------------------*
  Name:         OS_SetFPutStringHook

  Description:  set hook function in FPutString

  Arguments:    func : hook function

  Returns:      None.
 *---------------------------------------------------------------------------*/
void OS_SetFPutStringHook( OSFPutStringHookType func )
{
	OSi_FPutStringHook = func;
}

/*---------------------------------------------------------------------------*
  Name:         OS_GetFPutStringHook

  Description:  get hook function in FPutString

  Arguments:    None.

  Returns:      hook function
 *---------------------------------------------------------------------------*/
OSFPutStringHookType OS_GetFPutStringHook(void)
{
	return OSi_FPutStringHook;
}
#endif

//================================================================================
//              DUMMY PRINT (stub for FINALROM)
//================================================================================
#ifdef SDK_FINALROM
#ifdef OS_Printf
#undef OS_Printf
#endif
void    OS_Printf(const char *fmt, ...);
void OS_Printf(const char *fmt, ...)
{
#pragma unused( fmt )
}
#endif

//================================================================================
//              Blocking mode
//================================================================================
/*---------------------------------------------------------------------------*
  Name:         OS_SetPrintBlocking

  Description:  set blocking mode on debug print

  Arguments:    sw : blocking mode to set.
                     TRUE if want to block mode, or FALSE if not.

  Returns:      None
 *---------------------------------------------------------------------------*/
#ifndef SDK_FINALROM
void ISTDPrintSetBlockingMode(BOOL);
void ISDPrintSetBlockingMode(int);

void OS_SetPrintBlockingMode( BOOL sw )
{
    if ( OS_IsRunOnTwl() )
    {
        ISTDPrintSetBlockingMode( sw );
    }
    else
    {
        ISDPrintSetBlockingMode( (int)sw );
    }
}
#endif // ifndef SDK_FINALROM

/*---------------------------------------------------------------------------*
  Name:         OS_GetPrintBlockingMode

  Description:  get blocking mode on debug print

  Arguments:    None.

  Returns:      blocking mode.
                TRUE if blocking is available, FALSE if not.
 *---------------------------------------------------------------------------*/
#ifndef SDK_FINALROM
BOOL ISTDPrintGetBlockingMode(void);
int  ISDPrintGetBlockingMode(void);

BOOL OS_GetPrintBlockingMode( void )
{
    if ( OS_IsRunOnTwl() )
    {
        return ISTDPrintGetBlockingMode()? TRUE: FALSE;
    }
    else
    {
        return ISDPrintGetBlockingMode()? TRUE: FALSE;
    }
}
#endif // ifndef SDK_FINALROM
