/******************************************************************************
 *
 *                                    Global_func
 *
 * This module contains the global functions of CMON.
 *
 * History:
 *   +----------+------------+------------------------------------------------+
 *   |   Date   |     by     |     Description                                |
 *   +----------+------------+------------------------------------------------+
 *   |          | Coby       | Written                                        |
 *   +----------+------------+------------------------------------------------+
 *
 *****************************************************************************/


/* #include <ctype.h>***/

#define islower(C)	((boolean) ((C) >= 'a' AND (C) <= 'z'))
#define toupper(C)	((C) = (C) + 'A' - 'a')


#include <inittab.h>


#include "cc_def.h"
#include "global_f.h"

#ifndef public

#define BIN_TO_HEX(bin, hex, size) \
    if (size == 'Q') \
	quad_to_hex__(bin, hex); \
    else \
	bin_to_hex__(*(bin), hex, size)

#define HEX_TO_BIN(hex, bin, size) \
    if (size == 'Q') \
	hex_to_quad__(hex, (bin)); \
    else \
	hex_to_bin__(hex, (bin), size)

#endif

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

/*
 * Array for binary to hexadecimal convertion.
 */

const char   hex_char[] = {
        '0', '1', '2', '3',
        '4', '5', '6', '7',
        '8', '9', 'A', 'B',
        'C', 'D', 'E', 'F'
};

/*
 * Define local functions
 */

private void check_flag(/* flag, flag_array, flag_struct, flags, cur_char, 
                           count, response */);
private void value_flag(/* flag, flags, cur_char, flag_array, response */);

/*^L*/
/*-----------------------------------------------------------------------------
 *
 *                              Remove_space__(line)
 *
 * This function receives a line and converts all tabs to blanks, and all
 * multiple blanks to single blanks. It also removes all leading and trailing
 * blanks.
 *
 *---------------------------------------------------------------------------*/

public void remove_space__(line)
    str   line;
{
    l_str temp_line;
    int   i;

    /*
     * Replace tabs with spaces
     */

    for (i = 0; line[i] != '\0'; i++) {
        if (line[i] == TAB) {
            line[i] = ' ';
        }
    }

    /*
     * Remove leading blanks 
     */

    for (i = 0; line[i] == ' '; i++) {
    }

    if (i != 0) {
        strcpy(temp_line, &line[i]);
        strcpy(line, temp_line);
    }

    /*
     * Remove trailing blanks 
     */

    if (line[0] == '\0') {
        return;
    }

    for (i = strlen(line) - 1; line[i] == ' '; i--) {
    }
    line[i+1] = '\0';

    /*
     * Replace multiple blanks with a single blank
     */

    if (line[0] == '\0') {
        return;
    }

    i = 1;
    while (line[i] != '\0') {
        if ((line[i] == ' ') AND (line[i-1] == ' ')) {
            strcpy(temp_line, &line[i+1]);
            strcpy(&line[i], temp_line);
        }
        else {
            i++;
        }
    }

} /* remove_space__ */





/*-----------------------------------------------------------------------------
 *
 *                              Upper_case__(line)
 *
 * This function receives a line and converts all the lower case characters 
 * to upper case.
 *
 *---------------------------------------------------------------------------*/

public void upper_case__(line)
    str line;
{
    int i;

    for (i = 0; line[i] != '\0'; i++) {
        if (islower(line[i])) {
            line[i] = toupper(line[i]);
        }
    }
} /* upper_case__ */

/*^L*/
/*-----------------------------------------------------------------------------
 *
 *                            Hex_to_bin__(hex, bin, size)
 *
 * This function receives a string containing a hexadecimal number and returns
 * its binary equivalence.
 * The function returns a TRUE value if the string contains a non valid number
 * (an error was encountered), FALSE if everything was ok.
 * The function translate the number of hex charcter according to size
 *
 *---------------------------------------------------------------------------*/

