/****************************************************************************
**
**  Name:  LDR.C
**
**  Description:
**      Entry points for ELF Loader.
**      Primary API's for ELF Loader DLL (LDELF.DLL) live here.
**
**  $Log:   S:/tbird/arcppc/lelf/ldr.c_v  $
** 
**    Rev 1.9   31 Mar 1998 11:57:42   Winky
** C++
** 
**    Rev 1.7   31 Mar 1997 08:45:46   hera
** 
**    Rev 1.6   20 Mar 1997 09:38:04   hera
** 
**    Rev 1.5   20 Mar 1997 09:35:52   hera
** modify header document
**
**  Status:  CODED
**
**  $Header:   S:/tbird/arcppc/lelf/ldr.c_v   1.9   31 Mar 1998 11:57:42   Winky  $
**
**  Copyright (C) 1991 Microtek International.  All rights reserved.
**
*****************************************************************************/

                       /****************************
                        *                          *
                        *       INCLUDE FILES      *
                        *                          *
                        ****************************/
#ifndef _BASEWIND_
#include "basewind.h"
#endif

#include <ctype.h>
#include <io.h>
#include <sys\timeb.h>

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

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

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

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

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

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

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

#ifndef _ERRCODEC_
#include "errcodec.h"
#endif

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

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

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

#ifndef _SLDRSVR_
#include "sldrsvr.h"
#endif

#ifndef _BKROOT_
#include "bkroot.h"
#endif

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

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

#ifndef __LDR__
#include "ldr.h"
#endif

#ifndef __LDEBUG__
#include "ldebug.h"
#endif

#ifndef __LFLAGS__
#include "lflags.h"
#endif

#ifndef __ERR__
#include "err.h"
#endif

#ifndef __LPROFILE__
#include "lprofile.h"
#endif

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

#ifdef _PERFORMANCE_
#include <time.h>
#endif


Elf32_Endr HdrELF; /*ELF Header*/

                       /****************************
                        *                          *
                        *     LOCAL DEFINITIONS    *
                        *                          *
                        ****************************/
/*
** Note:  all data global to this DLL will either be recycled/reset on
** a new load invocation, or destroyed/deallocated at the end of this load.
*/
BOOL runMode = INTERACTIVE;/* batch vs. interactive */
CHAR errbuf[80];
GLOBALHANDLE htable;       /* handle to register correspondence table memory */
HANDLE cliServerHandle;
HANDLE dllServerHandle;
HWND hWnd;                 /* Window handle of Caller */
S8 ldrcfgFile[PATHLEN];
U16 reportWarning = TRUE;  /* warnings on by default */
U16 reportOn = TRUE;       /* report status by default */
U16 ldrDemangle = TRUE;   /* always demangle names by default (C++) */
BOOLEAN ldrReload = FALSE; /* Flag to indicate reload request */
U16 FAR *rtable;           /* pointer to register correspondence table:
                              R695 -> TBird 68k (constructed at load time) */
LDRSTATBLOCK LdrStatus;
LDRSTATBLOCK FAR *lstb = ((LDRSTATBLOCK FAR *) &LdrStatus); /* progress reporting status block */
BOOL reportedLoadAbort = FALSE;
static U32 lastLocation = 0;
U32 symtab_offset, symtab_size;
U32 strtab_offset, strtab_size;
//Hera 2/2/98
U32 sfname_offset, sfname_size;
U32 srcinfo_offset,srcinfo_size;
//Hera

/*
** Static data, needs to be retained for event callbacks on code/sym
** load complete - by ToolBar Presenter
*/
U32 startPC;
U32 stackTop, stackSize;
BOOLEAN hasStackInfo;
U16 version;

/*
** Address descriptors created in load session (only need to retain
** in case aborted load requires cleanup).
*/
DESCRIPTOR FAR *paddrStartPC;
DESCRIPTOR FAR *paddrStackTop;
DESCRIPTOR FAR *paddrLoadRegion;

TIMESTAMP_TYPE tsLoadfile;
/* The following are for options callback - Source Presenter */
BOOLEAN onDemand;
S8 lpLoadfile[PATHLEN];
S8 lpModule[ELF_IDNLEN];
STATIC BOOLEAN isLoading;   /* semaphore to prevent re-entrancy */
U32 ldrFlags;
Lowpc2MDesc  LowPC2MDesc[MAX_MODULE];
Offset2Index *pOffset2IndexHead = NULL;
Offset2Index *pOffset2IndexTail,*pOffset2IndexTemp;
ClassInfo *rootClassInfo;
                       /****************************
                        *                          *
                        *    EXTERNAL VARIABLES    *
                        *                          *
                        ****************************/
extern U16 nSections;
//extern U16 curbyte;
extern BOOLEAN hasToolUseDefined;
extern CHAR toolUseName[];
extern CHAR pwrViewsDir[];
extern CHAR *infomsg[];
BOOLEAN useDefaultSection = FALSE;
extern BOOLEAN mergeSections;
extern U32 endDebugPart;

                       /****************************
                        *                          *
                        *     LOCAL PROTOTYPES     *
                        *                          *
                        ****************************/
/* Local routines */
STATIC VOID Cleanup(U16, BOOLEAN);
STATIC BOOL IsSrecord(U16 cbyte);
PRIVATE RETCODE GetModuleName(LPSTR lpModuleRef, LPSTR lpModuleName);

                       /****************************
                        *                          *
                        *      EXECUTABLE CODE     *
                        *                          *
			****************************/
/******************************************************************************
**
**  LdrLoad
**
******************************************************************************/
RETCODE EXPORT LdrLoad(LPSTR cmdString, U32 argc, U32 argv[]) {
   BOOLEAN lOnDemand;
   U16 lLdrFlags;
   CHAR lpBinfile[PATHLEN];
   CHAR lpModuleName[ELF_IDNLEN] = "";
   CHAR lpModuleRef[ELF_IDNLEN] = "";
   SYM_DESCRIPTOR moduleDesc;
   RETCODE err = GOOD;

   /* check semaphore to prevent statics from getting trashed */
   if (isLoading)
      return(ER_IN_PROGRESS);
   /* Don't set till we hit LdrLoadWin - Clear statics for new load */
   memset(lpLoadfile, 0, PATHLEN);
   onDemand = TRUE;  /* default */
   ldrFlags = ELF_DEF_FLAGS;  /* default */
   memset(lpModule, 0, ELF_IDNLEN);
   /* tell CLI progress indicator where loader status will go */
   if ((err = CliLdrStatBlock(lstb)) != GOOD) {
      return (err);
   }

   /* set up defaults, clear statics in DLL for new load */
   runMode       = BATCH;
   reportWarning = TRUE;
   ldrDemangle   = TRUE;  /* name demangling by default */
   ldrReload     = FALSE; /* Do not reload unless user says so */

   /* process command-line args */
   if ((err = ProcessArgs(cmdString, argc, argv, (LPSTR)lpBinfile, &lOnDemand,
      &lLdrFlags, (LPSTR)lpModuleName, (LPSTR) lpModuleRef)) != GOOD) {
      runMode = INTERACTIVE;  /* reset */
      return(err);
   }
   if (lpModuleName[0] != '\0') {
      /* load module only */
      /* Get the module descriptor from moduleName and moduleReference */
      if ((err = SymGetModuleDesc(lpModuleName, lpModuleRef, &moduleDesc))
         == GOOD) {
         lstrcpy(lpModule, lpModuleName);
         /* Set up global for Source Presenter's callback */
         err = LdrLoadModuleByDesc((LPSTR)lpBinfile, (LPSTR)lpModuleName,
                                   moduleDesc);
         /* Restore statics to current state */
         lstrcpy((LPSTR)lpLoadfile, (LPSTR)lpBinfile);
         onDemand = lOnDemand;
         ldrFlags = lLdrFlags;
      }
      /* drop thru to return err */
   } else {
      /* Load everything */
      err = LdrLoadWin((LPSTR)lpBinfile, lOnDemand, lLdrFlags, NULL);
   }

   /* Reset for successive load */
   runMode = INTERACTIVE;

   /* Full 32-bit retcode returned from LdrLoadWin or Module */
   return(err);
}  /* LdrLoad */

