/******************************************************************************
 *
 *                               Svc
 *	This module contains functions which handle the various supervisor
 *	calls (svc's).
 *
 *
 * History:
 *   +----------+------------+------------------------------------------------+
 *   |   Date   |     by     |     Description                                |
 *   +----------+------------+------------------------------------------------+
 *   |          | Eyal       |                                                |
 *   +----------+------------+------------------------------------------------+
 * 
 *****************************************************************************/

#include "cc_def.h"
#include "global_f.h"
#include "svc.h"
#include "io.h"
#include "traps.h"

#define VIO_SEG_SIZE 32
#define SVC_PASSWORD 0x55555555

struct svc_regs {
    int r3, r2, r1, r0, pc;
#   if (CPU_TYPE == NS32SF641)
	int psr;
#   else
	short mod, psr;
#   endif
};

private const char *errp;

/*
 * Declaration of internal functions
 */
private svc_3(/* buf, len, port */);
private svc_4(/* buf, len, port */);
private svc_5(/* buf, inp_size */);
private svc_6(/* errnum */);
private svc_8(/* buf, inp_size */);
private svc_9(/* password */);
private svc_10(/* password, desc, srp */);
private svc_12(/* password, new_intbase, n_entries */);
private svc_13(/* password, new_entry, entry_n */);
private svc_14(/* password, dir_excp */);


/*^L*/
/*-----------------------------------------------------------------------------
 *
 *				    svc_main__
 *
 * This function is the main svc function. It recieves a pointer to a structure
 * which saves the values of r0-r3, pc and psr register at entry to the svc.
 * The value of r0 is used as the svc number. According to the svc number
 * another function is called to perform the svc job. Some function return a
 * value which is eventually used as the new value of r2 (since some svc's are
 * supposed to return a value in r2 upon completion). If an svc error occurs
 * then an error message is set and is returned by svc_main__. If the svc was
   successfully completed svc_main__ returns NULL.
 *---------------------------------------------------------------------------*/
public char *svc_main__(srp)
struct svc_regs *srp;
{
    int ret_val;

    /* Clear error message */
    errp = NULL;

    switch(srp->r0) {
	case 3:
	    ret_val = svc_3(srp->r1, srp->r2, srp->r3);
	    if (errp == NULL)
		srp->r2 = ret_val;
	    break;

	case 4:
	    svc_4(srp->r1, srp->r2, srp->r3);
	    break;

	case 5:
	    ret_val = svc_5(srp->r1, srp->r2);
	    if (errp == NULL)
		srp->r2 = ret_val;
	    break;

	case 6:
	    svc_6(srp->r1);
	    break;

	case 7:
	    errp = e_unknown_svc__;
	    break;

	case 8:
	    ret_val = svc_8(srp->r1, srp->r2);
	    if (errp == NULL)
		srp->r2 = ret_val;
	    break;

	case 9:
	    ret_val = svc_9(srp->r1, srp->psr);
	    if (errp == NULL)
		srp->psr = ret_val;
	    break;

	case 10:
	case 11:
	    svc_10(srp->r1, srp->r2, srp);
	    break;

	case 12:
	    svc_12(srp->r1, srp->r2, srp->r3);
	    break;

	case 13:
	    ret_val = svc_13(srp->r1, srp->r2, srp->r3);
	    if (errp == NULL)
		srp->r2 = ret_val;
	    break;

	case 14:
	    svc_14(srp->r1, srp->r2);
	    break;

	default:
	    errp = e_unknown_svc__;
    }

    /*
     * Check if NMI is pending
     */
    if (pending_flag__) {
	errp = e_nmi__;
    }


    if (errp != NULL)
	return(errp);

    if (srp->r0 != 10 && srp->r0 != 11)
	/* Modify the return pc so that we will return to the next instruction
	   after the svc.
	*/
	srp->pc += SVC_INST_SIZE;

    return(NULL);

}

/*^L*/

