Home

Resume

Blog

Teikitu


// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-==-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= //
//  »Project«   Teikitu Gaming System (TgS) (∂)
//  »File«      TgS Common - Console.c
//  »Author«    Andrew Aye (EMail: mailto:andrew.aye@gmail.com, Web: http://www.andrewaye.com)
//  »Version«   4.0
// ------------------------------------------------------------------------------------------------------------------------------ //
//  Copyright: © 2002-2010, Andrew Aye.  All Rights Reserved.
//  This software is free for non-commercial use. Redistribution and use in source and binary forms, with or without modification,
//  are permitted provided that the following conditions are met: 
//    Redistributions of source code must retain this copyright notice, this list of conditions and the following disclaimers. 
//    Redistributions in binary form must reproduce this copyright notice, this list of conditions and the following
//      disclaimers in the documentation and other materials provided with the distribution. 
//  Neither the names of the copyright owner nor the names of its contributors may be used to endorse or promote products derived
//  from this software without specific prior written permission. 
//  The intellectual property rights of the algorithms used reside with Andrew Aye.  You may not use this software, in whole or
//  in part, in support of any commercial product without the express written consent of the author.
//  There is no warranty or other guarantee of fitness of this software for any purpose. It is provided solely "as is".
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-==-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= //

#if TgS_COMPILE_CONSOLE
#include <string.h>


// -.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-. //
//  Constants
// -.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-. //

enum { ETgCON_OSCON_LINE_LENGTH             = 256 }; //< Maximum length of a console command/buffer/display line
enum { ETgCON_OSCON_MAX_LINE                = 256 }; //< Number of console lines retained in the buffer
enum { ETgCON_OSLOG_LINE_LENGTH             = 128 }; //< Maximum length of an on screen log line
enum { ETgCON_OSLOG_MAX_LINE                = 52  }; //< Number of on screen log lines

//enum { ETgCON_MAX_FCN_LIST                  = 256 }; //< Max

enum { ETgCON_MAX_COMMAND_FCN               = 256 }; //< Maximum number of functions that can be registered with the console

enum { ETgCON_MAX_CHANNEL                   = ETgCON_CHANEL_BITS }; //<Maximum number of channels in the system
enum { ETgCON_MAX_CHANNEL_OUTPUT            = 8 }; //< Maximum number of output devices that can be attached to a single channel




// -.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-. //
//  Type Definitions
// -.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-. //

TgTYPE_DECLARE( struct STg2_OnScreen_Line, STg2_OnScreen_Line );
struct STg2_OnScreen_Line
{
    TgCHAR                                      m_szLog[ETgCON_OSLOG_LINE_LENGTH];
    TgUINT32                                    m_uiSeverity;
    TgUINT32                                    m_uiUID;
    TgFLOAT32                                   m_fLife_Time;
    P_STg2_OnScreen_Line                        m_psNext;
};




// -.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-. //
//  File Local Data and Functions
// -.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-. //

                                            // System state and Configuration
static STg1_MP_CS                           s_tgSystem_Lock;
static ETgMODULE_STATE                      s_enConsole_State = ETgMODULE_STATE_FREED;

                                            // On Screen Console
static STg1_MP_CS                           s_tgOS_Lock; // Lock for the On Screen Buffers
static TgCHAR                               s_szOS_Console_Buffer[ETgCON_OSCON_MAX_LINE][ETgCON_OSCON_LINE_LENGTH];
static TgSINT32                             s_iOS_Console_Buffer_Index;
static TgSINT32                             s_iOS_Console_Cmd_Index;
static TgSINT32                             s_iOS_Console_Display_Index;
static TgSINT32                             s_iOS_Console_Cmd_History_Index;
static TgBOOL                               s_bOS_Console_Render;
static TgSINT32                             s_iOS_Console_Render_Page_Height;
static TgSINT32                             s_iOS_Console_Render_Font_Height;
static STg2_OnScreen_Line                   s_atgOS_Log_Data[ETgCON_OSLOG_MAX_LINE];
static P_STg2_OnScreen_Line                 s_ptgOS_Log_Display;
static P_STg2_OnScreen_Line                 s_ptgOS_Log_Free;
static TgFLOAT32                            s_fOS_Log_Default_Life_Time;

                                            // Console Command List
static TgFCN_CONSOLE                        s_atgFcn_List[ETgCON_MAX_COMMAND_FCN];
static TgUINT32                             s_auiFcn_Hash[ETgCON_MAX_COMMAND_FCN];
static CP_TgCHAR                            s_apzFcn_Name[ETgCON_MAX_COMMAND_FCN];
static CP_TgCHAR                            s_apzFcn_Desc[ETgCON_MAX_COMMAND_FCN];

                                            // Channel Descriptors and Information
static CP_TgCHAR                            s_pzOutput_Prefix[ETgCON_MAX_CHANNEL];
static TgUINT32                             s_nuiOutput_Prefix[ETgCON_MAX_CHANNEL];
static TgBOOL                               s_abOutput_Prefix[ETgCON_MAX_CHANNEL]; // Tracks whether we are at the start of a line
static P_STg2_Output                        s_aptgOutput[ETgCON_MAX_CHANNEL][ETgCON_MAX_CHANNEL_OUTPUT];
static TgUINT32                             s_auiUID_Filter[ETgCON_MAX_CHANNEL];
static TgUINT32                             s_auiSeverity_Filter[ETgCON_MAX_CHANNEL];

                                            // Default Functors
static STg2_Output                          s_tgDefault_Output_Break;
static STg2_Output                          s_tgDefault_Output_Abort;

                                            // Default Prefixes for channel output
static C_TgCHAR                             s_szPrefix_Console[] = TgT("CON: ");
static C_TgCHAR                             s_szPrefix_Log_Screen[] = TgT("SCN: ");
static C_TgCHAR                             s_szPrefix_Log[] = TgT("LOG: ");
static C_TgCHAR                             s_szPrefix_Log_FCN[] = TgT("FCN: ");
static C_TgCHAR                             s_szPrefix_Log_MEM[] = TgT("MEM: ");
static C_TgCHAR                             s_szPrefix_Message[] = TgT("MSG: ");
static C_TgCHAR                             s_szPrefix_Warning[] = TgT("WRN: ");
static C_TgCHAR                             s_szPrefix_Error[] = TgT("ERR: ");
static C_TgCHAR                             s_szPrefix_Critical[] = TgT("CRT: ");
static C_TgCHAR                             s_szPrefix_Initialize[] = TgT("INT: ");
static C_TgCHAR                             s_szPrefix_Loading[] = TgT("LDN: ");




