/******************************************************************************
 *
 *                                   Exec
 *
 * This module contains the routines which control the different execution
 * operations done by CMON.
 * Any exit to the users program is done through this module.
 *
 * History:
 *   +----------+------------+------------------------------------------------+
 *   |   Date   |     by     |     Description                                |
 *   +----------+------------+------------------------------------------------+
 *   |          | Coby       | Written                                        |
 *   +----------+------------+------------------------------------------------+
 *
 *****************************************************************************/


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

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

/*
 * The status for the BEGIN/JSR/CXP commands
 */

#ifndef public

typedef enum {
        NO_CALL_ISSUED,
        BEGIN_ISSUED,
        CALL_ISSUED
} call_status_type;

#endif

/*
 * BEGIN/JSR/CXP global variables.
 * The address in which the bpt instruction for the begin command was placed.
 * The users PC, MOD, SB, IS, US before the jsr/cxp command.
 * An indication if a begin or jsr/cxp was issued.
 */

public unsigned    old_user_pc__, old_user_mod__, old_user_sb__;
public unsigned    old_user_is__, old_user_us__;


#if (CORE_TYPE == SF)
public reg_desc	    *call_link_regp__;
public unsigned	    old_user_gen_reg__;
#endif

public call_status_type    call_status__= NO_CALL_ISSUED ;

/*
 * Execution global variables
 * The values for exec_mode__ and trace_mode__ are defined in traps_ext.s
 */

public unsigned  trace_mode__, bin_stepped_pc__;

public unsigned  step_count__ = 0;
public unsigned  exec_mode__ = MON_TIME;


/*
 * Symbols thats points to a BPT instructions.
 * used for return from BEGIN and CALL commands.
 *
 * Actully they are defined in cpu_ext.c 
 */
 
extern void call_bpt__();
extern void begin_bpt__();


#define SB_OFFSET_ENTRY 0
#define PC_OFFSET_ENTRY 8


/*
 * The call commands
 */

typedef enum {
        BEGIN_CMD,
        JSR_CMD,
        CXP_CMD
} call_cmd_type;

/*
 * Define local functions
 */

private void mod_offset_decode(/* line, hex_addr, bin_mod, response */);
private void exec_call(/* hex_addr, bin_mod, call_cmd, response */);
private void save_cur_user_regs(/* response */);
private void place_mod_on_stack(/* bin_mod, user_sp, response */);

/*^L*/
/*-----------------------------------------------------------------------------
 *
 *                    G_decode__(cmd, response)
 *
 * This function decodes and executes the go command.
 *
 *---------------------------------------------------------------------------*/

public void g_decode__(cmd, response)
    str cmd, response;
{ 
    response[0] = '\0';

    if (strlen(cmd) > 1) {
        ERROR( e_syntax__);
    }

    /*
     * Return to user mode
     */

    return_to_user__(FALSE);

} /* G_decode__ */





/*-----------------------------------------------------------------------------
 *
 *                    St_decode__(cmd, response)
 *
 * This function decodes and executes the step command.
 *
 *---------------------------------------------------------------------------*/

public void st_decode__(cmd, response)
    str      cmd, response;
{ 
    s_str    hex_count;
    boolean  err;
    unsigned len;

    /*
     * Get the count. This should be the only parameter.
     */

    get_first_arg__(&cmd[2], hex_count, &len, S_SIZE,
                   /* accept null arg */ TRUE);
    
    if (strlen(cmd) > len+3) {
        ERROR( e_syntax__);
        return;
    }
    
    if (len == 0) {
        step_count__ = 1;
    }
    else {
        hex_to_bin__( hex_count, &step_count__, 'D');
    }

    /*
     * Return to user mode
     */

    return_to_user__(TRUE);

} /* st_decode__ */

/*^L*/
/*-----------------------------------------------------------------------------
 *
 *                    Js_decode__(cmd, response)
 *
 * This function decodes the jump to subroutine command.
 *
 *---------------------------------------------------------------------------*/

public void js_decode__(cmd, response)
    str cmd, response;
{ 
    s_str    hex_addr;
    unsigned len;
    boolean  err;

    /*
     * Get the address parameter
     */

    get_first_arg__(&cmd[2], hex_addr, &len, S_SIZE,
                   /* accept null arg */ FALSE);
    if (len + 3 < strlen(cmd)) {
        ERROR( e_syntax__);
    }

    /*
     * Execute the command
     */
#   if (CORE_TYPE != SF)
	exec_call(hex_addr, 0, JSR_CMD);
#   else
	exec_call(hex_addr, &R15__, JSR_CMD);
#   endif

} /* js_decode__ */