/* Globals used by the Progress Indicator */
LPSTR lpBinfile_save;
BOOLEAN ondemand_save;
U32 flags_save;
U32 parenthWnd_save;
BOOLEAN inProgress = FALSE;
int hBinfile;


/*****************************************************************************
**
**  LdrLoadWin
**
**  lpBinfile   - filename (ELF formate)
** ondemand    - if FALSE, load everything;
**               if TRUE, load only globals (no line numbers, locals)
** flags       - code/symbols/status (see ldrsvr.h #defines)
** parenthWnd  - Handle of caller's window.
*****************************************************************************/
RETCODE EXPORT LdrLoadWin(LPSTR lpBinfile, BOOLEAN ondemand, U32 flags, U32
   parenthWnd) {

   BOOL oflag;
   OFSTRUCT of;
   RETCODE ldrerr = GOOD,ret = GOOD;
   RETCODE err = GOOD, symerr = GOOD;
   U16 lflags = 0, status;
   U32 nLoaded=0L;
   TIMESTAMP_TYPE mtime_disk;
   HCURSOR hOldCursor, hHourGlassCursor;
   BOOL cursorOn = FALSE;
   LFILE_TYPE loadFileType;
   //U16 stackPushSize;
   BOOLEAN  abortFromEsc;
   //TIMESTAMP_TYPE ts;
   //U16 hostos;
   //TOOLCHAIN_TYPE toolchainID;
    EMULATION_STATE emuState;
   LPSTR lpstrTmp;

#ifdef _PERFORMANCE_
    // To measure loader download performance
    U8 timebuff[30];
    time_t first, second;
    first = time(NULL);
#endif

   /* If this is a callback from the progress dialog, restore args */
   if (inProgress)
   {
      lpBinfile = lpBinfile_save;
      ondemand = ondemand_save;
      flags = flags_save;
      parenthWnd = parenthWnd_save;
      runMode = INTERACTIVE;
      reportedLoadAbort = TRUE;
   }

   /* Set/Clear initial state for user-abort checking */
   if ((err = TskCheckAbort(&abortFromEsc)) != GOOD)
   {
      LdrProgressDone(err);
      return(err);
   }

   /* semaphore to prevent re-entrancy (file buffers would get trashed) */
   if (isLoading)
   {
      LdrProgressDone(ER_IN_PROGRESS);
      return(ER_IN_PROGRESS);
   }
   isLoading = TRUE;

   /* Set up statics for callback from Source Presenter */
   /* NOTES: module is *only* available thru the CLI, and set there */
   lstrcpy((LPSTR)lpLoadfile, (LPSTR)lpBinfile);
   lpstrTmp = lpBinfile;
   /* make sure it will fit in the status buffer (and in the dialog) 
    */
   while (lstrlen(lpstrTmp) + 1 >= MAX_LOADFILE_NAME_LEN)
      lpstrTmp++;

   lstrcpy((LPSTR)(lstb->curLoadFile), (LPSTR)lpstrTmp);
   onDemand = ondemand;
   ldrFlags = flags;
   /* check status and warning options */
   reportOn = REPORT_STATUS(flags) ? TRUE : FALSE;
   reportWarning = REPORT_WARNING(flags) ? TRUE : FALSE;
   hWnd = (HWND) parenthWnd;

   if (runMode == INTERACTIVE)
       memset(lpModule, 0, ELF_IDNLEN);
   /* initialize static data */
   InitInfostring();
   startPC = stackTop = 0L;
   stackSize = 0L;
   hasStackInfo = FALSE;
   useDefaultSection = FALSE;
   /* initialize address descriptors for new load */
   paddrStartPC = paddrStackTop = paddrLoadRegion = 0L;

   /* This is the main entry point for the Loader.  As such, we need
   ** to determine if on-demand loading was specified.
   */
   if(ondemand)
      /* only load globals for now */
      lflags = ELF_GLOBALS;
   else
      /* load everything */
      lflags = (ELF_GLOBALS | ELF_LOCALS);

   
   /* Check for PowerViews directory, toolUse */
   if ( ((err = FindIniFile()) != GOOD) || ((err = GetToolUse()) != GOOD) )
   {
      isLoading = FALSE;
      LdrProgressDone(err);
      return(err);
   }
      
   /* Put up hourglass for a lengthy operation */
   if (!inProgress)
   {
      hHourGlassCursor = LoadCursor(NULL, IDC_WAIT);
      hOldCursor = SetCursor(hHourGlassCursor);
      cursorOn = TRUE;
   }

   /* This entry point implies we need to create modules/functions */
   oflag = MCREATE;
   if ((hBinfile = OpenELFFile((LPSTR)lpBinfile, &of)) == -1)
   {
      err = ER_CANNOT_OPEN;
      isLoading = FALSE;
      if (cursorOn)
         SetCursor(hOldCursor);
      LdrProgressDone(err);
      return(err);
   }

   GetTimestamp(hBinfile, &mtime_disk); // Get the time of .ELF
   tsLoadfile = mtime_disk;
   lstb->loadFileSize = filelength(hBinfile);
   /* Get Header info to determine different load file formats */
   if ((err = ProcessELFHdr(hBinfile, &loadFileType)) != GOOD)
   {
      isLoading = FALSE;
      CloseELFFile(hBinfile);
      if (cursorOn)
         SetCursor(hOldCursor);
      //LdrProgressDone(err);
   }
   lstb->curLocation = tell(hBinfile);
   switch (loadFileType) {
      case  LFILE_ELF:
         /* just continue thru code */
         break;
      case  LFILE_SREC:
	 CloseELFFile(hBinfile);
	 ldrerr = SldLoadWin((LPSTR)lpBinfile, flags, runMode, lstb);
         /* Need to clean up for subsequent loads */
	 //ClearParseState();
         isLoading = FALSE;
         LdrProgressDone(ldrerr);
         if (cursorOn)
            SetCursor(hOldCursor);
	 return(ldrerr);
      default:
         err = ER_UNRECOG_LFILE;
         goto LDFinal;
   }

   if (LOAD_SYM(flags))
   {
      /* now passes time, and return error if symbols already exist */
      /* now takes context-parm (initial vs. module-only load) */
      if ((symerr = SymAddLoadStart((LPSTR)of.szPathName, INITIAL_LOAD,
				    &mtime_disk)) == ER_SYMBOLS_LOADED)
      {
         err = ER_SYM_ALREADY;
         /* Only query if user is capable of response (Window invocation).
         ** If RELOAD or NOWARN option is set from cli invocation, we
         ** just do it.  Else we return error.
         */
	 switch (runMode)
	 {
            case BATCH:
               if (ldrReload || !reportWarning)
                  status = QOK;
	       else
	       {
                  CloseELFFile(hBinfile);
                  isLoading = FALSE;
                  if (cursorOn)
                     SetCursor(hOldCursor);
                  LdrProgressDone(ER_SYM_ALREADY_CLI);
                  return(ER_SYM_ALREADY_CLI);
               }
               break;
            case INTERACTIVE:
               if (reportWarning)
                  status = Query(err);
               else
                  status = QOK; /* OK to load */
               break;
         }
	 if (status == QOK)
	 {
            /* Unload symbols from ST */
            if ((symerr = SymRemoveSymbols()) != GOOD)
               Warning(symerr);
            /* try again */
	    if ((symerr = SymAddLoadStart((LPSTR)of.szPathName, INITIAL_LOAD,
					   &mtime_disk)) == ER_SYMBOLS_LOADED)
	    {
               /* Give up, symbol table refuses to clear */
               CloseELFFile(hBinfile);
               isLoading = FALSE;
               if (cursorOn)
                  SetCursor(hOldCursor);
               LdrProgressDone(err);
               return(err);
            }
	 }
	 else if (status == QCANCEL)
	 {
            CloseELFFile(hBinfile);
            isLoading = FALSE;
            if (cursorOn)
               SetCursor(hOldCursor);
            LdrProgressDone(ER_LDR_ABORT);
            return(err);
	 }
	 else
	 {
            /* Unrecognized error probably from CLI */
            CloseELFFile(hBinfile);
            isLoading = FALSE;
            LdrProgressDone(ER_SYM_ALREADY);
	    if (runMode == BATCH)
               return(ER_SYM_ALREADY_CLI);
      	    else
	       return(ER_SYM_ALREADY);
         }
      }
    } 

   //Program Execution Header
   lstb->curLocation = tell(hBinfile);
   if((err = ProcessExecPart(hBinfile)) != GOOD)
   {
      CloseELFFile(hBinfile);
      if (cursorOn)
         SetCursor(hHourGlassCursor);
      SetCursor(hOldCursor);
      isLoading = FALSE;
      LdrProgressDone(err);
      return (err);  /* exit */
    }
   startPC = HdrELF.e_entry;

   //Section Header Table
   lstb->curLocation = tell(hBinfile);
   FreeSections(nSections);
   if ((err = ProcessSecPart(hBinfile, &version, LOAD_SYM(flags))) != GOOD)
   {
      Cleanup(flags, ondemand);
      CloseELFFile(hBinfile);
      if (cursorOn)
	 SetCursor(hHourGlassCursor);
      SetCursor(hOldCursor);
      isLoading = FALSE;
      LdrProgressDone(err);
      return (err);  /* exit */
   }

   lstb->curLocation = tell(hBinfile);
    
   /* Check for user abort load process */
   if (((err = TskCheckAbort(&abortFromEsc)) != GOOD) ||
       (abortFromEsc != FALSE))
   {
      CloseELFFile(hBinfile);
      Cleanup(flags, ondemand);
      if (cursorOn)
         SetCursor(hOldCursor);
      isLoading = FALSE;
      LdrProgressDone(err != GOOD ? err : ER_LDR_ABORT);
      return(err != GOOD ? err : ER_LDR_ABORT);
   }
    
   if ((err = LdrProgressInc()) != GOOD)
   {
      CloseELFFile(hBinfile);
      Cleanup(flags, ondemand);
      if (cursorOn)
         SetCursor(hOldCursor);
      isLoading = FALSE;
      LdrProgressDone(err);
      return (err);
   }

   /* Debug Part(.debug for Dwarf version1) (.debug_info for Dwarf version2)*/
   if (LOAD_SYM(flags))
   {
      nLoaded = 0L;
      if ((err = ProcessDebugPart(hBinfile, oflag, lflags,
				  version,&nLoaded)) != GOOD)
      {
	 if (err == ER_LDR_ABORT)
	 {
	    CloseELFFile(hBinfile);
            Cleanup(flags, ondemand);
            if (cursorOn)
               SetCursor(hOldCursor);
            isLoading = FALSE;
            LdrProgressDone(ER_LDR_ABORT);
            return(ER_LDR_ABORT);
	  }
	 else
	    goto LDFinal;
      }
      
      lstb->curLocation = tell(hBinfile);

      ondemand = (LOAD_LOCALS(lflags)) ? FALSE : TRUE;
      ProcessDbgLines(hBinfile, ondemand, oflag);
      if ((symerr = SymAddLoadEnd()) != GOOD)
         Warning(symerr);
      wsprintf((LPSTR)errbuf, "%ld %s", nLoaded, infomsg[LD_SYM_LOADED]);
      /* suppress message if no modules loaded */
      if (nLoaded != 0L)
         InfoMsg(errbuf);
   }

   /* Do not load symbols if code loading failed */
   if (LOAD_CODE(flags))
   {
      /*read Data to Ram*/
      if (((err = BkProcessorMustBeHalted()) != GOOD) ||
	 ((err = ProcessDataPart(hBinfile, &nLoaded)) != GOOD))
      {
         /* RunAccess is not on, cannot load */
	 CloseELFFile(hBinfile);
         Cleanup(flags, ondemand);
         if (cursorOn)
            SetCursor(hOldCursor);
         isLoading = FALSE;
         LdrProgressDone(err);
         return(err);  /* exit */
      }
      lstb->curLocation = tell(hBinfile); 

#ifdef _PERFORMANCE_
      second = time(NULL);
      wsprintf(timebuf, " in %ld seconds", nLoaded,
         second-first);
#endif
      wsprintf(errbuf, "%ld %s", nLoaded, (LPSTR)infomsg[LD_CODE_LOADED]);

#ifdef _PERFORMANCE_
      lstrcat(errbuf, timebuf);
#endif
      InfoMsg(errbuf);
   }
   /* Check for user abort load process */
   if (((err = TskCheckAbort(&abortFromEsc)) != GOOD) ||
       (abortFromEsc != FALSE))
   {
      CloseELFFile(hBinfile);
      Cleanup(flags, ondemand);
      if (cursorOn)
         SetCursor(hOldCursor);
      isLoading = FALSE;
      LdrProgressDone(err != GOOD ? err : ER_LDR_ABORT);
      return(err != GOOD ? err : ER_LDR_ABORT);
   } 
   if ((err = LdrProgressInc()) != GOOD)
   {
      CloseELFFile(hBinfile);
      Cleanup(flags, ondemand);
      if (cursorOn)
         SetCursor(hOldCursor);
      isLoading = FALSE;
      LdrProgressDone(err);
      return (err);
   }
LDFinal:
   CloseELFFile(hBinfile);
   if (cursorOn)
      SetCursor(hOldCursor);
   if (err != ER_UNRECOG_LFILE) 
      InfoMsg(infomsg[LD_COMPLETE]);
   /* reset semaphore off */
   isLoading = FALSE;
   /* Any err set here pertains to symbol loading */
   if (err != GOOD)
   {
      LdrProgressDone(err);
      return(err);
   }
 
   if (LOAD_CODE(flags)) EnlEventNotify(EVENT_LDR_MEMCHANGED);
   ret = BkGetEmulationStatus((EMULATION_STATE FAR *)&emuState);
   if ((ret == GOOD) && (emuState == EM_HALTED))
   {
      if (hasStackInfo)
         EnlEventNotify(EVENT_LDR_STACKTOP);      
   }
   else 
     Warning(ER_NOREG_UPDATE);
   LdrProgressDone(GOOD);
   
   /* Only generate event. If loading had no errors */
   EnlEventNotify(EVENT_LDR_LOADCOMPLETE);

   return(GOOD);
}  /* LdrLoadWin */