// -.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-. //
//  File Local Functions
// -.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-. //

static TgVOID                               tgCN_UID_Print_Internal( C_TgUINT32 uiUID, C_TgUINT32 uiChannel_Mask, CP_TgCHAR pszText );
static TgVOID                               tgCN_Scroll_Display( C_TgSINT32 niLN, C_TgSINT32 niPG );

static TgSINT32                             tgCN_Default_Break_Write( PC_STg2_Output, CPC_TgUINT08, C_TgSINT32 );
static TgSINT32                             tgCN_Default_Break_Seek( PC_STg2_Output, C_ETgIO_SEEK, C_TgSINT32 );

static TgSINT32                             tgCN_Default_Abort_Write( PC_STg2_Output, CPC_TgUINT08, C_TgSINT32 );
static TgSINT32                             tgCN_Default_Abort_Seek( PC_STg2_Output, C_ETgIO_SEEK, C_TgSINT32 );




// -.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-. //
//  Public Functions
// -.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-. //

// ---- tgCN_Init_Module -------------------------------------------------------------------------------------------------------- //
// ------------------------------------------------------------------------------------------------------------------------------ //
TgRESULT tgCN_Init_Module()
{
    TgCOMPILER_ASSERT((1 << ETgCON_SEVERITY_BITS) > 3,0);

    TgUINT32                            uiIndex;

    // Verify the state of the system
    TgASSERT(ETgMODULE_STATE_FREED == s_enConsole_State);
    s_enConsole_State = ETgMODULE_STATE_INITIALIZING;

    // Initialize system variables
    TgVERIFY(TgS_OK == tgCM_MP_CS_Init( &s_tgSystem_Lock ));

    // Initialize On Screen Console and Logging
    tgCM_MP_CS_Init( &s_tgOS_Lock );
    memset( s_szOS_Console_Buffer, 0x00, sizeof( s_szOS_Console_Buffer ));
    s_iOS_Console_Buffer_Index = 0;
    s_iOS_Console_Cmd_Index = 0;
    s_iOS_Console_Display_Index = 0;
    s_iOS_Console_Cmd_History_Index = 0;
    s_bOS_Console_Render = TgFALSE;
    s_iOS_Console_Render_Page_Height = 0;
    s_iOS_Console_Render_Font_Height = 0;
    memset( s_atgOS_Log_Data, 0x00, sizeof( s_atgOS_Log_Data ) );
    s_ptgOS_Log_Display = NULL;
    s_ptgOS_Log_Free = s_atgOS_Log_Data;
    s_fOS_Log_Default_Life_Time = 10.0F;

    // Attach sequential elements of the on screen log link list
    for (uiIndex = 0; uiIndex + 1 < ETgCON_OSLOG_MAX_LINE; ++uiIndex)
    {
        s_atgOS_Log_Data[uiIndex].m_psNext = s_atgOS_Log_Data + uiIndex + 1;
    };

    // Initialize Console Command List
    memset( s_atgFcn_List, 0x00, sizeof(s_atgFcn_List) );
    for (uiIndex = 0; uiIndex < ETgCON_MAX_COMMAND_FCN; ++uiIndex)
    {
        s_auiFcn_Hash[uiIndex] = KTgEMPTY_HASH;
    };
    memset( (P_TgVOID)s_apzFcn_Name, 0x00, sizeof(s_apzFcn_Name) );
    memset( (P_TgVOID)s_apzFcn_Desc, 0x00, sizeof(s_apzFcn_Desc) );

    // Initialize Channel Descriptors and Information
    memset( s_nuiOutput_Prefix, 0x00, sizeof( s_nuiOutput_Prefix ) );
    memset( s_aptgOutput, 0x00, sizeof( s_aptgOutput ) );
    for (uiIndex = 0; uiIndex < ETgCON_MAX_CHANNEL; ++uiIndex)
    {
        s_pzOutput_Prefix[uiIndex] = NULL;
        s_abOutput_Prefix[uiIndex] = TgTRUE;
        s_auiUID_Filter[uiIndex] = ETgUID_NONE;
        s_auiSeverity_Filter[uiIndex] = (1 << ETgCON_SEVERITY_BITS) - 3;
    };

    // Create the two default output objects
    s_tgDefault_Output_Break.m_pfnWrite = tgCN_Default_Break_Write;
    s_tgDefault_Output_Break.m_pfnSeek = tgCN_Default_Break_Seek;
    s_tgDefault_Output_Break.m_pfnClose = NULL;
    s_tgDefault_Output_Abort.m_pfnWrite = tgCN_Default_Abort_Write;
    s_tgDefault_Output_Abort.m_pfnSeek = tgCN_Default_Abort_Seek;
    s_tgDefault_Output_Abort.m_pfnClose = NULL;

    // Preset some of the variables
    s_pzOutput_Prefix[ 0] = s_szPrefix_Console;
    s_nuiOutput_Prefix[ 0] = tgSZ_Length( s_pzOutput_Prefix[ 0] );
    s_pzOutput_Prefix[ 1] = s_szPrefix_Log_Screen;
    s_nuiOutput_Prefix[ 1] = tgSZ_Length( s_pzOutput_Prefix[ 1] );
    s_pzOutput_Prefix[ 2] = s_szPrefix_Log;
    s_nuiOutput_Prefix[ 2] = tgSZ_Length( s_pzOutput_Prefix[ 2] );
    s_pzOutput_Prefix[ 3] = s_szPrefix_Log_FCN;
    s_nuiOutput_Prefix[ 3] = tgSZ_Length( s_pzOutput_Prefix[ 3] );
    s_pzOutput_Prefix[ 4] = s_szPrefix_Log_MEM;
    s_nuiOutput_Prefix[ 4] = tgSZ_Length( s_pzOutput_Prefix[ 4] );
    s_pzOutput_Prefix[ 5] = s_szPrefix_Message;
    s_nuiOutput_Prefix[ 5] = tgSZ_Length( s_pzOutput_Prefix[ 5] );
    s_pzOutput_Prefix[ 6] = s_szPrefix_Warning;
    s_nuiOutput_Prefix[ 6] = tgSZ_Length( s_pzOutput_Prefix[ 6] );
    s_pzOutput_Prefix[ 7] = s_szPrefix_Error;
    s_nuiOutput_Prefix[ 7] = tgSZ_Length( s_pzOutput_Prefix[ 7] );
    s_pzOutput_Prefix[ 8] = s_szPrefix_Critical;
    s_nuiOutput_Prefix[ 8] = tgSZ_Length( s_pzOutput_Prefix[ 8] );
    s_pzOutput_Prefix[ 9] = s_szPrefix_Initialize;
    s_nuiOutput_Prefix[ 9] = tgSZ_Length( s_pzOutput_Prefix[ 9] );
    s_pzOutput_Prefix[10] = s_szPrefix_Loading;
    s_nuiOutput_Prefix[10] = tgSZ_Length( s_pzOutput_Prefix[10] );

    s_enConsole_State = ETgMODULE_STATE_INITIALIZED;
    return (TgS_OK);
}


