/****************************************************************************
**
**  Name:  dasm16.cpp
**
**  Description:
**     This is a public disassembler interface file
**
**  Status:  
**
**  $Log:   S:/tbird/mt2_186/dad186/dasm16.cpv  $
** 
**    Rev 1.0   16 Dec 1996 15:13:44   Judy
** Initial revision.
** 
**    Rev 1.16   15 Jun 1994 10:41:42   nghia
** Revised DadDasmInstByLine() to handle boundary condition correctly.
** 
**    Rev 1.15   10 Jun 1994 17:14:44   nghia
** Revised DasmInstRange to use the address range length instead of the
** Address range compare - It would be faster and stop disassemble at the end
** of the address range.
** 
**    Rev 1.14   28 Mar 1994 10:38:38   nghia
** Added DadCurrentDasmAddress() and DadSetDasmOpAddrSize().
** 
**    Rev 1.13   28 Feb 1994 17:14:46   marilyn
** Moved maxOutput routines to the address server. Updated interfaces.
** 
**    Rev 1.12   29 Oct 1993 13:28:40   nghia
** Added TskCheckAbort() to handle user abort for CPU16 dasm.
** Clear ESC key before dasm operation.
** 
**    Rev 1.11   27 Oct 1993 17:51:20   nghia
** Fixed PPR 9039 - Invalid dasm instruction format
** - Used global cpuMaxInputAddress and cpuMaxOutputAddress.
** 
**    Rev 1.10   24 Jul 1993 13:47:44   mindy
** fixed mixed_mode dasm problem.
** 
**    Rev 1.9   23 Jul 1993 15:23:40   ernie
** Removed setting of dasm address in IsTransfer() and IsCallorRts().  In both
** cases, bkptexec fills in the current dasm address into the dasmState. The
** value of transferAddr on entry is undefined.
** 
**    Rev 1.8   22 Jul 1993 10:29:56   ernie
** (For Mindy):
** 1. When emulation stops, shell dasm address is set to current pc.
** 2. Removed extra newline at end of dasm text.  This was messing up mixed
**    mode and shell dasm output.
** 
**    Rev 1.7   13 Jul 1993 19:23:12   doug
** Errors consolidated in errtext/mkerrors.h and use generic syntax error
** 
**    Rev 1.6   15 Jun 1993 11:29:38   mindy
** Fixed some bugs in the DadDasmGetRangeInstCount and DadDasmRangeOfInst
** routines.
** 
**    Rev 1.5   09 Jun 1993 09:01:58   ernie
** Added DadDasmIsTransfer() function to return info about transfer instrs
** 
**    Rev 1.3   25 May 1993 12:04:52   ernie
** Changed dasmSym to be system-wide global
** 
**    Rev 1.2.1.0   16 Jun 1993 09:29:46   mindy
** Fixed some bugs in the DadDasmGetRangeInstCount and DadDasmRangeOfInst
** routines.
** 
**    Rev 1.2   19 Apr 1993 08:32:54   doug
** add class pointer to set start address
** 
**    Rev 1.1   19 Apr 1993 07:48:52   ernie
** (for mindy)  Cleaned up problems with DadDasmIsCallOrRts().  Previously,
** this routine was using a temporary dasmState, but Bkptexec was depending
** on this routine to update the dasmstate (particularly the next address
** pointer).  Also added a TFree() which was missing before.
** 
**    Rev 1.0   08 Apr 1993 09:41:56   doug
** Initial revision.
** 
**    Rev 1.1   30 Mar 1993 16:12:10   ernie
** (for mindy): added symbol support
** 
**    Rev 1.0   12 Mar 1993 09:51:24   mindy
** Initial revision.
**
**  $Header:   S:/tbird/mt2_186/dad186/dasm16.cpv   1.0   16 Dec 1996 15:13:44   Judy  $
**
**  Copyright (C) 1993 Microtek International.  All rights reserved.
**
*****************************************************************************/

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

#include <string.h>

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

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

#ifndef _DAD_SERVER_
#include "dasm.h"
#endif

#ifndef _ASM16_
#include "asm16.h"
#endif

#ifndef _DSINFO16_
#include "dsinfo16.h"
#endif

#ifndef _SYMBLSVR_
#include "symblsvr.h"
#endif

#ifndef _DADDEF16_
#include "daddef16.h"
#endif

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

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

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

#ifndef _HOSTERRS_
#include "hosterrs.h"
#endif

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

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

#ifndef _EVENTS_
#include "events.h"
#endif

#ifndef _PVTASK_
#include "pvtask.h"
#endif

#ifndef __STDLIB_H_
#include <stdlib.h>
#endif

#ifndef __MATH_H_
#include <math.h>
#endif                 /****************************
                        *                          *
                        *     EXTERNALS            *
                        *                          *
                        ****************************/


extern const U8 endOfBuffer[];
extern const U8 endOfLine[];
extern HANDLE hLib;

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

HANDLE  cliServerHandle;
STATIC DESCRIPTOR cliAsmId=0, cliDasmId=0;
static U16 dummyAsm=0,dummyDasm=0;
DESCRIPTOR descEvt;
STATIC BOOLEAN initDadAlready = FALSE;
STATIC BOOLEAN dasmSym = TRUE;

/*
** #define DEBUG_MEM_LEAKS 1
*/
U32 memAlloced;
U32 memFreed;

                        /***************************
                        *                          *
                        *    PROTOTYPES            *
                        *                          *
                        ***************************/