/******************************************************************************
**
**  LdrLoadProgress
**
******************************************************************************/
RETCODE EXPORT LdrLoadProgress(LPSTR lpBinfile, BOOLEAN ondemand,
                               U32 flags, U32 parenthWnd) {
   /* lpBinfile   - filename (.ABS file)
   ** ondemand    - if FALSE, load everything;
   **               if TRUE, load only globals (no line numbers, locals)
   ** flags       - code/symbols/status (see ldrsvr.h LELF #defines)
   ** parenthWnd  - Handle of caller's window.
   */
   RETCODE ret;
   lpBinfile_save = lpBinfile;
   ondemand_save = ondemand;
   flags_save = flags;
   parenthWnd_save = parenthWnd;

   if (isLoading || inProgress) {
      LdrProgressDone(ER_IN_PROGRESS);
      return(ER_IN_PROGRESS);
   }
   inProgress = TRUE;
   lstb->curLocation = 0L;
   lastLocation = 0L;
   if ((ret = CliLdrStatBlock(lstb)) != GOOD) {
      inProgress = FALSE;
      return (ret);
   }
   ret = CliLdrProgressDialog((HANDLE)parenthWnd, (LDRPROC)LdrLoadWin);
   inProgress = FALSE;
   return (ret);

}  /* LdrLoadProgress */