public boolean hex_to_bin__(hex, bin, size)
    str      hex;
    unsigned *bin;
    char     size;
{
    int       len;
    char     *chp;

    /*
     * Translate Number of charcter acording to size parameter
     */

    switch (size) {
	case 'B':
	    len = 2;
	    break;
	case 'W':
	    len = 4;
	    break;
	case 'D':
	    len = 8;
	    break;
	default:
	    ERROR(e_syntax__);
    }
    *bin = 0;
    if (strlen (hex) > len){ 
        chp = &hex[strlen(hex) - len];
    }
    else {
        chp = hex;
    }
    for (;*chp != '\0';chp++){
	(*bin)  <<= 4;
        if ((ORD(*chp) >= ORD('0')) AND (ORD(*chp) <= ORD('9'))) {
            (*bin) += ORD(*chp) - ORD('0');
        }
        else if ((ORD(*chp) >= ORD('A')) AND (ORD(*chp) <= ORD('F'))) {
            (*bin) += (ORD(*chp) - ORD('A')) + 10;
        }
        else if ((ORD(*chp) >= ORD('a')) AND (ORD(*chp) <= ORD('f'))) {
            (*bin) += (ORD(*chp) - ORD('a')) + 10;
        }
        else {
            ERROR(e_syntax__);
        }
	
    }
    return(FALSE);
} /* hex_to_bin__ */





/*-----------------------------------------------------------------------------
 *
 *                            Bin_to_hex__(bin, hex, size)
 *
 * This function receives an unsigned integer and returns it in the form of 
 * a string containing a hexadecimal digits. The string will contain the
 * hexadecimal translation of bin according to size.if size is B W D then no 
 * leading zeroes are added if size is '2' '4' or '8' leading zeroes added
 *
 *---------------------------------------------------------------------------*/

public void bin_to_hex__(bin, hex, size)
    unsigned bin;
    str      hex;
    char     size;
{
    int      i, bin_dig, len;
    boolean  non_zero_found;

    /*
     * Translate Number of charcter acording to size parameter
     */
    non_zero_found = FALSE;
    switch (size) {
        case 'B':
            len = 1;
            break;
        case 'W':
            len = 3;
            break;
        case 'D':
            len = 7;
            break;
	case '2':
	    len = 1;
	    non_zero_found = TRUE;
	    break;
	case '4':
	    len = 3;
	    non_zero_found = TRUE;
	    break;
	case '8':
	    len = 7;
	    non_zero_found = TRUE;
	    break;

    }
    for (i = len; i >= 0; i--){
        bin_dig = (bin >> (i * 4)) & 0xf;
        if (non_zero_found OR (bin_dig != 0)) {
            *hex++ = hex_char[bin_dig];
            non_zero_found = TRUE;
        }
    }
    if (NOT non_zero_found) {
        *hex++ = '0';
    }
    *hex = '\0';
} /* bin_to_hex__ */

/*^L*/
/*-----------------------------------------------------------------------------
 *
 *                            Hex_to_quad__(hex, bin)
 *
 * This function receives a string containing a hexadecimal number and returns
 * its binary quad_word equivalence.
 * The function returns a TRUE value if the string contains a non valid number
 * (an error was encountered), FALSE if everything was ok.
 * If the number is larger than 16 hexadecimal digits only the first 16 are 
 * used.
 *
 *---------------------------------------------------------------------------*/

public boolean hex_to_quad__(hex, quad_bin)
    str      hex;
    unsigned *quad_bin;
{
    s_str    tmp_str;
    int      len;

    /*
     * Translate only 16 hex characters at the most.
     * If more than 8 hex characters then translation is made from two phase
     * first translate the high double word and then translate the low double
     * word.
     */

    len = strlen(hex);
    if (len > 16){
        len = 16;
    }
    if (len > 8){
        strncpy(tmp_str,hex,len - 8);
	tmp_str[len - 8] = '\0';
        if (hex_to_bin__(tmp_str, &quad_bin[1],'D')){
            return TRUE;
        }
	strncpy(tmp_str,&hex[len - 8], 8);
	tmp_str[8] = '\0';
	return (hex_to_bin__(tmp_str, &quad_bin[0], 'D'));
    }
    else{
        quad_bin[1] = 0;
	return (hex_to_bin__(hex, &quad_bin[0], 'D'));
    }
} /* hex_to_quad__ */





