/****************************************************************************
**
**  Name:  cpucli.c
**
**  Description:
**     Routines to support the Cpu Server cli interface.
**
**  Status: TESTED
**
**  $Log:   S:/tbird/mt2_68k/cpu/cpucli.c_v  $
** 
**    Rev 1.2   15 Dec 1997 15:05:02   hera
** added cpuonly option to reset command
** 
**    Rev 1.1   24 Mar 1997 08:36:30   gene
** 
**    Rev 1.0   13 Feb 1997 08:44:46   gene
** Initial revision.
** 
**    Rev 1.4   02 Jul 1996 16:01:50   kevin
** After reset cpu, send a message to cli to show core type
** 
**    Rev 1.3   28 Mar 1996 16:34:32   kevin
** Converting string of register's value will not use functions of address
** server.
** 
**    Rev 1.2   18 Jan 1996 14:07:14   kevin
** got rid of cpuonly option from cpu command
** 
**    Rev 1.1   03 Oct 1995 14:37:20   gene
** modify CliSignal cmdString[] error
** 
**    Rev 1.0   07 Sep 1995 10:10:40   gene
** Initial revision.
** 
**    Rev 1.22   07 Apr 1994 12:30:28   john
** Fixed bug in the signal names
** 
**    Rev 1.21   13 Jul 1993 19:28:18   doug
** Use generic syntax error.
** 
**    Rev 1.20   08 Apr 1993 14:39:20   doug
** signal command gives error if PowerScope in use
** 
**    Rev 1.19   30 Mar 1993 08:25:04   ernie
** 1. Changed interface to CpuFindRegisterName().
** 2. Changed _regname command handler to use new CpuGetRegisterInfo()
**    and CpuGetRegisterValueText() calls.
** 3. Changed reg display to include both numeric and symbolic display
**    of flag-type registers.
** 
**    Rev 1.18   05 Jan 1993 15:51:40   courtney
** Removed MemAlloc and related macros (no longer used).
** 
**    Rev 1.17   09 Oct 1992 14:55:04   doug
** a) add reset of cpu only
** b) one syntax error
** 
**    Rev 1.16   17 Sep 1992 10:57:58   doug
** show all registers as 8 digits
** 
**    Rev 1.15   15 Sep 1992 09:06:50   courtney
** Revised error return to standard format.
** 
**    Rev 1.14   08 Sep 1992 09:27:22   doug
** remove event stuff that is commented out
** 
**    Rev 1.13   02 Sep 1992 11:38:32   doug
** a) allow symbolic entry of register values
** b) radix routine not used
** 
**    Rev 1.12   02 Sep 1992 10:31:46   doug
** improve error message
** 
**    Rev 1.11   14 Aug 1992 08:25:44   doug
** removed all warnings except for STRING problem, which is a problem in
** basictyp.h (I wrote a PPR on this).
** 
**    Rev 1.10   08 Aug 1992 08:48:58   tom
** server registration change
** 
**    Rev 1.9   22 Jul 1992 16:05:00   doug
** a) calling from cli is unnessary (bug)
** 
**    Rev 1.8   21 Jul 1992 08:09:26   doug
** a) generic, no more 68030
** b) no aliases anymore
** c) only run-time config supported
** d) no floating point support
** e) SendMessage does not return error; return code is meaningless
** 
**    Rev 1.7   14 Jul 1992 11:26:18   courtney
** CPU Server is now table-driven (reads in processor-specific table
** at runtime).  Changes to reflect architecture change.
** 
**    Rev 1.6   13 May 1992 09:03:28   nghia
** Revised SendMessageToCli() to be a global routine.
** 
**    Rev 1.5   23 Apr 1992 12:11:50   nghia
** Fixed PPR 5390.
** Cleaned up error message return. Added format for CLI results.
** Fixed PPR 5278 - convert an 80bit value.
** Added new EVENT_REGISTERS_CHANGED_HALTED for cpu presenter.
** 
**    Rev 1.4   17 Apr 1992 16:18:48   nghia
** Added CLI command to find register Name.
** 
**    Rev 1.3   08 Apr 1992 10:49:58   nghia
** Fixed PPR #5277 - CLI command to dump registers content and value.
** 
**    Rev 1.2   10 Mar 1992 12:52:40   nghia
** Changed sscanf() to strtoul() for consistency with other CLI commands.
** 
**    Rev 1.1   02 Mar 1992 09:03:36   nghia
** Added floating-point support for the CPU CLI commands.
** 
**    Rev 1.0   28 Feb 1992 11:55:58   nghia
** Initial revision.
**
**  $Header:   S:/tbird/mt2_68k/cpu/cpucli.c_v   1.2   15 Dec 1997 15:05:02   hera  $
**
**
**  Copyright (C) 1991 Microtek International.  All rights reserved.
**
*****************************************************************************/

		       /****************************
			*                          *
			*       INCLUDE FILES      *
			*                          *
			****************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#ifndef _BASEWIND_
#include "basewind.h"
#endif

#ifndef _ADDR_
#include "addr.h"
#endif

#ifndef _CLISRV_
#include "clisrv.h"
#endif

#ifndef _CPU_
#include "cpu.h"
#endif

#ifndef _CPUCLI_
#include "cpucli.h"
#endif

#ifndef _CPUERR_
#include "cpuerr.h"
#endif

#ifndef _ENLIB_
#include "enlib.h"
#endif

#ifndef _HEAP_
#include "heap.h"
#endif

#ifndef _HOSTERRS_
#include "hosterrs.h"
#endif
			/***************************
			*                          *
			*     LOCAL DEFINITIONS    *
			*                          *
			****************************/