// ---- tgCN_Boot_Module -------------------------------------------------------------------------------------------------------- //
// ------------------------------------------------------------------------------------------------------------------------------ //
TgRESULT tgCN_Boot_Module()
{
    // Verify the state of the system
    TgASSERT(ETgMODULE_STATE_INITIALIZED == s_enConsole_State);
    s_enConsole_State = ETgMODULE_STATE_BOOTED;
    return (TgS_OK);
}


// ---- tgCN_Stop_Module -------------------------------------------------------------------------------------------------------- //
// ------------------------------------------------------------------------------------------------------------------------------ //
TgVOID tgCN_Stop_Module()
{
    if (ETgMODULE_STATE_FREED == s_enConsole_State || ETgMODULE_STATE_INITIALIZED == s_enConsole_State)
    {
        return;
    };

    // Verify the state of the system
    TgASSERT(ETgMODULE_STATE_BOOTED == s_enConsole_State);
    s_enConsole_State = ETgMODULE_STATE_STOPPED;
}


// ---- tgCN_Free_Module -------------------------------------------------------------------------------------------------------- //
// ------------------------------------------------------------------------------------------------------------------------------ //
TgVOID tgCN_Free_Module()
{
    TgUINT32                            uiIndex;

    if (ETgMODULE_STATE_FREED == s_enConsole_State)
    {
        return;
    };

    tgCM_MP_CS_Enter_Block( &s_tgSystem_Lock );

    // Verify the state of the system
    TgASSERT(ETgMODULE_STATE_STOPPED == s_enConsole_State || ETgMODULE_STATE_INITIALIZED == s_enConsole_State);
    s_enConsole_State = ETgMODULE_STATE_FREEING;

    // Clear out all of the console functions registered with the system.  Entities that install console functions are expected to
    // remove them when no longer needed.  Thus, by the time this module is freed all functions should have been removed.
    for (uiIndex = 0; uiIndex < ETgCON_MAX_COMMAND_FCN; ++uiIndex)
    {
        if (KTgEMPTY_HASH != s_auiFcn_Hash[uiIndex])
        {
            TgWARNING_MSGF( 0, TgT("%-16.16s(%-32.32s): Console command was not removed.\n"), TgT("Console"), TgT("Free_Module") );
            s_auiFcn_Hash[uiIndex] = 0;
            s_atgFcn_List[uiIndex] = 0;
            s_apzFcn_Name[uiIndex] = 0;
            s_apzFcn_Desc[uiIndex] = 0;
        }
    }

    // Remove all output objects from the system.  Similar to above, proper life time management requires the installing entity to
    // remove the object from the console when it is no longer needed or used.  At this point, they should have all been removed.
    for (uiIndex = 0; uiIndex < ETgCON_MAX_CHANNEL; ++uiIndex)
    {
        while (0 != s_aptgOutput[uiIndex][0])
        {
            TgWARNING_MSGF( 0, TgT("%-16.16s(%-32.32s): Output was not removed.\n"), TgT("Console"), TgT("Free_Module") );
            tgCN_Remove_Output( 1 << (uiIndex + ETgCON_SEVERITY_BITS), s_aptgOutput[uiIndex][0] );
        };
    };

    // Clear out all the prefix headers
    memset( s_abOutput_Prefix, 0x00, sizeof(s_abOutput_Prefix) );

    // Clear out on screen displays
    s_iOS_Console_Cmd_Index = 0;
    s_iOS_Console_Buffer_Index = 0;
    s_iOS_Console_Display_Index = 0;

    s_ptgOS_Log_Display = NULL;
    s_ptgOS_Log_Free = s_atgOS_Log_Data;
    for (uiIndex = 0; uiIndex + 1 < ETgCON_OSLOG_MAX_LINE; ++uiIndex)
    {
        s_atgOS_Log_Data[uiIndex].m_psNext = s_atgOS_Log_Data + uiIndex + 1;
    };

    // Destroy the critical section
    tgCM_MP_CS_Exit( &s_tgSystem_Lock );
    tgCM_MP_CS_Free( &s_tgOS_Lock );
    tgCM_MP_CS_Free( &s_tgSystem_Lock );

    s_enConsole_State = ETgMODULE_STATE_FREED;
}


// ---- tgCN_Update_Module ------------------------------------------------------------------------------------------------------ //
// Note: This should be called on the primary render thread so that messages will have at least one frame to render
// ------------------------------------------------------------------------------------------------------------------------------ //
TgRESULT tgCN_Update_Module( C_TgFLOAT32 fDT )
{
    P_STg2_OnScreen_Line                ptgOS_Log_Display, ptgOS_Log_Display_Prev;

    tgCM_MP_CS_Enter_Block( &s_tgOS_Lock );

    // Verify the state of the system (technically should have the system lock for comparison but its validation only)
    TgASSERT(ETgMODULE_STATE_BOOTED == s_enConsole_State);

    ptgOS_Log_Display = s_ptgOS_Log_Display;
    ptgOS_Log_Display_Prev = NULL;

    // Update all messages, removing those whose life is now negative and updating the time for active messages
    while (NULL != ptgOS_Log_Display)
    {
        PC_STg2_OnScreen_Line               ptgOS_Log_Display_Next = ptgOS_Log_Display->m_psNext;

        // If the life time of this message is no longer non-negative move it from the display list to the free list.  Otherwise
        // decrement the life time of the message by the time delta.
        if (0.0F >= ptgOS_Log_Display->m_fLife_Time)
        {
            if (NULL != ptgOS_Log_Display_Prev)
            {
                ptgOS_Log_Display_Prev->m_psNext = ptgOS_Log_Display_Next;
            }
            else
            {
                s_ptgOS_Log_Display = ptgOS_Log_Display_Next;
            };
            ptgOS_Log_Display->m_psNext = s_ptgOS_Log_Free;
            s_ptgOS_Log_Free = ptgOS_Log_Display;
        }
        else
        {
            ptgOS_Log_Display->m_fLife_Time -= fDT;
        };

        ptgOS_Log_Display = ptgOS_Log_Display_Next;
    };

    tgCM_MP_CS_Exit( &s_tgOS_Lock );
    return (TgS_OK);
}