/*-----------------------------------------------------------------------------
 *
 *                    Bal_decode__(cmd, response)
 *
 * This function decodes the Bal command
 *
 *---------------------------------------------------------------------------*/


public void bal_decode__(cmd, response)
    str cmd, response;
{ 
#   if (CORE_TYPE != SF)
	ERROR(e_syntax__);	
#   else /* CORE_TYPE == SF */

	s_str	hex_addr, reg_name;
	reg_desc	*regp;
	unsigned	len;

	get_first_arg__(&cmd[3], reg_name, &len, S_SIZE,
			/* allow_no_arg */ FALSE);
	if ((reg_name[0] != 'R') OR 
	    (regp = search_reg_tab__(reg_name)) == NULL) {
	     ERROR (e_syntax__);
	}
	get_first_arg__(&cmd[3 + len], hex_addr, &len, S_SIZE,
			/* allow_no_arg */ FALSE);
	exec_call(hex_addr, regp, JSR_CMD);

#   endif
} /* bal_decode__ */




/*-----------------------------------------------------------------------------
 *
 *                    Cx_decode__(cmd, response)
 *
 * This function decodes the call external procedure command.
 *
 *---------------------------------------------------------------------------*/

public void cx_decode__(cmd, response)
    str cmd, response;
{ 
#   if (CORE_TYPE != SF)
	s_str    hex_addr;
	address *bin_mod;

	response[0] = '\0';

	/*
	 * Get the address parameter
	 */

	mod_offset_decode(&cmd[2], hex_addr, &bin_mod);

	/*
	 * Execute the command
	 */

	exec_call(hex_addr, bin_mod, CXP_CMD);
#   else
	ERROR(e_syntax__);
#   endif
} /* cx_decode__ */

/*^L*/
/*-----------------------------------------------------------------------------
 *
 *                    B_decode__(cmd, response)
 *
 * This function decodes the begin command.
 *
 *---------------------------------------------------------------------------*/

public void b_decode__(cmd, response)
    str cmd, response;
{ 
    s_str	hex_addr, reg_name;
    address	*bin_mod;
    reg_desc	*regp;
    unsigned	len;
#   if (CORE_TYPE != SF)

	/*
	 * Get the address parameter
	 */
	mod_offset_decode(&cmd[1], hex_addr, &bin_mod);

	/*
	 * Execute the command
	 */
	exec_call(hex_addr, bin_mod, BEGIN_CMD);
	
#   else /* CORE_TYPE == SF */


	get_first_arg__(&cmd[1], reg_name, &len, S_SIZE,
			/* allow_no_arg */ FALSE);
	if ((reg_name[0] != 'R') OR 
	    (regp = search_reg_tab__(reg_name)) == NULL) {
	    /*
	     * To allow *OLD BEGIN COMMAND SYNTAX*
	     * Don't:  ERROR (e_syntax__);
	     *
	     * Insted use default values
	     */
	     exec_call("0", &R15__,BEGIN_CMD);
	     return;
	}
	get_first_arg__(&cmd[1 + len], hex_addr, &len, S_SIZE,
			/* allow_no_arg */ FALSE);
	exec_call(hex_addr, regp, BEGIN_CMD);

#   endif
} /* b_decode__ */

/*^L*/

#if (CORE_TYPE != SF)
/*----------------------------------------------------------------------------
 *
 *	32K Architecture call support routines.
 *
 *--------------------------------------------------------------------------*/

/*-----------------------------------------------------------------------------
 *
 *           Mod_offset_decode(line, hex_addr, bin_mod)
 *
 * This function decodes the mod and offset fields of the begin and cxp 
 * commands. It returns the address in hex format and the mod in binary format.
 *
 *---------------------------------------------------------------------------*/

