/****************************************************************************
**
**  Name:  dasm86.c
**
**  Description:
**     This is a disassembler
**
**  Status:  REVIEWED
**
**  $Log:   S:/tbird/mt2_186/dad186/dasm86.c_v  $
** 
**    Rev 1.0   16 Dec 1996 15:16:04   Judy
** Initial revision.
** 
**    Rev 1.21   18 Oct 1994 10:59:56   steve
** changed data type of the first parameter in AsmInst().
** 
**    Rev 1.20   17 Oct 1994 11:43:02   steve
** added DadGetDasmOpAddrSize()
** 
**    Rev 1.19   11 Oct 1994 11:17:52   ernie
** Changed dasm cache to use the incoming address type rather than physical
** as the bounds of cached data.  This avoids the overhead of converting
** each incoming address to physical before comparing against the cached
** range.  Performance of go until return in paged PROT32 mode increased
** from 45 seconds to 5 seconds.
** 
**    Rev 1.18   10 Oct 1994 18:00:06   steve
** fixed a bug which made DadDasmRangeOfInst cannot stop right away while 
** instLen was zero.    --- kevin
** 
**    Rev 1.17   27 Sep 1994 13:58:56   marilyn
** Revamped the event callback so that the pc always stays in 
** synch with the shell dasm session.  Also removed event BKPT_HALTED
** since MEM_HALTED is also callbacked.
** 
**    Rev 1.16   20 Sep 1994 11:53:40   marilyn
** Updated each dasm session to store and flush its own cache for
** performance improvements.
** 
**    Rev 1.15   14 Sep 1994 10:40:14   nghia
** Merged Kevin's changes for Asm address cli to support address space.
** 
**    Rev 1.14   13 Sep 1994 17:22:02   nghia
** Fixed PPR9617 - Dasm86 too slow in protected mode.
** Revised GetDataInt() to support memory caching inside of the dasm server.
** This memory cache is used in improving the dasm server performances.
** 
**    Rev 1.13   03 Aug 1994 10:41:40   mindy
** copy starting address to target addr in DadGetDasmInstruction
** 
**    Rev 1.12   03 Aug 1994 10:02:58   marilyn
** IsTransfer now checks the knownAddr field before returning a copy 
** of the target address.
** 
**    Rev 1.11   01 Aug 1994 17:46:10   marilyn
** Fixed bug in RangeOfInstructions where the last instruction if 1 byte
** was not dropped and then an extra end of line was returned.  Changed the
** algorithm to use the range length for dissassembling the range rather that
** the start and end offsets.  This interface was intended only for use with
** line numbers which have verified ranges.
** 
**    Rev 1.10   29 Jul 1994 10:29:46   steve
** Merged Kevin's 386 build 10 changes with Marilyn's
** 
**    Rev 1.9   26 Jul 1994 09:06:08   steve
** Kevin's ASM changes for 386 build 9
**
**    Rev 1.8   21 Jul 1994 10:59:34   marilyn
** Updated interface to AdrSet/GetAddrSegmentSelector.
** 
**    Rev 1.7   29 Jun 1994 15:08:34   marilyn
** Fixed bug in parseDasm where a second address was defaulting to the
** CS instead of the implied segment.
** 
**    Rev 1.6   23 Jun 1994 14:48:24   marilyn
** Kevin's bug fixes for the call instr.
** 
**    Rev 1.5   20 Jun 1994 11:40:48   marilyn
** Bug fixes for the default of CS in dasm.
** 
**    Rev 1.4   13 Jun 1994 18:40:40   marilyn
** Modified the dasm segment to be sticky.
** 
**    Rev 1.3   10 Jun 1994 16:58:12   marilyn
** Implemented changes based on 6/8/94 code review with Nghia.  
** Integrated with Kevin's bug fixes of 6/10/94.  Mostly changes
** to the address server interface and the use of pmode and addrOpSize.
** A couple of bug fixes.
** 
**    Rev 1.2   02 Jun 1994 13:50:30   mindy
** added Kevin's changes and changed GetDasmInst... interface
** 
**    Rev 1.1   27 May 1994 08:12:48   mindy
** added kevin's changes to get rid of warnings
** 
**    Rev 1.0   25 May 1994 14:19:26   nghia
** Initial revision.
**
**  $Header:   S:/tbird/mt2_186/dad186/dasm86.c_v   1.0   16 Dec 1996 15:16:04   Judy  $
**
**  Copyright (C) 1994 Microtek International.  All rights reserved.
**
****************************************************************************/

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



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

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

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

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

#ifndef _PDASM86_
#include "pdasm86.h"
#endif

#ifndef _LDASM86_
#include "ldasm86.h"
#endif

#ifndef _DADDEF86_
#include "daddef86.h"
#endif

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

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

#ifndef _PVTASK_
#include "pvtask.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 _SDPROBE_
#include "sdprobe.h"
#endif

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

#ifndef __STRING_H
#include <string.h>
#endif

#ifndef __MATH_H
#include <math.h>
#endif                 /****************************
                        *                          *
                        *     EXTERNALS            *
                        *                          *
                        ****************************/
extern HANDLE hLib;

                       /****************************
                        *                          *
                        *     LOCAL DEFINITIONS    *
                        *                          *
                        ****************************/
HANDLE  cliServerHandle;
DESCRIPTOR descEvt;
PROC_CPU cpuType;

STATIC DESCRIPTOR cliAsmId=0, cliDasmId=0;
STATIC BOOLEAN dummyAsm = FALSE;
STATIC BOOLEAN dummyDasm = FALSE;
STATIC BOOLEAN dasmSym = TRUE;
STATIC BOOLEAN initDadAlready = FALSE;
STATIC DAD_STATE *sessionList=NULL;

CHAR *SymSet[] = {"OFF", "ON"};
U32 memAlloced;
U32 memFreed;

const CHAR endOfBuffer[] = "\0";
const CHAR endOfLine[]="\r\n";

                        /***************************
                        *                          *
                        *    PROTOTYPES            *
                        *                          *
                        ***************************/
RETCODE BackUpInstructions(DESCRIPTOR id, U16 numInst, U16 *backedInst);
RETCODE DadAsmShowInfo(DESCRIPTOR id);
U16 GetBlockOfInstructions(DESCRIPTOR id, U16 numInst, LPSTR **instArray);
RETCODE  DadInit(VOID);
RETCODE DisassembleOpcode(LPSTR OpByte, DESCRIPTOR addrOffset, PMODE PMode,
   ADDR_OP_SIZE opSize, BOOLEAN symbols, DASM_INFO FAR *dasmInfo);
RETCODE DummyMemRead(LPSTR buffer);
RETCODE PRIVATE GetIntData(DAD_STATE FAR *dasmState, DESCRIPTOR address,
                           CHAR *dataBuf);
RETCODE PRIVATE UpdateDasmCache(DAD_STATE *dasmState, DESCRIPTOR address);
RETCODE PRIVATE FreeDasmCache(DAD_STATE FAR *dasmState);
VOID PRIVATE AddSessionToList(DAD_STATE FAR *dasmState);
VOID PRIVATE FreeSessionFromList(DAD_STATE FAR *dasmState);
RETCODE PRIVATE UpdateCLISessionDasmAddr(VOID);

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

/*************************************************************************
**
**  DadDasmOpen
**
*************************************************************************/
RETCODE EXPORT DadDasmOpen(DESCRIPTOR FAR *id, DESCRIPTOR addr) {
   DAD_STATE FAR  *DasmState;
   ADDR_SEGSEL_TYPE segType;
   U16 segValue;

   RETCODE err;
#ifdef DEBUG_MEM_LEAKS
   HeapDump(&memAlloced,&memFreed);
#endif
   if ((DasmState = (DAD_STATE FAR *)TMalloc(sizeof(DAD_STATE))) == NULL)
      return ER_OUT_OF_MEMORY;
   // initialize next, pageCache, memCacheBuf
   memset(DasmState,'\0',sizeof(DAD_STATE));
   if ((err = AdrGetPmode(&(DasmState->mode),NULL)) != GOOD)
      return err;
   /* base size on current pmode */
   DasmState->addrOpSize = ADDR_USE_AUTO;
   *id = (DESCRIPTOR)DasmState;
   AddSessionToList(DasmState);
   if ((err = AdrDuplicateAddress(addr,&DasmState->startAddr)) != GOOD)
      return err;
   if ((err = AdrGetAddrSegmentSelector(DasmState->startAddr, 
         &segType, &segValue)) != GOOD)
      return err;
   if (segType == ADDR_USE_DEF_DS) 
      return (AdrSetAddrSegmentSelector(DasmState->startAddr,ADDR_USE_CS,
            NULL));
   return GOOD;
}

/****************************************************************************
**  DadSetDasmOpAddrSize
**  purpose: setting operand/address size
**
**  id (in): descriptor of DASM session
**  opAddrSize (in): operand/address size
****************************************************************************/
RETCODE EXPORT DadSetDasmOpAddrSize(DESCRIPTOR id, ADDR_OP_SIZE opAddrSize) {
   DAD_STATE FAR *dasmState;

   if ((dasmState = (DAD_STATE FAR *)id) == NULL)
      return ER_DAD_INVALID_ID_DESCRIPTOR;
   dasmState->addrOpSize = opAddrSize;
   return(GOOD);
}