// ---- tgCN_Attach_Output ------------------------------------------------------------------------------------------------------ //
// Add the output object to the top of the stack of each of the channel's selected in the mask.  Return the value of all the
// channels that the object was successfully added.
// ------------------------------------------------------------------------------------------------------------------------------ //
TgUINT32 tgCN_Attach_Output( C_TgUINT32 uiChannel_Mask, PC_STg2_Output ptgOutput )
{
    TgUINT32                            uiChannel, uiAttached;

    // Verify function parameters
    if (NULL == ptgOutput)
    {
        return (0);
    };

    tgCM_MP_CS_Enter_Block( &s_tgSystem_Lock );

    // Verify the state of the system
    TgASSERT(ETgMODULE_STATE_BOOTED == s_enConsole_State);

    // Primary implementation of the function
    for (uiChannel = 0, uiAttached = 0; uiChannel < ETgCON_MAX_CHANNEL; ++uiChannel)
    {
        // Check to see if this channel is in the mask
        if (0 == (uiChannel_Mask & (1 << (uiChannel + ETgCON_SEVERITY_BITS))))
        {
            continue;
        };

        // If there is an available slot for the output object, add it to the head of the list
        if (0 == s_aptgOutput[uiChannel][ETgCON_MAX_CHANNEL_OUTPUT-1])
        {
            memmove( &(s_aptgOutput[uiChannel][1]), &(s_aptgOutput[uiChannel][0]),
                sizeof(s_aptgOutput[uiChannel][0])*(ETgCON_MAX_CHANNEL_OUTPUT-1) );

            s_aptgOutput[uiChannel][0] = ptgOutput;
            uiAttached |= 1 << (uiChannel + ETgCON_SEVERITY_BITS);
        };
    };

    tgCM_MP_CS_Exit( &s_tgSystem_Lock );
    return (uiAttached);
}


// ---- tgCN_Attach_Default_Break ----------------------------------------------------------------------------------------------- //
// ------------------------------------------------------------------------------------------------------------------------------ //
TgUINT32 tgCN_Attach_Default_Break( C_TgUINT32 uiChannel_Mask )
{
    return (tgCN_Attach_Output( uiChannel_Mask, &s_tgDefault_Output_Break ));
}


// ---- tgCN_Attach_Default_Abort ----------------------------------------------------------------------------------------------- //
// ------------------------------------------------------------------------------------------------------------------------------ //
TgUINT32 tgCN_Attach_Default_Abort( C_TgUINT32 uiChannel_Mask )
{
    return (tgCN_Attach_Output( uiChannel_Mask, &s_tgDefault_Output_Abort ));
}


// ---- tgCN_Remove_Output ------------------------------------------------------------------------------------------------------ //
// Remove the output object from each of the channel's selected in the mask.  Return the value of all the channels that the functor
// was successfully removed.
// ------------------------------------------------------------------------------------------------------------------------------ //
TgUINT32 tgCN_Remove_Output( C_TgUINT32 uiChannel_Mask, PC_STg2_Output ptgOutput )
{
    TgUINT32                            uiChannel, uiOutput, uiRemoved;

    // Verify function parameters
    if (NULL == ptgOutput)
    {
        return (0);
    };

    tgCM_MP_CS_Enter_Block( &s_tgSystem_Lock );

    // Verify the state of the system
    TgASSERT(ETgMODULE_STATE_BOOTED == s_enConsole_State || ETgMODULE_STATE_FREEING == s_enConsole_State);

    // Primary implementation of the function
    for (uiChannel = 0, uiRemoved = 0; uiChannel < ETgCON_MAX_CHANNEL; ++uiChannel)
    {
        // Check to see if this channel is in the mask
        if (0 == (uiChannel_Mask & (1 << (uiChannel + ETgCON_SEVERITY_BITS))))
        {
            continue;
        };

        // Iterate through all of the output objects for the channel - if found, remove and compact the list.
        for (uiOutput = 0; uiOutput < ETgCON_MAX_CHANNEL_OUTPUT; ++uiOutput)
        {
            if (0 == s_aptgOutput[uiChannel][uiOutput])
            {
                break;
            };

            if (ptgOutput == s_aptgOutput[uiChannel][uiOutput])
            {
                for (++uiOutput; uiOutput < ETgCON_MAX_CHANNEL_OUTPUT; ++uiOutput)
                {
                    s_aptgOutput[uiChannel][uiOutput-1] = s_aptgOutput[uiChannel][uiOutput];
                };

                s_aptgOutput[uiChannel][uiOutput-1] = 0;

                uiRemoved |= 1 << (uiChannel + ETgCON_SEVERITY_BITS);
                break;
            };
        };
    };

    tgCM_MP_CS_Exit( &s_tgSystem_Lock );
    return (uiRemoved);
}


// ---- tgCN_Remove_Default_Break ----------------------------------------------------------------------------------------------- //
// ------------------------------------------------------------------------------------------------------------------------------ //
TgUINT32 tgCN_Remove_Default_Break( C_TgUINT32 uiChannel_Mask )
{
    return (tgCN_Remove_Output( uiChannel_Mask, &s_tgDefault_Output_Break ));
}


// ---- tgCN_Remove_Default_Abort ----------------------------------------------------------------------------------------------- //
// ------------------------------------------------------------------------------------------------------------------------------ //
TgUINT32 tgCN_Remove_Default_Abort( C_TgUINT32 uiChannel_Mask )
{
    return (tgCN_Remove_Output( uiChannel_Mask, &s_tgDefault_Output_Abort ));
}