/********************************************************************************
**
**  LdrLoadModuleByDesc
**
******************************************************************************/
RETCODE EXPORT LdrLoadModuleByDesc(LPSTR lpBinfile, LPSTR moduleName,
                                   SYM_DESCRIPTOR moduleDesc) {
   BOOL hourGlassOn = FALSE;
   HCURSOR hOldCursor, hHourGlassCursor;
   OFSTRUCT of;
   TIMESTAMP_TYPE mtime_disk;
   U16 blockType, lflags = 0, ondemand;
   U32 blockPos, blockSize;
   U32 moduleFileOffset = 0L, typeOffset = 0L;
   RETCODE symerr, err = GOOD;

   /* Semaphore to prevent re-entrancy (file buffers would get trashed) */
   if (isLoading)
       return(ER_IN_PROGRESS);
   isLoading = TRUE;

   /* First, process parameters */
   lflags = ELF_LOCALS;
   /* We only need to open modules/functions/line blocks (not create them);
   ** they were created by the initial load.
   */
   if ((hBinfile = OpenELFFile((LPSTR)lpBinfile, &of)) == -1)
   {
      isLoading = FALSE;
      return(ER_CANNOT_OPEN);
   }
   GetTimestamp(hBinfile, &mtime_disk);
   tsLoadfile = mtime_disk;
   lstb->curLocation = tell(hBinfile); 
   /*
   ** For module-only load, ST returns error if timestamp passed is later than
   ** the initial timestamp for this module.  Note that we don't really track
   ** timestamps on individual modules, but on loadfiles.  What is a module-
   ** is it an object, or a source file??
   */
   symerr = SymAddLoadStart((LPSTR)of.szPathName, MODULE_LOAD, &mtime_disk);
   /*
   ** Open the Symbol Table context for adding local symbols:
   ** returns moffset (file offset of module in loadfile),
   ** toffset (type fix-up value).
   */
   if ((symerr = SymAddModuleOpenByDesc(moduleDesc,  &moduleFileOffset,
            &typeOffset)) != GOOD) {
      err = ER_MODULE_NOT_FOUND;
      goto CleanUpAndExit;
   }
   /* Move the file pointer to moduleFileOffset */
   if ((blockPos = SeekELFFile(hBinfile, moduleFileOffset,
	 SEEK_SET)) == -1L)
   {
      err = ER_BAD_SEEK;
      goto CleanUpAndExit;
   }
   lstb->curLocation = moduleFileOffset;

   blockSize = GetELFU32(hBinfile);
   blockType = GetELFU16(hBinfile);
   if(blockType != TAG_compile_unit)
   {
      err = ER_MODULE_NOT_FOUND;
      goto CleanUpAndExit;
   }
   lstb->curLocation += blockSize;
   SeekELFFile(hBinfile,lstb->curLocation,SEEK_SET);

   /* Put up hourglass for a lengthy operation */
   if (!inProgress) {
      hHourGlassCursor = LoadCursor(NULL, IDC_WAIT);
      hOldCursor = SetCursor(hHourGlassCursor);
      hourGlassOn = TRUE;
   }
   /* NOTES: module gets closed inside here */
   if ((err = ProcessDbgModuleOnDemand(hBinfile, lflags)) != GOOD) {
      /* User already alerted, bail out */
      /* Call SymAddLoadEnd to sort symbols */
      if ((symerr = SymAddLoadEnd()) != GOOD) {
         Warning(symerr);
      }
      goto CleanUpAndExit;
   }
   lstb->curLocation = tell(hBinfile); 
   ondemand = (LOAD_LOCALS(lflags)) ? FALSE : TRUE;
   /* Load line number information */
   err = ProcessDbgLinesOnDemand(hBinfile, moduleName);
   /* Call SymAddLoadEnd to sort symbols */
   if ((symerr = SymAddLoadEnd()) != GOOD) {
      Warning(symerr);
      if (err == GOOD) err = ER_CLOSE_SYM;
   }

CleanUpAndExit:
   if (hourGlassOn)
      SetCursor(hOldCursor);
   CloseELFFile(hBinfile);
   isLoading = FALSE;
   return(err);
}  /* LdrLoadModuleByDesc */


/********************************************************************************
**
**  LdrLoadModule
**
******************************************************************************/
RETCODE EXPORT LdrLoadModule(LPSTR lpBinfile, LPSTR moduleName) {
   BOOL oflag, cursorOn = FALSE;
   CHAR moduleRef[1] = "";
   HCURSOR hOldCursor, hHourGlassCursor;
   OFSTRUCT of;
   SYM_DESCRIPTOR moduleDesc;
   U16 blockType, lflags = 0, ondemand;
   U32 blockPos, blockSize;
   U32 moduleOffset, typeOffset = 0L;
   RETCODE symerr, err = GOOD;
   U32 NextModuleOffset;
   TIMESTAMP_TYPE mtime_disk;

   /* Semaphore to prevent re-entrancy (file buffers would get trashed) */
   if (isLoading)
       return(ER_IN_PROGRESS);
   isLoading = TRUE;

   /* First, process parameters */
   lflags = ELF_LOCALS;
   /* We only need to open modules/functions/line blocks (not create them);
   ** they were created by the initial load.
   */
   oflag = MOPEN;
   if ((hBinfile = OpenELFFile((LPSTR)lpBinfile, &of)) == -1)
   {
      err = ER_CANNOT_OPEN;
      isLoading = FALSE;
      return(err);
   }
   GetTimestamp(hBinfile, &mtime_disk);
   tsLoadfile = mtime_disk;
   lstb->curLocation = tell(hBinfile); 
   /*
   ** For module-only load, ST returns error if timestamp passed is later than
   ** the initial timestamp for this module.  Note that we don't really track
   ** timestamps on individual modules, but on loadfiles.  What is a module-
   ** is it an object, or a source file??
   */
   symerr = SymAddLoadStart((LPSTR)of.szPathName, MODULE_LOAD,&mtime_disk);

   /*
   ** Open the Symbol Table context for adding local symbols:
   ** returns moduleOffset (file offset of module in loadfile),
   ** typeOffset (type fix-up value).
   */
   if ((symerr = SymAddModuleOpen(moduleName, (LPSTR)moduleRef, &moduleOffset,
      &typeOffset)) != GOOD)
   {
      err = ER_MODULE_NOT_FOUND;
      /* This is fatal, since we only wanted to load this module */
      goto CleanUp;
   }
   if ((blockPos = SeekELFFile(hBinfile, moduleOffset, SEEK_SET)) == -1L)
   {
      err = ER_BAD_SEEK;
      goto CleanUp;
   }
   lstb->curLocation = moduleOffset;
   blockSize = GetELFU32(hBinfile);
   blockType = GetELFU16(hBinfile);
   if(blockType != TAG_compile_unit)
   {
      err = ER_MODULE_NOT_FOUND;
      goto CleanUp;
   }
   lstb->curLocation = tell(hBinfile);

   /* Put up hourglass for a lengthy operation */
   if (!inProgress) {
      hHourGlassCursor = LoadCursor(NULL, IDC_WAIT);
      hOldCursor = SetCursor(hHourGlassCursor);
      cursorOn = TRUE;
   }
   
   if ((err = ProcessDbgModule(hBinfile, oflag, lflags, moduleOffset, &NextModuleOffset, &moduleDesc)) != GOOD)
   {
      /* User already alerted, bail out */
      if ((symerr = SymAddLoadEnd()) != GOOD) 
         Warning(symerr);
      goto CleanUp;
   }

   lstb->curLocation = tell(hBinfile); 
   ondemand = (LOAD_LOCALS(lflags)) ? FALSE : TRUE;
   /* Load line number information */
   if ((err = ProcessDbgLinesOnDemand(hBinfile, moduleName)) != GOOD) {
      if ((symerr = SymAddLoadEnd()) != GOOD) Warning(symerr);
      goto CleanUp;
   }
   if ((symerr = SymAddLoadEnd()) != GOOD) {
      Warning(symerr);
      err = ER_CLOSE_SYM;
   }
CleanUp:
   /* Move to after call to SymAddLoadEnd, which sorts symbols */
   if (cursorOn)
      SetCursor(hOldCursor);
   CloseELFFile(hBinfile);
   isLoading = FALSE;
   return(err);
}  /* LdrLoadModule */