/****************************************************************************
**  DadGetDasmOpAddrSize
**  purpose: getting operand/address size
**
**  id (in): descriptor of DASM session
**  opAddrSize (out): operand/address size
****************************************************************************/
RETCODE EXPORT DadGetDasmOpAddrSize(DESCRIPTOR id, ADDR_OP_SIZE *opAddrSize) {
   DAD_STATE FAR *dasmState;

   if ((dasmState = (DAD_STATE FAR *)id) == NULL)
      return ER_DAD_INVALID_ID_DESCRIPTOR;
   *opAddrSize = dasmState->addrOpSize;
   return(GOOD);
}

/**************************************************************************
**
** DadCurrentDasmAddr
**
**************************************************************************/
RETCODE EXPORT DadCurrentDasmAddr(DESCRIPTOR id, DESCRIPTOR address) {
  if (((DAD_STATE FAR *)id) == NULL)
      return ER_DAD_INVALID_ID_DESCRIPTOR;
   /* copy the content of startAddr to address */
   return AdrCopyAddress(((DAD_STATE FAR *)id)->startAddr, address);
}

/****************************************************************************
**  DadGetDasmInstruction
**  purpose: An entry of DASM
**
****************************************************************************/
RETCODE EXPORT DadGetDasmInstruction(DESCRIPTOR addr,
                                     ADDR_OP_SIZE opSize,
                                     LPSTR instBytes,  LPSTR instText,
                                     DASM_INFO *info) {
                   
   RETCODE err;
   CHAR ldasmText[MAX_DASM_TEXT];
   PMODE pmode;

   if ((err = AdrGetPmode(&pmode,NULL)) != GOOD)
      return err;
   if (info->target == NULL) {
      if ((err = AdrDuplicateAddress(addr,&info->target)) != GOOD)
         return err;
   }
   else {
      if ((err = AdrCopyAddress(addr,info->target)) != GOOD)
         return err;
   }
   if ((err = Ldasm(instBytes, pmode, opSize, addr, dasmSym,
         ldasmText, info)) != GOOD)
      return(err);
   if ((err = LdasmText(ldasmText, instText)) != GOOD)
      return(err);
   return(GOOD);
}

/*************************************************************************
**
**  DadDasmIsCallOrRTS
**
************************************************************************/
RETCODE EXPORT DadDasmIsCallOrRTS(DESCRIPTOR id, DESCRIPTOR calledAddr,
                                  CALL_TYPE FAR *type) {
   DAD_STATE FAR  *dasmState;

#ifdef DEBUG_MEM_LEAKS
   HeapDump(&memAlloced,&memFreed);
#endif
   if ((dasmState = (DAD_STATE FAR *)id) == NULL)
      return ER_DAD_INVALID_ID_DESCRIPTOR;
   return IsCallOrRTS(dasmState,calledAddr,type);
}

/*************************************************************************
**
**  DadDasmIsTransfer
**
************************************************************************/
RETCODE EXPORT DadDasmIsTransfer(DESCRIPTOR id, DESCRIPTOR transferAddr,
      TRANSFER_TYPE *transfer) {
   DAD_STATE FAR  *dasmState;

   if ((dasmState = (DAD_STATE FAR *)id) == NULL)
      return ER_DAD_INVALID_ID_DESCRIPTOR;
   return (IsTransfer(dasmState, transferAddr,transfer));
}

/**************************************************************************
**
** DadDasmClose
**
***************************************************************************/
RETCODE EXPORT DadDasmClose(DESCRIPTOR id) {
   DAD_STATE FAR  *dasmState;
   RETCODE err;

#ifdef DEBUG_MEM_LEAKS
   HeapDump(&memAlloced,&memFreed);
#endif
   if ((dasmState = (DAD_STATE FAR *)id) == NULL)
      return ER_DAD_INVALID_ID_DESCRIPTOR;
   FreeSessionFromList(dasmState);
   if ((err = AdrDestroyAddress(dasmState->startAddr)) != GOOD)
      return err;
   if ((err = TFree((LPSTR)id)) != GOOD)
      return err;
   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) {
   U32 cpuMaxInputAddress, offset;
   RETCODE err = GOOD;
   DAD_STATE FAR  *dasmState;
   ADDR_SEGSEL_TYPE segType;
   U16 segValue;
   ADDR_TYPE addrType;

#ifdef DEBUG_MEM_LEAKS
   HeapDump(&memAlloced,&memFreed);
#endif
   if ((dasmState = (DAD_STATE FAR *)id) == NULL)
      return ER_DAD_INVALID_ID_DESCRIPTOR;
   
   /*
   ** check to see if the new address is valid
   */
   if ((err = AdrGetAddrOffset(address, &offset)) != GOOD)
      return err;
   if ((err = AdrGetMaxInputAddrOffset(address, &cpuMaxInputAddress)) != GOOD)
      return err;
   if (offset > cpuMaxInputAddress)
      return(ER_DAD_BOGUS_ADDRESS);
   if ((err = AdrGetAddrType(address,&addrType)) != GOOD)
      return err;
   if (addrType == ADDR_VIRTUAL) {
      if ((err = AdrGetAddrSegmentSelector(address,&segType,&segValue)) != GOOD)
         return err;
      if (segType == ADDR_USE_DEF_DS) {
         /* explicit segment value override is OK but otherwise 
         ** default to the current dasm segment          
         */
         if ((err = AdrGetAddrSegmentSelector(dasmState->startAddr,&segType,
               &segValue)) != GOOD)
            return err;
         if ((err = AdrCopyAddress(address, dasmState->startAddr)) != GOOD)
            return err;
         return (AdrSetAddrSegmentSelector(dasmState->startAddr,
               segType,&segValue));
      }
   }
   return(AdrCopyAddress(address, dasmState->startAddr));
}

/**************************************************************************
**
** DadGetDasmAddr
**
**************************************************************************/
RETCODE EXPORT DadGetDasmAddr(DESCRIPTOR id, DESCRIPTOR FAR *address) {

   DAD_STATE FAR  *dasmState;

#ifdef DEBUG_MEM_LEAKS
   HeapDump(&memAlloced,&memFreed);
#endif
   if ((dasmState = (DAD_STATE FAR *)id) == NULL)
      return ER_DAD_INVALID_ID_DESCRIPTOR;
   return(AdrDuplicateAddress(dasmState->startAddr,address));
}


/*************************************************************************
**
**  BackUpInstructions
**
**  Attempts to back up numInst.  Returns actual instructions backed up
**  in backedInst.  Starts with an estimate of 4 bytes per inst to back up.
**  Then steps forward to the first valid inst.
**
*************************************************************************/
RETCODE BackUpInstructions(DESCRIPTOR id, U16 numInst, U16 *backedInst) {
   DAD_STATE FAR  *dadState;
   U32 approxOffset;
   U16 lineCount;
   U8 instLen;
   BOOLEAN instValid, abortFromEsc;
   DESCRIPTOR rangeDesc;
   RETCODE err = GOOD;

   /* Clear ESC key */ 
   TskCheckAbort(&abortFromEsc);
   
   if ((dadState = (DAD_STATE FAR *)id) == NULL)
      return ER_DAD_INVALID_ID_DESCRIPTOR;
   *backedInst = 0;
   approxOffset = numInst*4;
   if ((err = AdrAdjustRange(dadState->startAddr,approxOffset,approxOffset,
         ADDR_RANGE_LOW)) != GOOD)
      return err;
   /*
   **  we are only guessing how much to back up so make
   **  sure we are aligned on a good instruction by stepping forward.
   */
   if ((err = CheckDasmInst(dadState->startAddr, dadState, dasmSym,
         &instLen,&instValid)) != GOOD)
      return err;
   if (!instValid) {
      do {
         if ((err = AdrAddToAddress(dadState->startAddr,2)) != GOOD)
            goto CLEANUP;
         if ((err = CheckDasmInst(dadState->startAddr, dadState, dasmSym,
               &instLen,&instValid)) != GOOD)
            return err;
      }
      while (!instValid);
   }
   /*
   **  return the number of instruction that we actually backed up
   **  GetRangeInstCount eats the range desc so dup it.
   */
   if ((err = AdrDuplicateAddress(dadState->startAddr,&rangeDesc)) != GOOD)
      return err;
   if ((err = DadDasmGetRangeInstCount(id,rangeDesc,backedInst,
         &lineCount)) != GOOD)
      goto CLEANUP;

CLEANUP:
   return err;
}

/*************************************************************************
**
**  GetBlockOfInstructions
**
**  Gets a block of numInst at the startAddr for id.  Modifies
**  the startAddr.  Does not backup!!
**
*************************************************************************/
U16 GetBlockOfInstructions(DESCRIPTOR id, U16 numInst, LPSTR **instArray) {
   DAD_STATE FAR  *dadState;
   LPSTR *instructions;
   BOOLEAN  abortFromEsc;
   U16 numDasmInst = 0;

   /* Clear ESC Key */
   TskCheckAbort(&abortFromEsc);
   if ((dadState = (DAD_STATE FAR *)id) == NULL)
      return numDasmInst;
   if ((instructions=(LPSTR *)TMalloc(numInst*sizeof(LPSTR)))==NULL) {
      return numDasmInst;
   }
   for (numDasmInst=0; numDasmInst<numInst; numDasmInst++) {
      if ((TskCheckAbort(&abortFromEsc) != GOOD) || (abortFromEsc))
         break;
      if (GetDasmInst(dadState, 1, &instructions[numDasmInst], dasmSym)) 
         break;
   }
   *instArray = instructions;
   return(numDasmInst);
}