/*-----------------------------------------------------------------------------
 *
 *                            Quad_to_hex__(bin, hex)
 *
 * This function receives an unsigned quad word and returns it in the form of 
 * a string containing a hexadecimal digits.
 * if the high order double word is 0 the string will be 8 hexadecimal digit
 * long, otherwise it is 16 digitis long.
 *
 *---------------------------------------------------------------------------*/

public void quad_to_hex__(quad_bin, hex)
    unsigned *quad_bin;
    str      hex;
{
    s_str   tmp_hex;
    int     i, tmp_len, len;

    if (quad_bin[1] == 0){
        bin_to_hex__(quad_bin[0], hex, 'D');
    }
    else {
        bin_to_hex__(quad_bin[1], hex, 'D');
        bin_to_hex__( quad_bin[0], tmp_hex, 'D');
	/*
	 * Add '0' for lower double word
	 */
	len = strlen(hex);
	tmp_len = strlen(tmp_hex);
	for (i = 0; i < 8 - tmp_len; i ++){
	    hex[i + len] = '0';
	}
	strcpy (&hex[i+len], tmp_hex);
    }
} /* quad_to_hex__ */


/*----------------------------------------------------------------------------
 *
 *              Quad_compare__(arg1,arg2)
 *
 * This function compares two quad word argument.
 * It returns: 1 iff arg1 > arg2 as unsigned quad word
 *             0 iff arg1 == arg2 as unsigned quad word
 *            -1 iff arg1 < arg2 as unsigned quad word
 *
 *--------------------------------------------------------------------------*/
 
public int quad_compare__(arg1, arg2)
    unsigned        *arg1, *arg2;
{
    if (arg1[1] == arg2[1]){
        if (arg1[0] == arg2[0]){
            return (0);
        }
        else{
            if (arg1[0] > arg2[0]){
                return (1);
            }
            else return (-1);
        }
    }
    else {
        if (arg1[1] > arg2[1]){
            return (1);
        }
        else return (-1);
    }
}
/*^L*/
/*-----------------------------------------------------------------------------
 *
 *        Get_first_arg__(line, first_arg, arg_len, max_len, allow_no_arg)
 *
 * This function receives a line and returns the first argument it finds.
 * The argument always ends with a blank, '=', ,':', ',', '/' or end of line.
 * The length of the argument is also returned.
 * The function call the ERROR macro if argument is longer than max_len,
 * OR allow_no_arg is FALSE and first arg is empty
 * If a blank preceeds the argument it is ignored, but is counted in the 
 * length.
 * This function is used to get the next arg each time, by simply passing
 * the address of the character in line which comes right after the previous
 * argument.
 *
 *---------------------------------------------------------------------------*/

public void get_first_arg__(line, first_arg, arg_len, max_len,allow_no_arg)
    str      line, first_arg;
    unsigned *arg_len;
    unsigned max_len;
    boolean  allow_no_arg;
{
    unsigned min, first_char;

    /*
     * If the line is empty, return
     */

    *arg_len = 0;
    first_arg[0] = '\0';
    if (line[0] == '\0') {
	if (allow_no_arg){
	    return;
	}
	else {
	    ERROR(e_syntax__);
	}
    }

    /*
     * Ignore leading blank. Only one is possible, since multiple blanks were
     * removed already.
     */

    first_char = (line[0] == ' ') ? 1 : 0;

    /*
     * Locate the first of each type of character
     */

    for (min = first_char; strchr(" =:/,", line[min]) == (char *) NULL; min++)
        ;
    min -= first_char;
    /* 
     * min = strcspn(&line[first_char], " =:/,");
     */

    /*
     * Return the command verb
     */

    if (min > max_len) {
        *arg_len     = 0;
        first_arg[0] = '\0';
	ERROR(e_syntax__);
    }
    if (( min == 0) AND NOT allow_no_arg) { 
	ERROR (e_syntax__);
    }
    *arg_len = first_char + min;
    strncpy(first_arg, &line[first_char], (int) min);
    first_arg[min] = '\0';
} /* get_first_arg__ */