#define MAX_NAME     25 /* Maximum number of characters in a name */
#define MAX_U32SIZE  8
#define MAX_U64SIZE  16
#define MAX_U32WIDTH 32
#define MAX_U64WIDTH 64
#define MAX_FPREG_STRSIZE 20

/* Local buffers */
S8    strBuf[80];
LPSTR status[2] = {"DISABLE", "ENABLE"};
typedef enum { DECIMAL_TYPE, OCTAL_TYPE, HEXADECIMAL_TYPE} RADIX_TYPE;

STATIC HANDLE cliServerHandle = (HANDLE)NULL;  /* force definition here */

			/****************************
			 *                          *
			 *    EXTERNAL VARIABLES    *
			 *                          *
			 ****************************/
/* CPU Server data elements Table - defined in CPU.C */
extern REGISTER_ENTRY *regTable;
extern SIGNAL_ENTRY *signalTable;
extern U16 nRegisters;
extern U16 nSignals;

			/****************************
			 *                          *
			 *     LOCAL PROTOTYPES     *
			 *                          *
			 ****************************/
RETCODE PRIVATE CheckCliToken(S8 *buf, U16  *token_no) ;
RETCODE PRIVATE ShowRegisterValue(U16 regId);
RETCODE PRIVATE displaySignalCmdResult(REG_ID sigId, U8 sigValue,
   S8 *sigName, RETCODE cmdErr);
RETCODE PRIVATE FindSignalId(S8 *sigNameStr, REG_ID *sigId);

			/****************************
			 *                          *
			 *      EXECUTABLE CODE     *
			 *                          *
			 ****************************/

/**************************************************************************
**
** InitCServer
**
***************************************************************************/
RETCODE EXPORT InitCServer(HANDLE cliHandle, HANDLE dllHandle) {
   CSERVER_NEW_REGISTRATION FAR * msgBufPtr;

   cliServerHandle = cliHandle;
   msgBufPtr =
      (CSERVER_NEW_REGISTRATION FAR *)TMalloc(sizeof(CSERVER_VARIABLE_VALUE));
   if (msgBufPtr == NULL) {
      return(ER_OUT_OF_MEMORY);
   }

   msgBufPtr->stringResourceHandle = dllHandle;

   msgBufPtr->serverNameIndex = 30;
   msgBufPtr->dllNameIndex = 31;
   msgBufPtr->numberOfCommandsIndex = 32;
   msgBufPtr->commandStartIndex = 33;
   SendMessage(cliHandle, CLI_NEW_SVR_REGISTRATION, CLI_NEW_SVR_REGISTRATION,
      (DWORD)msgBufPtr);
   return(GOOD);
}