/*-----------------------------------------------------------------------------
 *
 *				    svc_3
 *
 * This function is the implementation of svc 3 (read buffer from serial port).
 * It receives a bufffer address, requested number of bytes to be read and
 * port number. If requested number of bytes is negative then characters will
 * be read until CR is detected or until this number of characters (absolute
 * value) is read. We also assume that we read ASCII and not binary in this
 * case.
 *---------------------------------------------------------------------------*/

private svc_3(buf, len, port)
char *buf;
int len, port;
{
    l_str response;
    int act_len;

    if (len >= 0)
	/* Read in binary mode */
	act_len =
	    serial_read_buffer__(&port, buf, len, FALSE /* no timeout */);
    else
	/* Read in ASCII mode */
	act_len =
	    serial_read_line__(&port, buf, -len, FALSE /* no timeout */ );

    return(act_len);
}


/*-----------------------------------------------------------------------------
 *
 *				    svc_4
 *
 * This function is the implementation of svc 4 (write buffer to serial port). 
 *---------------------------------------------------------------------------*/
private svc_4(buf, len, port)
char *buf;
int len, port;
{
    serial_write_buffer__(port, buf, len);
}


/*^L*/
/*-----------------------------------------------------------------------------
 *
 *				    svc_5
 *
 * This function implements svc 5 (virtual i/o). It has two parts. In the first
 * part the monitor transmits the input from the svc request. In the second
 * part the monitor receives messages from the host and creates the output for
 * the svc request. The input for this function is the input buffer address and
 * the number of bytes in the input. The output is written to the same buffer
 * and the number of bytes in the output is returned by the function.
 *---------------------------------------------------------------------------*/

private svc_5(buf, inp_size)
char *buf;
int inp_size;
{
    static char vio_outbuf[VIO_SEG_SIZE*2+1] = { 'V' };
    static char vio_inbuf[VIO_SEG_SIZE*2+1]; /*  size O.K. ?? */
    int cur_size, out_size, bin, left, i, j, k;
    char hexbuf[9] ;
    char *bufp;

    /* Encode the input buffer into virtual i/o messages, breaking it
       it to segments of size VIO_SEG_SIZE. After each vio message the
       expected acknowledege is just 'V'.
    */

    for (bufp = buf, left = inp_size; left > 0;
	    bufp += VIO_SEG_SIZE, left -= cur_size) {

	/* Initial position (after the 'V') */
	j = 1;

	/* Calculte current message size */
	cur_size = MIN(left, VIO_SEG_SIZE) ;

	/* Encode current message */
	for (i = 0; i < cur_size; i++) {
	    bin_to_hex__( (int)*(bufp+i), hexbuf, '2');
	    vio_outbuf[j++] = hexbuf[0];
	    vio_outbuf[j++] = hexbuf[1];
	}

	/* Null-pad the current message */
	vio_outbuf[j++] = '\0';

	/* Write current message */
	write_message__(vio_outbuf);

	/* Read the response and check for acknowledege */
	read_message__(vio_inbuf, vio_outbuf);
	if (vio_inbuf[0] != 'V') {
	    errp = e_vio__;
	    return;
	}
    }

    /* Now get virtual i/o messages from host (if any) and decode them back
       into the svc original buffer.
    */

    /* Monitor messages in this part will contain only "V" */
    vio_outbuf[1] = '\0' ;

    /* The hex buffer will be used to decode 2 digits every time */
    hexbuf[2] = '\0' ;

    /* Initialize output size, this will eventually be returned and used as
       the new value of r2
    */
    out_size = 0;

    /* Write empty vio messages and get back messages from host until the host
       message is empty too.
    */
    k = 0;
    do {
	/* Write an empty message */
	write_message__(vio_outbuf);

	/* Read host message */
	read_message__(vio_inbuf, vio_outbuf);

	/* Check for validity */
	if (vio_inbuf[0] != 'V') {
	    errp = e_vio__;
	    return;
	 }

	/* Decode host message into the original svc buffer */
	if ((i = strlen(vio_inbuf)) > 1)
	    for (j = 1; j < i; ) {
		hexbuf[0] = vio_inbuf[j++];
		hexbuf[1] = vio_inbuf[j++];
		hex_to_bin__( hexbuf, &bin, 'D');
		buf[k++] = bin ;
		out_size++;
	    }
    /* Until an empty meesage from host ("V") */ 
    } while (i > 1);

    return(out_size);
}
/*^L*/
/*-----------------------------------------------------------------------------
 *
 *				    svc_6
 *
 * This function is the implementation of svc 6. It sends a run-time error
 * message to the host followed by an error number.
 *---------------------------------------------------------------------------*/