/*^L*/
/*-----------------------------------------------------------------------------
 *
 *            Get_value_for_change__(cmd, value, verify)
 *
 * This function receives the last part of a change command. This is the part 
 * which starts with the '=' sign.
 * It returns the value specified and a flag signaling if verify was requested.
 * If the value specified is too long, it is truncated.
 *
 *---------------------------------------------------------------------------*/

public void get_value_for_change__(cmd, value, verify)
    str      cmd, value;
    boolean  *verify;
{
    unsigned cur_char, len;
    boolean  err;
    l_str    temp_value;

    /*
     * Check that there is one, and only one, '=' sign.
     */

    cur_char = 0;
    if (cmd[cur_char] == ' ') {
        cur_char++;
    }

    if (cmd[cur_char] != '=') {
        ERROR( e_syntax__);
    }
    cur_char++;

    /*
     * Get the new value. If it is too long, truncate it.
     */

    get_first_arg__(&cmd[cur_char], temp_value, &len, L_SIZE,
                   /* accept null arg */ FALSE);

    if (len > S_SIZE) {
        strncpy(&value[strlen(value)-S_SIZE+1], temp_value, S_SIZE-1);
        value[S_SIZE] = '\0';
    }
    else {
        strcpy(value, temp_value);
    }

    /*
     * Get the verify flag
     */

    cur_char += len;
    get_verify_flag__(&cmd[cur_char], verify);

} /* get_value_for_change__ */

/*^L*/
/*-----------------------------------------------------------------------------
 *
 *                   Get_verify_flag__(cmd, verify)
 *
 * This function receives the last part of a command which has an optional
 * verify flag. All it should contain is "V" or null.
 * It returns a flag signaling if verify was requested.
 *
 *---------------------------------------------------------------------------*/

public void get_verify_flag__(cmd, verify)
    str      cmd;
    boolean  *verify;
{
    unsigned len;
    s_str    ver_flag;

    /*
     * Get the verify flag, if exists. This should be the last parameter.
     * If not present, it defaults to FALSE.
     */

    if (strlen(cmd) > 0 ) {
        get_first_arg__(cmd, ver_flag, &len, S_SIZE,
	               /* accept null arg */ TRUE);
        if (NOT STRINGS_EQUAL(ver_flag, "V")) {
            ERROR( e_syntax__);
        }
        if (cmd[len] != '\0') {
            ERROR( e_syntax__);
        }
        *verify = TRUE;
    }
    else {
	*verify  = FALSE;
    }
} /* get_verify_flag */

/*^L*/
/*-----------------------------------------------------------------------------
 *
 *      Interpret_flags__(flags, flag_array, flag_struct, count)
 *
 * This function receives a string containing a set of flags. It checks them
 * according to the information supplied in the flag structure and returns an 
 * array specifying for each flag if it was specified and what its value was.
 * Count is the number of flag types possible in this case.
 * Flags are either simple (don't have a =value part) or value flags.
 * Simple flags have an implied value if they are specified.
 * The structure tells us the type of the flag and the implied value if it is
 * a simple flag.
 *
 *---------------------------------------------------------------------------*/

public void interpret_flags__(flags, flag_array, flag_struct, count)
    str      flags;
    flag_array_type  flag_array;
    flag_struct_type flag_struct;
    unsigned count;
{
    unsigned cur_char, len, i;
    s_str    flag;
    boolean  err;

    /*
     * Initialize all flags to 'not specified'
     */

    for (i = 0; i <= count; i++) {
        flag_array[i].specified = FALSE;
    }

    /*
     * Go over each flag and check it. 
     */

    cur_char = 0;
    while (strlen(&flags[cur_char]) > 0) {
        get_first_arg__(&flags[cur_char], flag, &len, S_SIZE,
	               /* accept null arg */ TRUE);
        cur_char += len;

        check_flag(flag, flag_array, flag_struct, &flags[cur_char], &cur_char,
                   count);
    }

} /* interpret_flags__ */

