//==========================================================================
//
//      common/except.cxx
//
//      Exception handling implementation
//
//==========================================================================
//####COPYRIGHTBEGIN####
//
// -------------------------------------------
// The contents of this file are subject to the Cygnus eCos Public License
// Version 1.0 (the "License"); you may not use this file except in
// compliance with the License.  You may obtain a copy of the License at
// http://sourceware.cygnus.com/ecos
// 
// Software distributed under the License is distributed on an "AS IS"
// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.  See the
// License for the specific language governing rights and limitations under
// the License.
// 
// The Original Code is eCos - Embedded Cygnus Operating System, released
// September 30, 1998.
// 
// The Initial Developer of the Original Code is Cygnus.  Portions created
// by Cygnus are Copyright (C) 1998,1999 Cygnus Solutions.  All Rights Reserved.
// -------------------------------------------
//
//####COPYRIGHTEND####
//==========================================================================
//#####DESCRIPTIONBEGIN####
//
// Author(s):    nickg
// Contributors: nickg, jlarmour
// Date:         1999-02-16
// Purpose:      Exception handling implementation
// Description:  This file contains the code that registers and delivers
//               exceptions.
//
//####DESCRIPTIONEND####
//
//==========================================================================

#include <pkgconf/kernel.h>

#include <cyg/kernel/ktypes.h>         // base kernel types
#include <cyg/infra/cyg_trac.h>        // tracing macros
#include <cyg/infra/cyg_ass.h>         // assertion macros
#include <cyg/kernel/instrmnt.h>       // instrumentation

#include <cyg/kernel/except.hxx>       // our header

#include <cyg/hal/hal_arch.h>          // architecture definitions
#include <cyg/hal/hal_intr.h>          // vector definitions

#include <cyg/kernel/thread.hxx>       // thread interface

#include <cyg/kernel/thread.inl>       // thread inlines

#ifdef CYGPKG_KERNEL_EXCEPTIONS

// -------------------------------------------------------------------------
// Null exception handler. This is used to capture exceptions that are
// not caught by user supplied handlers.

void
cyg_null_exception_handler(
    CYG_ADDRWORD        data,                   // user supplied data
    cyg_code            exception_number,       // exception being raised
    CYG_ADDRWORD        exception_info          // any exception specific info
    )
{
    CYG_REPORT_FUNCTION();
    CYG_REPORT_FUNCARG3("data=%08x, exception=%d, info=%08x", data,
                        exception_number, exception_info);
    CYG_TRACE1( 1, "Uncaught exception: %d", exception_number);
    CYG_REPORT_RETURN();
}

// -------------------------------------------------------------------------
// Exception Controller constructor.

Cyg_Exception_Control::Cyg_Exception_Control()
{
    CYG_REPORT_FUNCTION();
#ifdef CYGSEM_KERNEL_EXCEPTIONS_DECODE

    for( int i = 0; i < CYGNUM_HAL_EXCEPTION_COUNT ; i++ )
        exception_handler[i] = cyg_null_exception_handler,
            exception_data[i] = 0;
#else

    exception_handler = cyg_null_exception_handler;    
    exception_data = 0;
    
#endif
    CYG_REPORT_RETURN();
}

// -------------------------------------------------------------------------
// Exception registation. Stores the handler function and data to be used
// for handling the given exception number. Where exceptions are not decoded
// only a single handler may be registered for all exceptions. This function
// also returns the old values of the exception handler and data to allow
// chaining to be implemented.