extern "C" {
RETCODE EXPORT DadCliAsmAddr(LPSTR cmdString, U32 argc, U32 argv[]);
RETCODE EXPORT DadCliAsmDummy(LPSTR cmdString, U32 argc, U32 argv[]);
RETCODE EXPORT DadCliAsmInst(LPSTR cmdString, U32 argc, U32 argv[]);
RETCODE EXPORT DadCliDasmAddr(LPSTR cmdString, U32 argc, U32 argv[]);
RETCODE EXPORT DadCliGetDasmInst(LPSTR cmdString, U32 argc, U32 argv[]);
RETCODE EXPORT DadCliGetPrevDasmInst(LPSTR cmdString, U32 argc, U32 argv[]);
RETCODE EXPORT DadDasmCliSet(LPSTR cmdString, U32 argc, U32 argv[]);
RETCODE EXPORT DadCliDasmDummy(LPSTR cmdString, U32 argc, U32 argv[]);
};
                         /***************************
                         *                          *
                         *     EXECUTABLE CODE      *
                         *                          *
                         ***************************/

/*************************************************************************
**
**  DadDasmOpen
**
*************************************************************************/
RETCODE EXPORT DadDasmOpen(DESCRIPTOR FAR *id, DESCRIPTOR addr) {
   DasmInfo FAR  *dasmState = new DasmInfo(addr);

#ifdef DEBUG_MEM_LEAKS
   HeapDump(&memAlloced,&memFreed);
#endif
   if(dasmState == NULL) return ER_OUT_OF_MEMORY;
   *id = (DESCRIPTOR)dasmState;
   return(GOOD);
}

/*************************************************************************
**
**  DadDasmIsCallOrRTS
**
************************************************************************/
#pragma argsused
RETCODE EXPORT DadDasmIsCallOrRTS(DESCRIPTOR id,
	                          DESCRIPTOR calledAddr,CALL_TYPE *type) {
   DasmInfo FAR  *dasmState;
   LPSTR tmpPtr;
   RETCODE err;
#ifdef DEBUG_MEM_LEAKS
   HeapDump(&memAlloced,&memFreed);
#endif
   if((dasmState = (DasmInfo FAR *)id) == NULL)
      return ER_DAD_INVALID_ID_DESCRIPTOR;
   if((tmpPtr=TMalloc(MAX_LINE_LENGTH))==NULL) return(ER_OUT_OF_MEMORY);
   memset(tmpPtr,'\0', 1);
   if((err=dasmState->Disassemble(tmpPtr))!=GOOD) return(err);
   TFree(tmpPtr);
   if ((*type = dasmState->GetCallType()) == INST_CALL) {
      if ((err = AdrSetAddrOffset(calledAddr,dasmState->GetDestOffset()))
         != GOOD) return(err);
   }
   return(GOOD);
}

/*************************************************************************
**
**  DadDasmIsTransfer
**
************************************************************************/
#pragma argsused
RETCODE EXPORT DadDasmIsTransfer(DESCRIPTOR id, DESCRIPTOR transferAddr,
      TRANSFER_TYPE *transfer) {
   RETCODE err;
   LPSTR tmpPtr;
   DasmInfo FAR  *dasmState;
   if((dasmState = (DasmInfo FAR *)id) == NULL)
      return ER_DAD_INVALID_ID_DESCRIPTOR;
   if((tmpPtr=TMalloc(MAX_LINE_LENGTH))==NULL) return(ER_OUT_OF_MEMORY);
   memset(tmpPtr,'\0', 1);
   if((err=dasmState->Disassemble(tmpPtr))!=GOOD) return(err);
   TFree(tmpPtr);
   transfer->transfer = dasmState->IsTransfer();
   transfer->call = (dasmState->GetCallType() == INST_CALL);
   transfer->destKnown = dasmState->IsKnownAddr();
   if (dasmState->IsKnownAddr()) {
      if ((err = AdrSetAddrOffset(transferAddr, dasmState->GetDestOffset()))
         != GOOD) return(err);
   }
   return(GOOD);
}

/**************************************************************************
**
** DadDasmClose
**
***************************************************************************/
RETCODE EXPORT DadDasmClose(DESCRIPTOR id) {
   DasmInfo FAR  *dasmState;
#ifdef DEBUG_MEM_LEAKS
   HeapDump(&memAlloced,&memFreed);
#endif
   if((dasmState = (DasmInfo FAR *)id) == NULL)
      return ER_DAD_INVALID_ID_DESCRIPTOR;
   delete(dasmState);
   return(GOOD);
}

/*************************************************************************
**
** DadSetDasmSymbol
**
***************************************************************************/
RETCODE EXPORT DadSetDasmSymbol(BOOLEAN enable) {
   BOOLEAN oldDasmSym = dasmSym;
   dasmSym = enable;
   if (dasmSym != oldDasmSym)
      return(EnlEventNotify(EVENT_DASM_SYM_CHANGED));
   else
      return(GOOD);
}

/************************************************************************
**
** DadGetDasmSymbol
**
**************************************************************************/
RETCODE EXPORT DadGetDasmSymbol(BOOLEAN *enable) {
   *enable = dasmSym;
   return(GOOD);
}

/*************************************************************************
**
** DadSetDasmAddr
**
************************************************************************/
RETCODE EXPORT DadSetDasmAddr(DESCRIPTOR id, DESCRIPTOR address) {
#ifdef DEBUG_MEM_LEAKS
   HeapDump(&memAlloced,&memFreed);
#endif
   if(((DasmInfo FAR *)id) == NULL) return ER_DAD_INVALID_ID_DESCRIPTOR;
   return ((DasmInfo FAR *)id)->SetStartAddress(address);
}

/**************************************************************************
**
** DadGetDasmAddr
**
**************************************************************************/
RETCODE EXPORT DadGetDasmAddr(DESCRIPTOR id, DESCRIPTOR FAR *address) {
#ifdef DEBUG_MEM_LEAKS
   HeapDump(&memAlloced,&memFreed);
#endif
   if(((DasmInfo FAR *)id) == NULL) return ER_DAD_INVALID_ID_DESCRIPTOR;
   return ((DasmInfo FAR *)id)->GetStartAddress(address);
}