/* note that all cli entry points must verify that the init table
   routine has been read!!
*/
/***************************************************************************
**
**  CpuCliSignals
**
**  Status:  PRELIMINARY
**
**  description:
**     Cli interface to the signals display
**
**  input:
**     <none>
**
**  output:
**     message:
**        signals value(get signals).
**     <none> : signals modify
**
*****************************************************************************/
RETCODE EXPORT CpuCliSignal(LPSTR cmdString, U32 argc, U32 argv[]) {
   RETCODE     ret ;
   REG_ID      sigId;
   U8          sigValue;
   U16         sigToken;
   PROC_SYSTEM_TYPE systemType;

   if((ret = ProcReturnSystemType(&systemType))!=GOOD) return(ret);
   if(systemType==PROC_POWERSCOPE) return(ER_SIG_NOT_AVAIL_IN_POWERSCOPE);
   if (argc >= 3)
      if (stricmp(&cmdString[(int)argv[2]],"-")==0) {
	 strcat(&cmdString[(int)argv[1]],&cmdString[(int)argv[2]]);
	 argc--;
	 argv[2] = argv[3] ;
      }
   switch(argc) {
	  case 1: /* signal <cr> */
	 /* display all signals state */
	 for (sigId = 0; sigId<nSignals; sigId++) {
	     ret = CpuGetSignal(sigId, (U8 FAR *) &sigValue);
	     displaySignalCmdResult(sigId, sigValue, NULL, ret);
	 }
	 break;

      case 2: /* signal signal_name <cr> */
	 if ((ret = FindSignalId(&(cmdString[(int)argv[1]]), &sigId))!=GOOD) {
	    displaySignalCmdResult(0, 0, &(cmdString[(int)argv[1]]), ret);
	    return(ret);
	 }
	 ret = CpuGetSignal(sigId, (U8 FAR *) &sigValue);
	 displaySignalCmdResult(sigId, sigValue, NULL,ret);
	 break;

      case 3:  /* signal signame [enable/disable] */
	 if ((ret = CheckCliToken(&(cmdString[(int)argv[2]]), &sigToken))
	    != GOOD) return(ret);

	 /* Enable = 1, Disable = 2 according to cliToken_tbl[] index */
	 if (sigToken == 1) sigValue = TRUE;
	 else if (sigToken == 2) sigValue = FALSE;
	 else return(ER_INVALID_ARG);

         if (stricmp(&(cmdString[(int)argv[1]]),"all") == 0) {
            for (sigId = 0; sigId<nSignals; sigId++) {
                ret = CpuSetSignal((REG_ID) sigId, sigValue);;
                displaySignalCmdResult(sigId, sigValue, NULL, ret);
            }
         } else {
            if ((ret = FindSignalId(&(cmdString[(int)argv[1]]), &sigId))!=GOOD) {
               /* signal name found! */
               displaySignalCmdResult(0, 0, &(cmdString[(int)argv[1]]), ret);
               return(ret);
            }
            /* Set new state for the signal */
            ret = CpuSetSignal((REG_ID) sigId, sigValue);
            displaySignalCmdResult(sigId, sigValue, NULL, ret);
         }
         break ;
      default:
	 return(ER_CLI_SYNTAX) ;
   }  /* end of switch */
   return(GOOD) ;
}