/*^L*/
/*-----------------------------------------------------------------------------
 *
 *  Check_flag(flag, flag_array, flag_struct, flags, cur_char, count)
 *
 * This function receives a string containing a flag. It checks if it is 
 * legal according to the flag structure and sets the appropriate value in
 * the flag array. Flags contains the rest of the flag list starting with the 
 * first character after the flag. It is used to find the values for some of 
 * the flags.
 *
 *---------------------------------------------------------------------------*/

private void check_flag(flag, flag_array, flag_struct, flags, cur_char, count)
    str      flag, flags;
    unsigned *cur_char;
    unsigned count;
    flag_array_type  flag_array;
    flag_struct_type flag_struct;
{
    unsigned i, flag_num;
    
    /*
     * Check the flag
     */

    for (i = 0; i <= count; i++) {
        if (STRINGS_EQUAL(flag, flag_struct[i].flag_str)) {
            flag_num = flag_struct[i].flag;
            if (flag_array[flag_num].specified) {
                ERROR( e_syntax__);
            }
            flag_array[flag_num].specified = TRUE;
            if (flag_struct[i].simple) {
                flag_array[flag_num].value = flag_struct[i].value;
            }
            else {
                value_flag(flag_struct[i].flag, flags, cur_char, flag_array);
            }
            return;
        }
    }

} /* check_flag */

/*^L*/
/*-----------------------------------------------------------------------------
 *
 *      Value_flag(flag, flags, cur_char, flag_array)
 *
 * This function receives a flag. It gets its value from the flags string and
 * sets it.
 *
 *---------------------------------------------------------------------------*/

private void value_flag(flag, flags, cur_char, flag_array)
    unsigned flag;
    flag_array_type flag_array;
    unsigned *cur_char;
    str      flags;
{
    s_str    hex_value;
    unsigned value, len;
    boolean  err;

    /*
     * Get the flags value.
     */

    if (flags[*cur_char] == ' ') {
        cur_char++;
    }
    if (flags[*cur_char] == '=') {
        cur_char++;
    }
    get_first_arg__(&flags[*cur_char], hex_value, &len, S_SIZE,
                   /* accept null arg */ FALSE);
    *cur_char += len;

    hex_to_bin__(hex_value, &value, 'D');
    flag_array[flag].value = value;

} /* value_flag */




/*-----------------------------------------------------------------------------
 *  
 *                 Null_func__ (cmd,response)
 *
 * This is a dummy function to be called by every commands that is accepted
 * for comptabilty reasones.
 *
 *---------------------------------------------------------------------------*/
 
public void null_func__ (cmd,response)
    str           cmd,response;
{

}






/*-----------------------------------------------------------------------------
 *
 *                                Init_data__()
 *
 * This function is used to copy cmon's initialized data from ROM to RAM upon
 * reset. It also initializes uninitialized data to zero.
 * It uses a linker-created initialization table which provides information
 * about data segments that should be initialized
 *
 *---------------------------------------------------------------------------*/

#define UNDEF 0xffffffff

public void init_data__()
{
#ifndef  DEBUG
    extern INITTAB _INIT_TABLE[] ;  /* _INIT_TABLE is defined by the Linker */
    INITTAB *inittab_entry ;        /* a pointer to the current init table
                                       entry */
 
    for (inittab_entry = _INIT_TABLE;       /* start of init table */
         inittab_entry->i_secname[0] != 0;  /* last entry is a null one */
         inittab_entry++) {
 
         if (inittab_entry->i_srcaddr == UNDEF)
            /* No source address - its an uninitialized data (bss) area.
               Should be set to zero.
            */
            /* memset(inittab_entry->i_trgaddr, '\0', inittab_entry->i_size)*/;
        else
            /* It's an initialized data area - copy from ROM to RAM */
            memcpy(inittab_entry->i_trgaddr,
                   inittab_entry->i_srcaddr,
                   inittab_entry->i_size) ;
    }
#endif
}