private void mod_offset_decode(line, hex_addr, bin_mod)
    str      line, hex_addr;
    address **bin_mod;
{
    boolean  err;
    unsigned mod_len, offset_len, bin_offset, len;
    s_str    hex_mod, hex_offset;

    /*
     * Get the mod and offset
     */

    get_first_arg__(line, hex_mod, &mod_len, S_SIZE,
                   /* accept null arg */ FALSE);
    if (strlen(hex_mod) > 4) {
        ERROR( e_syntax__);
    }
    get_first_arg__(&line[mod_len], hex_offset, &offset_len, S_SIZE,
                   /* accept null arg */ FALSE);
    if (strlen(hex_offset) > 4) {
        ERROR( e_syntax__);
    }
    if (strlen(line) > mod_len + offset_len) {
        ERROR( e_syntax__);
    }

    /*
     * Translate mod and offset to binary
     */

    hex_to_bin__( hex_mod, bin_mod, 'D');
    hex_to_bin__( hex_offset, &bin_offset, 'D');

    /*
     * Get the start PC of the module
     */

    bin_to_hex__( (*bin_mod)[2] + bin_offset, hex_addr, 'D');

} /* mod_offset_decode */

/*^L*/
/*-----------------------------------------------------------------------------
 *
 *            Exec_call(addr, bin_mod, call_cmd)
 *
 * This function executes the begin/jsr/cxp commands. It receives the address
 * off the begun/called procedure and an indication which command is executed.
 * Bin_mod is used by the begin and cxp commands.
 *
 *---------------------------------------------------------------------------*/

private void exec_call(hex_addr, bin_mod, call_cmd)
    str       hex_addr;
    address  *bin_mod;
    call_cmd_type call_cmd;
{
    unsigned len, user_sp;
    address  addr;
    boolean  err;

    hex_to_bin__ ( hex_addr, &addr, 'D');
    /*
     * Check that no jsr/cxp is currently being executed.
     */

    if ((call_cmd != BEGIN_CMD) AND (call_status__ == CALL_ISSUED)) {
        ERROR( e_multiple_cxp__);
    }

    /*
     * Save the current PC, MOD, SB, IS and US 
     */

    save_cur_user_regs();

    /*
     * Get the users current SP
     */

    get_reg_val__(&SP__, &user_sp);

    /*
     * In cxp and begin commands, update the MOD and SB registers.
     * The MOD is placed on the stack.
     */

    if (call_cmd != JSR_CMD) {
        place_mod_on_stack(bin_mod, &user_sp);
    }

    /*
     * Rebuild the users stack and users PC to be as if a JSB/CXP instruction 
     * was issued. To do this we put on the stack an artificial return address
     * which points to a BPT instruction. The PC is updated to contain the 
     * start address of the called procedure.
     */

    user_sp -= 4;
    if (call_cmd == BEGIN_CMD) {
	mem_write__('D', user_sp, begin_bpt__, TRUE);
    }
    else {
	mem_write__('D', user_sp, call_bpt__, TRUE);
    }
    put_reg_val__(&PC__, &addr);
    put_reg_val__(&SP__, &user_sp);

    /*
     * Return to user mode in case of jsr/cxp
     */

    if (call_cmd != BEGIN_CMD) {
        call_status__ = CALL_ISSUED;
        return_to_user__(FALSE);
    }
    else {
        call_status__ = BEGIN_ISSUED;
    }

} /* exec_call */

/*^L*/
/*-----------------------------------------------------------------------------
 *
 *                        Save_cur_user_regs()
 *
 * This function saves the current user registers which might change durring 
 * the call. These registers will be recovered upon return from the call.
 *
 *---------------------------------------------------------------------------*/

private void save_cur_user_regs()
{

    /*
     * Save the current PC, MOD, SB, IS and US 
     */

    get_reg_val__(&PC__,  &old_user_pc__);
    get_reg_val__(&MO__,  &old_user_mod__);
    get_reg_val__(&SB__,  &old_user_sb__);
    get_reg_val__(&IS__,  &old_user_is__);
    get_reg_val__(&US__,  &old_user_us__);

} /* save_cur_user_regs */





/*-----------------------------------------------------------------------------
 *
 *          Place_mod_on_stack(bin_mod, user_sp)
 *
 * This function is called for cxp and begin commands. It updates the MOD and 
 * SB registers according to bin_mod. 
 *
 *---------------------------------------------------------------------------*/

private void place_mod_on_stack(bin_mod, user_sp)
    address *bin_mod;
    unsigned *user_sp;
{
    address sb_addr;
    unsigned masked_mod;

    put_reg_val__(&MO__, &bin_mod);
    mem_read__('D', bin_mod + SB_OFFSET_ENTRY, &sb_addr);
    put_reg_val__(&SB__, &sb_addr);
    *user_sp -= 4;
    mem_write__('D', *user_sp, bin_mod, TRUE);

} /* place_mod_on_stack */

/*^L*/