/***************************************************************************
**
**  CpuCliRegister
**
*****************************************************************************/
RETCODE EXPORT CpuCliRegister(LPSTR cmdString, U32 argc, U32 argv[]) {
   RETCODE       ret;
   BOOLEAN       isWriteData ;
   REG_ID        regId, tempId;
   U16           i;
   U32           regValue;
   S8            regName[MAX_NAME];
   S8            *ptr, lpnext[MAX_NAME]; /* lookahead buffer */
   BOOLEAN       pushBack = FALSE;

/* this syntax is obsolete!  only single register display/update is handled
    (in addition to full dump) */
   /*
   *** SYNTAX SUPPORTED:
   ***    CPU_REGISTER
   ***    CPU_REGISTER A0 1234 D0      (assign A0=1234, display D0)
   ***    CPU_REGISTER A0 D0           (display A0, D0)
   ***    CPU_REGISTER A0 0xffff D0 3  (assign A0=0xffff, assign D0=3)
   ***  (requires single-token lookahead)
   */

   if (argc == 1) {
      /* Dump all registers and their value */
      for (i = 0; i<nRegisters; i++) {
	 if ((ret = ShowRegisterValue(i)) != GOOD) return(ret);
      }
      return(GOOD);
   }

   /*
   *** Parsing Algorithm:
   *** while (number of processed arg < number of arg + 1)
   ***   if (pushbacked is true) then arg = pushbacked arg
   ***   else arg is a new token
   ***   set writeData flag = false
   ***   if (arg is a register id) then get its name
   ***   else error invalid syntax
   ***   if ()
   ***   look ahead for the next arg. -> increment number of process argument
   ***      if (look-ahead is a reg_id) then set pushbacked = true
   ***      else look-ahead is a data assignment
   ***         set writeData flag = true
   ***         if (data is valid) then set the previous reg_id with the data
   ***         else error invalid data format
   ***   if (writeData = false) then
   ***      get data of the reg_id to display
   ***      if (pushbacked is true) then
   ***         continue (number of processed arg already counted)
   ***   else
   ***      increment the number of processed arg.
   *** end of loop.
   ***
    */

   /* Do the command one by one - "register <reg_name list><cr>"  */
   i = 1;
   while ( i < argc ) {
      if (pushBack) {  /* we've already got it */
	 ptr = lpnext;
	 pushBack = FALSE;
      }
      else
	 ptr = &(cmdString[(int)argv[i]]);

      isWriteData = FALSE; /* display one register */
      /* isWriteData is set TRUE if we want to modify register,
       * else FALSE for display only
       */

      /* look thru all register to see if this matches a register name */
      if (CpuFindRegisterId((U8 *FAR)ptr, &regId) == GOOD)
	 lstrcpy((LPSTR)regName, (LPSTR)ptr);
      else
	 return (ER_INVALID_ARG);

      /* peek at next argument to see if it's a data or register id */
      if (i < argc-1) {
	 lstrcpy((S8 FAR *)lpnext, (S8 FAR *)&cmdString[(int)argv[++i]]);

	 /* If the next argument is a register then display the previous one */
	 if (CpuFindRegisterId((U8 FAR *)lpnext, &tempId) == GOOD)
	    pushBack = TRUE;
	 else {  /* Found register data assignment */
/**<<<**
              DESCRIPTOR addrDesc;
	    isWriteData = TRUE;

	    if((ret = AdrCreateAddress(&addrDesc))!=GOOD) return(ret);
	    if((ret = AdrConvTextToAddress(addrDesc, lpnext))!=GOOD)
	       return(ret);
	    if((ret = AdrGetAddrOffset(addrDesc, &regValue))!=GOOD)
	       return(ret);
	    if((ret = AdrDestroyAddress(addrDesc))!=GOOD) return(ret);
**<<<**/
/**>>>**/
              CHAR szValue[20];
              CHAR* endptr;
              U8   i=0;

	    isWriteData = TRUE;
              if (lpnext[0]=='0' && (lpnext[1]=='x' || lpnext[1]=='X'))
                 i = 2;
              szValue[0]='0';
              szValue[1]='x';
              strcpy(&szValue[2], &lpnext[i]);
              regValue = strtoul(szValue, &endptr, 0);
              if (*endptr != '\0')
                 return(!GOOD);
/**>>>**/
	    if ((ret = CpuSetRegister(regId, regValue)) != GOOD)
	       return(ret);
	 }
      } /* if argc-1 */

      if (!isWriteData) {
	 /* just display previous reg, push this one back */
	 if ((ret = ShowRegisterValue(regId)) != GOOD) return(ret);
	 if (pushBack)
	    continue;  /* don't increment i - look-ahead already did */
      }
      i++;   /* next argument */
   } /* end of while */

   return(GOOD);

}

/***************************************************************************
**
**  CpuCliResetCPU
**
*****************************************************************************/
#pragma argsused
RETCODE EXPORT CpuCliResetCPU(LPSTR cmdString, U32 argc, U32 argv[]) {
PROBE_TYPE processor;
U16 coreType;
RETCODE err;

   if ((err = ProcReturnSpecificProcessor(&processor)) != GOOD)
      return(err);

   if (argc==1)
      err = CpuResetCPU();
   else
      err = ER_CLI_SYNTAX;

   if (processor == M68302_MP) {
      if (CpuGetCoreType(&coreType) != GOOD)
         return(ER_CMD_ABORT);
      if (coreType == 1)
         SendMessageToCli((LPSTR) "Core type is 68008") ;
      else
         SendMessageToCli((LPSTR) "Core type is 68000") ;
   }
   if((argc==2) && (!strcmpi(&(cmdString[(int)argv[1]]), "cpuonly")))
      return(CpuResetCPUOnly());
   return(err);
}