/******************************************************************************
**
**  ProcessArgs - Process CLI-passed arguments
**
******************************************************************************/
RETCODE ProcessArgs(LPSTR cmdString, U32 argc, U32 argv[], LPSTR lpFileName,
   BOOLEAN *ondemand, U16 *ldrflags, LPSTR lpModuleName, LPSTR lpModuleRef) {
   /* cmdString - raw string from command-line
   ** argc - number of argv-type parameters
   ** argv - string array indices for each parameter
   ** lpfile - returned filename
   ** ondemand - returned ondemand flag
   ** ldrflags - returned loader flags
   ** lpModuleName - module name (if module-only load)
   ** lpModuleRef - module path reference (if module-only load)
   */
   LOOP_VAR i;
   U16 index;
   CHAR cparm[ARGLEN];
   RETCODE err = GOOD;

   if (argc < 2) {
      /* error propagated as return from load */
      return(ER_CLI_SYNTAX);
   }
   /* we copy to local buffer so we can use strnicmp */
   /* actually, we *can* use far CHAR string, with _fstrnicmp */
   /* take load filename */
   lstrcpy((LPSTR)lpFileName, (LPSTR)&cmdString[argv[1]]);
   /* set default options */
   *ondemand = TRUE;            /* on demand is default */
   *ldrflags = ELF_DEF_FLAGS;  /* load code, symbols, display progress,
                                   issue warnings and no asm */
   /* process options */
   for (i = 2; i < argc; i++) {
      index = argv[i];
      lstrcpy((LPSTR)cparm, (LPSTR)&cmdString[index]);
      switch (toupper(cparm[0])) {
         case 'A':   /* load assembly modules */
            if (strnicmp(cparm, "ASM", 3) == 0) *ldrflags |= ELF_ASM;
            else return(ER_CLI_SYNTAX);
            break;
         case 'C':   /* load code */
            if (strnicmp(cparm, "COD", 3) == 0) *ldrflags |= ELF_CODE;
            else return(ER_CLI_SYNTAX);
            break;
         case 'D':   /* ondemand load */
            if (strnicmp(cparm, "DEM", 3) == 0) *ondemand = TRUE;
            else return(ER_CLI_SYNTAX);
            break;
         case 'M':   /* module-only load, mangled names */
            if (strnicmp(cparm, "MOD", 3) == 0) {
               /* next argument is module name */
               if (i < argc-1) {
                  /* what happens to this memory from cmdString?
                  ** can I just use it and pass around pointers, or m
                  ** ust I allocate for strings I wish to save?
                  ** Ans: you can use it, the CLI will not deallocate
                  ** till the app is done.
                  */
		  lstrcpy((LPSTR)lpModuleRef, (LPSTR)&cmdString[argv[++i]]);
                  /* Parse the lpModuleRef to path and module name */
                  if ((GetModuleName(lpModuleRef, lpModuleName) != GOOD) ||
                     (lstrlen(lpModuleName) == 0)) {
                     return(ER_CLI_SYNTAX);
                  }
               }
               else {
                  return(ER_MODULE_NOT_FOUND);
               }
            } else if (strnicmp(cparm, "MAN", 3) == 0) {
               ldrDemangle = TRUE;
            } else {
               return(ER_CLI_SYNTAX);
            }
            break;
         case 'N':   /* turn off option */
            if (strnicmp(cparm, "NOSYM", 5) == 0)
               *ldrflags &= ~ELF_DBG;
            else if (strnicmp(cparm, "NOCOD", 5) == 0)
	       *ldrflags &= ~ELF_CODE;
            else if (strnicmp(cparm, "NOPRO", 5) == 0)
	       *ldrflags &= ~ELF_STATUS;
            else if (strnicmp(cparm, "NODEM", 5) == 0)
               *ondemand = FALSE;
            else if (strnicmp(cparm, "NOASM", 5) == 0)
	       *ldrflags &= ~ELF_ASM;
            /* no more war */
            else if (strnicmp(cparm, "NOWAR", 5) == 0) {
	       *ldrflags &= ~ELF_WARNING;
               reportWarning = FALSE;
            }
            else if (strnicmp(cparm, "NOMAN", 5) == 0)
               ldrDemangle = FALSE;  /* do not demangle var names */
            else
               return(ER_CLI_SYNTAX);
            break;
         case 'P':   /* progress indicator */
            if (strnicmp(cparm, "PRO", 3) == 0)
	       *ldrflags |= ELF_STATUS;
            else return(ER_CLI_SYNTAX);
            break;
         case 'R':   /* reload */
            if (strnicmp(cparm, "REL", 3) == 0)
               ldrReload = TRUE;
            else return(ER_CLI_SYNTAX);
            break;
         case 'S':   /* symbol load */
            if (strnicmp(cparm, "SYM", 3) == 0)
	       *ldrflags |= ELF_DBG;
            else if (strnicmp(cparm, "SUPER", 5) == 0)
	       *ldrflags &= ~ELF_SPACE;
            else return(ER_CLI_SYNTAX);
            break;
         case 'U':   /* User Mode */
            if (strnicmp(cparm, "USER", 4) == 0)
	       *ldrflags |= ELF_SPACE;
            else return(ER_CLI_SYNTAX);
            break;
         case 'W':   /* warnings */
            if (strnicmp(cparm, "WAR", 3) == 0) {
	       *ldrflags |= ELF_WARNING;
               reportWarning = TRUE;
            }
            else return(ER_CLI_SYNTAX);
            break;
         default:
            return(ER_CLI_SYNTAX);
      }
   }

   /* check for consistency of arguments */
   if (LOAD_ASM(*ldrflags) && !LOAD_SYM(*ldrflags)) {
      return(ER_ARGS_NEED_SYM);
   }

   return(GOOD);
}  /* ProcessArgs */

/******************************************************************************
**
**  LdrCliSetToolUse
**
******************************************************************************/
RETCODE EXPORT LdrCliSetToolUse(LPSTR cmdString, U32 argc, U32 argv[]) {
   RETCODE err = GOOD;
   CHAR tmpString[80];
   U16 strLength;
	
   /* check semaphore to prevent statics from getting trashed */
   if (isLoading)
      return(ER_IN_PROGRESS);
   if ((err = FindIniFile()) != GOOD) {
      return(err);
   }
   /* User enter :>CompilerUsed to view the current CompilerUsed setting */
   if (argc < 2) {
      /* Get CompilerUsed from PWRVIEWS.INI */
      GetPrivateProfileString((LPSTR) LAPP_NAME, (LPSTR) LAPP_TOOL_USE,
         "", (LPSTR) toolUseName, MAX_STR_SIZE, (LPSTR) pwrViewsDir);
      tmpString[0] = (CHAR)'\0'; /* init tmpString */
      lstrcat((LPSTR)tmpString, (LPSTR)&cmdString[argv[0]]);
      lstrcat((LPSTR)tmpString, "=");
      lstrcat((LPSTR)tmpString, (LPSTR) toolUseName);
      SendMessageToCLI(tmpString);
      /* Return the result to CLI */
      return(GOOD);
   }
   /* Set global flag to force loader to re-read the sectionnames when load */
   hasToolUseDefined = FALSE;
   /* Get the "CompilerUsed" specified and verify */
   if ((lstrcpy((LPSTR) toolUseName, (LPSTR) &cmdString[argv[1]]) == NULL) ||
         ((strLength = lstrlen(toolUseName)) == 0)) {
      return(ER_MUST_DEFINE_TOOLUSE);
   }
   
   /* Check if the arguments is none - Set "CompilerUsed" = UNKNOWN */
   if (strcmpi(toolUseName, ARG_NONE) == 0) {
      strcpy(toolUseName, TOOL_UNKNOWN);
   } else {
      if (toolUseName[0] == '=')
		  return(ER_CLI_SYNTAX);
	}
   if ((err = SetToolUse()) != GOOD ) return(err);
   if ((err = GetSectionNames()) != GOOD) return(err);
   
   /* "CompilerUsed" has been defined and saved into the PWRVIEWS.INI */
   return(GOOD);
}

