/****************************************************************************
**
**  Name:  memCopy.c
**
**  Description:
**     This is the memory copy command code for the Thunderbird
**     memory server.
**
**
**
**  $Log:   S:/tbird/mt2_68k/mem/memcopy.c_v  $
** 
**    Rev 1.1   26 Feb 1998 15:19:14   Eric
** Fixed bug in Memory copy.
** 
**    Rev 1.0   13 Feb 1997 08:54:56   gene
** Initial revision.
** 
**    Rev 1.4   21 Mar 1996 17:12:52   kevin
** 
**    Rev 1.3   18 Jan 1996 14:58:46   kevin
** fixed a bug of mem copy
** 
**    Rev 1.2   28 Nov 1995 09:07:06   kevin
** changed prototype of ErrDisplayError()
** 
**    Rev 1.1   03 Oct 1995 17:50:56   gene
** modify IsCopyCmdKeyword()
** 
**    Rev 1.0   07 Sep 1995 09:13:58   gene
** Initial revision.
** 
**    Rev 1.10   10 Mar 1994 14:24:46   john
** CliMemCopy should not always return GOOD
** 
**    Rev 1.9   09 Mar 1994 10:53:22   john
** Corrected a minor bug in the length field
** 
**    Rev 1.8   03 Mar 1994 11:48:28   john
** Added CPU32+ handling
** 
**    Rev 1.7   09 Sep 1993 15:40:50   ron
** ppr 8818: call RegisterHelpEntry for memory copy dialog (F1 support)
** 
**    Rev 1.6   16 Jul 1993 11:53:42   ernie
** Removed error.h include.  All errors now in errcodec.h
** 
**    Rev 1.5   15 Jul 1993 07:33:46   doug
** Use generic syntax error.
** 
**    Rev 1.4   09 Jul 1993 16:02:20   tom
** PPR 8230:  Reject memory copy when first from address >= second from address.
** As an extra freebie, reject memory copy when length is 0 or less.
** 
**    Rev 1.3   09 Jul 1993 15:32:32   doug
** Use generic syntax error.
** 
**    Rev 1.2   28 Jun 1993 08:39:52   paul
** Delete debug code to invoke copy dialog from command line
** 
**    Rev 1.1   21 Jun 1993 15:47:58   ron
** added handling of the Help button in the memory copy dialog
** 
**    Rev 1.0   25 May 1993 17:42:08   paul
** Initial revision.
**  $Header:   S:/tbird/mt2_68k/mem/memcopy.c_v   1.1   26 Feb 1998 15:19:14   Eric  $
**
**
**  Copyright (C) 1993 Microtek International.  All rights reserved.
**
*****************************************************************************/

/*
 * -----------------------------------------
 *  General purpose section function index
 * -----------------------------------------
 *
 *  DestroyCopyAddrDescriptors(void)
 *  CreateCopyAddrDescriptors(void)
 *    alloc and dealloc descriptors for the user interface
 *    address objects.
 *
 *  SetAddressSpaceDefaults(void)
 *    set up any defaults that the parser/dialog may
 *    be expecting.
 *
 *
 * -------------------------------------
 *  parser section function index
 * -------------------------------------
 *
 *  IsCopyCmdKeyword( LPSTR str )
 *     test passed string againt tokens for the copy command
 *
 *  ParseCliCopyMemory(LPSTR cmdString, U32 argc, U32 argv[])
 *     Parse command line and convert results into address
 *     descriptors and other variables, the native form used by
 *     the MemCopy() call in memory.c   (this is just what the other
 *     CLI functions do, they just do it locally, with in line code).
 *
 *
 *   LocalMemCliCopyMemory(LPSTR cmdString, U32 argc, U32 argv[])
 *     Local entry point from MemCli.c DLL entry point.
 *     Calls ParseCliCopyMemory, then MemCopy().
 *     This extra level is performed to let us keep
 *     the native form (descriptors, ie, the cpy_* variables)
 *     local to this module.
 *
 *
 *
 * -------------------------------------
 *  dialog section function index
 * -------------------------------------
 *  MemCpyXferToLocal(HWND hDlg)
 *  MemCpyUnloadControls(HWND hDlg)
 *  MemCpyLoadControlsFromLocal(HWND hDlg)
 *    manages a local copy of memory parameters, just in case the user
 *    wants to press cancel.
 *
 *
 *   BOOL EXPORT MemCpyDlg(HWND hDlg, .... . LONG lParam)
 *      the dialog proc
 *
 *
 *  See also:
 *    Dialog Template:  mcpydlg.dlg, mcpydlg.res
 *    Dialog Constants: mcpydlg.h
 *
 *    note:  IDD_CPY_... are dialog controls, per convention
 *
 *
 *
 */


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

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

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

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

#ifndef _CLIULIB_
#include "cliulib.h"
#endif

#ifndef _HLPENTRY_
#include "hlpentry.h"
#endif

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

#ifndef _MEMCLI_
#include "memcli.h"
#endif

#ifndef _MEMLOCAL_
#include "memlocal.h"
#endif

#ifndef _PROC_
#include "proc.h"
#endif

#ifndef _SDPROBE_
#include "sdprobe.h"
#endif

#ifndef _SSHARED_
#include "sshared.h"
#endif

#ifndef __STDLIB_H
#include "stdlib.h"
#endif

#ifndef __STDIO_H
#include "stdio.h"
#endif

#ifndef __STRING_H
#include "string.h"
#endif

#ifndef _TBIRDMEM_
#include "tbirdmem.h"
#endif


#ifndef _MCPYDLG_
#include "mcpydlg.h"      // this is the dialog box constants
#endif


                       /****************************
                        *                          *
                        *     LOCAL DEFINITIONS    *
                        *                          *
                        ****************************/

// size of symbol fields in dialog boxes.
#define   ADDR_LENGTHS   60


// keyword tokens returned by IsCopyCmdKeyword()
#define KW_LENGTH        10
#define KW_TO            11
#define KW_TARGET        12