/***************************************************************************
**
**  CpuCliFindRegName
**
*****************************************************************************/
#pragma argsused
RETCODE EXPORT CpuCliFindRegName(LPSTR cmdString, U32 argc, U32 argv[]) {
   RETCODE ret;
   S8 *endPtr, lpId[MAX_NAME];
   REG_ID regId;
   S8 regName[MAX_NAME];
   S8 regValue[MAX_NAME];
   U16 sortIndex;
   U16 type;

   if (argc != 2) return(ER_CLI_SYNTAX);
   lstrcpy((LPSTR)lpId, (LPSTR) &(cmdString[(int)argv[1]]));
   regId = (REG_ID) strtoul(lpId, &endPtr, 0);
   if (*endPtr != '\0')  /* Syntax error */
      return(ER_INVALID_ARG);
   if ((ret = CpuGetRegisterInfo(regId, regName, &sortIndex, &type, NULL))
	!= GOOD) return(ret);
   /* Put it in the buffer and return */
   if ((ret = CpuGetRegisterValueText(regId, type, regValue))
	!= GOOD) return(ret);
   sprintf(strBuf, "Id: %02d = register %6s; Sort %d; Type %d; Value = %s",
      regId, regName, sortIndex, type, regValue);
   SendMessageToCli((LPSTR) strBuf) ;
   return(ret) ;
}

/****************************************************************************
**
**  SendMessageToCli.
**
*****************************************************************************/
RETCODE SendMessageToCli(LPSTR msgPtr) {
   GLOBALHANDLE msgBufHandle;
   CSERVER_RESULTS FAR  *msgBufPtr;
   U16 msgTextSize;
   LOOP_VAR loop;

   msgTextSize = lstrlen(msgPtr);
   msgBufHandle = GlobalAlloc(GMEM_MOVEABLE, (sizeof(CSERVER_RESULTS) +
     msgTextSize + 2));
   if (msgBufHandle == (HANDLE)NULL) {
	  return(ER_OUT_OF_MEMORY);
   }
   else if((msgBufPtr=(CSERVER_RESULTS FAR *)GlobalLock(msgBufHandle)) == NULL) {
	  return(ER_WINDOWS_MEMLOCK);
   }
   /* Initialize message buffer */
   msgBufPtr->target               = 0;
   msgBufPtr->variantCode          = CLI_SERVER_RESULTS;
   msgBufPtr->resultTextLength     = msgTextSize; /* message string length */
   for (loop = 0; loop < msgTextSize; loop++ ) {
      msgBufPtr->messageText[loop]  =  *msgPtr++;
   }
   msgBufPtr->messageText[loop]  =  '\0' ;

   /* output message to memBufPtr->messageText[] */
   /*
   *** CLI Server will consume the handle when it done.
    */
   SendMessage(cliServerHandle, CLI_SERVER_RESULTS, msgBufHandle,
	 (DWORD)CLI_SERVER_RESULTS );
   return (GOOD);
}
						/****************************
						 *                          *
						 *       LOCAL ROUTINES     *
						 *                          *
						 ****************************/

/* does anyone still use this routine?? really? */
/****************************************************************************
**
**  CheckCliToken
**
**  Status:  PRELIMINARY
**
**  Description:
**     Find cli token from input buffer
**
**  Parameters:
**     input:
**        *cmdBuf:  the input buffer.
**     output:
**        *token_no:  the token find at this line.
**
*****************************************************************************/
RETCODE PRIVATE CheckCliToken(S8 *cmdBuf, U16  *clitoken_no) {
   S16       i, j ;
   STATIC S8 *clitoken_tbl[] = {"enable", "disable", "all",
				   "*"} ; /* end mark is '*' */

   i = *clitoken_no = 0 ;
   j = strlen(cmdBuf) ;
   do {
      if((j == strlen(clitoken_tbl[i])) && (!strcmpi(clitoken_tbl[i],cmdBuf)))
	 *clitoken_no = i+1 ; /* from 1 to 3 */
   } while ((clitoken_tbl[++i][0] != '*') && !(*clitoken_no)) ; /* 1 to 3 */

   if (*clitoken_no == 0)
      return(ER_KEY_TOKEN_NOT_FOUND) ; /* no match for keyword token */
   else
      return(GOOD) ;
}

