/******************************************************************************
 *
 *                               Traps
 *
 * This module contains the functions which connect between the trap handlers
 * and CMON.
 * Note that when a trap handling is done, if we need to enter monitor time
 * the cmd_loop__ function is called. This function never returns to the 
 * caller.
 *
 * History:
 *   +----------+------------+------------------------------------------------+
 *   |   Date   |     by     |     Description                                |
 *   +----------+------------+------------------------------------------------+
 *   |          | Coby       | Written                                        |
 *   +----------+------------+------------------------------------------------+
 * 
 *****************************************************************************/


#include "cc_def.h"
#include "global_f.h"
#include "bpt.h"
#include "cmd.h"
#include "cpu_d.h"
#include "exec.h"
#include "io.h"
#include "mem.h"
#include "reset.h"
#include "traps.h"


extern void call_bpt__();

/*-----------------------------------------------------------------------------
 *                                 
 *                                 Variable definitions
 * 
 *---------------------------------------------------------------------------*/

/*
 * The values for reset_type__ and trap_type are defined in global_def.h
 * Pending_flag signals if a trap occured while cmon was in pending mode.
 */ 


public  unsigned reset_type__;        /* 0 - reset, 1 - nmi, 2 - dbg         */
public  boolean  pending_flag__;
public  unsigned trap_type__;

/*
 * Define local functions
 */

private void init_cmon(/*  */);
private void wait_for_ack(/* message, response */);
private void sw_bpt_report(/* need_cont_prompt, came_from_bpt */);

/*^L*/
/*-----------------------------------------------------------------------------
 *
 *                                jump_to_demo()
 *
 * This function is called after RESET and jump to application at
 * DEMO address if the DEMO_BIT in the DIP switch is clear
 *
 *---------------------------------------------------------------------------*/

#ifdef DEMO_ADDR
private void jump_to_demo()
{
    address  addr;
    unsigned user_sp;
    address  bpt_addr;
	addr = (address) DEMO_ADDR;

#if (CORE_TYPE == SF )
        /*
         * Save the current PC Link register pointer and contents
         */
	get_reg_val__(&PC__,  &old_user_pc__);
	call_link_regp__ = &R15__;
	get_reg_val__(&R15__,  &old_user_gen_reg__);

        /*
         * Change PC
         */
	put_reg_val__(&PC__,  &addr);

        /*
         * Return to user
         */
	bpt_addr = (address) call_bpt__;
	put_reg_val__(&R15__,  &bpt_addr);
#else
	get_reg_val__(&SP__,  &user_sp);
	user_sp -= 4;
	mem_write__('D',  user_sp,  call_bpt__,  TRUE);
        /*
         * Change PC & SP
         */
	put_reg_val__(&PC__,  &addr);
	put_reg_val__(&SP__,  &user_sp);
#endif

	return_to_user__(FALSE);
}
#endif /* DEMO_ADDR */

/*^L*/
/*-----------------------------------------------------------------------------
 *
 *                                Cont_reset__()
 *
 * This function is called by RESET and also if an NMI or DBG trap occured 
 * while CMON was in monitor time. It waits for a "!" or an operation mode
 * command.
 * Reset_type is a flag specifying which trap it was.
 *
 *---------------------------------------------------------------------------*/

public void cont_reset__()
{
l_str message;

    switch (reset_type__) {
        case(RESET) :
            init_cmon();
#ifdef DEMO_ADDR
    	    if (DEMO_ON) {
	        jump_to_demo();
	    }	
#endif
	    if (cur_com_mode__ != LAN)
		wait_for_ack(reset_message__);
            break;
        case(NMI) :
#ifdef DSPM_SUPPORT
	    check_nmi__(message);
	    wait_for_ack(message);
	    break;
#endif
        case(DBG) :
            wait_for_ack(nmi_message__);
    }

    pending_flag__ = FALSE;
    cmd_loop__();

} /* cont_reset__ */





/*-----------------------------------------------------------------------------
 *
 *                                Init_cmon()
 *
 * This function is called upon reset and initializes cmon's global variables.
 *
 *---------------------------------------------------------------------------*/

private void init_cmon()
{

    /*
     * Initialize the I/O and breakpoint drivers.
     */
    init_io__();
#ifdef        MMU_SUPPORT
    init_hw_bpt();
#endif
    /*init_sw_bpt__();*/

    /*
     * Print P on LED Display
     */
#ifdef LED
	LED(LED_P);
#endif

    /*
     * Initialize global variables.
     */

    old_user_pc__    = 0;

} /* init_cmon */        

 
/*-----------------------------------------------------------------------------
 *
 *                                Cont_nmi_user__()
 *
 * This function is activated if an NMI or DBG trap occured while CMON was in 
 * user or pending mode. Nmi_dbg is a flag specifying which trap it was.
 * They can be triggered by external intervention. DBG can be triggered in the
 * 532 by a hardware breakpoint too.
 *
 *---------------------------------------------------------------------------*/