// processor==CPU32
#define KW_SPACE_S0      30
#define KW_SPACE_UD      31
#define KW_SPACE_UP      32
#define KW_SPACE_S3      33
#define KW_SPACE_S4      34
#define KW_SPACE_SD      35
#define KW_SPACE_SP      36
#define KW_SPACE_CPU     37

// processor==CPU16
#define KW_SPACE_DATA       38
#define KW_SPACE_PROGRAM    37


#define KW_ILLEGAL_KEYWORD   40
#define KW_BAD_SPACE         41

// text for the keywords
#define TO_TEXT      "to"
#define LENGTH_TEXT  "length"
#define BYTE_TEXT "byte"
#define WORD_TEXT "word"
#define LONG_TEXT "long"
#define DWORD_TEXT "dword"
#define UNKNOWN_TEXT "unknown"
#define ON_TEXT "on"
#define OFF_TEXT "off"
#define CPU_TEXT "cpu"
#define UD_TEXT "ud"
#define UP_TEXT "up"
#define SD_TEXT "sd"
#define SP_TEXT "sp"

#define S0_TEXT "s0"
#define S3_TEXT "s3"
#define S4_TEXT "s4"

#define DATA_TEXT    "data"
#define PROGRAM_TEXT "program"

#define TARGET_TEXT "target"


                       /****************************
                        *                          *
                        *    EXTERNAL VARIABLES    *
                        *                          *
                        ****************************/
// Handle of the DLL instance: saved by LibMain
extern HANDLE hLib;

// Handle to send to shell
extern HANDLE cliServerHandle;
                       /***************************
                        *                         *
                        *    PRIVATE VARIABLES    *
                        *                         *
                        ***************************/

// ---------------------------------------------------------
//    SHARED DATA "GLOBAL" TO FUNCTIONS IN THIS MODULE
// ---------------------------------------------------------
//  This is the native form of the memory copy command
//  the parser (ParseCliCopyMemory) and dialog both
//  place results here.
//
// used for symbol parsing and range calculations
// that use Address server (addr.dll)
PRIVATE DESCRIPTOR  cpySrcDesc    = NULL;   // passed to MemCopy()
PRIVATE DESCRIPTOR  cpyDestDesc   = NULL;
PRIVATE DESCRIPTOR  cpySrcEndDesc = NULL;   // used to parse symbols into length

// data as passed to firmware is NOT entirely in descriptors
// some components are passed instead.
PRIVATE BOOLEAN     cpySrcTarget;     // passed in SD_COPY_SRC_TARG
PRIVATE U32         cpyLength;        // passed in SD_COPY_LENGTH
PRIVATE BOOLEAN     cpyDestTarget;    // passed in SD_COPY_DES_TARG



//--------------------------------------------------------
//  Variables local to the dialog
//--------------------------------------------------------
// these are dialog-local copies (dlgLocal...) of params, to be
// thrown away if user presses cancel.
//   loaded from native form by MemCpyXferToLocal(..)
//
//--------------------------------------------------------
//

PRIVATE CHAR dlgLocalSrcStartAddr[ADDR_LENGTHS];
PRIVATE CHAR dlgLocalSrcEndAddr[ADDR_LENGTHS];
PRIVATE CHAR dlgLocalDestStartAddr[ADDR_LENGTHS];


// for the following group, the values defined by control IDs
// ie, "dlgLocalPartition_End_or_Length" is one of
//      (IDD_CPY_END_USES_ADDRESS or IDD_CPY_END_USES_LENGTH)
//  must be translated from real values
PRIVATE  BOOLEAN     dlgLocalSrcTarget;
PRIVATE  BOOLEAN     dlgLocalDestTarget;
PRIVATE  ADDR_SPACE  dlgLocalSrcSpace;
PRIVATE  ADDR_SPACE  dlgLocalDestSpace;

// controls dialog box mode
PRIVATE  WORD   dlgLocalPartitionEndOrLength = IDD_CPY_END_USES_ADDRESS;

// controls CreateCopyAddrDescriptors activity
PRIVATE  BOOLEAN  descriptorsCreated = FALSE;


                       /****************************
                        *                          *
                        *     LOCAL PROTOTYPES     *
                        *                          *
                        ****************************/


RETCODE PRIVATE DestroyCopyAddrDescriptors(VOID);
RETCODE PRIVATE CreateCopyAddrDescriptors(VOID);
RETCODE PRIVATE SetAddressSpaceDefaults(VOID);
RETCODE PRIVATE IsCopyCmdKeyword( LPSTR str, U16 FAR *cmdType );
RETCODE PRIVATE ParseCliCopyMemory(LPSTR cmdString, U32 argc, U32 argv[]);

RETCODE PRIVATE MemCpyXferToLocal(VOID);
RETCODE PRIVATE MemCpyUnloadControls(HWND hDlg);
RETCODE PRIVATE MemCpyLoadControlsFromLocal(HWND hDlg);


BOOL EXPORT MemCpyDlgProc(HWND hDlg, WORD message, WORD wParam, LONG lParam);









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


/**************************************************************
**    DestroyCopyAddrDescriptors
**
**  note that addr library will accept NULL descriptor.
**    it just returns an error code...
**    return the first error that appears
**
** ************************************************************
*/

RETCODE PRIVATE DestroyCopyAddrDescriptors(VOID) {

   RETCODE err;
   RETCODE returnedErr = GOOD;

// prefer memory leaks to access of uninitialized memory
   descriptorsCreated = FALSE;

   err = AdrDestroyAddress(cpySrcDesc);
   if(returnedErr==GOOD)
      returnedErr=err;

   err = AdrDestroyAddress(cpySrcEndDesc);
   if(returnedErr==GOOD)
      returnedErr=err;

   err = AdrDestroyAddress(cpyDestDesc);
   if(returnedErr==GOOD)
      returnedErr=err;


   return returnedErr;
}