/*************************************************************************
**
** DadDasmInst
**
*************************************************************************/
RETCODE EXPORT DadDasmInst(DESCRIPTOR id, U16 noInst, LPSTR *buffer,
                           U16 FAR *bufLen) {
   DAD_STATE FAR *dasmState;
   RETCODE err=GOOD;
   BOOLEAN abortFromEsc;

#ifdef DEBUG_MEM_LEAKS
   HeapDump(&memAlloced,&memFreed);
#endif
   /* Clear ESC Key */
   TskCheckAbort(&abortFromEsc);
   *bufLen = 0;
   if ((dasmState = (DAD_STATE FAR *)id) == NULL)
      return ER_DAD_INVALID_ID_DESCRIPTOR;
   /*
   ** get the start address sync'd up to an instruction boundary
   ** Nope lets not do that.  Invalid inst are just dumped out as
   ** DC.W insts.
   **
   ** CheckDasmInst(dasmState,dasmSym,&instLen,&instValid);
   ** if (!instValid) {
   **    * if no good instruction found at starting address - try backing up *
   **    while (!backedInst) {
   **       if ((err = BackUpInstructions(id, 1, &backedInst)) != GOOD)
   **          return err;
   **    }
   **
   */
   if ((err = GetDasmInst(dasmState, noInst, buffer, dasmSym)) != GOOD)
      return(err);
   *bufLen = (U16)lstrlen(*buffer);
   return(err);
}

/*************************************************************************
**
** DadDasmInstByLine
**
**  The starting address for this dasmState must be instruction aligned!!
**
*************************************************************************/
RETCODE EXPORT DadDasmInstByLine(DESCRIPTOR id, U16 lines, LPSTR *buffer,
                                 U16 FAR *bufLen) {
   DAD_STATE FAR *dasmState;
   U16 noLines;
   LPSTR instBuf;
   LPSTR bufPtr;
   RETCODE err=GOOD;
   U32 cpuMaxInputAddress, offset;
   BOOLEAN  abortFromEsc;

   /* Clear ESC Key */
   TskCheckAbort(&abortFromEsc);
   *bufLen = 0;
   if ((dasmState = (DAD_STATE FAR *)id) == NULL)
      return ER_DAD_INVALID_ID_DESCRIPTOR;
   /*
      ** get the start address sync'd up to an instruction boundary
   */
   if ((err = AdrGetAddrOffset(dasmState->startAddr,&offset)) != GOOD)
      return err;
   if ((err = AdrGetMaxInputAddrOffset(dasmState->startAddr,
         &cpuMaxInputAddress)) != GOOD)
      return err;
   if (offset > cpuMaxInputAddress)
      return ER_DAD_INVALID_INST_ADDR;
   if ((*buffer = TMalloc(lines*MAX_LINE_LENGTH)) == NULL )
      return(ER_OUT_OF_MEMORY);
   bufPtr = *buffer;
   memset(*buffer, '\0',lines*MAX_LINE_LENGTH);
   while (lines > 0) {
      if (((err = TskCheckAbort(&abortFromEsc)) != GOOD) || abortFromEsc)
         return(err != GOOD ? err : ER_ABORT_FROM_ESC);

      if ((err = GetDasmInstByLine(dasmState, &noLines, &instBuf, dasmSym)) 
           != GOOD) {
         /* We are at the top of physical memory so discontinue
            disassembling memory.  */
         if (err == ER_ADR_RESULT_OVERFLOW) {
            err = GOOD;
            memmove(bufPtr,instBuf,lstrlen(instBuf));
            *bufLen = lstrlen(*buffer);
            TFree(instBuf);
            return err;
         }
      }
      memmove(bufPtr,instBuf,lstrlen(instBuf));
      if ((err = TFree(instBuf)) != GOOD) return err;

      if (lines > noLines) {
         lines -= noLines;
         lstrcat(bufPtr,(LPSTR)endOfLine);
         bufPtr += lstrlen(bufPtr);
      }
      else {
         lines = 0;
      }
   }
   *bufLen = lstrlen(*buffer);
   return(GOOD);
}