private svc_6(errnum)
int errnum;
{
    static char errmsg[15];
    char hexbuf[9] ;

    /* Encode the error message. Leading zeros are eliminated from the error code. */
    strcpy(errmsg, e_rts__);
    bin_to_hex__( errnum, hexbuf, 'D');
    strcat(errmsg, hexbuf);

    /* Send the message */
    errp = errmsg;
}

/*-----------------------------------------------------------------------------
 *
 *				    svc_8
 *
 * This function is used for terminal i/o. It receives a buffer address and
 * size. If the current opertaion mode is transparent it calls svc_3 or svc_4
 * for to read from or write to a serial port. Otherwise (stand-aside mode)
 * it calls svc_5 to perform virtual i/o.
 *---------------------------------------------------------------------------*/

private svc_8(buf, inp_size)
short *buf;
int inp_size;
{
    if ((cur_op_mode__ == STAND_ALONE)
#if (TRANSPARENT == TRUE)
    || (cur_op_mode__ == TRANSP) 
#endif
    )
    {
	/* The first word in the buffer indicates the required operation type:
	   0 - read, 1 - write. The second word is the number of bytes to be
	   read/written. These two word are stripped from the buffer when
	   calling svc_3 or svc_4. The i/o is performed on port 0.
	*/
	if (buf[0] == 0) {
	    /* read operation */
	    buf[1] = svc_3(buf+2, buf[1], 0);
	    return(buf[1] + 4);
	}
	else if(buf[0] == 1)
	    /* write operation */
	    svc_4(buf+2, buf[1], 0);
	else {
	    /* unknown operation */
	    errp = e_unknown_svc__;
	    return;
	}
    }
    else
	return(svc_5(buf, inp_size));
}
/*-----------------------------------------------------------------------------
 *
 *				    svc_9
 *
 * This function implements svc 9. It receives a password an the old psr value
 * and returns the new psr value. **** NOTE *** This way is the best since it
 * covers both SwordFish (where psr is 32 bit long) and 32K cpu's (only 16 bit)
 * such that the psr size is transparent to this routine. This function clears
 * the U bit in the psr in order to switch to supervisor mode.
 *---------------------------------------------------------------------------*/

private svc_9(password, psr)
int password;
unsigned int psr;
{
    unsigned int new_psr;

    /* Verify password */
    if (password != SVC_PASSWORD) {
	errp = e_unknown_svc__;
	return;
    }

    /* Clear the U bit in the psr */
    new_psr = psr;
    CLEAR_BIT(PSR_U, new_psr) ;

    return(new_psr);
}
/*^L*/
/*-----------------------------------------------------------------------------
 *
 *				    svc_10 & 11
 *
 * This function implements svc 10. It receives a password a trap procedure
 * descriptor and a pointer to the svaed registers structure. It modifies the
 * saved mod, psr and pc so that the return will be to the trap routine. The
 * mod, psr and pc of the calling routine are overriden. This is used when one
 * handler wants to give control to another handler (e.g. when an FPU emulation
 * routine is reached after UND trap and it decides that this is REALLY and
 * undefined opcode it may want to pass control to the original UND trap handler
 * whose address was obtained using svc 13).
 * Actually the spec of this svc says that r0-r7 should be restored too. This
 * will be done in assembly before exiting from the svc.
 *
 * This function also implements svc 11. The differences between svc 10 and 11
 * cannot be found here. They can be found in the assembly envelope. Svc 11
 * does not restore r0-r7. Svc 11 does not override the saved pc, psr and mod.
 * It allocates space for those of the trap routine on to of stack. In this
 * routine we refer only to this space and override it. But the original saved
 * register still exist below. See the assembly envelope.
 *---------------------------------------------------------------------------*/