# else   /* CORE_TYPE == SF */
/*----------------------------------------------------------------------------
 *
 *	SF Architecture call support routines.
 *
 *--------------------------------------------------------------------------*/
/*-----------------------------------------------------------------------------
 *
 *            Exec_call(addr, regp, call_cmd)
 *
 * This function executes the begin/jsr/bal commands. It receives the address
 * off the begun/called procedure an indication which command is executed, and 
 * pointer to register descriptor used for link
 *
 *---------------------------------------------------------------------------*/

private void exec_call(hex_addr, regp, call_cmd)
    str		hex_addr;
    reg_desc	*regp;
    call_cmd_type call_cmd;
{
    address  addr;
    address  bpt_addr;

    hex_to_bin__ ( hex_addr, &addr, 'D');
    /*
     * Check that no jsr is currently being executed.
     */

    if ((call_cmd != BEGIN_CMD) AND (call_status__ == CALL_ISSUED)) {
        ERROR( e_multiple_cxp__);
    }

    /*
     * Save the current PC Link register pointer and contents
     */
    
    get_reg_val__(&PC__, &old_user_pc__);
    call_link_regp__ = regp;
    get_reg_val__(regp, &old_user_gen_reg__);
    
    /*
     * Change PC
     */
    
    put_reg_val__(&PC__, &addr);

    /*
     * Return to user mode in case of jsr/cxp
     */

    if (call_cmd != BEGIN_CMD) {
        call_status__ = CALL_ISSUED;
	bpt_addr = (address) call_bpt__;
	put_reg_val__(regp, &bpt_addr);
        return_to_user__(FALSE);
    }
    else {
	bpt_addr = (address) begin_bpt__;
	put_reg_val__(regp, &bpt_addr);
        call_status__ = BEGIN_ISSUED;
    }

} /* exec_call */

#endif /* CORE_TYPE == SF */

/*^L*/
/*-----------------------------------------------------------------------------
 *
 *                    Return_to_user(set_trace_bit)
 *
 * This function sets the PSR trace bit according to the requested value,
 * and returns to user mode.
 *
 *---------------------------------------------------------------------------*/

public void return_to_user__(active_step_n)
    boolean  active_step_n;
{
    unsigned user_psr, user_pc;
    boolean  bpt_on_pc;
    s_str    temp_value;
    

    /*
     * Turning over to user mode. Place back the breakpoints. 
     */

    get_reg_val__(&PC__, &user_pc);
    place_back_sw_bpt__(user_pc, &bpt_on_pc);

    /*
     * Set the PSR T bit to requested value. If there is a software breakpoint
     * on the PC or active watchpoints, set it to 1.
     *
     *   active_step_n | bpt_on_pc | active_wpt | trace_mode
     *   --------------+-----------+------------+-----------
     *          0      |      0    |      0     | non_cmon
     *          0      |      0    |      1     | watch
     *          0      |      1    |      0     | step_before_go
     *          0      |      1    |      1     | step_before_watch
     *          1      |      0    |      0     | step_n
     *          1      |      0    |      1     | step_n
     *          1      |      1    |      0     | step_before_step_n
     *          1      |      1    |      1     | step_before_step_n
     */

    get_reg_val__(&PSR__, &user_psr);
    if ((active_step_n) OR (bpt_on_pc) OR (active_wpt__)) {
        SET_BIT(PSR_T, user_psr);
	if (active_step_n) {
	    trace_mode__ = bpt_on_pc ? STEP_BEFORE_STEP_N : STEP_N;
	}
        else if (active_wpt__) {
            trace_mode__ = bpt_on_pc ? STEP_BEFORE_WATCH : WATCH;
        }
        else {
            trace_mode__ = STEP_BEFORE_GO;
        }
    }
    else {
        CLEAR_BIT(PSR_T, user_psr);
        trace_mode__ = NON_CMON_TRACE;
    }
    put_reg_val__(&PSR__, &user_psr);

    /*
     * Remember the current PC (so we can place a BPT instruction on it after
     * the step is done, if needed).
     */

    exec_mode__ = USER_TIME;
    leave_cmon__();

} /* return_to_user__ */

/*^L*/
/*-----------------------------------------------------------------------------
 *
 *                    Check_step_n__()
 *
 * This function decrements the step count by one and checks if the counter
 * reached 0. If so it returns an indication that the step ended.
 *
 *---------------------------------------------------------------------------*/

public boolean check_step_n__()
{

    step_count__--;
    return ((boolean) (step_count__ == 0) );

} /* check_step_n__ */