/******************************************************************************
**
**  LdrCliSetSecNames
**
******************************************************************************/
RETCODE EXPORT LdrCliSetSecNames(LPSTR cmdString, U32 argc, U32 argv[]) {
   RETCODE err = GOOD;
   CHAR sectionNames[MAX_STR_SIZE], tmpString[MAX_STR_SIZE];

   /* Check semaphore to prevent statics from getting trashed */
   if (isLoading)
      return(ER_IN_PROGRESS);
   if ((err = FindIniFile()) != GOOD) {
      return(err);
   }
   /* User enter :>SectionNamesUsed to view the current SectionNames setting */
   sectionNames[0] = '\0'; /* initialize sectionNames to NULL */
   if ((GetPrivateProfileString((LPSTR) LAPP_NAME, (LPSTR) LAPP_TOOL_USE,
         "", (LPSTR) toolUseName, MAX_STR_SIZE, (LPSTR) pwrViewsDir) == 0)
                              || (strcmpi(toolUseName, TOOL_UNKNOWN) == 0) )
      return(ER_NO_TOOLUSE_DEFINED);

   if (argc < 2) {
      if (GetPrivateProfileString((LPSTR) LAPP_NAME, (LPSTR) toolUseName,
         "", (LPSTR) sectionNames, MAX_STR_SIZE, (LPSTR) pwrViewsDir) == 0)
         return(ER_NO_SECTION_NAME_DEFINED);
      tmpString[0] = '\0'; /* init tmpString */
      lstrcat((LPSTR)tmpString, (LPSTR)toolUseName);
      lstrcat((LPSTR)tmpString, "=");
      lstrcat((LPSTR)tmpString, (LPSTR)sectionNames);
      SendMessageToCLI(tmpString);
      /* Return the result to CLI */
      return(GOOD);
   }

   /* Set global flag to force loader to re-read the sectionNames when load */
   hasToolUseDefined = FALSE;
   /* Get the "sectionNames" specified and verify */
   if ((lstrcpy((LPSTR) sectionNames, (LPSTR) &cmdString[argv[1]]) == NULL) ||
         (lstrlen(sectionNames) == 0)) {
      return(ER_NO_SECTION_NAME_DEFINED);
   }
   /* Check for sectionNames delimiter ',' for validity */
   if (_fstrstr((LPSTR)sectionNames, (LPSTR) DELIMITER_SET) == NULL)
      return(ER_INVALID_SECTION_NAME);
   /*
   ** Save the user specified section names back into the PWRVIEWS.INI
   ** using "CompilerUsed" as key for the sectionNames.
   */
   if (!WritePrivateProfileString((LPSTR) LAPP_NAME, (LPSTR) toolUseName,
         (LPSTR) sectionNames, (LPSTR) pwrViewsDir)) {
      return(ER_CANNOT_WRITE_TO_FILE);
   }

   /* Section names of "CompilerUsed" has been defined and saved into
   ** the PWRVIEWS.INI
   */
   return(GOOD);
}

/******************************************************************************
**
**  LdrCliMergeSections
**
******************************************************************************/
RETCODE EXPORT LdrCliMergeSections(LPSTR cmdString, U32 argc, U32 argv[]) {
   RETCODE err;
   BOOLEAN mergeValue;
   CHAR tmpString[80];

   /* Check semaphore to prevent statics from getting trashed */
   if (isLoading)
      return(ER_IN_PROGRESS);
   if ((err = FindIniFile()) != GOOD) {
      return(err);
   }
   /* User enter :>MergeSections to view the current MergeSections setting */
   if (argc < 2) {
      /* Get MergeSections from PWRVIEWS.INI */
      mergeValue = (BOOLEAN) GetPrivateProfileInt((LPSTR) LAPP_NAME,
         (LPSTR) LAPP_MERGE_SEC, (S16) FALSE, (LPSTR) pwrViewsDir);
      tmpString[0] = (CHAR)'\0'; /* init tmpString */
      lstrcat((LPSTR)tmpString, (LPSTR)&cmdString[argv[0]]);
      lstrcat((LPSTR)tmpString, "=");
      if (mergeValue)
         lstrcat((LPSTR)tmpString, (LPSTR)"on");
      else
         lstrcat((LPSTR)tmpString, (LPSTR)"off");

      SendMessageToCLI(tmpString);
      /* Return the result to CLI */
      return(GOOD);
   }
   
	/* Save the specified value to PWRVIEWS.INI */
   if (lstrcmpi((LPSTR)"on", &cmdString[argv[1]]) == 0) {
      if (!WritePrivateProfileString((LPSTR) LAPP_NAME, (LPSTR) LAPP_MERGE_SEC,
            (LPSTR)"1", (LPSTR) pwrViewsDir))
         return(ER_CANNOT_WRITE_TO_FILE);
      mergeSections = TRUE;      
   }
   else if (lstrcmpi((LPSTR)"off", &cmdString[argv[1]]) == 0) {
      if (!WritePrivateProfileString((LPSTR) LAPP_NAME, (LPSTR) LAPP_MERGE_SEC,
            (LPSTR)"0", (LPSTR) pwrViewsDir))
         return(ER_CANNOT_WRITE_TO_FILE);
      mergeSections = FALSE;
   }
   else {
      return(ER_CLI_SYNTAX);
   }
   /* "MergeSections" has been defined and saved into the PWRVIEWS.INI */
   return(GOOD);
}

/******************************************************************************
**
**  ProcessELFHdr
**
******************************************************************************/
RETCODE ProcessELFHdr(HANDLE hfile, LFILE_TYPE *loadFileType) {
   /* flags - load code or symbols only? */
   RETCODE err = GOOD;
   U16 cbyte;    
   /* Although we obtain new file offsets and other interesting information
      for each load from the loadfile, certain info is only loaded if symbol
      load is requested (anything to do with the Symbol Server - like size
      of stack-push object).
   */
   
   if ((err = GetELFHdr(hfile)) != GOOD) {
      /* Determine what kind of loadfile this is */
      cbyte = GetELFByte(hfile);
      if (IsSrecord(cbyte) == TRUE) {
	 *loadFileType = LFILE_SREC;
         SeekELFFile(hfile,0,SEEK_SET);
         return(GOOD);
      }
      return(err);
   }
   *loadFileType = LFILE_ELF;
   return(GOOD);
}  /* ProcessELFHdr */

/******************************************************************************
**
**  GetELFHdr
**
******************************************************************************/
#pragma argsused
RETCODE GetELFHdr(HANDLE hfile) {
   /* flags - load code/symbols only? */

   RETCODE err;
   PROCESSOR_FAMILY pFamily;
   PROC_CPU cpu;
   char elf[5];

   sprintf(elf,"%c",0x7F);
   strcat(elf,"ELF");

   if(_lread(hfile,(LPSTR)&HdrELF,sizeof(Elf32_Endr)) != sizeof(Elf32_Endr))
	 return(ER_BADGET);
   if(strncmp(elf,HdrELF.e_ident,4) != 0){ /*not ELF file*/
     SeekELFFile(hfile,0,SEEK_SET);
     return 1;
   }
   if(HdrELF.e_ident[EI_DATA] == ELFDATA2MSB)
   {
      SeekELFFile(hfile,EI_NIDENT,SEEK_SET);
      HdrELF.e_type = GetELFU16(hfile);
      HdrELF.e_machine = GetELFU16(hfile);
      HdrELF.e_version = GetELFU32(hfile);
      HdrELF.e_entry = GetELFU32(hfile);
      HdrELF.e_phoff = GetELFU32(hfile);
      HdrELF.e_shoff = GetELFU32(hfile);
      HdrELF.e_flags = GetELFU32(hfile);
      HdrELF.e_ehsize = GetELFU16(hfile);
      HdrELF.e_phentsize = GetELFU16(hfile);
      HdrELF.e_phnum = GetELFU16(hfile);
      HdrELF.e_shentsize = GetELFU16(hfile);
      HdrELF.e_shnum = GetELFU16(hfile);
      HdrELF.e_shstrndx = GetELFU16(hfile);
   }
   /* lets see what our system thinks is set up - be sure it matches the
      Loader's register translation table */
/*   ProcReturnProcFamily(&pFamily);
   if (pFamily != FAMILY_68K) {
      Warning(ER_NOT_CPU);
      return (ER_NOT_CPU);
   }*/
   /* Get CPU type from the probe */
   if ((err = ProcReturnCpu(&cpu)) != GOOD)
      return(err);
  
   //HdrELF.e_machine = cpu;

   return(GOOD);
}  /* GetELFHdr */

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

   /* capture these */
   cliServerHandle = cliHandle;
   dllServerHandle = dllHandle;

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