/*************************************************************************
**
** DadDasmRangeOfInst
**
** Description:
**   Uses the address range provided as the address to start disassembling
**   instructions at.  Uses the range length to stop dissassembling.
**   This means the dissassembly returned is range inclusive but does
**   not limit the last instruction to fit within the range.  In other
**   words if the last instruction includes the end range but also
**   exceeds the end range the instruction is still returned as the
**   whole instruction thereby exceeding the end range.  
**   NOTE:  THis interface is intended for use where the range of the
**   dissassembled instrutions compromises a line of source and the
**   instruction range is therefore validated.
**   The state table address is updated to point to the
**   next valid instruction available to disassemble.
**   NOTES: addrRange will be destroyed (even if an error has occurred)
**
***************************************************************************/
RETCODE EXPORT DadDasmRangeOfInst(DESCRIPTOR id, DESCRIPTOR addrRange,
                                  LPSTR *buffer, U16 FAR *bufLen) {
   U32 cpuMaxInputAddress;
   U32 rangeLen, instLen;
   DAD_STATE FAR *dasmState;
   RETCODE err=GOOD;
   U32 offset, prevOffset;
   LPSTR bufPtr;
   BOOLEAN abortFromEsc;

   /* Clear ESC Key */
   TskCheckAbort(&abortFromEsc);

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

   if ((bufPtr = (LPSTR)TMalloc(MAX_INST_PER_BUFFER*MAX_LINE_LENGTH))
       == NULL) {
      err = ER_OUT_OF_MEMORY;
      goto CLEANUP;
   }
   *buffer = bufPtr;
   memset(*buffer, '\0',MAX_INST_PER_BUFFER*MAX_LINE_LENGTH);
   if ((err = AdrGetMaxInputAddrOffset(addrRange, &cpuMaxInputAddress))
         != GOOD)
      goto CLEANUP;
   if ((err = AdrGetAddrOffset(addrRange, &offset)) != GOOD)
      goto CLEANUP;
   if (offset > cpuMaxInputAddress) {
      err = ER_DAD_INVALID_INST_ADDR;
      goto CLEANUP;
   }
   prevOffset = offset;
   if ((err = AdrGetEndAddrOffset(addrRange, &offset)) != GOOD)
      goto CLEANUP;
   if (offset > cpuMaxInputAddress) {
      err = ER_DAD_INVALID_INST_ADDR;
      goto CLEANUP;
   }
   /* Set start address to disassemble */
   if ((err = DadSetDasmAddr(id,addrRange)) != GOOD) 
      goto CLEANUP;
   if ((err = AdrGetAddrRangeLength(addrRange,&rangeLen)) != GOOD)
      goto CLEANUP;
   if (rangeLen == 0)  /* maximum range use cpuMaxInput */
      rangeLen = cpuMaxInputAddress;

   while (rangeLen) {
      if (((err = TskCheckAbort(&abortFromEsc)) != GOOD) || abortFromEsc) {
         err = (err != GOOD ? err : ER_ABORT_FROM_ESC);
         goto CLEANUP;
      }
      if ((err = GetDasmInstNoAlloc(dasmState,bufPtr,
         dasmSym)) != GOOD) goto CLEANUP;
      if ((err = AdrGetAddrOffset(dasmState->startAddr, &offset)) != GOOD)
         goto CLEANUP;
      instLen = offset - prevOffset;
      prevOffset = offset;
      if (instLen > rangeLen || instLen == 0)
         rangeLen = 0;
      else
         rangeLen -= instLen;
      if (rangeLen) {
         /* Caller expects crlf on all but the last line of output */
         lstrcat(bufPtr, (LPSTR)endOfLine);
         bufPtr += lstrlen(bufPtr);
         /* is there room for another instruction ? */
         if (*buffer + MAX_INST_PER_BUFFER*MAX_LINE_LENGTH - bufPtr
               < MAX_LINE_LENGTH)
            break;  
      }
   }
   *bufLen = lstrlen(*buffer);
   
CLEANUP:
   AdrDestroyAddress(addrRange);
   return(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.
**   NOTE: EATS ADDRRANGE descriptors.
**
***************************************************************************/
RETCODE EXPORT DadDasmGetRangeInstCount(DESCRIPTOR id,
                                        DESCRIPTOR addrRange,
                                        U16 FAR *instCount,
                                        U16 FAR *lineCount) {
   U32 cpuMaxInputAddress, offset,rangeLen;
   BOOLEAN instValid;
   DAD_STATE FAR *dasmState;
   DESCRIPTOR startAddr;
   U8 instLen;
   RETCODE err=GOOD;
   BOOLEAN abortFromEsc;
#ifdef DEBUG_MEM_LEAKS
   HeapDump(&memAlloced,&memFreed);
#endif

   /* Clear ESC Key */
   TskCheckAbort(&abortFromEsc);
   *instCount = 0;
   *lineCount = 0;
   dasmState = (DAD_STATE FAR *) id;
   if (dasmState == NULL) {
      err = ER_DAD_INVALID_ID_DESCRIPTOR;
      goto CLEANUP1;
   }
   if ((err = AdrGetMaxInputAddrOffset(addrRange,&cpuMaxInputAddress)) != GOOD)
      return err;
   if ((err = AdrGetAddrOffset(addrRange,&offset)) != GOOD)
      return err;
   if (offset > cpuMaxInputAddress)
      return ER_DAD_INVALID_INST_ADDR;
   if ((err = AdrGetEndAddrOffset(addrRange,&offset)) != GOOD)
      return err;
   if (offset > cpuMaxInputAddress)
      return ER_DAD_INVALID_INST_ADDR;
   if ((err = AdrDuplicateAddress(addrRange,&startAddr)) != GOOD)
      goto CLEANUP1;
   if ((err = AdrGetAddrRangeLength(addrRange,&rangeLen)) != GOOD)
       goto CLEANUP2;
   if (rangeLen == 0)  /* maximum range use cpuMaxInput */
      rangeLen = cpuMaxInputAddress;

   while (rangeLen) {
      if (((err = TskCheckAbort(&abortFromEsc)) != GOOD) || abortFromEsc) {
         err = (err != GOOD ? err : ER_ABORT_FROM_ESC);
         break;
      }
      if ((err = CheckDasmInst(startAddr, dasmState, dasmSym, &instLen,
                               &instValid)) != GOOD)  goto CLEANUP2;
      (*instCount)++;
      if (instLen <= MAX_INST_PER_LINE)
         (*lineCount)++;
      else
         (*lineCount) += ((instLen/MAX_INST_PER_LINE) +
               ((instLen%MAX_INST_PER_LINE > 0)?1:0));
      if ((err = AdrAddToAddressOverflow(startAddr,instLen)) != GOOD)
      if (err == ER_ADR_RESULT_OVERFLOW) {
         err = GOOD;
         goto CLEANUP2;
      }
      if (instLen > rangeLen)
         rangeLen = 0;
      else
         rangeLen -= instLen;
   }
CLEANUP2:
   if (!err)
      err = AdrDestroyAddress(startAddr);
   else
      AdrDestroyAddress(startAddr);
CLEANUP1:
   if (!err)
      err = AdrDestroyAddress(addrRange);
   else
      AdrDestroyAddress(addrRange);
   return 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) {
   U32 cpuMaxInputAddress, offset;
   DAD_STATE FAR *dasmState;
   U8 instLen,instLineCount;
   S16 linesRequested;
   BOOLEAN instValid;
   BOOLEAN abortFromEsc;
   RETCODE err = GOOD;

#ifdef DEBUG_MEM_LEAKS
   HeapDump(&memAlloced,&memFreed);
#endif
   /* Clear ESC Key */
   TskCheckAbort(&abortFromEsc);
   *lineCount = 0;
   instLineCount = 0;
   linesRequested = (S16)lines;
   if ((dasmState = (DAD_STATE FAR *)id) == NULL)
      return ER_DAD_INVALID_ID_DESCRIPTOR;
   if ((err = AdrGetMaxInputAddrOffset(dasmState->startAddr,
         &cpuMaxInputAddress)) != GOOD)
      return err;
   if ((err = AdrGetAddrOffset(dasmState->startAddr,&offset)) != GOOD)
      return err;
   if (offset > cpuMaxInputAddress)
      return ER_DAD_INVALID_INST_ADDR;
   if ((err = AdrDuplicateAddress(dasmState->startAddr,nextOffset)) != GOOD)
      return err;
   while (linesRequested > 0) {
      if ((err = CheckDasmInst(*nextOffset, dasmState, dasmSym,
            &instLen, &instValid)) != GOOD)
         return err;
      if (instLen <= MAX_INST_PER_LINE)
         instLineCount = 1;
      else
         instLineCount = (instLen/MAX_INST_PER_LINE) +
               ((instLen%MAX_INST_PER_LINE > 0)?1:0);
      (*lineCount) += instLineCount;
      linesRequested -= instLineCount;
      if ((err = AdrAddToAddress(*nextOffset,instLen)) != GOOD)  {
         AdrDestroyAddress(*nextOffset);
         return err;
      }
   }
   return err;
}

/***************************************************************************
**
** DadDasmPrevInst
**
***************************************************************************/
RETCODE EXPORT DadDasmPrevInst(DESCRIPTOR id, U16 noInst, LPSTR *buffer,
                               U16 FAR *bufLen) {
   LPSTR bufPtr;
   U16 numDasmInst, i, copyStart;
   U16 backedInst,totalBackedInst;
   LPSTR *prevInst;
   BOOLEAN  abortFromEsc;
   RETCODE  err = GOOD;


#ifdef DEBUG_MEM_LEAKS
   HeapDump(&memAlloced,&memFreed);
#endif
   /* Clear ESC Key */
   TskCheckAbort(&abortFromEsc);
   *bufLen = 0;
   numDasmInst = noInst + 1;
   totalBackedInst = 0;
   while (totalBackedInst < (noInst + 1)) {
      if ((err = BackUpInstructions(id,numDasmInst,&backedInst)) != GOOD)
         return err;
      if (numDasmInst > backedInst)
         numDasmInst -= backedInst;
      totalBackedInst += backedInst;
   }
   /*
   **  we want to backup N instruction from the current instruction
   **  therefor we do not count the current instruction and we do not
   **  get it as part of the block.
   */
   numDasmInst = GetBlockOfInstructions(id,totalBackedInst-1,&prevInst);
   if( !numDasmInst ) {
      err= ER_DAD_AT_TOP_OF_MEMORY;
      goto CLEANUP;
   }
   if ((err = TskCheckAbort(&abortFromEsc)) != GOOD)
      goto CLEANUP;
  
   if (abortFromEsc!=0) {
      err = ER_ABORT_FROM_ESC;
      goto CLEANUP;
   }

   if( noInst > numDasmInst )
      copyStart = 0;
   else
      copyStart = numDasmInst-noInst;
   if((*buffer=TMalloc(MAX_LINE_LENGTH * noInst))==NULL) {
      err=ER_OUT_OF_MEMORY;
      goto CLEANUP;
   }
   bufPtr = *buffer;
   memset(*buffer,'\0',noInst*MAX_LINE_LENGTH);
   for( i = copyStart; i < numDasmInst; i++ ) {
      lstrcpy(bufPtr,prevInst[i]);
      bufPtr += lstrlen(bufPtr);
      if (i < numDasmInst-1) {
         lstrcpy(bufPtr,(LPSTR)endOfLine);
         bufPtr += lstrlen(bufPtr);
      }
      else {
         lstrcpy(bufPtr,(LPSTR)endOfBuffer);
         bufPtr += lstrlen(bufPtr);
      }
   }
   *bufLen = lstrlen(*buffer);

CLEANUP:
   for( i = 0; i < numDasmInst; i++ ) TFree(prevInst[i]);
   TFree((LPSTR)prevInst);
   return(err);
}


/**************************************************************************
**
**  DadAsmOpen
**
***************************************************************************/
RETCODE EXPORT DadAsmOpen(DESCRIPTOR FAR *id, DESCRIPTOR startAddr) {
   DAD_STATE FAR *asmTable;
   RETCODE err;

#ifdef DEBUG_MEM_LEAKS
   HeapDump(&memAlloced,&memFreed);
#endif
   if ((asmTable = (DAD_STATE FAR *)TMalloc(sizeof(DAD_STATE))) == NULL)
      return ER_OUT_OF_MEMORY;
   memset(asmTable,'\0',sizeof(DAD_STATE));
   if ((err = AdrGetPmode(&(asmTable->mode),NULL)) != GOOD)
      return err;
   /* base size on current pmode */
   asmTable->addrOpSize = ADDR_USE_AUTO;
   *id = (DESCRIPTOR)asmTable;
   return(AdrDuplicateAddress(startAddr,&asmTable->startAddr));
}

/**************************************************************************
**
**  DadAsmClose
**
****************************************************************************/
RETCODE EXPORT DadAsmClose(DESCRIPTOR id) {
   DAD_STATE FAR  *dadState;
   RETCODE err;

#ifdef DEBUG_MEM_LEAKS
   HeapDump(&memAlloced,&memFreed);
#endif
   if (id == NULL)
      return ER_DAD_INVALID_ID_DESCRIPTOR;
   dadState = (DAD_STATE FAR *)id;
   if ((err = AdrDestroyAddress(dadState->startAddr)) != GOOD)
      return err;
   if ((err = TFree((LPSTR)dadState)) != GOOD)
      return err;
   return(GOOD);
}

/**************************************************************************
**
**  DadSetAsmAddr
**
**************************************************************************/
RETCODE EXPORT DadSetAsmAddr(DESCRIPTOR id, DESCRIPTOR address) {
   DAD_STATE FAR *asmState;
   RETCODE err;
   U32 cpuMaxOutputAddress, offset;
   ADDR_SEGSEL_TYPE segType;
   U16 segValue;
   ADDR_TYPE addrType;

#ifdef DEBUG_MEM_LEAKS
   HeapDump(&memAlloced,&memFreed);
#endif
   if ((asmState = (DAD_STATE FAR *)id) == NULL)
      return ER_DAD_INVALID_ID_DESCRIPTOR;
   if ((err = AdrGetMaxOutputAddrOffset(address,&cpuMaxOutputAddress))
         != GOOD)
      return err;
   if ((err = AdrGetAddrOffset(address,&offset)) != GOOD)
      return err;
   if (offset >= cpuMaxOutputAddress)
      return ER_DAD_INVALID_INST_ADDR;
   if ((err = AdrGetAddrType(address,&addrType)) != GOOD)
      return err;
   if (addrType == ADDR_VIRTUAL) {
      if ((err = AdrGetAddrSegmentSelector(address,&segType,&segValue)) != GOOD)
         return err;
      if (segType == ADDR_USE_DEF_DS) {
         /* explicit segment value override is OK but otherwise 
         ** default to the current dasm segment          
         */
         if ((err = AdrGetAddrSegmentSelector(asmState->startAddr,&segType,
               &segValue)) != GOOD)
            return err;
         if ((err = AdrCopyAddress(address, asmState->startAddr)) != GOOD)
            return err;
         return (AdrSetAddrSegmentSelector(asmState->startAddr,
               segType,&segValue));
      }
   }
   return(AdrCopyAddress(address, asmState->startAddr));
}

/**************************************************************************
**
**  DadGetAsmAddr
**
***************************************************************************/
RETCODE EXPORT DadGetAsmAddr(DESCRIPTOR id, DESCRIPTOR FAR *address) {

#ifdef DEBUG_MEM_LEAKS
   HeapDump(&memAlloced,&memFreed);
#endif
   if (id == NULL)
      return ER_DAD_INVALID_ID_DESCRIPTOR;
   return(AdrDuplicateAddress(((DAD_STATE FAR *)id)->startAddr,address));
}

/************************************************************************
**
** DadAsmShowInfo
**
**************************************************************************/
RETCODE DadAsmShowInfo(DESCRIPTOR id) {
   CHAR resultBuff[128];
   CHAR strBuf[ADDR_BUFF_SZ];
   DAD_STATE FAR *asmTable;
   RETCODE err;
   ADDR_SPACE asmSpace;

   if ((asmTable = (DAD_STATE FAR *)id) == NULL)
      return ER_DAD_INVALID_ID_DESCRIPTOR;
   if ((err=AdrConvAddressToText(asmTable->startAddr, strBuf))
      != GOOD) return(err);
   wsprintf(resultBuff,"asm address: %s", strBuf);
/**186**
   switch (asmTable->addrOpSize) {
      case ADDR_USE_AUTO:
         strcat(resultBuff, "auto,  space: ");
         break;
      case ADDR_USE_16:
         strcat(resultBuff, "16-bits,  space: ");
         break;
      case ADDR_USE_32:
         strcat(resultBuff, "32-bits,  space: ");
         break;
      default:
         strcat(resultBuff, "???,  space: ");
         break;
   }
   if ((err=AdrGetAddrSpace(asmTable->startAddr, &asmSpace)) != GOOD)
      return(err);
   switch (asmSpace) {
      case SPACE_USER:
         strcat(resultBuff, "user");
         break;
      case SPACE_SMM:
         strcat(resultBuff, "smm");
         break;
      case SPACE_IO:
         strcat(resultBuff, "io");
         break;
      default:
         strcat(resultBuff, "???");
         break;
   }
**186**/   
   return(SendCliMessage(cliServerHandle, resultBuff));
}

/************************************************************************
**
** DadAsm
**
**************************************************************************/
RETCODE EXPORT DadAsm(DESCRIPTOR id, LPSTR inInst, LPSTR *outInst,
                      LPWORD numBytes) {

   LPU8 memBuf;
   DAD_STATE FAR *asmTable;
   RETCODE err;
   U8 OpCode[MAX_CODE_LENGTH];

#ifdef DEBUG_MEM_LEAKS
   HeapDump(&memAlloced,&memFreed);
#endif
   if ((asmTable = (DAD_STATE FAR *)id) == NULL)
      return ER_DAD_INVALID_ID_DESCRIPTOR;

   *numBytes = 0;
   if ((err = AsmInst(id, inInst, outInst, numBytes, OpCode)) != GOOD) 
      return err;
#ifdef DEBUG_MEM_LEAKS
   HeapDump(&memAlloced,&memFreed);
#endif

   /*
   **  don't forget we have to TMalloc the buffer we are sending
   **  to the memory server.
   */
   if ((memBuf = (LPU8)TMalloc(*numBytes)) == NULL)
      return ER_OUT_OF_MEMORY;
   memmove(memBuf, OpCode, *numBytes);

   if ((err = MemWriteSized(asmTable->startAddr, *numBytes, memBuf,
      BYTE_SIZE)) != GOOD) return err;

   if ((err = AdrAddToAddress(asmTable->startAddr, *numBytes)) != GOOD)
      return err;

#ifdef DEBUG_MEM_LEAKS
   HeapDump(&memAlloced,&memFreed);
#endif
   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 = lstrlen(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);
}

/*********************************************************************
**
**   DadInit :
**
**************************************************************************/
RETCODE  DadInit(VOID)  {
   DESCRIPTOR startAddr;
   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;
      /* 09/27/94 Marilyn:
      **  Removed this event for updating.  Only one halt event
      **  should be registered on.  This event was used to
      **  update the pc for the shell.  The MEM_HALTED event
      **  can be used instead, reducing the callback overhead.
      **  if ((err = EnlRegister(EVENT_BKPT_HALTED,
      **        (EVCALLBACK) myCallback,&descEvt)) != GOOD)
      **     return err;
      */
  
      /* 09/27/94 Marilyn:
      **  Register on EVENT_CPU_PC_EDIT so that the shell session
      **  for dasm always stays in synch with the PC.
      */
      if ((err = EnlRegister(EVENT_CPU_PC_EDIT,
            (EVCALLBACK) myCallback,&descEvt)) != GOOD)
         return err;
      if ((err = ProcReturnCpu(&cpuType)) != GOOD)
         return err;
      
      if ((err=AdrCreateAddress(&startAddr)) != GOOD)
         return(err);
      if ((err=AdrSetAddrSegmentSelector(startAddr,ADDR_USE_CS,NULL)) != GOOD)
         return err;
      if ((err=AdrSetAddrOffset(startAddr,0L)) != GOOD)
         return err;
      if ((err=DadDasmOpen(&cliDasmId,startAddr)) != GOOD)
         return err;
      if ((err=AdrCremateAddress(&startAddr)) != GOOD)
         return err;
      initDadAlready = TRUE;
   }
   return GOOD;
}

/*************************************************************************
**
**  DadCallback :
**
**  Return function for event callback.
**
****************************************************************************/
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
         */
         DAD_STATE FAR *dasmState = sessionList;
         while (dasmState != NULL) {
            if ((err = FreeDasmCache(dasmState)) != GOOD)
               return err;
            dasmState = (DAD_STATE FAR *)dasmState->next;
         }
         // When execution halts we want to set CLI dasm address to PC
         if (eventNum == EVENT_MEM_HALTED) {
            if ((err = UpdateCLISessionDasmAddr()) != GOOD)
               return err;
            if ((err = EnlEventNotify(EVENT_DASM_HALTED)) != GOOD)
               return err;
         }
         break;              
      }  /* end case */
      case EVENT_CPU_PC_EDIT:
         if ((err = UpdateCLISessionDasmAddr()) != GOOD)
            return err;
         break;
      default :
         return GOOD;
   }
   return GOOD;
}