/**************************************************************************
**
** DadCurrentDasmAddr
**
**************************************************************************/
RETCODE EXPORT DadCurrentDasmAddr(DESCRIPTOR id, DESCRIPTOR address) {
   if(((DasmInfo FAR *)id) == NULL) return ER_DAD_INVALID_ID_DESCRIPTOR;
   return ((DasmInfo FAR *)id)->StartAddress(address);
}

/***************************************************************************
**
** DadSetDasmOpAddrSize
**
****************************************************************************/
#pragma argsused /* suppress warnings */
RETCODE EXPORT DadSetDasmOpAddrSize(DESCRIPTOR id, ADDR_OP_SIZE opAddrSize) {
  return GOOD; /* NOTES: For Intel compatibility Only - Do nothing */ 
}

/*************************************************************************
**
** DadDasmInst
**
*************************************************************************/
RETCODE EXPORT DadDasmInst(DESCRIPTOR id, U16 noInst, LPSTR *buffer,
                           U16 FAR *bufLen) {
   DasmInfo FAR *dasmState;
   LPSTR tmpPtr;
   BOOLEAN abortFromEsc;

   /* Clear ESC key */ 
   TskCheckAbort(&abortFromEsc);
   dasmState = (DasmInfo FAR *)id;
   if((tmpPtr=TMalloc(noInst*MAX_LINE_LENGTH))==NULL)
      return(ER_OUT_OF_MEMORY);
   *buffer = tmpPtr;
   memset(tmpPtr,'\0', 1);
   dasmState->Disassemble(tmpPtr,noInst);
   *bufLen = (U16)lstrlen(*buffer);

   // Need to wipe out last CRLF if there is one. and Adjust length
   tmpPtr = *buffer + (*bufLen) - 1;
   while( ((*tmpPtr=='\n') || (*tmpPtr=='\r')) && (tmpPtr != *buffer) ) {
      (*bufLen)--;
      tmpPtr--;
   }
   tmpPtr[1]='\0';   
      
   return GOOD;
}
  
/*************************************************************************
**
** DadDasmInstByLine
**
**  The starting address for this dasmState must be instruction aligned!!
**
*************************************************************************/
RETCODE EXPORT DadDasmInstByLine(DESCRIPTOR id, U16 lines, LPSTR *buffer,
                                 U16 FAR *bufLen) {
   RETCODE err;
   DasmInfo FAR *dasmState;
   LPSTR tmpPtr;
   BOOLEAN abortFromEsc;

   /* Clear ESC key */ 
   TskCheckAbort(&abortFromEsc);
   dasmState = (DasmInfo FAR *)id;
   if ((tmpPtr=TMalloc(lines*MAX_LINE_LENGTH)) == NULL)
      return ER_OUT_OF_MEMORY;
   *buffer = tmpPtr;
   memset(tmpPtr,'\0', 1);
   
   if ((err = dasmState->Disassemble(tmpPtr, lines)) != GOOD) {
      if (err == ER_ADR_RESULT_OVERFLOW) {
         /* 06/13/94 - Nghia
         ** Increment the Start Address to overflow and return GOOD.
         */
         dasmState->IncStartAddressOverFlow();
         err = GOOD;
      }
   }
   *bufLen = (U16)lstrlen(*buffer);

   // Need to wipe out last CRLF if there is one. and Adjust length
   tmpPtr = *buffer + (*bufLen) - 1;
   while( ((*tmpPtr=='\n') || (*tmpPtr=='\r')) && (tmpPtr != *buffer) ) {
      (*bufLen)--;
      tmpPtr--;
   }
   tmpPtr[1]='\0';   
      
   return err;
}

/*************************************************************************
**
** DadDasmRangeOfInst
**
** Description:
**   Uses the start address provided as the address to start disassembling
**   instructions at.  Uses the end address to stop dissassembling.
**   The addresses of all the instructions are included in the text
**   returned.  The state table address is updated to point to the
**   next instruction available to disassemble.
**
***************************************************************************/
RETCODE EXPORT DadDasmRangeOfInst(DESCRIPTOR id, DESCRIPTOR addrRange,
                                  LPSTR *buffer, U16 FAR *bufLen) {
   DasmInfo FAR *dasmState;
   DESCRIPTOR saveStartAddr;
   LPSTR tmpPtr;
   RETCODE err=GOOD, firstErr=GOOD;
   U16 len;
   U32 offset, rangeLen;
   U32 maxInputAddr;
   BOOLEAN abortFromEsc;

#ifdef DEBUG_MEM_LEAKS
   HeapDump(&memAlloced,&memFreed);
#endif
   /* Clear ESC key */ 
   TskCheckAbort(&abortFromEsc);

   if((dasmState = (DasmInfo FAR *)id) == NULL ) {
      err=ER_DAD_INVALID_ID_DESCRIPTOR;
      goto CLEANUP;
   }

   if ((err = AdrGetMaxInputAddrOffset(addrRange,&maxInputAddr)) != GOOD)
      return err;
   if ((err = AdrGetAddrOffset(addrRange,&offset)) != GOOD) goto CLEANUP;
   if (offset > maxInputAddr) {
      err=ER_DAD_INVALID_INST_ADDR;
      goto CLEANUP;
   }
   if ((err = AdrGetEndAddrOffset(addrRange,&offset)) != GOOD) goto CLEANUP;
   if (offset > maxInputAddr) {
      err=ER_DAD_INVALID_INST_ADDR;
      goto CLEANUP;
   }

   if((err=AdrGetAddrRangeLength(addrRange, &rangeLen))!=GOOD) goto CLEANUP;
   // Worst case there's an instruction every two bytes.
   if(rangeLen<2) rangeLen=2;  /* malloc returns NULL for zero len request */
   if((tmpPtr=TMalloc((rangeLen/2)*MAX_LINE_LENGTH))==NULL) {
      err=ER_OUT_OF_MEMORY;
      goto CLEANUP;
   }
   *buffer = tmpPtr;
   memset(tmpPtr,'\0', 1);
   
   if((err=dasmState->GetStartAddress(&saveStartAddr))!=GOOD) goto CLEANUP;
   if((err=dasmState->SetStartAddress(addrRange))!=GOOD) goto CLEANUP;
   while (rangeLen > 0) {
      if ((err = dasmState->Disassemble(tmpPtr)) != GOOD )
         goto CLEANUP;
      // 06/10/94 - Decrement the range length with the number of bytes
      // that it disassembled.
      rangeLen -= dasmState->InstLength();
      
      if (((err = TskCheckAbort(&abortFromEsc)) != GOOD) || abortFromEsc) {
         err = (err != GOOD ? err : ER_ABORT_FROM_ESC);
         goto CLEANUP;
      }
      tmpPtr += strlen(tmpPtr);  // speed up strcat, etc.
   }
   // Need to wipe out last CRLF if there is one. and Adjust length
   tmpPtr = *buffer + lstrlen(*buffer) - 1;
   while( ((*tmpPtr=='\n') || (*tmpPtr=='\r')) && (tmpPtr != *buffer) )
      tmpPtr--;
   tmpPtr[1]='\0';   

CLEANUP:
   *bufLen = (U16)lstrlen(*buffer);
   firstErr = err;
   err = dasmState->SetStartAddress(saveStartAddr);
   AdrDestroyAddress(saveStartAddr);
   AdrDestroyAddress(addrRange);
   return firstErr ? firstErr : err;
}