private svc_10(password, desc, srp)
int password;
unsigned int desc;
struct svc_regs *srp;
{
#if (CORE_TYPE != SF)
    unsigned short mod, offset;

    /* Verify password */
    if (password != SVC_PASSWORD) {
	errp = e_unknown_svc__;
	return;
    }

    /* Extract mod and offset from the descriptor */
    mod = desc & 0xffff;
    offset = desc >> 16;

    /* Modify mod register */
    srp->mod = mod;

    /* Clear psr T,U and S bits */
    CLEAR_BIT(PSR_S, srp->psr);
    CLEAR_BIT(PSR_U, srp->psr);
    CLEAR_BIT(PSR_T, srp->psr);

    /* Calculate trap-routine pc and modify pc. The trap-routine program base
       the third double-word in its mod table entry, pointed by mod.
    */
    srp->pc = ((int *)mod)[2] + offset;
#endif
}


/*-----------------------------------------------------------------------------
 *
 *				    svc_12
 *
 * This function implements svc 12. It receives a password, new int table
 * address and number of entries to be copied from the old int table to the
 * new one.
 *---------------------------------------------------------------------------*/

private svc_12(password, new_intbase, n_entries)
int password, new_intbase, n_entries;
{
    int old_intbase;

    /* Verify password */
    if (password != SVC_PASSWORD) {
	errp = e_unknown_svc__;
	return;
    }

    /* Store old intbase value and load the new value */
    get_reg_val__(&IN__, &old_intbase);
    put_reg_val__(&IN__, &new_intbase);

    /* Copy the required number of entries from old int table to the new one */
    memcpy(new_intbase, old_intbase, n_entries*4);
}
/*^L*/
/*-----------------------------------------------------------------------------
 *
 *				    svc_13
 *
 * This function implements svc 13. It receives a password, a new value for
 * an int table entry and the number of the entry to be replaced. 
 *---------------------------------------------------------------------------*/

private svc_13(password, new_entry, entry_n)
int password, new_entry, entry_n;
{
    int old_entry, cur_intbase;

    /* Verify password */
    if (password != SVC_PASSWORD) {
	errp = e_unknown_svc__;
	return;
    }

    /* Get current intbase value */
    get_reg_val__(&IN__, &cur_intbase);

    /* Replace the value of the required int table entry */
    old_entry = ((int *)cur_intbase)[entry_n];
    ((int *)cur_intbase)[entry_n] = new_entry;

    return(old_entry);

}
/*-----------------------------------------------------------------------------
 *
 *				    svc_14
 *
 * This function implements svc 14. It change the cpu exception mode to the
 * required mode. It receives a password and an exception mode flag. It changes
 * the exception mode and modifies the intbase register to ponit to the correct
 * interrupt table (there are separate tables - one for each exception mode).
 *---------------------------------------------------------------------------*/

private svc_14(password, mode)
int password;
int mode;
{
    int intbase=0;

    /* Verify password */
    if (password != SVC_PASSWORD) {
	errp = e_unknown_svc__;
	return;
    }

#if (DE_AVAILABLE)
    if (mode == DIRECT_EXCEPTION_MODE)
    /* Assign direct exception table address to intbase */
	intbase = cmon_de_intbase__;
#endif
#if (NON_DE_AVAILABLE)
    if (mode == NON_DIRECT_EXCEPTION_MODE)
    /* Assign non-direct exception table address to intbase */
	intbase = cmon_normal_intbase__;
#endif

    if (intbase == 0) {
	errp = e_unknown_svc__;
	return;
    }
    /* Change exception mode in cfg */
    change_exception_mode__(mode);

    /* Modify intbase register */
    put_reg_val__(&IN__, &intbase);
}