// ---- tgCN_Set_Prefix --------------------------------------------------------------------------------------------------------- //
// ------------------------------------------------------------------------------------------------------------------------------ //
TgVOID tgCN_Set_Prefix( C_TgUINT32 uiChannel_Mask, CPC_TgCHAR pszPrefix )
{
    TgUINT32                            uiChannel;

    tgCM_MP_CS_Enter_Block( &s_tgSystem_Lock );

    // Verify the state of the system
    TgASSERT(ETgMODULE_STATE_BOOTED == s_enConsole_State);

    for (uiChannel = 0; uiChannel < ETgCON_MAX_CHANNEL; ++uiChannel)
    {
        // Check to see if this channel is in the mask
        if (0 == (uiChannel_Mask & (1 << (uiChannel + ETgCON_SEVERITY_BITS))))
        {
            continue;
        };

        s_pzOutput_Prefix[uiChannel] = pszPrefix;
        s_nuiOutput_Prefix[uiChannel] = NULL == pszPrefix ? 0 : tgSZ_Length( pszPrefix );
    };

    tgCM_MP_CS_Exit( &s_tgSystem_Lock );
}


// ---- tgCN_Set_UID_Filter ------------------------------------------------------------------------------------------------- //
// ------------------------------------------------------------------------------------------------------------------------------ //
TgVOID tgCN_Set_UID_Filter( C_TgUINT32 uiChannel_Mask, C_TgUINT32 uiUID )
{
    TgUINT32                            uiChannel;

    tgCM_MP_CS_Enter_Block( &s_tgSystem_Lock );

    // Verify the state of the system
    TgASSERT(ETgMODULE_STATE_BOOTED == s_enConsole_State);

    for (uiChannel = 0; uiChannel < ETgCON_MAX_CHANNEL; ++uiChannel)
    {
        // Check to see if this channel is in the mask
        if (0 == (uiChannel_Mask & (1 << (uiChannel + ETgCON_SEVERITY_BITS))))
        {
            continue;
        };

        s_auiUID_Filter[uiChannel] = uiUID;
    };

    tgCM_MP_CS_Exit( &s_tgSystem_Lock );
}


// ---- tgCN_Set_Severity_Filter ------------------------------------------------------------------------------------------------ //
// ------------------------------------------------------------------------------------------------------------------------------ //
TgVOID tgCN_Set_Severity_Filter( C_TgUINT32 uiChannel_Mask, C_TgUINT32 uiSeverity )
{
    TgUINT32                            uiChannel;

    tgCM_MP_CS_Enter_Block( &s_tgSystem_Lock );

    // Verify the state of the system
    TgASSERT(ETgMODULE_STATE_BOOTED == s_enConsole_State);

    for (uiChannel = 0; uiChannel < ETgCON_MAX_CHANNEL; ++uiChannel)
    {
        // Check to see if this channel is in the mask
        if (0 == (uiChannel_Mask & (1 << (uiChannel + ETgCON_SEVERITY_BITS))))
        {
            continue;
        };

        s_auiSeverity_Filter[uiChannel] = uiSeverity;
    };

    tgCM_MP_CS_Exit( &s_tgSystem_Lock );
}


// ---- tgCN_Print -------------------------------------------------------------------------------------------------------------- //
// ------------------------------------------------------------------------------------------------------------------------------ //
TgVOID tgCN_Print( C_TgUINT32 uiChannel_Mask, CP_TgCHAR pszText )
{
    // Execute the standard print function
    tgCN_UID_Print_Internal( ETgUID_NONE, uiChannel_Mask, pszText );
}


// ---- tgCN_PrintF ------------------------------------------------------------------------------------------------------------- //
// ------------------------------------------------------------------------------------------------------------------------------ //
TgVOID tgCN_PrintF( C_TgUINT32 uiChannel_Mask, CP_TgCHAR pszText, ... )
{
    TgCHAR                              szBuffer[ETgCON_OSCON_LINE_LENGTH+1];
    va_list                             argptr;

    // Create the text string from the printf format
    va_start( argptr, pszText );
    tgSZ_PrintVF( szBuffer, ETgCON_OSCON_LINE_LENGTH, pszText, argptr );
    va_end(argptr);

    // Execute the standard print function
    tgCN_UID_Print_Internal( ETgUID_NONE, uiChannel_Mask, szBuffer );
}


// ---- tgCN_UID_Print ---------------------------------------------------------------------------------------------------------- //
// ------------------------------------------------------------------------------------------------------------------------------ //
TgVOID tgCN_UID_Print( C_TgUINT32 uiUID, C_TgUINT32 uiChannel_Mask, CP_TgCHAR pszText )
{
    // Execute the standard print function
    tgCN_UID_Print_Internal( uiUID, uiChannel_Mask, pszText );
}


// ---- tgCN_UID_PrintF --------------------------------------------------------------------------------------------------------- //
// ------------------------------------------------------------------------------------------------------------------------------ //
TgVOID tgCN_UID_PrintF( C_TgUINT32 uiUID, C_TgUINT32 uiChannel_Mask, CP_TgCHAR pszText, ... )
{
    TgCHAR                              szBuffer[ETgCON_OSCON_LINE_LENGTH+1];
    va_list                             argptr;

    // Create the text string from the printf format
    va_start( argptr, pszText );
    tgSZ_PrintVF( szBuffer, ETgCON_OSCON_LINE_LENGTH, pszText, argptr );
    va_end(argptr);

    // Execute the standard print function
    tgCN_UID_Print_Internal( uiUID, uiChannel_Mask, szBuffer );
}