/*^L*/
/*-----------------------------------------------------------------------------
 *
 *            Search_reg_tab__(reg)
 *
 * This function receives a register name and returns a pointer to its entry
 * in the register table. If the register name was not found in the table it
 * returns NULL.
 *
 *---------------------------------------------------------------------------*/

public reg_desc *search_reg_tab__(reg_name)
str reg_name;
{
    const reg_desc **regpp;

    for (regpp = reg_tab__; *regpp != NULL; regpp++)
	if (strcmp((*regpp)->name, reg_name) == 0)
	    break;

    return(*regpp);

} /* search_reg_tab__ */

/*^L*/
/*-----------------------------------------------------------------------------
 *
 *            Get_reg_val__(regp, bin)
 *
 * This function receives a pointer to a register table entry and a pointer
 * to a buffer where the register value is expected to be returned.
 * It either uses the register saved value or calls a function that
 * returns the register value. Note that the binary value is always zero-
 * extended to quad-word.
 *
 *---------------------------------------------------------------------------*/

public void get_reg_val__(regp, bin)
reg_desc *regp;
quad_word bin;
{
    if (regp->read_func == NULL)
	if (regp->size == 'Q') {
	    bin[0] = regp->save_addr[0];
	    bin[1] = regp->save_addr[1];
	}
	else
	    bin[0] = regp->save_addr[0];
    else
	(*(regp->read_func))(bin);
	
}

/*^L*/
/*-----------------------------------------------------------------------------
 *
 *            Put_reg_val__(regp, bin)
 *
 * This function receives a pointer to a register table entry and a pointer
 * to a buffer containing the new register binary value.
 * It either stores the new value into the register save variable or 
 * calls a function that performs the modification.
 *
 *---------------------------------------------------------------------------*/

public void put_reg_val__(regp, bin)
reg_desc *regp;
quad_word bin;
{
    if (regp->write_func == NULL) {
	switch (regp->size) {
	    case 'B' :
		* (unsigned char *)regp->save_addr = bin[0];
		break;
	    case 'W' :
		* (unsigned short *)regp->save_addr = bin[0];
		break;
	    case 'D' :
		*regp->save_addr = bin[0];
		break;
	    case 'Q' :
		regp->save_addr[0] = bin[0];
		regp->save_addr[1] = bin[1];
		break;
	}
    }
    else
	(*(regp->write_func))(bin);
} /* put_reg_val__ */
 
/*^L*/
/*-----------------------------------------------------------------------------
 *
 *            Print_reg_val__(regp, response)
 *
 * This function prints the response for a print register command. It receives
 * a pointer to a register table entry and according to the information in this
 * entry it either uses the register saved value or calls a function that
 * returns the register value. It returns in response a string of the forem
 * =<register-value-in-hexadecimal> .
 *
 *---------------------------------------------------------------------------*/

public void print_reg_val__(regp, response)
    reg_desc *regp;
    str response;
{
    quad_word bin;

    response[0] = '=';

    get_reg_val__(regp, bin);
    BIN_TO_HEX(bin, &response[1], regp->size);

} /* print_reg_val__ */

/*^L*/
/*-----------------------------------------------------------------------------
 *
 *            Change_reg_val__(regp, value, verify)
 *
 * This function 
 *
 *---------------------------------------------------------------------------*/
change_reg_val__(regp, value, verify)
reg_desc *regp;
str value;
boolean verify;
{
    quad_word bin, bin_ver;

    HEX_TO_BIN(value, bin, regp->size);
    put_reg_val__(regp, bin);

    /* Don't perform register value verification. This is often meaningless
       a register copy and not the register itself is modified. Besides 
       verification is intended for memory write, not for registers.
    */

    /*
    if (verify) {
    }
    */
}