/**************************************************************
**  CreateCopyAddrDescriptors( )
**
** create address descriptors for the copy.
**  if any are not created, calls DestroyCopyAddrDescriptors()
**
**  we ignore any errors from DestroyCopyAddrDescriptors()...we
**  want to return original reason for the error.
**
***************************************************************
*/
RETCODE PRIVATE CreateCopyAddrDescriptors(VOID) {

   RETCODE err = GOOD;


   if(descriptorsCreated == TRUE)
      return GOOD;


   cpySrcDesc    = NULL;
   cpySrcEndDesc = NULL;
   cpyDestDesc   = NULL;


   err = AdrCreateAddress(&cpySrcDesc);
   if (err != GOOD)  {
      DestroyCopyAddrDescriptors();
      return err;
   }

   err = AdrCreateAddress(&cpySrcEndDesc);
   if (err != GOOD)  {
      DestroyCopyAddrDescriptors();
      return err;
   }

   err = AdrCreateAddress(&cpyDestDesc);
   if (err != GOOD)  {
      DestroyCopyAddrDescriptors();
      return err;
   }

   descriptorsCreated = TRUE;

return GOOD;

}





/**************************************************************
**   SetAddressSpaceDefaults(  )
**
** set up any defaults that the parser/dialog may
** be expecting.  Currently this is:
**
**  space     = SPACE_SD;
** map mode  = FALSE (use map, not target)
**  (write into address descriptors)
**
**  Note that the "default" case (neither CPU32 nor CPU16)
**  does not need to return an error...since space would be
**  UNUSED in such cases anyway (x86 does not use space).
**
**************************************************************
*/

RETCODE PRIVATE SetAddressSpaceDefaults(VOID) {

   ADDR_SPACE srcSpace;
   ADDR_SPACE destSpace;
   RETCODE    err = GOOD;



   srcSpace  = SPACE_SD;
   destSpace = SPACE_SD;


// write to destinations
   err = AdrSetAddrSpace(cpySrcDesc, srcSpace);
   if (err != GOOD)  {
      return err;
   }



   err = AdrSetAddrSpace(cpyDestDesc, destSpace);
   if (err != GOOD)  {
      return err;
   }

   cpySrcTarget  = FALSE;  // IDD_CPY_SRC_MAP;
   cpyDestTarget = FALSE;  // IDD_CPY_To_Map;
   return GOOD;
}




/**************************************************************
**   IsCopyCmdKeyword( LPSTR string )
**
**
**   Test incoming token against the possible keywords.
**   returns token ID based on the lexeme.
**
**  NOTE:  use "strnicmp" rather than *NON* ANSI "strncmpi"
**/

RETCODE PRIVATE IsCopyCmdKeyword( LPSTR str, U16 FAR *cmdType ) {

   U8        paramLen;
   RETCODE   err;
   PROC_CPU  cpuType;


   err = ProcReturnCpu(&cpuType);
   if (err != GOOD)  {
      return err;
   }


   paramLen = strlen(str);

// keywords
   if (strnicmp(str, TO_TEXT, paramLen)==0) {
      *cmdType = KW_TO;
      return GOOD;
   }

   if (strnicmp(str, LENGTH_TEXT, paramLen)==0) {
      *cmdType = KW_LENGTH;
      return GOOD;
   }

   if (strnicmp(str, TARGET_TEXT, paramLen)==0) {
      *cmdType = KW_TARGET;
      return GOOD;
   }

// spaces
   if (strnicmp(str, CPU_TEXT, paramLen)==0)  {
      *cmdType = KW_SPACE_CPU;
      return GOOD;
   }


// space keywords have processor dependence
   switch(cpuType) {
      case PROC_CPU_CPU32:
      case PROC_CPU_CPU32P:
      case PROC_CPU_68000:

         if (strnicmp(str, UD_TEXT, paramLen)==0) {
            *cmdType = KW_SPACE_UD;
            return GOOD;
         }

         if (strnicmp(str, UP_TEXT, paramLen)==0) {
            *cmdType = KW_SPACE_UP;
            return GOOD;
         }

         if (strnicmp(str, SD_TEXT, paramLen)==0) {
            *cmdType = KW_SPACE_SD;
            return GOOD;
         }

         if (strnicmp(str, SP_TEXT, paramLen)==0) {
            *cmdType = KW_SPACE_SP;
            return GOOD;
         }

         if (strnicmp(str, S0_TEXT, paramLen)==0) {
            *cmdType = KW_SPACE_S0;
            return GOOD;
         }

         if (strnicmp(str, S3_TEXT, paramLen)==0) {
            *cmdType = KW_SPACE_S3;
            return GOOD;
         }

         if (strnicmp(str, S4_TEXT, paramLen)==0) {
            *cmdType = KW_SPACE_S4;
            return GOOD;
         }
         break;


      case PROC_CPU_CPU16:
         if (strnicmp(str, DATA_TEXT, paramLen)==0) {
            *cmdType = KW_SPACE_DATA;
            return GOOD;
         }

         if (strnicmp(str, PROGRAM_TEXT, paramLen)==0) {
            *cmdType = KW_SPACE_PROGRAM;
            return GOOD;
         }

      default:
         *cmdType = KW_ILLEGAL_KEYWORD;
   }  // end-switch


//  neither CPU32 of CPU16 was defined
   return GOOD;

}




/**************************************************************
 *  ParseCliCopyMemory(...)
 *
 *   Create the intermediate form from command line params
 *   this incorporates all parsing for the memory copy command
 *
 *  Input: command line from shell.
 *    LPSTR cmdString
 *    U32 argc
 *    U32 argv[]
 *
 *  Output:  none
 *
 *  Global variables changed:   descriptors that hold addresses of copy
 *    cpySrcDesc       offset,space
 *    cpyLength
 *    cpyDestDesc      offset,space
 *    cpySrcTarget     TRUE/FALSE (FALSE=MAP)
 *    cpyDestTarget    TRUE/FALSE
 *
 *  Also uses:
 *    cpySrcEndDesc   for offset ONLY, used only to parse symbols
 *
 *
 ************************************************************
 */