/*************************************************************************
**
** DadDasmGetRangeInstCount
**
** Description:
**   Given an address range (start and end addresses) determine the
**   number of disassembled instruction within the range.  Also determine
**   the number of lines for the instructions within the range.
**   The state table address is NOT updated.
**
***************************************************************************/
RETCODE EXPORT DadDasmGetRangeInstCount(DESCRIPTOR id,
                                        DESCRIPTOR addrRange,
                                        U16 FAR *instCount,
                                        U16 FAR *lineCount) {
   DasmInfo FAR *dasmState;
   DESCRIPTOR saveStartAddr;
   RETCODE err=GOOD, firstErr=GOOD;
   U32 offset, maxOffset, rangeLen;
   U32 maxInputOffset;
   BOOLEAN abortFromEsc;
   
#ifdef DEBUG_MEM_LEAKS
   HeapDump(&memAlloced,&memFreed);
#endif
   *instCount = 0;
   *lineCount = 0;
   if((dasmState = (DasmInfo FAR *)id) == NULL ) {
      err=ER_DAD_INVALID_ID_DESCRIPTOR;
      goto CLEANUP1;
   }

   if ((err = AdrGetMaxInputAddrOffset(addrRange,&maxInputOffset)) != GOOD)
      return err;
   if ((err = AdrGetAddrOffset(addrRange,&offset)) != GOOD) goto CLEANUP1;
   if (offset > maxInputOffset) {
      err=ER_DAD_INVALID_INST_ADDR;
      goto CLEANUP1;
   }
   if ((err = AdrGetEndAddrOffset(addrRange,&offset)) != GOOD) goto CLEANUP1;
   if (offset > maxInputOffset) {
      err=ER_DAD_INVALID_INST_ADDR;
      goto CLEANUP1;
   }
   if ((err=AdrGetAddrRangeLength(addrRange, &rangeLen))!=GOOD)
      goto CLEANUP1;
   // need to preserve starting address - don't want to change it
   if((err=dasmState->GetStartAddress(&saveStartAddr))!=GOOD) goto CLEANUP1;
   if((err=dasmState->SetStartAddress(addrRange))!=GOOD) goto CLEANUP1;
   
   while (rangeLen > 0) {
      if((err=dasmState->DecodeMemory()) != GOOD ) goto CLEANUP2;
      // 06/10/94 - Decrement the range length with the number of bytes
      // that it disassembled or decoded.
      rangeLen -= dasmState->InstLength();
      
      (*instCount)++;
      if((err=dasmState->IncStartAddress()) != GOOD ) goto CLEANUP2;
      
      if (((err = TskCheckAbort(&abortFromEsc)) != GOOD) || abortFromEsc) {
         err = (err != GOOD ? err : ER_ABORT_FROM_ESC);
         goto CLEANUP2;
      }
   }

CLEANUP2:
   *lineCount = *instCount;         // one line per instruction
   firstErr = err;
   err=dasmState->SetStartAddress(saveStartAddr);
   if( !firstErr ) firstErr = err;
   err = AdrDestroyAddress(saveStartAddr);

CLEANUP1:
   if( !firstErr ) firstErr = err;
   err = AdrDestroyAddress(addrRange);
   return firstErr ? firstErr : err;
}

/*************************************************************************
**
** DadDasmGetRangeOffset
**
** Description:
**   Given a line number count and using the current address offset,
**   determine the next address offset and the actual number of lines
**   required for display of the range.
**
***************************************************************************/
RETCODE EXPORT DadDasmGetRangeOffset(DESCRIPTOR id,
                                     U16 lines,
                                     DESCRIPTOR *nextOffset,
                                     U16 FAR *lineCount) {
   DasmInfo FAR *dasmState;
   DESCRIPTOR saveStartAddr;
   RETCODE err=GOOD, firstErr=GOOD;
   BOOLEAN abortFromEsc;
   
#ifdef DEBUG_MEM_LEAKS
   HeapDump(&memAlloced,&memFreed);
#endif
   *lineCount = 0;
   if((dasmState = (DasmInfo FAR *)id) == NULL )
      return(ER_DAD_INVALID_ID_DESCRIPTOR);

   // need to preserve starting address - don't want to change it
   if((err=dasmState->GetStartAddress(&saveStartAddr))!=GOOD) return(err);

   while( lines-- ) {
      if((err=dasmState->DecodeMemory()) != GOOD ) break;
      (*lineCount)++;
      if((err=dasmState->IncStartAddress()) != GOOD ) break;
      if (((err = TskCheckAbort(&abortFromEsc)) != GOOD) || abortFromEsc) {
         err = (err != GOOD ? err : ER_ABORT_FROM_ESC);
         break;
      }      
   }

CLEANUP:
   firstErr = err;
   err=dasmState->GetStartAddress(nextOffset);
   if( !firstErr ) firstErr = err;
   err=dasmState->SetStartAddress(saveStartAddr);
   if( !firstErr ) firstErr = err;
   err = AdrDestroyAddress(saveStartAddr);
   return firstErr ? firstErr : err;
}