// ---- tgCN_Process_Input ------------------------------------------------------------------------------------------------------ //
// This can only be called by a single thread (the input system).  It is not globally multi-thread safe
// ------------------------------------------------------------------------------------------------------------------------------ //
TgVOID tgCN_Process_Input( TgCHAR tgKey, TgBOOL bCtrl )
{
    // Enforce the string length limits of the buffer.
    if (s_iOS_Console_Cmd_Index + 4 > ETgCON_OSCON_LINE_LENGTH)
    {
        return;
    };

    tgCM_MP_CS_Enter_Block( &s_tgOS_Lock );

    switch (tgKey) { 
    case 0x08: //« Backspace
        s_iOS_Console_Cmd_Index = tgCM_MAX_S32( s_iOS_Console_Cmd_Index - 1, 2 );
        s_szOS_Console_Buffer[s_iOS_Console_Buffer_Index][s_iOS_Console_Cmd_Index] = 0;
        break;
    case 0x09: //« Tab
        s_szOS_Console_Buffer[s_iOS_Console_Buffer_Index][s_iOS_Console_Cmd_Index++] = ' ';
        s_szOS_Console_Buffer[s_iOS_Console_Buffer_Index][s_iOS_Console_Cmd_Index++] = ' ';
        s_szOS_Console_Buffer[s_iOS_Console_Buffer_Index][s_iOS_Console_Cmd_Index++] = ' ';
        s_szOS_Console_Buffer[s_iOS_Console_Buffer_Index][s_iOS_Console_Cmd_Index++] = ' ';
        s_szOS_Console_Buffer[s_iOS_Console_Buffer_Index][s_iOS_Console_Cmd_Index] = 0;
        break;
    case 0x0D: //« Enter
        tgCN_Execute_Command( s_szOS_Console_Buffer[s_iOS_Console_Buffer_Index] + 2 );
        s_iOS_Console_Display_Index = s_iOS_Console_Buffer_Index;
        s_iOS_Console_Buffer_Index = (s_iOS_Console_Buffer_Index + 1) % ETgCON_OSCON_MAX_LINE;
        s_szOS_Console_Buffer[s_iOS_Console_Buffer_Index][0] = '>';
        s_szOS_Console_Buffer[s_iOS_Console_Buffer_Index][1] = ' ';
        s_szOS_Console_Buffer[s_iOS_Console_Buffer_Index][2] = 0;
        s_iOS_Console_Cmd_Index = 2;
        s_iOS_Console_Cmd_History_Index = 0;
        break;
    case '`':
        s_bOS_Console_Render = !s_bOS_Console_Render;
        break;

    case 0x21:
        if (bCtrl) {
            tgCN_Scroll_Display(  0, -1 );
        } else {
        };
        break;
    case 0x22:
        if (bCtrl) {
            tgCN_Scroll_Display(  0,  1 );
        } else {
        };
        break;
    case 0x23:
        if (bCtrl) {
            s_iOS_Console_Display_Index = s_iOS_Console_Buffer_Index;
        } else {
        };
        break;
    case 0x24:
        if (bCtrl) {
        } else {
        };
        break;
    case 0x25: //« Left
        break;
    case 0x26:
        if (bCtrl)
        {
            tgCN_Scroll_Display( -1,  0 );
        }
        else if (s_iOS_Console_Cmd_History_Index < ETgCON_OSCON_MAX_LINE - 1)
        {
            TgSINT32                            iIndex;

            ++s_iOS_Console_Cmd_History_Index;

            iIndex = s_iOS_Console_Buffer_Index - s_iOS_Console_Cmd_History_Index;

            tgSZ_Copy(
                s_szOS_Console_Buffer[s_iOS_Console_Buffer_Index],
                ETgCON_OSCON_LINE_LENGTH,
                s_szOS_Console_Buffer[(ETgCON_OSCON_MAX_LINE + iIndex) % ETgCON_OSCON_MAX_LINE]
            );
        };
        break;
    case 0x27: //« Right
        break;
    case 0x28:
        if (bCtrl)
        {
            tgCN_Scroll_Display(  1,  0 );
        }
        else if (s_iOS_Console_Cmd_History_Index > 0)
        {
            if (0 == --s_iOS_Console_Cmd_History_Index)
            {
                s_szOS_Console_Buffer[s_iOS_Console_Buffer_Index][0] = 0;
            }
            else
            {
                TgSINT32                            iIndex;

                iIndex = s_iOS_Console_Buffer_Index - s_iOS_Console_Cmd_History_Index;

                tgSZ_Copy(
                    s_szOS_Console_Buffer[s_iOS_Console_Buffer_Index],
                    ETgCON_OSCON_LINE_LENGTH,
                s_szOS_Console_Buffer[(ETgCON_OSCON_MAX_LINE + iIndex) % ETgCON_OSCON_MAX_LINE]
                );
            };
        };
        break;

    default:
        if (tgKey >= 0x20)
        {
            s_szOS_Console_Buffer[s_iOS_Console_Buffer_Index][s_iOS_Console_Cmd_Index++] = tgKey;
            s_szOS_Console_Buffer[s_iOS_Console_Buffer_Index][s_iOS_Console_Cmd_Index] = 0;
        };
        break;
    };

    tgCM_MP_CS_Exit( &s_tgOS_Lock );
}


// ---- tgCN_Insert_Command ----------------------------------------------------------------------------------------------------- //
// ------------------------------------------------------------------------------------------------------------------------------ //
TgBOOL tgCN_Insert_Command( CPC_TgCHAR pszCMD, TgFCN_CONSOLE pfnFCN, CPC_TgCHAR pszDesc )
{
    C_TgUINT32                          uiHash = tgSZ_Hash( pszCMD );
    TgUINT32                            uiIndex;
    TgBOOL                              bRet = TgFALSE;

    // Validate function parameters
    TgASSERT(KTgEMPTY_HASH != uiHash);

    tgCM_MP_CS_Enter_Block( &s_tgSystem_Lock );

    // Search for the hash in the hash list and terminate if found (re-entry inserts and hash collisions are not supported)
    for (uiIndex = 0; uiIndex < ETgCON_MAX_COMMAND_FCN; ++uiIndex)
    {
        if (uiHash == s_auiFcn_Hash[uiIndex])
        {
            tgCM_MP_CS_Exit( &s_tgSystem_Lock );
            return (bRet);
        };
    };

    // Search for the first available empty function location, and add the command
    for (uiIndex = 0; uiIndex < ETgCON_MAX_COMMAND_FCN; ++uiIndex)
    {
        if (0 == s_atgFcn_List[uiIndex])
        {
            s_atgFcn_List[uiIndex] = pfnFCN;
            s_auiFcn_Hash[uiIndex] = uiHash;
            s_apzFcn_Name[uiIndex] = pszCMD;
            s_apzFcn_Desc[uiIndex] = pszDesc;
            bRet = TgTRUE;
            break; // Fall through to execute the remainder of the function
        };
    };

    tgCM_MP_CS_Exit( &s_tgSystem_Lock );
    return (bRet);
}