/*******************************************************************
**
**  UpdateCLISessionDasmAddr
**
********************************************************************/
RETCODE PRIVATE UpdateCLISessionDasmAddr(VOID) {

   DESCRIPTOR pc;
   U32 offset, cpuMaxInputAddress;
   RETCODE err;
 
   if( cliDasmId ) {
      if((err=CpuGetPC(&pc))!=GOOD) return(err);
      if ((err = AdrGetMaxInputAddrOffset(pc, &cpuMaxInputAddress))
            != GOOD)
         return err;
      if ((err = AdrGetAddrOffset(pc, &offset)) != GOOD)
         return err;
      if (offset < cpuMaxInputAddress) {
         // If pc is valid then destroy old desc and copy pc desc.
         if((err=AdrDestroyAddress(
                 ((DAD_STATE FAR  *)cliDasmId)->startAddr))!= GOOD)
            return(err);
         ((DAD_STATE FAR  *)cliDasmId)->startAddr = pc;
      }
      else { // if bad address destroy pc desc
         if((err=AdrDestroyAddress(pc))!= GOOD) return(err);
         return(ER_DAD_BOGUS_ADDRESS);
      }
   }
   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=AdrSetAddrSegmentSelector(startAddr,ADDR_USE_CS,
         NULL)) != 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;
   RETCODE err;
   U32 addrOffset=0;
   ADDR_SPACE asmSpace;
   U16 args;

#ifdef DEBUG_MEM_LEAKS
   HeapDump(&memAlloced,&memFreed);
#endif
   if (argc > 4)
      return (ER_CLI_SYNTAX);
   if (!cliAsmId) {
      AsmCliOpen(addrOffset);
   }
   if (argc == 1)
      return(DadAsmShowInfo(cliAsmId));

   /* if there are no args set the default op size to AUTO */
   if ((err = DadSetDasmOpAddrSize(cliAsmId, ADDR_USE_AUTO)) != GOOD)
      return err;
   
   args = 1;
   