/******************************************************************************
**
**  LdrGetStartPC
**
******************************************************************************/
RETCODE EXPORT LdrGetStartPC(DESCRIPTOR FAR *pstartPC) {
   RETCODE aderr;

   /* Uses static data */
   if ((aderr = AdrCreateAddress(pstartPC)) != GOOD)
      return(aderr);
   /* retain static copy in case of cleanup */
   paddrStartPC = pstartPC;
   if ((aderr = AdrSetAddrOffset(*pstartPC, startPC)) != GOOD)
      return(aderr);
   return(GOOD);
}  /* LdrGetStartPC */

/******************************************************************************
**
**  LdrGetStack
**
******************************************************************************/
RETCODE EXPORT LdrGetStack(DESCRIPTOR FAR *pstackTop, U32 *ssize) {
   RETCODE aderr;

   /* uses static data */
   if ((aderr = AdrCreateAddress(pstackTop)) != GOOD)
      return(aderr);
   /* retain static copy in case of cleanup */
   paddrStackTop = pstackTop;
   /* Modification: stack base is actually returned, which is found
   ** by adding the 'top' of the stack to the size.  The stack
   ** grows *down* for all 68xxx processors
   */
   if ((aderr = AdrSetAddrOffset(*pstackTop, stackTop+stackSize)) != GOOD)
      return(aderr);
   *ssize = stackSize;

    return(GOOD);
}  /* LdrGetStacktop */

/******************************************************************************
**
**  LdrGetOptions
**
******************************************************************************/
#pragma argsused
RETCODE EXPORT LdrGetOptions(LPSTR lpBinfile, BOOLEAN FAR *ondemand,
   U32 FAR *ldrflags, LPSTR lpmodule) {
   /* lpBinfile - loadfile name - use size PATHLEN defined in ldrsvr.h
   ** ondemand - true if ondemand specified (default operation)
   ** ldrflags - code/symbols/status flags
   ** lpmodule - module name (if module-only load) - use size IEEE_IDNLEN
   */
   /* NOTES: the caller is assumed to allocate a buffer, we can't just use
   ** pointer assignment, since by the time we return to the caller the
   ** DLL may be unloaded, in which case the static data is gone!
   */
   lstrcpy(lpBinfile, lpLoadfile);
   *ondemand = onDemand;
   *ldrflags = ldrFlags;
   lstrcpy(lpmodule, lpModule);
   return(GOOD);
}  /* LdrGetOptions */

/*****************************************************************************
**
**  LdrGetLoadRegion
**
*****************************************************************************/
RETCODE EXPORT LdrGetLoadRegion(LR_TYPE lregion, DESCRIPTOR FAR *pAddr) {
   U32 startAddr;
   U32 range;
   U16 codeflag;
   U32 aderr;
   RETCODE err = GOOD;

   /* first, check if any code was loaded.  Code loading will load all
   ** the code and data regions.  Check our static flag, leftover from
   ** the last load. Even if code was not loaded, we still need to create
   ** this lovely address descriptor and fill it in.
   */
   codeflag = LOAD_CODE(ldrFlags);
   if ((aderr = AdrCreateAddress(pAddr)) != GOOD)
      return(aderr);

   /* Hold a static copy in case of cleanup */
   paddrLoadRegion = pAddr;

   /* NOTES:
   ** Only code is needed at this time (by source/dasm presenter).
   ** The types LR_DATA and LR_ROMDATA may not be contiguous (depend on
   ** the MRI compiler-generated sections: vars/zerovars, and const/strings,
   ** respectively.  Till they are needed, they will not be supported.  To
   ** properly support, need to return an array of address descriptors for
   ** the discontiguous regions.  Code should be contiguous since the compiler
   ** generates a single section for it.
   */
   if (codeflag) {
      switch (lregion) {
         case LR_CODE:
            err = PSGetCode(&startAddr, &range);
            break;
         case LR_DATA:
#ifdef GET_DATA_RANGE
            err = PSGetData(&startAddr, &range);
            break;
#endif
         case LR_ROMDATA:
         default:
            err = ER_UNRECOGNIZED_SEC;
            break;
      }
      if (err != GOOD)
         return(ER_NOCODE);
   } else {
      startAddr = RANGE_NOT_SET;
      range = 0L;
      return(ER_NOCODE);
   }
   if ((aderr = AdrSetAddrOffset(*pAddr, startAddr)) != GOOD)
      return(aderr);
   /* Sets end field */
   if ((aderr = AdrSetAddrRangeLength(*pAddr, range)) != GOOD)
      return(aderr);
   return(err);
}  /* LdrGetLoadRegion */

/******************************************************************************
**
**  LdrGetFileStatus(Actor call)
**
******************************************************************************/
RETCODE EXPORT LdrGetFileStatus(LPSTR lpsource, BOOL *pstatus) {
   S16 hmodule;
   TIMESTAMP_TYPE tsModule;
   OFSTRUCT of;

   // get handle from filename 
   if ((hmodule = OpenFile(lpsource, (OFSTRUCT FAR *)&of, OF_READ)) == -1) {
      *pstatus = !GOOD;
      return(ER_MODULE_NOT_FOUND);
   }
   GetTimestamp(hmodule, &tsModule);
   // compare module timestamp to loadfile timestamp 
   if (DiffTimestamp(&tsModule, &tsLoadfile) > 0)
      *pstatus = !GOOD;
   else
      *pstatus = GOOD;
   _lclose(hmodule);
   return(GOOD);
}/* LdrGetFileStatus */

/******************************************************************************
**
**  Cleanup
**
******************************************************************************/
STATIC VOID Cleanup(U16 flags, BOOLEAN ondemand) {
   RETCODE symerr;

   /* When user decides to abort load operation, or for some error
   ** condition encountered, cleanup.  This routine is *not* called
   ** for a successful load.
   ** NOTES: !!! Aborting a load still loses some memory, there are apparently
   ** other things which need to be cleaned up here.
   */
   if (LOAD_SYM(flags)) {
      if ((symerr = SymAddLoadEnd()) != GOOD) {
         Warning(symerr);
      }
      if (!ondemand)
         FreeSections(nSections);
      SymRemoveSymbols();  /* lets clean up */
   }
   /*  Clean up address descriptors created in this load session */
   if (paddrStartPC)
      AdrDestroyAddress(*paddrStartPC);
   if (paddrStackTop)
      AdrDestroyAddress(*paddrStackTop);
   if (paddrLoadRegion)
      AdrDestroyAddress(*paddrLoadRegion);
   isLoading = FALSE;
   hasStackInfo = FALSE;
}  /* Cleanup */

/******************************************************************************
**
**  IsSrecord
**
******************************************************************************/
STATIC BOOL IsSrecord(U16 cbyte) {
    /* check for S-record or global symbol */
    if (cbyte == 'S' || cbyte == '$')
        return(TRUE);
    return(FALSE);
}  /* IsSrecord */