public void cont_nmi_user__()
{
    boolean  quit, hw_bpt_trigger, hw_bpt_found;

    if (reset_type__ == NMI) {
	enter_cmon__("");
    }
#ifdef MMU_SUPPORT
    else {

        /*
         * Check the cause of the trap
         */

        get_dbg_trap_type(&hw_bpt_trigger);
        if (hw_bpt_trigger) {
            check_hw_bpt__(&hw_bpt_found, &quit,response);
            if (quit) {
                enter_cmon__("");
            }
            else {
                enter_cmon__(response);
            }
        }
        else {
            enter_cmon__(e_dbg__);
        }
    }
#else
    else {
        enter_cmon__(e_dbg__);
    }
#endif /* MMU_SUPPORT*/
} /* cont_nmi_user__ */

/*^L*/
/*-----------------------------------------------------------------------------
 *
 *                                Cont_trace__()
 *
 * This function continues the treatment of the trace trap.
 *
 *---------------------------------------------------------------------------*/

public void cont_trace__()
{
    boolean available, step_triggered, wpt_triggered, quit;
    boolean after_step;
    unsigned user_psr;

    /*
     * Operate according to the trace mode. 
     * If this is a non-cmon trace, report it and enter normal operation mode.
     * If we are in watch mode, check if a watch condition or step condition
     * has occured. In step mode only step conditions are checked.
     * Note that if a watch condition ocurred, the called function reports it.
     * If this was a step before go, we do nothing.
     */

    after_step = FALSE;
    switch (trace_mode__) {
        case NON_CMON_TRACE:
            enter_cmon__(e_non_cmon_trace__);
            break;
        case STEP_BEFORE_GO:
	    get_reg_val__(&PSR__, &user_psr);
	    CLEAR_BIT(PSR_T, user_psr);
	    put_reg_val__(&PSR__, &user_psr);
	    trace_mode__ = NON_CMON_TRACE;
	    after_step = TRUE;
            break;
        case WATCH:
            if (check_sw_bpt__(TRUE)  OR check_wpt__()) {
                enter_cmon__("");
            }
            break;
        case STEP_BEFORE_WATCH:
            if (check_sw_bpt__(TRUE)  OR check_wpt__()) {
                enter_cmon__("");
            }
	    trace_mode__ = WATCH;
	    after_step = TRUE;
            break;
        case STEP_N:
            if (check_sw_bpt__(TRUE) OR check_wpt__() ){
                enter_cmon__("");
            }
	    if (check_step_n__()){
		enter_cmon__(step_ended__);
	    }
            break;
        case STEP_BEFORE_STEP_N:
            if (check_sw_bpt__(TRUE) OR check_wpt__() ){
                enter_cmon__("");
            }
	    if (check_step_n__()){
		enter_cmon__(step_ended__);
	    }
	    trace_mode__ = STEP_N;
	    after_step = TRUE;
            break;
    }

    /*
     * If this was a step before ... we need to replace the BPT command in
     * the address we just stepped over.
     */

    if (after_step)     {
	if (setjmp(return_env__) == 0) {
	    mem_write__(BPT_INST_SIZE, bin_stepped_pc__, BPT_CMD, TRUE);
	    invalidate_cache__('I', FALSE, "", bin_stepped_pc__, &available);
        }
	else {
	    enter_cmon__(response__);
	}
    }

    /*
     * Check if while treating this trap another trap occured.
     */

    if (pending_flag__) {
        enter_cmon__(e_pending__);
    }

    /*
     * Return to user mode. 
     */ 
    exec_mode__ = USER_TIME;
    leave_cmon__();



} /* cont_trace__ */

/*^L*/
/*-----------------------------------------------------------------------------
 *
 *                                Cont_bpt__()
 *
 * This function continues the treatment of the breakpoint trap. This trap
 * is activated only by software breakpoints.
 * The function doesn't perform any function since the check of which 
 * breakpoint occured is done by enter_cmon__.
 *
 *---------------------------------------------------------------------------*/

public void cont_bpt__()
{
    if (check_sw_bpt__(FALSE)){
        enter_cmon__("");
    }
    enter_cmon__(e_non_cmon_bpt__);

} /* cont_bpt__ */