/********* 186 *************
   if ((U16)argc > args) {
      if (strncmpi(&cmdString[(U16)argv[args]], "auto",
                   strlen(&cmdString[(U16)argv[args]])) == 0) {
         args++;
         if ((err = DadSetDasmOpAddrSize(cliAsmId, ADDR_USE_AUTO)) != GOOD)
            return err;
      }
      else if (strncmpi(&cmdString[(U16)argv[args]], "use16",
                        strlen(&cmdString[(U16)argv[args]])) == 0) {
         args++;
         if ((err = DadSetDasmOpAddrSize(cliAsmId, ADDR_USE_16)) != GOOD)
            return err;
      }
      else if (strncmpi(&cmdString[(U16)argv[args]], "use32",
                           strlen(&cmdString[(U16)argv[args]])) == 0) {
         args++;
         if ((err = DadSetDasmOpAddrSize(cliAsmId, ADDR_USE_32)) != GOOD)
            return err;
      }
      else {
         if ((err = DadSetDasmOpAddrSize(cliAsmId, ADDR_USE_AUTO)) != GOOD)
            return err;
      }
   }
********** 186 *************/

   if( (U16)argc > args ) {
      if ((err = AdrCreateAddressFromText(&cmdString[(U16)argv[args]],
            NULL, &address)) != GOOD)
         return err;
      args++;
/********** 186 *************
      asmSpace = SPACE_USER;
      if ((U16)argc > args) {
         if (strncmpi(&cmdString[(U16)argv[args]], "user",
                      strlen(&cmdString[(U16)argv[args]])) == 0) {
            asmSpace = SPACE_USER;
         }
         else if (strncmpi(&cmdString[(U16)argv[args]], "smm",
                           strlen(&cmdString[(U16)argv[args]])) == 0) {
            asmSpace = SPACE_SMM;
         }
         else if (strncmpi(&cmdString[(U16)argv[args]], "io",
                           strlen(&cmdString[(U16)argv[args]])) == 0) {
            asmSpace = SPACE_IO;
         }
         else {
            AdrDestroyAddress(address);
            return(ER_CLI_SYNTAX);
         }
         if ((err=AdrSetAddrSpace(address, asmSpace)) != GOOD) {
            AdrDestroyAddress(address);
            return(err);
         }
         args++;
      }
********** 186 *************/

      if (address == NULL || (U16)argc > args)
         return(ER_CLI_SYNTAX);
      else
         err = DadSetAsmAddr(cliAsmId,address);
   }

   return(DadAsmShowInfo(cliAsmId));
}

/***************************************************************************
**
**  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;
   U8        buffer[25], i;

#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;
   if (dummyAsm) {
      if ((err=DummyGetAsmLine(dummyInput)) != GOOD)
         return(err);
   }
   else {
      dummyInput[0] = '\0';
      for (i=1; i<argc; i++) {
         strcat(dummyInput,&cmdString[(U16)argv[i]]);
         strcat(dummyInput, " ");
      }
   }
   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);
   lstrcat(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=AdrSetAddrSegmentSelector(startAddr,ADDR_USE_CS,NULL)) != 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];
   CHAR addrBuf[ADDR_BUFF_SZ];
   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 = AdrCreateAddressFromText(&cmdString[(U16)argv[1]], NULL,
            &address)) != GOOD)
         return err;
   }
   if( !cliDasmId ) {
      if((err=DasmCliOpen(addrOffset))!=GOOD) return(err);
   }
   if( argc == 2 ) {
      if ((err = DadSetDasmAddr(cliDasmId,address)) != GOOD) {
         AdrDestroyAddress(address);
         return(err);
      }
   }
   else {
      if ((err=DadGetDasmAddr(cliDasmId,&address)) != GOOD )
         return(err);
   }
   if ((err = AdrConvAddressToTextWithParams(address, FALSE, TRUE,
         addrBuf)) != GOOD)  {
      AdrDestroyAddress(address);
      return(err);
   }
   wsprintf(resultBuff,"dasm address: ");
   strcat(resultBuff,addrBuf);
   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) {
   U16 args;
   RETCODE err = GOOD;
   U32 offset=0;
   DESCRIPTOR address=NULL;
   ADDR_SPACE addrSpace;

#ifdef DEBUG_MEM_LEAKS
   HeapDump(&memAlloced,&memFreed);
#endif
   if (argc > 5)
      return ER_CLI_SYNTAX;
   if( !cliDasmId ) {
      if((err=DasmCliOpen(offset))!=GOOD) return(err);
   }
   /* if there are no args set the default op size to AUTO */
   if ((err = DadSetDasmOpAddrSize(cliDasmId,ADDR_USE_AUTO)) != GOOD)
      return err;
   *numInst = 10;
   args = 1;
   
/********** 186 **********
   if ((U16)argc > args) {
      if (strncmpi(&cmdString[(U16)argv[args]], "auto",
                   strlen(&cmdString[(U16)argv[args]])) == 0) {
         args++;
         if ((err = DadSetDasmOpAddrSize(cliDasmId,ADDR_USE_AUTO)) != GOOD)
            return err;
      }
      else if (strncmpi(&cmdString[(U16)argv[args]], "use16",
                        strlen(&cmdString[(U16)argv[args]])) == 0) {
         args++;
         if ((err = DadSetDasmOpAddrSize(cliDasmId,ADDR_USE_16)) != GOOD)
            return err;
      }
      else if (strncmpi(&cmdString[(U16)argv[args]], "use32",
                           strlen(&cmdString[(U16)argv[args]])) == 0) {
         args++;
         if ((err = DadSetDasmOpAddrSize(cliDasmId,ADDR_USE_32)) != GOOD)
            return err;
      }
   }
********** 186 **********/

   if( (U16)argc > args ) {
      if ((err = AdrCreateAddressFromText(&cmdString[(U16)argv[args]],
            NULL, &address)) != GOOD)
         return err;
      args++;
      if ( (U16)argc > args ) {
         ADDR_SEGSEL_TYPE segType;
         ADDR_TYPE addrType;
         U16 segValue;
         if ((err = AdrCreateAddressFromText(&cmdString[(U16)argv[args]],
               NULL, endAddress)) == GOOD) { // should be destroyed by caller
            if ((err = AdrGetAddrType(*endAddress,&addrType)) != GOOD)
               return err;
            if (addrType == ADDR_VIRTUAL) {
               if ((err = AdrGetAddrSegmentSelector(*endAddress,&segType,
                     &segValue)) != GOOD)
                  return err;
               if (segType == ADDR_USE_DEF_DS) {
                  /* explicit segment value override is OK but otherwise
                  ** default to the current dasm segment
                  */
                  if ((err = AdrGetAddrSegmentSelector(address,&segType,
                        &segValue)) != GOOD)
                     return err;
                  if ((err = AdrSetAddrSegmentSelector(*endAddress,segType,
                        &segValue)) != GOOD)
                     return err;
               }
            }
            *numInst = 0;
            args++;
         }
      }
/********** 186 **********
      if ((U16)argc > args) {
         addrSpace = SPACE_USER;
         if (strncmpi(&cmdString[(U16)argv[args]], "user",
                      strlen(&cmdString[(U16)argv[args]])) == 0) {
            addrSpace = SPACE_USER;
         }
         else if (strncmpi(&cmdString[(U16)argv[args]], "smm",
                           strlen(&cmdString[(U16)argv[args]])) == 0) {
            addrSpace = SPACE_SMM;
         }
         else if (strncmpi(&cmdString[(U16)argv[args]], "io",
                              strlen(&cmdString[(U16)argv[args]])) == 0) {
            addrSpace = SPACE_IO;
         }
         else {
            err = ER_CLI_SYNTAX;
            goto CLEANUP;
         }
         if ((err = AdrSetAddrSpace(address, addrSpace)) != GOOD)
            goto CLEANUP;
         if (*numInst == 0) {
            if ((err = AdrSetAddrSpace(*endAddress, addrSpace)) != GOOD)
               goto CLEANUP;
         }
         args++;
      }
********** 186 **********/
      
      if( (U16)argc > args ) {
         err = ER_CLI_SYNTAX;
         goto CLEANUP;
      }

      if (address != NULL)
         err = DadSetDasmAddr(cliDasmId,address);
   }

CLEANUP:
   if (!err) {
      if (address != NULL)
         err = AdrDestroyAddress(address);
   }
   else {
      if (address != NULL)
         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;
   BOOLEAN  abortFromEsc;


#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;
      }
   }
   else {
      DAD_STATE FAR  *state;
      ADDR_COMPARE addrResults;
      state = (DAD_STATE FAR *)cliDasmId;
      while( 1 ) {
         if((err = AdrCompareAddresses(state->startAddr,endAddr,
               &addrResults)) != GOOD)
            return err;
         if(addrResults==FIRST_ADDR_GREATER) break;
         if ((err=DadDasmInst(cliDasmId, 1, &resultBuff, &buflen)) != GOOD) {
            if (resultBuff != NULL) TFree(resultBuff);
            goto CLEANUP;
         }
         err= SendCliMessage(cliServerHandle,resultBuff);
         if (!err)
            err = TFree(resultBuff);
         else
            TFree(resultBuff);
         if( err ) break;

         if ((err = TskCheckAbort(&abortFromEsc)) != GOOD)
            break;
         if (abortFromEsc!=0) {
            err = ER_ABORT_FROM_ESC;
            break;
         }
      }
CLEANUP:
      if (!err)
         err = AdrDestroyAddress(endAddr);
      else
         AdrDestroyAddress(endAddr);
   }
   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);
}