/***************************************************************************
**
** DadDasmPrevInst
**
***************************************************************************/
RETCODE EXPORT DadDasmPrevInst(DESCRIPTOR id, U16 noInst, LPSTR *buffer,
                               U16 FAR *bufLen) {
   DasmInfo FAR *dasmState;
   DESCRIPTOR newStart;
   LPSTR tmpPtr;
   RETCODE err, firstErr=GOOD;
   U16 lineCount, instCount;
   U32 approxOffset, offset;
   BOOLEAN abortFromEsc;
   
   if((dasmState = (DasmInfo FAR *)id) == NULL )
      return(ER_DAD_INVALID_ID_DESCRIPTOR);

   if((err=dasmState->GetStartAddress(&newStart))!=GOOD) return(err);

   approxOffset = (noInst+2)*6;     // max possible number of bytes needed to
                                    // fill noInst instructions.
   
   if ((err = AdrGetAddrOffset(newStart,&offset)) != GOOD) goto CLEANUP;
   if (approxOffset > offset) {
      if((err=AdrSubtractFromAddress(newStart,offset)) != GOOD) goto CLEANUP;
   }
   else {
      if((err=AdrSubtractFromAddress(newStart,approxOffset))!= GOOD)
         goto CLEANUP;
   }
   if((err=dasmState->SetStartAddress(newStart))!=GOOD) goto CLEANUP;
   while(1) {
      if((err=dasmState->DecodeMemory()) != GOOD ) goto CLEANUP;
      if( dasmState->ValidInstruction() ) break;
      if((err=dasmState->IncStartAddress(2L)) != GOOD ) goto CLEANUP;
      if (((err = TskCheckAbort(&abortFromEsc)) != GOOD) || abortFromEsc) {
         err = (err != GOOD ? err : ER_ABORT_FROM_ESC);
         goto CLEANUP;
      }      
   }
   AdrDestroyAddress(newStart);
   if((err=dasmState->GetStartAddress(&newStart))!=GOOD) return(err);
   if ((err = AdrSetEndAddrOffset(newStart,offset)) != GOOD) goto CLEANUP;
   if ((err = DadDasmGetRangeInstCount(id,newStart,&instCount,&lineCount))
       != GOOD) return(err);
   if( instCount == 0 ) return(ER_DAD_AT_TOP_OF_MEMORY);
   while( instCount-- > (noInst+1) ) { // last inst would be first inst of
      // of last group.  Don't want to repeat this instruction.
      
      // Cycle through the "extra" instructions decoding memory
      // and incrementing the start address.
      if((err=dasmState->DecodeMemory())!=GOOD) return(err);
      if((err=dasmState->IncStartAddress())!=GOOD) return(err);
      if (((err = TskCheckAbort(&abortFromEsc)) != GOOD) || abortFromEsc) 
         return(err != GOOD ? err : ER_ABORT_FROM_ESC);          
   }
   if((tmpPtr=TMalloc(noInst*MAX_LINE_LENGTH))==NULL) return(ER_OUT_OF_MEMORY);
   *buffer = tmpPtr;
   memset(tmpPtr,'\0', 1);
   instCount--;   // adjust count so we don't print out last instruction.
   while( instCount-- ) {
      if((err=dasmState->Disassemble(tmpPtr))!=GOOD) return(err);
   }
   *bufLen = (U16)lstrlen(*buffer);
   // Need to wipe out last CRLF if there is one. and Adjust length
   tmpPtr = *buffer + (*bufLen) - 1;
   while( ((*tmpPtr=='\n') || (*tmpPtr=='\r')) && (tmpPtr != *buffer) ) {
      (*bufLen)--;
      tmpPtr--;
   }
   tmpPtr[1]='\0';   
   return(GOOD);
   
CLEANUP:
   firstErr = err;
   err = AdrDestroyAddress(newStart);
   return(firstErr ? firstErr : err);
}

/**************************************************************************
**
**  DadAsmOpen
**
***************************************************************************/
RETCODE EXPORT DadAsmOpen(DESCRIPTOR FAR *id, DESCRIPTOR startAddr) {
   AsmInfo FAR  *AsmState = new AsmInfo(startAddr);
   if(AsmState == NULL) return ER_OUT_OF_MEMORY;
   *id = (DESCRIPTOR)AsmState;
   return(GOOD);
}

/**************************************************************************
**
**  DadAsmClose
**
****************************************************************************/
RETCODE EXPORT DadAsmClose(DESCRIPTOR id) {
   AsmInfo FAR  *asmState;
#ifdef DEBUG_MEM_LEAKS
   HeapDump(&memAlloced,&memFreed);
#endif
   if((asmState = (AsmInfo FAR *)id) == NULL)
      return ER_DAD_INVALID_ID_DESCRIPTOR;
   delete(asmState);
   return(GOOD);
}

/**************************************************************************
**
**  DadSetAsmAddr
**
**************************************************************************/
RETCODE EXPORT DadSetAsmAddr(DESCRIPTOR id, DESCRIPTOR address) {
#ifdef DEBUG_MEM_LEAKS
   HeapDump(&memAlloced,&memFreed);
#endif
   if(((AsmInfo FAR *)id) == NULL) return ER_DAD_INVALID_ID_DESCRIPTOR;
   return ((AsmInfo FAR *)id)->SetStartAddress(address);
}