// ---- tgCN_Remove_Command ----------------------------------------------------------------------------------------------------- //
// ------------------------------------------------------------------------------------------------------------------------------ //
TgBOOL tgCN_Remove_Command( CPC_TgCHAR pszCMD )
{
    C_TgUINT32                          uiHash = tgSZ_Hash( pszCMD );
    TgUINT32                            uiIndex;
    TgBOOL                              bRet = TgFALSE;

    // Validate function parameters
    TgASSERT(KTgEMPTY_HASH != uiHash);

    tgCM_MP_CS_Enter_Block( &s_tgSystem_Lock );

    // Search for the given hash and remove it and the function
    for (uiIndex = 0; uiIndex < ETgCON_MAX_COMMAND_FCN; ++uiIndex)
    {
        if (uiHash == s_auiFcn_Hash[uiIndex])
        {
            s_atgFcn_List[uiIndex] = NULL;
            s_auiFcn_Hash[uiIndex] = KTgEMPTY_HASH;
            s_apzFcn_Name[uiIndex] = 0;
            s_apzFcn_Desc[uiIndex] = 0;
            bRet = TgTRUE;
            break; // Fall through to execute the remainder of the function
        };
    };

    tgCM_MP_CS_Exit( &s_tgSystem_Lock );
    return (bRet);
}


// ---- tgCN_Execute_Command ---------------------------------------------------------------------------------------------------- //
// ------------------------------------------------------------------------------------------------------------------------------ //
TgBOOL tgCN_Execute_Command( CPC_TgCHAR pszCmdLN )
{
    TgUINT32                            uiLength = tgSZ_Length( pszCmdLN ) + 1;
    CP_TgCHAR                           aszCmd[ETgMAX_CMD_LINE_COUNT];
    TgUINT32                            nuiArg = 0;
    P_TgCHAR                            szCmdLN;
    TgBOOL                              bRet = TgFALSE;

    // Create a copy of the command line on the local stack (parsing modifies the string)
    TgALLOCA( TgCHAR, uiLength, szCmdLN );
    tgSZ_Copy( szCmdLN, uiLength, pszCmdLN );

    // Parse the command line (use the global function used to parse the executable command line)
    nuiArg = tgGB_Parse_Command_Line( szCmdLN, aszCmd );

    // Make sure we have a command to execute before taking the critical section
    if (nuiArg > 0)
    {
        C_TgUINT32                          uiHash = tgSZ_Hash( aszCmd[0] );
        TgUINT32                            uiIndex;

        tgCM_MP_CS_Enter_Block( &s_tgSystem_Lock );

        for (uiIndex = 0; uiIndex < ETgCON_MAX_COMMAND_FCN; ++uiIndex)
        {
            if (uiHash != s_auiFcn_Hash[uiIndex])
            {
                continue;
            };

            // This function call must be within the critical section so that removals can be guaranteed that there are no in-flight
            // executions of there functions.  This means it is safe for a system to remove a console function and then tear down
            // all support for it - without having to worry about possible delay execution of the function.

            if (0 != s_atgFcn_List[uiIndex])
            {
                s_atgFcn_List[uiIndex]( nuiArg, aszCmd );
                bRet = TgTRUE;
            };

            break; // Hash collisions are not allowed so there can only be the one possible function
        };

        tgCM_MP_CS_Exit( &s_tgSystem_Lock );
    };

    TgFREEA( szCmdLN );
    return (bRet);
}


// ---- tgCN_Print_Commands ----------------------------------------------------------------------------------------------------- //
// ------------------------------------------------------------------------------------------------------------------------------ //
TgVOID tgCN_Print_Commands( P_STg2_Output ptgOutput )
{
    TgSINT32                            iIndex;

    tgCM_MP_CS_Enter_Block( &s_tgSystem_Lock );

    // Search for the first available empty function location, and add the command
    for (iIndex = 0; iIndex < ETgCON_MAX_COMMAND_FCN; ++iIndex)
    {
        if (0 != s_atgFcn_List[iIndex])
        {
            tgIO_PrintF( ptgOutput, TgT("%-16.16s(%-32.32s): %s\n"), TgT("Command"), s_apzFcn_Name[iIndex], s_apzFcn_Desc[iIndex]);
        };
    };

    tgCM_MP_CS_Exit( &s_tgSystem_Lock );
}


// ---- tgCN_IO_Write ----------------------------------------------------------------------------------------------------------- //
// ------------------------------------------------------------------------------------------------------------------------------ //
TgSINT32 tgCN_IO_Write( PC_STg2_Output ptgOUT, CPC_TgUINT08 pbyData, C_TgSINT32 iSize )
{
    tgCN_Print( ETgCON_CHANEL_CONSOLE, (CP_TgCHAR)pbyData );
    return (iSize);
}


// ---- tgCN_IO_Seek ------------------------------------------------------------------------------------------------------------ //
// ------------------------------------------------------------------------------------------------------------------------------ //
TgSINT32 tgCN_IO_Seek( PC_STg2_Output ptgOUT, C_ETgIO_SEEK etgSeek, C_TgSINT32 iOffset )
{
    return(~0u);
}




// -.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-. //
//  File Local Functions
// -.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-. //