/*-----------------------------------------------------------------------------
 *
 *                                Cont_abort__()
 *
 * This function continues the treatment of the abort trap. This trap
 * is activated by the MMU.
 *
 *---------------------------------------------------------------------------*/

public void cont_abort__()
{
#ifdef MMU_SUPPORT
    abort_type abort_cause;
    boolean    quit, hw_bpt_found;
    l_str      response;

    exec_mode__ = MON_TIME;

    /*
     * Check the cause of the abort.
     */

    get_abort_trap_type(&abort_cause, response);
    if (response[0] != '\0') {
        enter_cmon__(response);
    }

    switch(abort_cause) {
        case UNKNOWN_ABT:
            enter_cmon__(e_abort__);
        case INVALID_PTE_ABT:
            enter_cmon__(e_non_valid_page__);
        case PROT_VIOLATION_ABT:
            enter_cmon__(e_protect__);
        case HW_BPT_ABT:
            check_hw_bpt__(&hw_bpt_found, &quit, response);
            if (quit) {
                enter_cmon__("");
            }
            else {
                enter_cmon__(response);
            }
    }
#else
   enter_cmon__(e_abort__);
#endif /* MMU_SUPPORT */ 

} /* cont_abort__ */




/*-----------------------------------------------------------------------------
 *
 *                             Cont_svc_err__()
 *
 * This function is activated if an error is detected during an svc execution.
 * In this case control should be returned to cmon. This function receives 
 * an svc error message to be sent to the host.
 *
 *---------------------------------------------------------------------------*/

public void cont_svc_err__(errmsg)
char *errmsg;
{
    enter_cmon__(errmsg);
}

/*-----------------------------------------------------------------------------
 *
 *                             Cont_misc_traps__()
 *
 * This function is activated if trap which is not used by cmon occurs.
 * Trap_type is a flag specifying which trap it was.
 *
 *---------------------------------------------------------------------------*/

public void cont_misc_traps__()
{

    switch (trap_type__) {
        case NVI_TRAP:
            enter_cmon__(e_nvi__);
        case FPU_TRAP:
            enter_cmon__(e_fpu__);
        case ILL_TRAP:
            enter_cmon__(e_ill__);
        case DVZ_TRAP:
            enter_cmon__(e_dvz__);
        case FLG_TRAP:
            enter_cmon__(e_flg__);
        case UND_TRAP:
            enter_cmon__(e_und__);
        case RBE_TRAP:
            enter_cmon__(e_rbe__);
        case NBE_TRAP:
            enter_cmon__(e_nbe__);
        case OVF_TRAP:
            enter_cmon__(e_ovf__);
    }

} /* cont_misc_traps__ */


/*^L*/
/*-----------------------------------------------------------------------------
 *
 *                      Wait_for_ack(message, response)
 *
 * This function writes the message it receives to port 0, and waits for an
 * acknowledgement of the form "!" or "OMT". It is used by reset, nmi and dbg 
 * interrupts.
 *
 *---------------------------------------------------------------------------*/

private void wait_for_ack(message)
    str      message;
{ 
    l_str    response;

    
    response[0] = '\0';
    while (response[0] != '!' ) {
        write_message__(message);
        read_message__(response, "");
    }
    write_message__(ready_prompt__);

} /* wait_for_ack */





/*-----------------------------------------------------------------------------
 *
 *              Enter_cmon(message)
 *
 * This function writes the message it receives to the current port, preceeding
 * it by a continuation prompt if needed. It also checks if there is a software
 * breakpoint on the current PC. If the function was called from the bpt trap
 * there must be such a breakpoint.
 * It then removes the software breakpoints and enters the command loop.
 *
 *---------------------------------------------------------------------------*/

public void enter_cmon__(message)
    str      message;
{
    /*
     * Old spec:
     * Turn off step [while | until] watch point
     */
    stop_sw__();


    /*
     * Remove the software breakpoints
     */
    remove_sw_bpt__();


    /*
     * Check if NMI is pending
     */
    if (pending_flag__) {
	l_str msg;

#ifdef DSPM_SUPPORT
	check_nmi__(msg);
	message = msg;
#else
	if (message[0] == '\0') {
	    strcpy(msg, e_nmi__);
	    message = msg;
	}
#endif
	pending_flag__ = FALSE;
    }


    /*
     * Write the message, if needed. If requested, preceed it with a 
     * continuation prompt.
     */
    if (message[0] != '\0') {
        write_message__( message);
    }

    /*
     * Enter the command loop
     */
    write_message__( ready_prompt__);

    
    /*
     * Signal that we entered monitor time.
     */
    exec_mode__    = MON_TIME;

    cmd_loop__();

} /* enter_cmon__ */