RETCODE PRIVATE ParseCliCopyMemory(LPSTR cmdString, U32 argc, U32 argv[]) {

   RETCODE    err;
   U16        keywordID;
   U16        currentToken;   // index in argv[] array.
   BOOLEAN    done;           // flag for "while" completion
   LPSTR      endptr;         // used by strtoul
   ADDR_COMPARE result;       // result of comparison

   BOOLEAN    srcSpaceSpecified;
   BOOLEAN    destSpaceSpecified;
   BOOLEAN    srcTargetSpecified;
   BOOLEAN    destTargetSpecified;
   BOOLEAN    needDestTargetSpecified;


 // init all vars that have default values
 //   Map is default
   cpySrcTarget  = FALSE;
   cpyDestTarget = FALSE;

   srcSpaceSpecified   = FALSE;
   destSpaceSpecified  = FALSE;
   srcTargetSpecified  = FALSE;
   destTargetSpecified = FALSE;




// check for minimum number of tokens (1)
   if(argc < 1)
      return ER_CLI_SYNTAX;



//  First token must be address
//   <addr> =  Symbol | NumericAddress

   currentToken = 1;
   err = AdrConvTextToAddress(cpySrcDesc,
                        &cmdString[(U16) argv[ currentToken ] ]  );

   if(err!=GOOD)
      return err;    // address not first param
                     // "no from address"...ER_NO_FROM_ADDR
   currentToken = 2;


// second token can be one of:
//  LENGTH  Symbol  Number  OTHER_TOKEN
//
// if second token = LENGTH
//  third token -->  <addr> =  Number
   err = IsCopyCmdKeyword( &(cmdString[(U16)argv[currentToken]]), &keywordID );

   if(keywordID == KW_LENGTH)  {
   // process length (a number ONLY, no symbols)
   // allow format to determine base
      cpyLength = strtoul(&cmdString[(U16) argv[ currentToken+1 ] ],&endptr, 0);

      // make sure length >= 0
      if (cpyLength <= 0)
          return ER_CLI_SYNTAX;

      currentToken = 4;    // eat up two tokens (2,3)
      dlgLocalPartitionEndOrLength = IDD_CPY_END_USES_LENGTH;
   }
   else    // length has not been specified via keyword,
           //  look for an address
   {
      // if second token = Symbol | Number
      err = AdrConvTextToAddress(cpySrcEndDesc,
                                   &cmdString[(U16) argv[ currentToken ]]);
      if(err!=GOOD)
         return err;      // length not present

      // make sure first address < second address.
      err = AdrCompareAddresses(cpySrcDesc, cpySrcEndDesc, &result);
      if (result != SECOND_ADDR_GREATER)
          return ER_CLI_SYNTAX;

      // calculate difference to get length
      // extract difference between two addresses
      err = AdrRangeOfAddresses( cpySrcDesc, cpySrcEndDesc, &cpyLength);
      if(err!=GOOD)
         return err;

      dlgLocalPartitionEndOrLength = IDD_CPY_END_USES_ADDRESS;

      currentToken = 3;    // eat just one token (2)
    }


// ----------------------------------------------
//     now expect "to" or space keywords
// ----------------------------------------------
// now look for any keywords SD SP...TARGET TO
// stop on EOL, or exit via "return" on illegal keyword



   done = FALSE;    // set to TRUE when "TO" is reached
   while(done == FALSE)  {
      err = IsCopyCmdKeyword( &(cmdString[(U16)argv[ currentToken]] ), &keywordID );

      switch(keywordID) {
         case KW_SPACE_SD:
            if(srcSpaceSpecified == TRUE)
                return ER_CLI_SYNTAX;
            err = AdrSetAddrSpace(cpySrcDesc, SPACE_SD);
            if(err!=GOOD)
                return err;
            srcSpaceSpecified = TRUE;
            break;

         case KW_SPACE_SP:
            if(srcSpaceSpecified == TRUE)
                return ER_CLI_SYNTAX;
            err = AdrSetAddrSpace(cpySrcDesc, SPACE_SP);
            if(err!=GOOD)
                return err;
            srcSpaceSpecified = TRUE;
            break;

         case KW_SPACE_UD:
            if(srcSpaceSpecified == TRUE)
                return ER_CLI_SYNTAX;
            err = AdrSetAddrSpace(cpySrcDesc, SPACE_UD);
            if(err!=GOOD)
                return err;
            srcSpaceSpecified = TRUE;
            break;

         case KW_SPACE_UP:
            if(srcSpaceSpecified == TRUE)
                return ER_CLI_SYNTAX;
            err = AdrSetAddrSpace(cpySrcDesc, SPACE_UP);
            if(err!=GOOD)
                return err;
            srcSpaceSpecified = TRUE;
            break;

         case KW_SPACE_CPU:
            if(srcSpaceSpecified == TRUE)
                return ER_CLI_SYNTAX;
            err = AdrSetAddrSpace(cpySrcDesc, SPACE_CPU);
            if(err!=GOOD)
                return err;
            srcSpaceSpecified   = TRUE;
            break;

         case KW_SPACE_S0:
            if(srcSpaceSpecified == TRUE)
                return ER_CLI_SYNTAX;
            err = AdrSetAddrSpace(cpySrcDesc, SPACE_UNDEF0);
            if(err!=GOOD)
                return err;
            srcSpaceSpecified = TRUE;
            break;

         case KW_SPACE_S3:
            if(srcSpaceSpecified == TRUE)
                return ER_CLI_SYNTAX;
            err = AdrSetAddrSpace(cpySrcDesc, SPACE_UNDEF3);
            if(err!=GOOD)
                return err;
            srcSpaceSpecified = TRUE;
            break;

         case KW_SPACE_S4:
            if(srcSpaceSpecified == TRUE)
                return ER_CLI_SYNTAX;
            err = AdrSetAddrSpace(cpySrcDesc, SPACE_UNDEF4);
            if(err!=GOOD)
                return err;
            srcSpaceSpecified = TRUE;
            break;

         case KW_TARGET:
            if(srcTargetSpecified == TRUE)
                return ER_CLI_SYNTAX;
            cpySrcTarget = TRUE;
            srcTargetSpecified = TRUE;
            //Eric 2/24/98
            if(currentToken+1 == argc) return GOOD;
            else return ER_CLI_SYNTAX;
            //eof Eric 2/24/98
            break;

         case KW_TO:
            done = 1;
            break;

         default:
            return ER_CLI_SYNTAX;  // TO is missing
      }

      currentToken++;           // should never exceed argc
      if(currentToken >= argc)
         return ER_CLI_SYNTAX;  // TO is missing

   }  // end-while




// -------------------------------------------------------
//  source partition is parsed...now parse dest address
// -------------------------------------------------------

   err = AdrConvTextToAddress(cpyDestDesc,
                                &cmdString[(U16) argv[ currentToken ] ]);

//
   if(err==GOOD)  {
      currentToken++;                   // step past the address
      needDestTargetSpecified = FALSE;

      if(currentToken >= argc)   // normal end of line...no keywords
          return GOOD;
   } else {

// Address is apparently not present...it may be a keyword.
// Assuming this, make source address == dest address
      err = AdrConvTextToAddress(cpyDestDesc,
                        &cmdString[(U16) argv[ 1 ] ]  );
      if(err!=GOOD)
         return err;
      needDestTargetSpecified = TRUE;

   }



// Now look space and TARGET keywords
// exit while when all tokens processed (argc >= count)

   done = FALSE;    // set to TRUE when EOL is reached

   while(done == FALSE)  {
   err = IsCopyCmdKeyword( &(cmdString[(U16)argv[ currentToken]] ), &keywordID );

   if(err!=GOOD)
      return err;

      switch(keywordID) {
         case KW_SPACE_SD:
            if(destSpaceSpecified == TRUE)
                return ER_CLI_SYNTAX;
            err = AdrSetAddrSpace(cpyDestDesc, SPACE_SD);
            if(err!=GOOD)
                return err;
            destSpaceSpecified = TRUE;
            break;

         case KW_SPACE_SP:
            if(destSpaceSpecified == TRUE)
                return ER_CLI_SYNTAX;
            err = AdrSetAddrSpace(cpyDestDesc, SPACE_SP);
            if(err!=GOOD)
                return err;
            destSpaceSpecified = TRUE;
            break;

         case KW_SPACE_UD:
            if(destSpaceSpecified == TRUE)
                return ER_CLI_SYNTAX;
            err = AdrSetAddrSpace(cpyDestDesc, SPACE_UD);
            if(err!=GOOD)
                return err;
            destSpaceSpecified = TRUE;
            break;

         case KW_SPACE_UP:
            if(destSpaceSpecified == TRUE)
                return ER_CLI_SYNTAX;
            err = AdrSetAddrSpace(cpyDestDesc, SPACE_UP);
            if(err!=GOOD)
                return err;
            destSpaceSpecified = TRUE;
            break;

         case KW_SPACE_CPU:
            if(destSpaceSpecified == TRUE)
                return ER_CLI_SYNTAX;
            err = AdrSetAddrSpace(cpyDestDesc, SPACE_CPU);
            if(err!=GOOD)
                return err;
            destSpaceSpecified = TRUE;
            break;


         case KW_SPACE_S0:
            if(destSpaceSpecified == TRUE)
                return ER_CLI_SYNTAX;
            err = AdrSetAddrSpace(cpyDestDesc, SPACE_UNDEF0);
            if(err!=GOOD)
                return err;
            destSpaceSpecified = TRUE;
            break;

         case KW_SPACE_S3:
            if(destSpaceSpecified == TRUE)
                return ER_CLI_SYNTAX;
            err = AdrSetAddrSpace(cpyDestDesc, SPACE_UNDEF3);
            if(err!=GOOD)
                return err;
            destSpaceSpecified = TRUE;
            break;

         case KW_SPACE_S4:
            if(destSpaceSpecified == TRUE)
                return ER_CLI_SYNTAX;
            err = AdrSetAddrSpace(cpyDestDesc, SPACE_UNDEF4);
            if(err!=GOOD)
                return err;
            destSpaceSpecified = TRUE;
            break;

         /*Eric 2/24/98
         case KW_TARGET:
            if(destTargetSpecified == TRUE)
                return ER_CLI_SYNTAX;
            cpyDestTarget = TRUE;
            destTargetSpecified = TRUE;
            break;
         eof Eric 2/24/98*/

         default:
            return ER_CLI_SYNTAX;  // illegal keywords elicit this response
      }

      currentToken++;            // should never exceed argc
      if(currentToken >= argc)   // normal end of line
          done = TRUE;

   }  // end-while


// if no destination address was specified, target should have been
// used instead.  otherwise, it is an error.
    if( (needDestTargetSpecified == TRUE) &&
       (destTargetSpecified == FALSE) )
       return ER_CLI_SYNTAX;

   return GOOD;
}