/**************************************************************************
**
**  DadGetAsmAddr
**
***************************************************************************/
RETCODE EXPORT DadGetAsmAddr(DESCRIPTOR id, DESCRIPTOR FAR *address) {
#ifdef DEBUG_MEM_LEAKS
   HeapDump(&memAlloced,&memFreed);
#endif
   if(((AsmInfo FAR *)id) == NULL) return ER_DAD_INVALID_ID_DESCRIPTOR;
   return ((AsmInfo FAR *)id)->GetStartAddress(address);
}

/************************************************************************
**
** DadAsm
**
**************************************************************************/
RETCODE EXPORT DadAsm(DESCRIPTOR id, LPSTR inInst,
                      LPSTR *outInst, LPWORD numBytes) {
   RETCODE err;
   AsmInfo FAR *asmState;
   *numBytes = 0;
   asmState = (AsmInfo FAR *)id;
   if((err=asmState->Asm(inInst, outInst))!=GOOD) return(err);
   *numBytes = asmState->InstructionLength();
   return(GOOD);
}

/**************************************************************************
**
** SendCliMessage :      CLI Function
**
***************************************************************************/
RETCODE SendCliMessage(HANDLE cliHandle, LPSTR msgPtr) {
   HANDLE msgBufHandle;
   CSERVER_RESULTS FAR  *msgBufPtr;
   U16 msgTextSize;

#ifdef DEBUG_MEM_LEAKS
   HeapDump(&memAlloced,&memFreed);
#endif
   msgTextSize = strlen(msgPtr);
   if((msgBufHandle=GlobalAlloc(GMEM_MOVEABLE, sizeof(CSERVER_RESULTS)
     +msgTextSize+2))==NULL)
      return(ER_OUT_OF_MEMORY);
   if((msgBufPtr=(CSERVER_RESULTS *)GlobalLock(msgBufHandle)) == NULL)
      return(ER_WINDOWS_MEMLOCK);
   msgBufPtr->target               = 0;    /*@@ CLI: not yet def'ed @@*/
   msgBufPtr->variantCode          = 0x401;    /*@@ CLI: not yet def'ed @@*/
   msgBufPtr->resultTextLength     = msgTextSize; /* message string length */
   memmove(msgBufPtr->messageText,msgPtr,msgTextSize+1); /* copy the null */
   if(SendMessage( cliHandle, 0x401, msgBufHandle, 0x401L ))
      return(GOOD);
   return(ER_SEND_MESSAGE_FAILED);
}


/**************************************************************************
**
** 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);
}

/**************************************************************************
**
**  AsmCliOpen
**
****************************************************************************/
RETCODE AsmCliOpen(U32 offset) {
   RETCODE err = GOOD;
   DESCRIPTOR startAddr;

#ifdef DEBUG_MEM_LEAKS
   HeapDump(&memAlloced,&memFreed);
#endif
   if ((err=AdrCreateAddress(&startAddr)) != GOOD)
      return(err);
   if ((err=AdrSetAddrOffset(startAddr,offset)) != GOOD)
      goto CLEANUP;
   if ((err=DadAsmOpen(&cliAsmId,startAddr))!= GOOD)
      goto CLEANUP;
CLEANUP:
   if (!err)
      err = AdrDestroyAddress(startAddr);
   else
      AdrDestroyAddress(startAddr);
   return(err);
}

/***************************************************************************
**
**  DadCliAsmAddr
**
***************************************************************************/
RETCODE EXPORT DadCliAsmAddr(LPSTR cmdString, U32 argc, U32 argv[]) {
   DESCRIPTOR address;
   char resultBuff[128];
   RETCODE err;
   U32 addrOffset=0;

#ifdef DEBUG_MEM_LEAKS
   HeapDump(&memAlloced,&memFreed);
#endif
   if (argc > 2 )
      return (ER_CLI_SYNTAX);
   if( argc == 2 ) {
      if ((err = AdrCreateAddress(&address)) != GOOD)
         return err;
      if ((err = AdrConvTextToAddress(address,&cmdString[(U16)argv[1]]))
         != GOOD){
         AdrDestroyAddress(address);
         return err;
      }
   }
   if( !cliAsmId ) {
      AsmCliOpen(addrOffset);
   }
   if( argc == 2 ) {
      if((err=DadSetAsmAddr(cliAsmId,address))!=GOOD) {
         AdrDestroyAddress(address);
         return(err);
      }
      if((err=AdrDestroyAddress(address))!=GOOD)
         return(err);
   }
   if( (err=DadGetAsmAddr(cliAsmId,&address)) != GOOD ) return(err);
   if((err=AdrGetAddrOffset(address,&addrOffset))!=GOOD) {
      AdrDestroyAddress(address);
      return(err);
   }
   wsprintf(resultBuff,"asm address offset: %lx", addrOffset);
   if((err=AdrDestroyAddress(address))!=GOOD) return(err);
   return(SendCliMessage(cliServerHandle,resultBuff));
}

/***************************************************************************
**
**  DadCliAsmDummy
**
***************************************************************************/
RETCODE EXPORT DadCliAsmDummy(LPSTR cmdString, U32 argc, U32 argv[]) {
   char buffer[80];

#ifdef DEBUG_MEM_LEAKS
   HeapDump(&memAlloced,&memFreed);
#endif
   if( argc == 2 ) {
      dummyAsm = atoi(&cmdString[(U16)argv[1]]);
   }
   wsprintf(buffer,"asm debug is %s",dummyAsm?"ON":"OFF");
   return(SendCliMessage(cliServerHandle,buffer));
}

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

   RETCODE   err = GOOD;
   LPSTR     outBuf;
   LPSTR     dummyInput;
   U16       numBytes;
   char      buffer[25];