/**************************************************************************
**
**  DadDasmCliSet
**
**************************************************************************/
RETCODE EXPORT DadDasmCliSet(LPSTR cmdString, U32 argc, U32 argv[]) {
   BOOLEAN enable;
   RETCODE err;
   S8   enbSym;
   CHAR strBuf[80];

   if( !cliDasmId ) {
      if((err=DasmCliOpen(0L))!=GOOD) return(err);
   }
   if( argc == 2 ) {
      if( (enbSym=FindString(2,SymSet,&cmdString[(U16)argv[1]])) < 0 )
         return(ER_CLI_SYNTAX);
      enable = enbSym;
      if((err=DadSetDasmSymbol(enable))!=GOOD) return(err);
   }
   if((err=DadGetDasmSymbol(&enable))!=GOOD) return(err);
   enable = enable?1:0;  /* must be one to access SymSet table */
   sprintf((LPSTR)strBuf, "Symbolic disassembler output is %s",
           SymSet[enable]);
   return(SendCliMessage(cliServerHandle, (LPSTR)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));
}

/*********************** nongeneric part of DASM **********************/

/****************************************************************************
**
**    IsCallOrRTS
**
****************************************************************************/
RETCODE IsCallOrRTS(DAD_STATE FAR *dasmState, DESCRIPTOR calledAddr,
                    CALL_TYPE *type) {
   CHAR  OpByte[MAX_CODE_LENGTH];
   RETCODE err = GOOD;
   DASM_INFO dasmInfo;

   if((err=GetIntData(dasmState, dasmState->startAddr, 
         OpByte))!=GOOD) return(err);
   if((err=AdrDuplicateAddress(calledAddr, &dasmInfo.target))!= GOOD)
      return(err);
   if((err=DisassembleOpcode(OpByte,dasmState->startAddr,
                             dasmState->mode, dasmState->addrOpSize,
                             FALSE,&dasmInfo)) !=GOOD) {
      AdrDestroyAddress(dasmInfo.target);
      return(err);
   }
   if ((err=AdrAddToAddress(dasmState->startAddr,dasmInfo.bytesUsed)) != GOOD) {
      AdrDestroyAddress(dasmInfo.target);
      return err;
   }
   if ((err=AdrCopyAddress(dasmState->startAddr,calledAddr)) != GOOD) {
      AdrDestroyAddress(dasmInfo.target);
      return(err);
   }

   if (dasmInfo.callType) {
      *type = INST_CALL;
      err = AdrCopyAddress(dasmInfo.target, calledAddr);
   }
   else if (dasmInfo.retnType)
      *type = INST_RETURN;
   else
      *type = INST_NONE;

   AdrDestroyAddress(dasmInfo.target);
   return(err);
}

/*****************************************************************************
**
**    IsTransfer
**
*****************************************************************************/
RETCODE IsTransfer(DAD_STATE FAR *dasmState,
                   DESCRIPTOR transferAddr,
                   TRANSFER_TYPE *transfer) {
   CHAR OpByte[MAX_CODE_LENGTH];
   RETCODE err = GOOD;
   DASM_INFO dasmInfo;

   memset(transfer,'\0',sizeof(TRANSFER_TYPE));
   if((err=GetIntData(dasmState, dasmState->startAddr, 
         OpByte))!=GOOD) return(err);
   if((err=AdrDuplicateAddress(transferAddr, &(dasmInfo.target)))!= GOOD)
      return(err);
   if((err=DisassembleOpcode(OpByte,dasmState->startAddr,
                             dasmState->mode, dasmState->addrOpSize,
                             FALSE,&dasmInfo)) != GOOD) {
      AdrDestroyAddress(dasmInfo.target);
      return(err);
   }
   if ((err = AdrAddToAddress(dasmState->startAddr,
         dasmInfo.bytesUsed)) != GOOD) {
      AdrDestroyAddress(dasmInfo.target);
      return err;
   }
   transfer->transfer = dasmInfo.transfer;
   transfer->call = dasmInfo.callType;
   if ((dasmInfo.transfer) && (dasmInfo.knownAddr)) {
      transfer->destKnown = TRUE;
      err = AdrCopyAddress(dasmInfo.target, transferAddr);
   }
   AdrDestroyAddress(dasmInfo.target);
   return(err);
}

/*****************************************************************************
**
**    DasmInst
**
****************************************************************************/
RETCODE DasmInst(LPSTR OpByte, DAD_STATE FAR *dasmState,
                 CHAR *inst, U8 *InstLen, BOOLEAN DadSymbol) {
   DASM_INFO Info;
   RETCODE   err, err1;
   U8 i;
   CHAR text[MAX_DASM_TEXT];
   CHAR *strOp;

   *InstLen = 0;
   // output the address
   if ((err = AdrConvAddressToTextWithParams(dasmState->startAddr,
          FALSE, TRUE, text)) != GOOD) return (err);
   strcpy(inst,text);
   /* pad out the address to take up 14 chars */
   for (i=strlen(inst); i <= 14; i++)
      strcat(inst," ");
   inst += strlen(inst);
   if ((err = AdrDuplicateAddress(dasmState->startAddr,&Info.target)) != GOOD)
      return(err);
   err = Ldasm(OpByte,dasmState->mode,dasmState->addrOpSize,
         dasmState->startAddr,DadSymbol,text,&Info);

   if ((err1 = AdrDestroyAddress(Info.target)) != GOOD)
      return(err1);

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

   // format the raw data
   strOp = (CHAR *)OpByte;
   for (i=0; i<Info.bytesUsed && i<6; i++) {
      inst += sprintf(inst, "%02X", *strOp);
      strOp++;
   }
   if (Info.bytesUsed < 6)
      for (i=Info.bytesUsed; i<6; i++)
         inst += sprintf(inst, "  ");

   inst += sprintf(inst, " ");

   // output the result
   if ((err = LdasmText(text, inst)) != GOOD)
      return(err);
   if (Info.bytesUsed > 6) { 
      inst += strlen(inst);
      sprintf(inst,"%s%14s ", endOfLine, " ");
      inst += strlen(inst);
   }
   for (i=6; i<Info.bytesUsed; i++) {
      inst += sprintf(inst, "%02X", *strOp);
      strOp++;
   }

   *InstLen = Info.bytesUsed;
   return(GOOD);
}


/*****************************************************************************
**
**    GetDasmInst
**
*****************************************************************************/
RETCODE GetDasmInst(DAD_STATE FAR* dasmState, U16 numInst, LPSTR *buffer,
                    BOOLEAN DasmSymbol) {

   CHAR OpByte[MAX_CODE_LENGTH];
   U8  InstLen;
   LOOP_VAR instCnt;
   RETCODE err;
   BOOLEAN  abortFromEsc;
   LPSTR tmpPtr;

   /* allocate the memory for the data to be sent back */
   /* max size buffer is 64k and max line length is 256, which
   ** means the maximum number of instructions per buffer is 250
   */
   if (numInst > MAX_INST_PER_BUFFER)
      numInst = MAX_INST_PER_BUFFER;
   if( (tmpPtr= TMalloc(numInst*MAX_LINE_LENGTH)) == NULL ) 
      return(ER_OUT_OF_MEMORY);
   *buffer = tmpPtr;
   /* make sure the first byte is \0 since strcat is being used by dasminst */
   memset(tmpPtr,'\0', 1);

   for( instCnt = 0; instCnt < numInst; instCnt++ ) {
      err = TskCheckAbort(&abortFromEsc);
      if(err!=GOOD) return err;
      if (abortFromEsc!=0) {
         return(ER_ABORT_FROM_ESC);
      }

      if((err=GetIntData(dasmState, dasmState->startAddr, 
            OpByte)) != GOOD) {
         if( !instCnt ) return(err);
         strcat((LPSTR) *buffer, (LPSTR)endOfBuffer);
         err = GOOD;   /* we've found one instruction so return GOOD */
         return(err);
      }

      if((err=DasmInst(OpByte, dasmState, *buffer, &InstLen,
            DasmSymbol)) !=GOOD) return(err);

      if ((err = AdrAddToAddressOverflow(dasmState->startAddr,
            InstLen)) != GOOD) {
         if (err == ER_ADR_RESULT_OVERFLOW) {
            strcat(*buffer,(LPSTR)endOfBuffer);
            err = GOOD;
            break;
         }
         return err;
      }
         
      if (instCnt < numInst - 1)
         strcat(*buffer,(LPSTR)endOfLine);
      else
         strcat(*buffer,(LPSTR)endOfBuffer);
      *buffer += strlen(*buffer);
   }
   *buffer = tmpPtr;
   return(err);
}