void
Cyg_Exception_Control::register_exception(
    cyg_code                exception_number,       // exception number
    cyg_exception_handler   handler,                // handler function
    CYG_ADDRWORD            data,                   // data argument
    cyg_exception_handler   **old_handler,          // handler function
    CYG_ADDRWORD            *old_data               // data argument
    )
{
    CYG_REPORT_FUNCTION();
    CYG_REPORT_FUNCARG5("exception=%d, handler func=%08x, data=%08x, "
                        "space for old handler=%08x,space for old data=%08x",
                        exception_number, handler, data, old_handler,
                        old_data);

    CYG_ASSERT( exception_number <= CYGNUM_HAL_EXCEPTION_MAX,
                "Out of range exception number");
    CYG_ASSERT( exception_number >= CYGNUM_HAL_EXCEPTION_MIN,
                "Out of range exception number");


    // Should we complain if there is already a registered
    // handler, or should we just replace is silently?
    
#ifdef CYGSEM_KERNEL_EXCEPTIONS_DECODE

    if( old_handler != NULL )
        *old_handler = exception_handler[exception_number -
                                        CYGNUM_HAL_EXCEPTION_MIN];
    if( old_data != NULL )
        *old_data = exception_data[exception_number - 
                                  CYGNUM_HAL_EXCEPTION_MIN];
    exception_handler[exception_number - CYGNUM_HAL_EXCEPTION_MIN] = handler;
    exception_data[exception_number - CYGNUM_HAL_EXCEPTION_MIN] = data;
    
#else
    
    if( old_handler != NULL )
        *old_handler = exception_handler;
    if( old_data != NULL )
        *old_data = exception_data;
    exception_handler = handler;
    exception_data = data;
    
#endif
    CYG_REPORT_RETURN();
}

// -------------------------------------------------------------------------
// Exception deregistation. Revert the handler for the exception number
// to the default.

void
Cyg_Exception_Control::deregister_exception(
    cyg_code                exception_number        // exception number
    )
{
    CYG_REPORT_FUNCTION();
    CYG_REPORT_FUNCARG1("exception number=%d", exception_number);

    CYG_ASSERT( exception_number <= CYGNUM_HAL_EXCEPTION_MAX,
                "Out of range exception number");
    CYG_ASSERT( exception_number >= CYGNUM_HAL_EXCEPTION_MIN,
                "Out of range exception number");

#ifdef CYGSEM_KERNEL_EXCEPTIONS_DECODE

    exception_handler[exception_number - CYGNUM_HAL_EXCEPTION_MIN] = 
        cyg_null_exception_handler;
    exception_data[exception_number - CYGNUM_HAL_EXCEPTION_MIN] = 0;
    
#else
    
    exception_handler = cyg_null_exception_handler;
    exception_data = 0;
    
#endif

    CYG_REPORT_RETURN();
}

// -------------------------------------------------------------------------
// Exception delivery. Call the appropriate exception handler.

void
Cyg_Exception_Control::deliver_exception(
    cyg_code            exception_number,       // exception being raised
    CYG_ADDRWORD        exception_info          // exception specific info
    )
{
    CYG_REPORT_FUNCTION();
    CYG_REPORT_FUNCARG2("exception number=%d, exception info=%08x",
                        exception_number, exception_info);

    cyg_exception_handler *handler = NULL;
    CYG_ADDRWORD data = 0;

    CYG_ASSERT( exception_number <= CYGNUM_HAL_EXCEPTION_MAX,
                "Out of range exception number");
    CYG_ASSERT( exception_number >= CYGNUM_HAL_EXCEPTION_MIN,
                "Out of range exception number");
    
#ifdef CYGSEM_KERNEL_EXCEPTIONS_DECODE

    handler = exception_handler[exception_number - CYGNUM_HAL_EXCEPTION_MIN];
    data = exception_data[exception_number - CYGNUM_HAL_EXCEPTION_MIN];
    
#else
    
    handler = exception_handler;
    data = exception_data;
    
#endif

    // The handler will always be a callable function: either the user's
    // registered function or the null handler. So it is always safe to
    // just go ahead and call it.
    
    handler( data, exception_number, exception_info );

    CYG_REPORT_RETURN();
}

// -------------------------------------------------------------------------
// Exception delivery function called from the HAL as a result of a
// hardware exception being raised.

externC void
cyg_hal_deliver_exception( CYG_WORD code, CYG_ADDRWORD data )
{
    CYG_REPORT_FUNCTION();
    Cyg_Thread::self()->deliver_exception( (cyg_code)code, data );
    CYG_REPORT_RETURN();
}

// -------------------------------------------------------------------------
// Where exceptions are global, there is a single static instance of the
// exception control object. Define it here.

#ifdef CYGSEM_KERNEL_EXCEPTIONS_GLOBAL

Cyg_Exception_Control Cyg_Thread::exception_control 
                                              CYG_INIT_PRIORITY(INTERRUPTS);

#endif

// -------------------------------------------------------------------------

#endif // ifdef CYGPKG_KERNEL_EXCEPTIONS

// -------------------------------------------------------------------------
// EOF common/except.cxx