/******************************************************************************
**
**  GetModuleNameName
**
******************************************************************************/
PRIVATE RETCODE GetModuleName(LPSTR lpModuleRef, LPSTR lpModuleName) {
   BOOLEAN found = FALSE;
   U16 i;
   U16 pathLength;
   LPSTR tmpPtr = lpModuleRef;

   if ((pathLength = lstrlen(lpModuleRef)) == 0)
      return(GOOD);
   /* Algorithm:
   ** Search from right to left for the last slash or a colon,
   ** if found
   **    copy moduleName
   ** else only module name is specified
   **    copy moduleName and set moduleRef to empty tring.
   ** return results
   */
   for (i = pathLength; i > 0 && !found; i--) {
      if ((CHAR)*(tmpPtr+i) == '.')
         *(tmpPtr+i) = '\0'; /* Excluding the extension for module name */
      if ((i < pathLength) && (((CHAR)*(tmpPtr+i) == '\\') ||
            ((CHAR)*(tmpPtr+i) == '/') || ((CHAR)*(tmpPtr+i) == ':')) ) {
         lstrcpy(lpModuleName, (LPSTR)tmpPtr+i+1); /* backup 1 position */
         found = TRUE;
      }
   }
   /* There is no path reference in lpModuleRef */
   if (!found) {
      lstrcpy(lpModuleName, lpModuleRef);
      *lpModuleRef = '\0'; /* NULL string */
   }
   return(GOOD);
} /* GetModuleName */

/******************************************************************************
**
**  LdrProgressInc
**
**  Description:
**
**    In order to avoid strange jerking around (of the progress indicator),
**    don't go more than N% forward in one increment, and don't go more
**    than 80% of the way if we are loading both code and symbols, unless 
**    the number of symbols is greater than the number of modules, and
**    don't back up.  (This still doesn't look right in some cases, but for
**    the average case seems to look ok.)
**
******************************************************************************/
RETCODE EXPORT LdrProgressInc() {
   static struct timeb last_time;
   static struct timeb this_time;
   static BOOL called = FALSE;
   S32 timedif;
   RETCODE err;
   BOOL answer;
   static U32 lastNumBytes = 0;
   static U32 lastNumSymbols = 0;
   U32 tmpLocation;
   static U16 lastNumModules = 0;
   U16 cflag;
   U16 sflag;
   U16 N;
   S16 buttonID;

   /*
    * First, make sure that we don't update more than twice per second,
    * to avoid performance penalties and annoyingly-active displays.
    */
   if (!called) {
      ftime(&last_time);
      called = TRUE;
   }
   else {
      ftime(&this_time);
      timedif = ((this_time.time * 1000) + this_time.millitm)
                - ((last_time.time * 1000) + last_time.millitm);
      if (timedif < 0) {
         last_time = this_time;
      }
      if (timedif < 500) return (GOOD);
      last_time = this_time;
   }

   /*
   ** Collect stats whether we are interactive or batch...
   */
   cflag = LOAD_CODE(ldrFlags);
   /* NOTES: Nghia - 10/26/93
   ** Only check symbol statistic if LOAD_SYM is TRUE.  We don't want to
   ** get any old information.
   */
   sflag = LOAD_SYM(ldrFlags);    
   if (sflag != 0)
      SymGetLdrStats((LPSTR)&(lstb->curModule),
                     (U32 FAR *)(&lstb->numSymbols),
                     (U16 FAR *)(&lstb->numModules),
                     (U32 FAR *)(&lstb->numTypes));
   

   /*
    * But only display them if we are interactive
    */
   if ((err = CliLdrProgressActive((BOOL FAR *) &answer)) != GOOD) {
      return (err);
   }
   if (answer == TRUE) {
      /* Don't back up */
      if (lstb->curLocation < lastLocation) {
         lstb->curLocation = lastLocation;
      }
      /*
       * Don't bump it more than N% in one jump, where N is based on
       * loadfile size.
       */
      if (lstb->loadFileSize < 1000L) {
         N = 20; /* max 20% per increment */
      }
      else if (lstb->loadFileSize < 10000L) {
         N = 10; /* max 10% per increment */
      }
      else {
         N = 5; /* max 5% per increment */
      }
      tmpLocation = lastLocation + (lstb->loadFileSize / (100 / N));
      if (tmpLocation > lstb->loadFileSize)
         tmpLocation = lstb->loadFileSize;
      if (lstb->curLocation > tmpLocation)
         lstb->curLocation = tmpLocation;
      /* 
       * If loading both code and symbols, don't pass the 80% mark
       * until the number of symbols exceeds the number of modules.
       */
      if (cflag && sflag && (lstb->numSymbols < lstb->numModules + 1)) {
         if (lstb->curLocation > (8 * lstb->loadFileSize / 10)) {
            lstb->curLocation = (8 * lstb->loadFileSize / 10);
         }
      }

      /*
       * Finally, don't bother doing anything unless something reasonably
       * significant has happened.
       */
      if ((lstb->numModules > lastNumModules) || 
          (lstb->numSymbols > lastNumSymbols + 10) ||
          (lstb->curLocation > lastLocation + 100) ||
          (lstb->curLocation < lastLocation - 100) ||
          (lstb->numBytes > lastNumBytes + 100)) {
         if ((err = CliLdrProgressStep()) != GOOD) {
            if (err == ER_LDR_ABORT) {
               err = ErrMessageBox((LPSTR) "MICEpack/SLD", 
                     (LPSTR) "Really Cancel the Load?", 
                     MB_ICONQUESTION | MB_YESNO,
                     HE_CANCEL_LOAD, (S16 FAR *) &buttonID);
               if (err != GOOD) {
                  return (err);
               }
               if (buttonID == IDYES) {
                  return (ER_LDR_ABORT);
               }
            }
            else {
               return (err);
            }
         }
      }
      lastNumModules = lstb->numModules;
      lastNumSymbols = lstb->numSymbols;
      lastNumBytes = lstb->numBytes;
      lastLocation = lstb->curLocation;
   }
   return(GOOD);
} /* LdrProgressInc */

/******************************************************************************
**
**  LdrProgressDone
**
**  Description:
**     If the input argument is not GOOD, terminate the dialog immediately.
**
******************************************************************************/
RETCODE EXPORT LdrProgressDone(RETCODE lderr) {
   RETCODE err;
   BOOL answer;
   BOOL keepDialog;

   lstb->startPC = startPC;
   if (hasStackInfo) {
      lstb->stackBase = stackTop;
      lstb->stackSize = stackSize;
   }
   /* NOTES: Nghia - 10/26/93
   ** Only check symbol statistic if LOAD_SYM is TRUE.  We don't want to
   ** get any old information.
   */
   if (LOAD_SYM(ldrFlags))
      SymGetLdrStats((LPSTR)&(lstb->curModule),
                     (U32 FAR *)(&lstb->numSymbols),
                     (U16 FAR *)(&lstb->numModules),
                     (U32 FAR *)(&lstb->numTypes));
   if ((err = CliLdrProgressActive((BOOL FAR *) &answer)) != GOOD) {
      return (err);
   }
   if (answer == TRUE) {
      if (lderr == GOOD)
         keepDialog = TRUE;
      else
         keepDialog = FALSE;
      if ((err = CliLdrProgressDone(keepDialog)) != GOOD) {
         return (err);
      }
      if (!keepDialog && (lderr != ER_LDR_ABORT || !reportedLoadAbort)) {
         /* NOTES: 10/29/93 - Nghia
         ** ProcessDataPart() will handle collecting Loading verify error
         */
         ErrDisplayError(lderr, CHECK_MODE);
         reportedLoadAbort = TRUE;
      }
   }
   return(GOOD);
} /* LdrProgressDone */

/******************************************************************************
**
**  LdrGetIniDir
**
******************************************************************************/
RETCODE EXPORT LdrGetIniDir(LPSTR lpIniDir) {
   RETCODE err;
   
   // find the PwrViews directory and return the pointer to the string  
   if ((err = FindIniFile()) == GOOD) 
      err = ((lstrcpy(lpIniDir,(LPSTR)pwrViewsDir) == NULL) ?
            ER_FAILED_STRCPY : GOOD);
   return(err);
}  /* LdrGetIniDir */

/******************************************************************************
**
**  LdrSpaceMode
**
******************************************************************************/
SPACE_MODE EXPORT LdrSpaceMode(VOID) {
   //return(LOAD_USER(ldrFlags) ? USER_MODE : SUPERVISOR_MODE);
   return(USER_MODE);
}  /* LdrSpaceMode */

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