/*******************************************************
**
**  MemDisplayError(HWND hwnd, RETCODE err)
**
**  Displays and error message box using message server.
**
**  there is a BUG in ErrDisplayErrorEx...no window handle!!!
**    (will replace it)
**  called  ErrDisplayErrorExOwned(HWND hwnd.....)
**
** if hWnd == NULL, it means we were called from CLI
**
**
**  note that NULL passed in hDlg will have it call Ex.
**  this means DLG vs CLI calls differ.
************************************************************
*/
#pragma argsused
VOID MemDisplayError(HWND hwnd, RETCODE errCode)
{
   S16  buttonID;

// for now, just call "Ex"
// future functon will call ExOwned() that takes a window handle
   ErrDisplayErrorEx( errCode, FORCE_POPUP,
               MB_TYPE_OK, (S16 FAR *)&buttonID );

}



/*****************************************************************
**
** MemCliCopyMemory
**  registered with CLI in the .RC file
**
** Description:
**    Cli interface to the copy (move) memory content
**
**
** Input:
**    cmdString
**    argc
**    argv
**
** Output: no parameters
**
**
**     DLL entry point from CLI.
**     Calls ParseCliCopyMemory, then MemCopy().
**
**
*****************************************************************************/



#pragma argsused
RETCODE EXPORT MemCliCopyMemory(LPSTR cmdString, U32 argc, U32 argv[]) {

   RETCODE  err;
   HCURSOR  hSaveCursor, hHourGlass;


   err = CreateCopyAddrDescriptors();
   if(err!=GOOD)
      return err;


   err = ParseCliCopyMemory(cmdString, argc, argv);
   if(err!=GOOD)
      return err;


//  perform the copy, using the native (parsed) form.

   hHourGlass = LoadCursor(NULL, IDC_WAIT);
   hSaveCursor = SetCursor(hHourGlass);
   err = MemCopy(cpySrcDesc, cpyLength, cpySrcTarget,
                     cpyDestDesc,  cpyDestTarget);

   SetCursor(hSaveCursor);
   if (err == GOOD)
      SendCliMessage(cliServerHandle,(LPSTR)"Copy successful.");

// do not destroy the descriptors


   return(err);
}