// ---- tgCN_UID_Print_Internal ------------------------------------------------------------------------------------------------- //
// ------------------------------------------------------------------------------------------------------------------------------ //
static TgVOID tgCN_UID_Print_Internal( C_TgUINT32 uiUID, C_TgUINT32 uiChannel_Mask, CP_TgCHAR pszText )
{
    C_TgUINT32                          uiSeverity = uiChannel_Mask & ((1 << ETgCON_SEVERITY_BITS) - 1);
    TgUINT32                            uiChannel, uiOutput, uiLength;
    CP_TgCHAR                           pszStart;
    TgBOOL                              bEmitPrefix;

    if (ETgMODULE_STATE_BOOTED != s_enConsole_State)
    {
        tgPM_DBG_ERR_Write( NULL, (CP_TgUINT08)pszText, tgSZ_Length(pszText) );
        return;
    };

    // Enter the critical sections for the system and output system (if required)
    if (0 != (uiChannel_Mask & ETgCON_CHANEL_LOG_SCREEN))
    {
        tgCM_MP_CS_Enter_Block( &s_tgOS_Lock );
    };
    tgCM_MP_CS_Enter_Block( &s_tgSystem_Lock );

    for (pszStart = pszText; TgT('\0') != *pszText;pszStart = pszText)
    {
        // Calculate the length of the first string up to the end or the first new line character
        for (uiLength = 1; TgT('\0') != *pszText && TgT('\n') != *pszText; ++uiLength, ++pszText);

        // Include the NULL terminator for the string and determine if we should emit a prefix on the next token
        if (TgT('\0') != *pszText)
        {
            bEmitPrefix = TgTRUE;
            ++pszText;
        }
        else
        {
            bEmitPrefix = TgFALSE;
            --uiLength;
        };

        for (uiChannel = 0; uiChannel < ETgCON_MAX_CHANNEL; ++uiChannel)
        {
            // Continue if this channel is not part of the output request
            C_TgUINT32                          uiChannel_ID = 1 << (uiChannel + ETgCON_SEVERITY_BITS);

            // Skip if the message is not included for this channel
            if (0 == (uiChannel_Mask & uiChannel_ID))
            {
                continue;
            };

            // Check to see if the message passes the UID filter
            if ((ETgUID_NONE != s_auiUID_Filter[uiChannel]) && (uiUID != s_auiUID_Filter[uiChannel]))
            {
                continue;
            };

            // Check to see if the message passes the severity filter
            if (s_auiSeverity_Filter[uiChannel] <= uiSeverity)
            {
                continue;
            };

            // Screen output is kept within a ring buffer log for display purposes as well sent to the output functions
            if (ETgCON_CHANEL_LOG_SCREEN == uiChannel_ID && NULL != s_ptgOS_Log_Free)
            {
                PC_STg2_OnScreen_Line               ptgOS_Log_Display = s_ptgOS_Log_Free;

                s_ptgOS_Log_Free = ptgOS_Log_Display->m_psNext;

                tgSZ_CopyN( ptgOS_Log_Display->m_szLog, ETgCON_OSCON_LINE_LENGTH, pszStart, uiLength );
                ptgOS_Log_Display->m_uiSeverity = uiSeverity;
                ptgOS_Log_Display->m_uiUID = uiUID;
                ptgOS_Log_Display->m_fLife_Time = s_fOS_Log_Default_Life_Time;
                ptgOS_Log_Display->m_psNext = s_ptgOS_Log_Display;

                s_ptgOS_Log_Display = ptgOS_Log_Display;
            };

            // Iterate through all of the output objects for this channel
            for (uiOutput = 0; uiOutput < ETgCON_MAX_CHANNEL_OUTPUT; ++uiOutput)
            {
                if (0 == s_aptgOutput[uiChannel][uiOutput])
                {
                    break;
                };

                if (s_abOutput_Prefix[uiChannel] && 0 != s_pzOutput_Prefix[uiChannel])
                {
                    s_aptgOutput[uiChannel][uiOutput]->m_pfnWrite( s_aptgOutput[uiChannel][uiOutput],
                        (PC_TgUINT08)s_pzOutput_Prefix[uiChannel], s_nuiOutput_Prefix[uiChannel]*sizeof(TgCHAR) );
                };

                s_aptgOutput[uiChannel][uiOutput]->m_pfnWrite(
                    s_aptgOutput[uiChannel][uiOutput], (PC_TgUINT08)pszStart, uiLength*sizeof(TgCHAR) );
            };

            s_abOutput_Prefix[uiChannel] = bEmitPrefix;
        };
    };

    // Exit the critical sections for the system and output system (if required)
    if (0 != (uiChannel_Mask & ETgCON_CHANEL_LOG_SCREEN))
    {
        tgCM_MP_CS_Exit( &s_tgOS_Lock );
    };
    tgCM_MP_CS_Exit( &s_tgSystem_Lock );
}


// ---- tgCN_Scroll_Display ----------------------------------------------------------------------------------------------------- //
// NOTE: This should only be called by tgCN_Process_Input due to synchronization on internal variables
// ------------------------------------------------------------------------------------------------------------------------------ //
static TgVOID tgCN_Scroll_Display( C_TgSINT32 niLN, C_TgSINT32 niPG )
{
    C_TgUINT32                          uiPageAmount = s_iOS_Console_Render_Page_Height /  s_iOS_Console_Render_Font_Height - 1;
    C_TgSINT32                          iMax = (s_iOS_Console_Buffer_Index - s_iOS_Console_Display_Index + ETgCON_OSCON_MAX_LINE) % ETgCON_OSCON_MAX_LINE;
    C_TgSINT32                          iMin = (iMax + 1 - ETgCON_OSCON_MAX_LINE) % ETgCON_OSCON_MAX_LINE;
    C_TgUINT32                          uiD = tgCM_CLP_U32( (TgSINT)(niLN + niPG*uiPageAmount), iMin, iMax );
    C_TgUINT32                          iNew_Disp = (s_iOS_Console_Display_Index + uiD + ETgCON_OSCON_MAX_LINE) % ETgCON_OSCON_MAX_LINE;

    s_iOS_Console_Display_Index = iNew_Disp;
}


// ---- Default Functions for the Break Functor --------------------------------------------------------------------------------- //
// ------------------------------------------------------------------------------------------------------------------------------ //
static TgSINT32 tgCN_Default_Break_Write( PC_STg2_Output ptgOut, CPC_TgUINT08 pszText, C_TgSINT32 niText )
{
    tgPM_Break();
    return (0);
}


static TgSINT32 tgCN_Default_Break_Seek( PC_STg2_Output ptgOut, C_ETgIO_SEEK etgSeek, C_TgSINT32 iPos )
{
    tgPM_Break();
    return (0);
}


// ---- Default Functions for the Abort Functor --------------------------------------------------------------------------------- //
// ------------------------------------------------------------------------------------------------------------------------------ //
static TgSINT32 tgCN_Default_Abort_Write( PC_STg2_Output ptgOut, CPC_TgUINT08 pszText, C_TgSINT32 niText )
{
    tgPM_Abort();
    return (0);
}


static TgSINT32 tgCN_Default_Abort_Seek( PC_STg2_Output ptgOut, C_ETgIO_SEEK etgSeek, C_TgSINT32 iPos )
{
    tgPM_Abort();
    return (0);
}


#else
static TgUINT32 s_uiEMPTY_FILE;
#endif // TgS_COMPILE_CONSOLE