#ifdef DEBUG_MEM_LEAKS
   HeapDump(&memAlloced,&memFreed);
#endif
   if (argc != 2)
      return ER_DAD_BAD_NUM_PARM;
   if ((dummyInput = (LPSTR)TMalloc(MAX_LINE_LENGTH)) == NULL)
      return ER_OUT_OF_MEMORY;
   strcpy(dummyInput,&cmdString[(U16)argv[1]]);
   if (!cliAsmId) {
      AsmCliOpen(0L);
   }
#ifdef DEBUG_MEM_LEAKS
   HeapDump(&memAlloced,&memFreed);
#endif
   if ((err=DadAsm(cliAsmId,dummyInput,&outBuf,(LPWORD)&numBytes)) != GOOD)
      return(err);
#ifdef DEBUG_MEM_LEAKS
   HeapDump(&memAlloced,&memFreed);
#endif
   sprintf(buffer,"\r\nNumber of bytes: %#x\0",numBytes);
   strcat(outBuf,buffer);
   if ((err = SendCliMessage(cliServerHandle,outBuf)) != GOOD)
      return err;
   if ((err = TFree(dummyInput)) != GOOD)
      return err;
   if ((err = TFree(outBuf)) != GOOD)
      return err;

   return(err);
}

/**************************************************************************
**
**  DasmCliOpen
**
**************************************************************************/
RETCODE DasmCliOpen(U32 offset) {
   DESCRIPTOR startAddr;
   RETCODE err;

#ifdef DEBUG_MEM_LEAKS
   HeapDump(&memAlloced,&memFreed);
#endif
   if ((err=AdrCreateAddress(&startAddr)) != GOOD)
      return(err);
   if ((err=AdrSetAddrOffset(startAddr,offset)) != GOOD)
      goto CLEANUP;
   if ((err=DadDasmOpen(&cliDasmId,startAddr)) != GOOD)
      goto CLEANUP;

CLEANUP:
   if (!err)
      err = AdrDestroyAddress(startAddr);
   else
      AdrDestroyAddress(startAddr);
   return(err);
}

/***************************************************************************
**
**  DadCliDasmAddr
**
**************************************************************************/
RETCODE EXPORT DadCliDasmAddr(LPSTR cmdString, U32 argc, U32 argv[]) {
   DESCRIPTOR address;
   char resultBuff[128];
   RETCODE err;
   U32 addrOffset=0;

#ifdef DEBUG_MEM_LEAKS
   HeapDump(&memAlloced,&memFreed);
#endif
   if (argc > 2) return (ER_CLI_SYNTAX);
   if (argc == 2) {
      if ((err = AdrCreateAddress(&address)) != GOOD)
         return err;
      if ((err = AdrConvTextToAddress(address,&cmdString[(U16)argv[1]]))
         != GOOD) {
         AdrDestroyAddress(address);
         return err;
      }
   }
   if( !cliDasmId ) {
      if((err=DasmCliOpen(addrOffset))!=GOOD) return(err);
   }
   if( argc == 2 ) {
      if ((err = DadSetDasmAddr(cliDasmId,address)) != GOOD) {
         AdrDestroyAddress(address);
         return(err);
      }
      if((err=AdrDestroyAddress(address))!=GOOD) return(err);
   }
   if ((err=DadGetDasmAddr(cliDasmId,&address)) != GOOD )
      return(err);
   if ((err=AdrGetAddrOffset(address,&addrOffset)) != GOOD) {
      AdrDestroyAddress(address);
      return(err);
   }
   wsprintf(resultBuff,"dasm address offset: %lx", addrOffset);
   if ((err=AdrDestroyAddress(address)) != GOOD)
      return(err);
   return(SendCliMessage(cliServerHandle,resultBuff));
}

/**************************************************************************
**
**  ParseDasm
**
***************************************************************************/
RETCODE ParseDasm(LPSTR cmdString, U32 argc, U32 argv[],
                  U16 FAR *numInst, DESCRIPTOR FAR *endAddress) {
   RETCODE err = GOOD;
   U32 offset=0;
   DESCRIPTOR address;

#ifdef DEBUG_MEM_LEAKS
   HeapDump(&memAlloced,&memFreed);
#endif
   if( !cliDasmId ) {
      if((err=DasmCliOpen(offset))!=GOOD) return(err);
   }
   if((err=DadGetDasmAddr(cliDasmId,&address))!=GOOD) return(err);
   *numInst = 10;
   if( argc > 1 ) {
      if ((err = AdrConvTextToAddress(address,&cmdString[(U16)argv[1]]))
         != GOOD)
         goto CLEANUP;
      if ((err = AdrGetAddrOffset(address, &offset)) != GOOD)
         goto CLEANUP;
      if ((offset & 0xfffffffeL) != offset) {
         /* if odd round down to previous even */
         offset &= 0xfffffffeL;
         if ((err=AdrSetAddrOffset(address, offset))!=GOOD)
            goto CLEANUP;
      }
      if ((err=DadSetDasmAddr(cliDasmId,address))!=GOOD)
         goto CLEANUP;
      if ( argc == 3 ) {
         if((err=AdrDuplicateAddress(address,endAddress))!=GOOD)
            goto CLEANUP;
         if ((err = AdrConvTextToAddress(*endAddress,
            &cmdString[(U16)argv[2]])) != GOOD) {
            AdrDestroyAddress(*endAddress);
            goto CLEANUP;
         }
         if ((err = AdrGetAddrOffset(*endAddress,&offset)) != GOOD) {
            AdrDestroyAddress(*endAddress);
            goto CLEANUP;
         }
         if (offset & 0x1) {
            offset += (offset & 0x1);  /* if odd bump to next highest even */
            if ((err=AdrSetAddrOffset(*endAddress, offset)) != GOOD) {
               AdrDestroyAddress(*endAddress);
               goto CLEANUP;
            }
         }
         *numInst = 0;
      }
   }
CLEANUP:
   if (!err) {
      err = AdrDestroyAddress(address);
   }
   else {
      AdrDestroyAddress(address);
   }
   return(err);
}