/*****************************************************************************
**
**    GetDasmInstNoAlloc
**
*****************************************************************************/
RETCODE GetDasmInstNoAlloc(DAD_STATE FAR *dasmState, LPSTR buffer,
                    BOOLEAN DasmSymbol) {

   CHAR OpByte[MAX_CODE_LENGTH];
   U8   InstLen;
   RETCODE err;
  
  /* make sure the first byte is \0 since strcat is being used by dasminst */
   buffer[0] = '\0';
   if((err=GetIntData(dasmState, dasmState->startAddr, 
         OpByte)) != GOOD) return(err);
   if((err=DasmInst(OpByte, dasmState, buffer, &InstLen,
          DasmSymbol)) != GOOD)
       return(err);
   if ((err = AdrAddToAddressOverflow(dasmState->startAddr,InstLen)) != GOOD) {
      if (err == ER_ADR_RESULT_OVERFLOW) return(GOOD);
      return(err);
   }
   return(GOOD);
}

/*****************************************************************************
**
**    GetDasmInstByLine
**
*****************************************************************************/
RETCODE GetDasmInstByLine(DAD_STATE FAR *dasmState, U16 *lines, LPSTR *buffer,
                          BOOLEAN DasmSymbol) {
   CHAR OpByte[MAX_CODE_LENGTH];
   U8  instLen;
   RETCODE err;
   LPSTR tmpPtr;
   
   *lines = 0;
   if( (tmpPtr = TMalloc(MAX_LINE_LENGTH)) == NULL ) {
      return(ER_OUT_OF_MEMORY);
   }
   *buffer = tmpPtr;
   /* make sure the first byte is \0 since strcat is being used by dasminst */
   memset(tmpPtr,'\0', 1);

   if ((err=GetIntData(dasmState, dasmState->startAddr, 
         OpByte))!=GOOD) return(err);

   if ((err=DasmInst(OpByte, dasmState, *buffer, &instLen,
      DasmSymbol)) != GOOD) return(err);

   strcat(*buffer,(LPSTR)endOfBuffer);
   if (instLen <= MAX_INST_PER_LINE)
      (*lines)++;
   else
      *lines = (instLen/MAX_INST_PER_LINE) +
               ((instLen%MAX_INST_PER_LINE > 0)?1:0);
   return(AdrAddToAddressOverflow(dasmState->startAddr,instLen));
}

/*****************************************************************************
**
**    CheckDasmInst
**
**    This routine only returns an error due to catastophic conditions.
**    Like being out of memory, unable to converse with address server etc,
**    This routine never returns an error because it is unable to
**    dissasemble.  The instValid parameter returns whether the
**    dissasembly was successful.
**
**
*****************************************************************************/
RETCODE CheckDasmInst(DESCRIPTOR startAddr, DAD_STATE FAR *dasmState,
                      BOOLEAN DasmSymbol, U8 *instLen,
                      BOOLEAN *instValid) {

   CHAR OpByte[MAX_CODE_LENGTH];
   DASM_INFO dasmInfo;
   RETCODE err, err1;

   *instValid = FALSE;
   *instLen = 0;
   if((err=GetIntData(dasmState, startAddr, OpByte))!=GOOD)
      return(err);
   if ((err = AdrDuplicateAddress(startAddr,&dasmInfo.target)) != GOOD)
      return(err);
   err=DisassembleOpcode(OpByte,startAddr,dasmState->mode,
         dasmState->addrOpSize,DasmSymbol,&dasmInfo);
   if ((err1 = AdrDestroyAddress(dasmInfo.target)) != GOOD)
      return(err1);

   if (err == GOOD) {
      *instValid = TRUE;
      *instLen = dasmInfo.bytesUsed; //!!
   } else {
      *instLen = dasmInfo.bytesUsed; //!!
   }
   return(err);
}

/****************************************************************************
*
*  FindString
*
*****************************************************************************/
S8 FindString(U8 Count, CHAR **Source, CHAR *Search) {
   U8   idx;
   idx = 0;

   while ((idx < Count) && (stricmp(Search, Source[idx]) != 0)) idx++;
   if( idx == Count ) return(-1);
   return(idx);
}

/*****************************************************************************
**
**    GetIntData
**
**    09/13/94 - Nghia 
**    This routine keeps a cache of one page (256 bytes) to help improve
**    performance.  This one page cache will be used to reduce unnecessary
**    address translation.  Also, the cache buffer is implemented as static
**    buffer to reduce the overhead of allocate and deallocate memory.
**
*****************************************************************************/
RETCODE PRIVATE GetIntData(DAD_STATE FAR *dasmState, 
                           DESCRIPTOR address, 
                           CHAR *dataBuf) {
   RETCODE err;
   U32 addrOffset, cacheOffset;
   BOOLEAN result;
   DESCRIPTOR endAddress;

   if ((AdrIsAddrInRange(address, dasmState->cacheAddress, &result) != GOOD)
         || !result) {
      if ((err = UpdateDasmCache(dasmState, address)) != GOOD) return(err);
   } else {
      if ((err = AdrDuplicateAddress(address, &endAddress)) != GOOD)
         return(err);
      if ((err = AdrAddToAddressOverflow(endAddress, (U32)MAX_CODE_LENGTH))
         != GOOD) {
         if (err != ER_ADR_RESULT_OVERFLOW) return(err);
      }
      if ((AdrIsAddrInRange(endAddress,dasmState->cacheAddress,&result)!=GOOD)
            || !result) {
         if ((err = UpdateDasmCache(dasmState, address)) != GOOD) return(err);
      }
      if ((err = AdrDestroyAddress(endAddress)) != GOOD) return(err);
   }
   if ((err = AdrGetAddrOffset(address, &addrOffset)) != GOOD) return(err);
   if ((err = AdrGetAddrOffset(dasmState->cacheAddress, &cacheOffset)) != GOOD)
      return(err);
   memcpy(dataBuf, (dasmState->memCacheBuf)+(U16)(addrOffset-cacheOffset),
      MAX_CODE_LENGTH);
   return(GOOD);
}

RETCODE PRIVATE UpdateDasmCache(DAD_STATE *dasmState, DESCRIPTOR address) {
   RETCODE err;
   U32 size;
   if ((err = FreeDasmCache(dasmState)) != GOOD) return(err);
   if ((err = AdrRemainingBytes(address, &size)) != GOOD) return(err);
   if ((size == 0) || (size > PAGE_SIZE)) size = PAGE_SIZE;
   if ((err = AdrDuplicateAddress(address, &dasmState->cacheAddress)) != GOOD)
      return(err);
   if ((err = AdrSetAddrRangeLength(dasmState->cacheAddress, size))
         != GOOD) {
      FreeDasmCache(dasmState);
      return(err);
   }
   if (dummyDasm) {
      if ((err = DummyMemRead(dasmState->memCacheBuf)) != GOOD)
         FreeDasmCache(dasmState);
         return(err);
   } else {
      if ((err = MemReadSizedNoAlloc(address, size,
            dasmState->memCacheBuf, BYTE_SIZE, CACHE_USE)) != GOOD) {
         FreeDasmCache(dasmState);
         return(err);
      }
   }
   return(GOOD);
}

/****************************************************************************
**
**   FreeDasmCache
**
****************************************************************************/
RETCODE PRIVATE FreeDasmCache(DAD_STATE FAR *dasmState) {
   RETCODE err;
   if (dasmState == NULL)
      return ER_DAD_INVALID_ID_DESCRIPTOR;
   if (dasmState->cacheAddress) {
      if ((err = AdrDestroyAddress(dasmState->cacheAddress)) != GOOD)
         return(err);
      dasmState->cacheAddress = NULL;
   }
   return(GOOD);
}

/*****************************************************************************
**
**    DisassembleOpcode
**
*****************************************************************************/
RETCODE DisassembleOpcode(LPSTR OpByte, DESCRIPTOR startAddr, PMODE PMode,
   ADDR_OP_SIZE opSize, BOOLEAN symbol, DASM_INFO FAR *dasmInfo) {

   CHAR  TextBuffer[MAX_DASM_TEXT];

   return(Ldasm(OpByte, PMode, opSize, startAddr, symbol,
         TextBuffer, dasmInfo));
}

/**************************************************************************
**
** AddSessionToList
**
**************************************************************************/
VOID PRIVATE AddSessionToList(DAD_STATE FAR *dasmState) {

   DAD_STATE FAR *temp=NULL;

   if (sessionList == NULL) { 
      /* first in the list */
      sessionList = dasmState;
      dasmState->next = NULL;
   }
   else {
      /* add to end of list */
      temp = sessionList;
      while (temp->next != NULL)
         temp = (DAD_STATE FAR *)temp->next;
      temp->next = dasmState;
      dasmState->next = NULL;
   }
   return;
}

/*************************************************************************
**
** FreeSessionFromList
**
*************************************************************************/
VOID PRIVATE FreeSessionFromList(DAD_STATE FAR *dasmState) {

   DAD_STATE FAR *temp=NULL;
   DAD_STATE FAR *prev=NULL;

   if (sessionList == NULL)
      return;
   if (dasmState == NULL)
      return;
   /* first find the matching session */
   temp = sessionList;
   while ((temp != NULL) && (temp != dasmState)) {
      prev = temp;
      temp = (DAD_STATE FAR *)temp->next;
   }
   if (temp != dasmState)
      /* we didn't find it */
      return;
   /* otherwise we found it
   ** so detach it from the list */
   if (prev == NULL) 
      /* first in the list */
      sessionList = NULL;
   else 
      prev->next = temp->next;
   return;
}

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