// ------------------------------------------------------
//   Memory Copy dialog and its support procedures
// ------------------------------------------------------



/***********************************************************
**
**  MemCpyXferToLocal(VOID)
**
**
** copy native form into local data area
** error conditions from address server errors
**
** Called from:   INITDIALOG mesaage in dialog proc
** inputs:
**    cpySrcDesc
**    cpyDestDesc
**    cpyLength
** outputs:
** returns:    address server errors
**
**/
#pragma argsused
RETCODE PRIVATE MemCpyXferToLocal(VOID)
{
   RETCODE err;


   err = AdrConvAddressToTextWithParams(cpySrcDesc,
                                         TRUE, FALSE,
                                         dlgLocalSrcStartAddr);
   if(err!=GOOD)
      return err;


// set display mode from previous usage
   if(dlgLocalPartitionEndOrLength == IDD_CPY_END_USES_LENGTH)  {
      wsprintf(dlgLocalSrcEndAddr, "0x%lX", cpyLength);
   } else {
       err = AdrConvAddressToTextWithParams(cpySrcEndDesc,
                                         TRUE, FALSE,
                                         dlgLocalSrcEndAddr);
      if(err!=GOOD)
         return err;
    // force other mode
      dlgLocalPartitionEndOrLength = IDD_CPY_END_USES_ADDRESS;
   }


// destination
   err = AdrConvAddressToTextWithParams(cpyDestDesc,
                                         TRUE, FALSE,
                                        dlgLocalDestStartAddr);
   if(err!=GOOD)
      return err;


// the address space is fixed when in dialog
   dlgLocalSrcSpace = dlgLocalDestSpace = SPACE_SD;

// set the target/map from the current definition
   dlgLocalSrcTarget  =  cpySrcTarget;
   dlgLocalDestTarget =  cpyDestTarget;

   return GOOD;
}





/***********************************************************
**
**  MemCpyUnloadControls(HWND hDlg)
**
**
** Fetch contents of dialog box controls and convert to
** native form.
**
** Called from:   IDOK mesaage in dialog proc
** inputs:  hDlg  dialog window handle
**
** global variables changed:
**    cpySrcDesc
**    cpyDestDesc
**    cpyLength
**  if successful all are changed.
**
**  if "not successful" only SOME are changed.
**  This is a syntax error.  We simply return the error code.
**  The error is then displayed by a call to the errortext
**  server from within the dialog procedure and the "exit dialog"
**  is NOT permitted.
**
*/

RETCODE PRIVATE MemCpyUnloadControls(HWND hDlg) {

   RETCODE    err;
   LPSTR      endptr;         // used by strtoul


   GetDlgItemText(hDlg,IDD_CPY_SRC_START, dlgLocalSrcStartAddr, ADDR_LENGTHS);
   GetDlgItemText(hDlg,IDD_CPY_SRC_END,   dlgLocalSrcEndAddr,   ADDR_LENGTHS);
   GetDlgItemText(hDlg,IDD_CPY_DEST_START,dlgLocalDestStartAddr,ADDR_LENGTHS);

   err = AdrConvTextToAddress(cpySrcDesc, dlgLocalSrcStartAddr );

   if(err!=GOOD)
      return err;


// save as length ONLY.
   // dlgLocalSrcEndAddr contains a length field


// if mode is absolute, convert absolute to length
   if(dlgLocalPartitionEndOrLength == IDD_CPY_END_USES_ADDRESS ) {
      err = AdrConvTextToAddress(cpySrcEndDesc,
                                  dlgLocalSrcEndAddr );
      if(err!=GOOD)
         return err;

   // calculate difference to get length
   // extract difference between two addresses
         err = AdrRangeOfAddresses( cpySrcDesc, cpySrcEndDesc, &cpyLength);
         if(err!=GOOD)
            return err;
   } else {
   // process length (a number ONLY, no symbols)
   // allow format to determine base
      cpyLength = strtoul( dlgLocalSrcEndAddr ,&endptr, 0);

      if(err!=GOOD)
         return err;

      dlgLocalPartitionEndOrLength = IDD_CPY_END_USES_LENGTH;

   }


// destination
   err = AdrConvTextToAddress(cpyDestDesc,
                                  dlgLocalDestStartAddr );
   if(err!=GOOD)
      return err;


   // within dialog, address space always is "SD" for CPU32 or
   // CPU16.
   err = AdrSetAddrSpace(cpySrcDesc, dlgLocalSrcSpace);
   if(err!=GOOD)
      return err;

   err = AdrSetAddrSpace(cpyDestDesc, dlgLocalDestSpace);
   if(err!=GOOD)
      return err;

   // get the target/map (holds TRUE/FALSE)
   cpySrcTarget  = dlgLocalSrcTarget;
   cpyDestTarget = dlgLocalDestTarget;
   return GOOD;
}