/************************************************************************
**
**  DadCliGetDasmInst
**
***************************************************************************/
RETCODE EXPORT DadCliGetDasmInst(LPSTR cmdString, U32 argc, U32 argv[]) {
   RETCODE err = GOOD;
   U16 buflen, numInst=1;
   LPSTR resultBuff=NULL;
   DESCRIPTOR endAddr = NULL;

#ifdef DEBUG_MEM_LEAKS
   HeapDump(&memAlloced,&memFreed);
#endif
   if( (err=ParseDasm(cmdString,argc,argv,&numInst,&endAddr)) != GOOD )
      return(err);
   if( numInst ) {
      if((err=DadDasmInst(cliDasmId, numInst, &resultBuff, &buflen))!=GOOD) {
         if (resultBuff != NULL) TFree(resultBuff);
         AdrDestroyAddress(endAddr);
         return err;
      }
      err= SendCliMessage(cliServerHandle,resultBuff);
      if ((err = TFree(resultBuff)) != GOOD) {
         AdrDestroyAddress(endAddr);
         return err;
      }
   }
   return(err);
}

/*************************************************************************
**
**  DadCliGetPrevDasmInst
**
***************************************************************************/
RETCODE EXPORT DadCliGetPrevDasmInst(LPSTR cmdString, U32 argc, U32 argv[]) {
   RETCODE err;
   U16 buflen, numInst=1;
   LPSTR resultBuff;
   DESCRIPTOR end;

#ifdef DEBUG_MEM_LEAKS
   HeapDump(&memAlloced,&memFreed);
#endif
   if((err=ParseDasm(cmdString,argc,argv,&numInst,&end))!=GOOD) return(err);
   if( (err=DadDasmPrevInst(cliDasmId, numInst, &resultBuff, &buflen))!=GOOD)
      return(err);
   err= SendCliMessage(cliServerHandle,resultBuff);
   TFree(resultBuff);
   return(err);
}

BOOLEAN ParseBoolean(LPSTR ptr) {
   BOOLEAN val = FALSE;
   if( ptr[0] == '1' )
      val = TRUE;
   else if(strncmpi(ptr, "on", strlen(ptr))==0) 
      val = TRUE;
   else if(strncmpi(ptr, "true", strlen(ptr))==0) 
      val = TRUE;
   return(val);
}

/**************************************************************************
**
**  DadDasmCliSet
**
**************************************************************************/
RETCODE EXPORT DadDasmCliSet(LPSTR cmdString, U32 argc, U32 argv[]) {
   BOOLEAN enable;
   RETCODE err;
   char strBuf[80];
   if( !cliDasmId ) {
      if((err=DasmCliOpen(0L))!=GOOD) return(err);
   }
   if( argc == 2 ) {
      enable=ParseBoolean(&cmdString[(U16)argv[1]]);
      if((err=DadSetDasmSymbol(enable))!=GOOD) return(err);
   }
   if((err=DadGetDasmSymbol(&enable))!=GOOD) return(err);
   sprintf(strBuf,"Symbolic disassembler output is %s",enable?"on":"off");
   return(SendCliMessage(cliServerHandle,strBuf));
}

/**************************************************************************
**
** DadCliDasmDummy
**
**************************************************************************/
RETCODE EXPORT DadCliDasmDummy(LPSTR cmdString, U32 argc, U32 argv[]) {
   char buffer[80];

   if( argc == 2 ) {
      dummyDasm =(U8) atoi(&cmdString[(U16)argv[1]]);
   }
   wsprintf(buffer,"dasm debug is %s",dummyDasm?"ON":"OFF");
   return(SendCliMessage(cliServerHandle,buffer));
}

RETCODE  DadInit(VOID)  {
   FARPROC myCallback;
   RETCODE err;
   if (!initDadAlready) {
      myCallback = MakeProcInstance((FARPROC)DadCallback,hLib);
      if((err=EnlRegister(EVENT_MEM_HALTED,(EVCALLBACK) myCallback,&descEvt))
          != GOOD) return err;
      if((err=EnlRegister(EVENT_MEM_EDIT,(EVCALLBACK) myCallback,&descEvt))
          != GOOD) return err;
      if((err=EnlRegister(EVENT_BKPT_HALTED,(EVCALLBACK) myCallback,&descEvt))
          != GOOD) return err;
      if((err=DasmCliOpen(0L))!=GOOD) return err;
      initDadAlready = TRUE;
   }
   return GOOD;
}

RETCODE EXPORT DadCallback(U32 eventNum) {
   RETCODE err;
#ifdef DEBUG_MEM_LEAKS
   HeapDump(&memAlloced,&memFreed);
#endif
   switch (eventNum) {
      case EVENT_MEM_HALTED :
      case EVENT_MEM_EDIT :
         /*
         ** Do my business first and finally propagate event
         */
         if ((err = FreeDasmCache()) != GOOD) return err;
         if (eventNum == EVENT_MEM_HALTED) {
            if ((err = EnlEventNotify(EVENT_DASM_HALTED)) != GOOD)
               return err;
         }
         break;
      case EVENT_BKPT_HALTED:
         // When execution breaks we want to set CLI dasm address to PC
         if( cliDasmId ) {
            DESCRIPTOR pc;
            if((err=CpuGetPC(&pc))!=GOOD) return(err);
            ((DasmInfo FAR *)cliDasmId)->SetStartAddress(pc);
            if((err=AdrDestroyAddress(pc))!= GOOD) return(err);
         }
         break;              

      default :
         return GOOD;
   }
   return GOOD;
}

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