RETCODE PRIVATE ShowRegisterValue(U16 regId) {
   RETCODE err;
   S8 valueText[32];
   U16 type;
   if ((err = CpuGetRegisterInfo(regId,NULL,NULL,&type,NULL)) != GOOD)
       return(err);
   strcpy(valueText,"0x");
   if ((err = CpuGetRegisterValueText(regId, 0, &valueText[2])) != GOOD)
      return(err);
   sprintf(strBuf, "%6s = %10s",regTable[regId].regName, valueText);
   SendMessageToCli((LPSTR) strBuf);
   if (type != 0) {
      if ((err = CpuGetRegisterValueText(regId, type, valueText)) != GOOD)
	 return(err);
      sprintf(strBuf, "       = %s",valueText);
      SendMessageToCli((LPSTR) strBuf);
   }
   return(GOOD);
}

/****************************************************************************
**
**  displaySignalCmdResult
**
**  Status:  PRELIMINARY
**
**  Description:
**     Generate command result string message according to the cmdErr input.
**
**  Parameters:
**     input:
**        sigId     Signal id.
**        sigValue  Current state of signal.
**        sigName   Name string of signal
**        cmdErr    Command result error code to generate message.
**     output:
**        none.
**
*****************************************************************************/
RETCODE PRIVATE displaySignalCmdResult(REG_ID sigId, U8 sigValue,
				       S8 *sigName, RETCODE cmdErr) {
    U8 statusId;
    S8 signalName[MAX_NAME];
    S8 *strPtr1, *strPtr2;

    switch(cmdErr) {
       case GOOD :
	  /* Current  signal state is enable,
	     set its correspond status string */
	  if (!sigValue) statusId = 0;
	  else statusId = 1;

	  /* clobber the first & character found.  these are put in
	     to allow windows to properly underline characters */
	  strcpy(signalName, signalTable[sigId].signalName);
	  strPtr2 = strPtr1 = strchr(signalName, '&');
	  if (strPtr1)
	     strcpy(strPtr1, ++strPtr2);

	  /* Generate message to display */
	  sprintf(strBuf, "%s  %s",
		 signalName, status[statusId]);
	  break;
       case ER_SIGNAL_NOT_FOUND :
	  sprintf(strBuf, "Signal name %s not found!", sigName) ;
	  break;
       default:   /* an error occurred, so report it */
	  sprintf(strBuf, "Get %s signal error = %8.8lX",
		       signalTable[sigId].signalName, cmdErr);
    }
    /* Send message to CLI server */
    return(SendMessageToCli((LPSTR) strBuf));
}

/****************************************************************************
**
**  FindSignalId
**
**  Status:  TESTED
**
**  Description:
**     Find the id of a signal name.
**
**  Parameters:
**     input:
**        sigNameStr    string name of signal.
**
**     output:
**        sigId         id of signal.
**
*****************************************************************************/
RETCODE PRIVATE FindSignalId(S8 *sigNameStr, REG_ID *sigId) {
   U16 i;
   S8 signalName[MAX_NAME];
   S8 *strPtr1, *strPtr2;
   /* Compare sigNameStr against the signalTable */
   for (i = 0; i<nSignals; i++) {
      /* strip the first & out of the signal name.  Windows uses the
	 & to do underlining */
      strcpy(signalName, signalTable[i].signalName);
      strPtr1 = strPtr2 = strchr(signalName, '&');
      if (strPtr1)
	 strcpy(strPtr1, ++strPtr2);
      
      if (!strcmpi(sigNameStr, signalName)) {
	 *sigId = i;
	 return(GOOD);
      }
   }
   return(ER_SIGNAL_NOT_FOUND);
}

/******************
* End of CPUCLI.C *
*******************/