/***********************************************************
**
**  MemCpyLoadControlsFromLocal(HWND hDlg)
**
**
** Store contents of local variables into the dialog box
** controls.
**
** Called from:   INITDIALOG mesaage in dialog proc
** inputs:  hDlg  window handle
**
** inputs from global variables:
**  dlgLocalSrcStartAddr[ADDR_LENGTHS];
**  dlgLocalSrcEndAddr[ADDR_LENGTHS];
**  dlgLocalDestStartAddr[ADDR_LENGTHS];
**  dlgLocalSrcTarget;
**  gLocalDestTarget;
**  dlgLocalSrcSpace;
**  dlgLocalDestSpace;
**  dlgLocalPartitionEndOrLength;
**
**
*/


RETCODE PRIVATE MemCpyLoadControlsFromLocal(HWND hDlg) {

   RETCODE err;
   PROC_SYSTEM_TYPE  systemType;



// transfer to controls
   SetDlgItemText (hDlg, IDD_CPY_SRC_START,   dlgLocalSrcStartAddr );
   SetDlgItemText (hDlg, IDD_CPY_SRC_END,     dlgLocalSrcEndAddr );
   SetDlgItemText (hDlg, IDD_CPY_DEST_START,  dlgLocalDestStartAddr );

// set end/length radio and title text
   CheckRadioButton (hDlg, IDD_CPY_END_USES_ADDRESS,
                           IDD_CPY_END_USES_LENGTH,
                           dlgLocalPartitionEndOrLength );

// set src/dest space radio button to sd
   CheckRadioButton (hDlg, IDD_CPY_SRC_UP,
                           IDD_CPY_SRC_SD,
                           IDD_CPY_SRC_SD );

   CheckRadioButton (hDlg, IDD_CPY_DEST_UP,
                           IDD_CPY_DEST_SD,
                           IDD_CPY_DEST_SD );


// set MAP and TARGET radio buttons
//  note:  dlgLocalSrcTarget is set FALSE ("redundantly")
//  on PURPOSE.  It may not be redundant.
//  (it depends on TRUE/FALSE implementation)
//  If it is not TRUE it does not mean that it is FALSE!
//  Instead, it may be indeterminate.
//  Other code, somewhere else, may check for FALSE, and
//  found the indeterminate value instead. (zap! Ouch!)


//RETCODE EXPORT


   err = ProcReturnSystemType( &systemType );
   if(err!=GOOD)
      return err;


   if(systemType == PROC_POWERPACK) {
      if(dlgLocalSrcTarget==TRUE) {
       CheckRadioButton (hDlg, IDD_CPY_SRC_MAP,
                           IDD_CPY_SRC_TARGET,
                           IDD_CPY_SRC_TARGET );
      }
      else {
          CheckRadioButton (hDlg, IDD_CPY_SRC_MAP,
                                  IDD_CPY_SRC_TARGET,
                                  IDD_CPY_SRC_MAP );
         dlgLocalSrcTarget = FALSE;
      }


      if(dlgLocalDestTarget==TRUE) {
         CheckRadioButton (hDlg, IDD_CPY_DEST_MAP,
                               IDD_CPY_DEST_TARGET,
                               IDD_CPY_DEST_TARGET );
      }
      else {
         CheckRadioButton (hDlg, IDD_CPY_DEST_MAP,
                                 IDD_CPY_DEST_TARGET,
                                 IDD_CPY_DEST_MAP );
         dlgLocalDestTarget = FALSE;
      }
   } // end powerpack
   else if (systemType == PROC_MICEPACK)
      if (dlgLocalSrcTarget==TRUE) {
         CheckRadioButton (hDlg, IDD_CPY_SRC_MAP,
                           IDD_CPY_SRC_TARGET,
                           IDD_CPY_SRC_TARGET );
      } else {
         CheckRadioButton (hDlg, IDD_CPY_SRC_MAP,
                           IDD_CPY_SRC_TARGET,
                           IDD_CPY_SRC_MAP );
         dlgLocalSrcTarget = FALSE;
      } // end micepack
   else {
       EnableWindow(GetDlgItem(hDlg, IDD_CPY_SRC_MAP)    , FALSE);
       EnableWindow(GetDlgItem(hDlg, IDD_CPY_SRC_TARGET) , FALSE);
       EnableWindow(GetDlgItem(hDlg, IDD_CPY_DEST_MAP)   , FALSE);
       EnableWindow(GetDlgItem(hDlg, IDD_CPY_DEST_TARGET), FALSE);

   }

 // set focus to somewhere safe (mainly done for EnableWindow() calls
 // above, but I do not like special cases).  In NO case can focus
 // be left on the radio button that has just been disabled.  Keyboard
 // operations in the dialog will be halted...can't exit via abort either.
 // Focus would normally be set (by Windows) to this control anyway,
 // as specified by the dialog template.
   SetFocus( GetDlgItem(hDlg, IDD_CPY_SRC_START) );


   return GOOD;
}




/******************************************************
**
**   MemCpyDlgProc
**
** The dialog window procedure
**
**  returns result to DialogBox callerwith EndDialog() function::
**    TRUE:   user pressed OK
**    FALSE:  user pressed cancel
**
**  After pressing OK, if parse is not successful (or
**  some other error occurs), we display the error, and
**  permit a retry.  User can exit with Cancel, though.
**
**  Note that errors are NOT propagated up to Actor level.
**
**
**  The params follow Windows 3.0 conventions, NOT those of Win3.1
**
**  Return value is standard also: a Windows BOOL, NOT the
**  Powerviews BOOLEAN!
**  returns TRUE of message was handled, FALSE if not handled.
**
******************************************************
*/
#pragma argsused
BOOL EXPORT MemCpyDlgProc(HWND hDlg, WORD message, WORD wParam, LONG lParam) {

   RETCODE  err;
   ADDR_SPACE space[] = {SPACE_UP,SPACE_UD,SPACE_SP,SPACE_SD};

   switch (message)
   {
      case WM_INITDIALOG:

     // some items are not saved between invocations
      err = SetAddressSpaceDefaults();
      RegisterHelpEntry(HI_MESSAGEBOXEX, hDlg, HE_MEMCOPY);
      if(err!=GOOD) {
          MemDisplayError(hDlg, err);
          return TRUE;
      }

      err = MemCpyXferToLocal();     // load local copies of params
      if(err!=GOOD)  {
         MemDisplayError(hDlg, err);
         return TRUE;
      }

      MemCpyLoadControlsFromLocal(hDlg);
      return TRUE;



      case WM_COMMAND:

      switch(wParam) {
          case IDOK:
             err = MemCpyUnloadControls(hDlg);
             if(err!=GOOD)  {
                MemDisplayError(hDlg, err);   // parse error
                return TRUE;
             }
             EndDialog(hDlg, TRUE);
             return TRUE;

             case IDHELP:
              WinHelp(hDlg, "PWRVIEWS.HLP",
                 HELP_CONTEXT, HE_MEMCOPY);
              return FALSE;
  

           case IDCANCEL:
              EndDialog(hDlg, FALSE);
              return TRUE;


      case IDD_CPY_END_USES_ADDRESS:
      case IDD_CPY_END_USES_LENGTH:
         dlgLocalPartitionEndOrLength = wParam;
         CheckRadioButton (hDlg, IDD_CPY_END_USES_ADDRESS,
                          IDD_CPY_END_USES_LENGTH,
                           wParam );
         return TRUE;


   // MAP or TARGET control
      case IDD_CPY_SRC_TARGET:
         //Eric 2/22/98 
         dlgLocalSrcTarget = TRUE ;
         CheckRadioButton (hDlg, IDD_CPY_SRC_MAP,
                                  IDD_CPY_SRC_TARGET, wParam );
         EnableWindow(GetDlgItem(hDlg, IDD_CPY_DEST_START), FALSE);
         EnableWindow(GetDlgItem(hDlg, IDD_CPY_DEST_UP), FALSE);
         EnableWindow(GetDlgItem(hDlg, IDD_CPY_DEST_UD), FALSE);
         EnableWindow(GetDlgItem(hDlg, IDD_CPY_DEST_SP), FALSE);
         EnableWindow(GetDlgItem(hDlg, IDD_CPY_DEST_SD), FALSE);
         return TRUE;
         //eof Eric 2/22/98
      case IDD_CPY_SRC_MAP:
         dlgLocalSrcTarget = FALSE ;
         CheckRadioButton (hDlg, IDD_CPY_SRC_MAP,
                                  IDD_CPY_SRC_TARGET, wParam );
         //Eric 2/22/98
         EnableWindow(GetDlgItem(hDlg, IDD_CPY_DEST_START), TRUE);
         EnableWindow(GetDlgItem(hDlg, IDD_CPY_DEST_UP), TRUE);
         EnableWindow(GetDlgItem(hDlg, IDD_CPY_DEST_UD), TRUE);
         EnableWindow(GetDlgItem(hDlg, IDD_CPY_DEST_SP), TRUE);
         EnableWindow(GetDlgItem(hDlg, IDD_CPY_DEST_SD), TRUE);
         //eof Eric 2/22/98
         return TRUE;

      case IDD_CPY_SRC_UP:
      case IDD_CPY_SRC_UD:
      case IDD_CPY_SRC_SP:
      case IDD_CPY_SRC_SD:
         CheckRadioButton (hDlg, IDD_CPY_SRC_UP,
                                  IDD_CPY_SRC_SD, wParam );
         dlgLocalSrcSpace = space[wParam-IDD_CPY_SRC_UP];
         return TRUE;

      case IDD_CPY_DEST_UP:
      case IDD_CPY_DEST_UD:
      case IDD_CPY_DEST_SP:
      case IDD_CPY_DEST_SD:
         CheckRadioButton (hDlg, IDD_CPY_DEST_UP,
                                  IDD_CPY_DEST_SD, wParam );
         dlgLocalDestSpace = space[wParam-IDD_CPY_DEST_UP];
         return TRUE;

//      case IDD_CPY_SRC_MAP:
//         dlgLocalSrcTarget = FALSE ;
//         CheckRadioButton (hDlg, IDD_CPY_SRC_MAP,
//                                IDD_CPY_SRC_TARGET, wParam );
//         return TRUE;

//      case IDD_CPY_DEST_TARGET:
//         dlgLocalDestTarget = TRUE ;
//         CheckRadioButton (hDlg, IDD_CPY_DEST_MAP,
//                                    IDD_CPY_DEST_TARGET, wParam );
//         return TRUE;


//      case IDD_CPY_DEST_MAP:
//         dlgLocalDestTarget = FALSE ;
//         CheckRadioButton (hDlg, IDD_CPY_DEST_MAP,
//                                  IDD_CPY_DEST_TARGET, wParam );
//         return TRUE;

      }
   }
return (FALSE);   // Didn't process a message
}




/******************************************************
**
** MemDlgCopy   copy using a dialog
**
**   Execute the dialog
**
**  called from ACTOR memory presenter menu item...
**
*******************************************************
**/

RETCODE EXPORT MemDlgCopyMemory(HWND hwnd)   {

   int      dlgRet;    // "int" is return type of DialogBox()
   RETCODE  err;
   RETCODE  copyErr;
   HCURSOR  hSaveCursor, hHourGlass;



   err = CreateCopyAddrDescriptors();
   if(err!=GOOD)
      return err;


   dlgRet = DialogBox(hLib,           // instance (for dlg template)
           "MCPY_DIALOG",
            hwnd,                     // parent handle
           (FARPROC) MemCpyDlgProc);  // within DLL, no proc instance address reqd


// if user pressed OK, perform the copy
// using the native (parsed) form.
   if(dlgRet==TRUE)  {

      hHourGlass = LoadCursor(NULL, IDC_WAIT);
      hSaveCursor = SetCursor(hHourGlass);

      copyErr = MemCopy(cpySrcDesc, cpyLength, cpySrcTarget,
                        cpyDestDesc,  cpyDestTarget);

      SetCursor(hSaveCursor);

   }
   else copyErr = GOOD;

   return copyErr;
}


/******************************** E O F ***********************************/


