/****************************************************************************
**
**  Name:  LDR.C
**
**  Description:
**      Entry points for IEEE 695 Loader.
**      Primary API's for 695 Loader DLL (LD695.DLL) live here.
**
**  Status:  CODED
**
**  $Log:   S:/tbird/arcppc/l695/ldr.c_v  $
** 
**    Rev 1.1   22 Jan 1997 17:05:20   kevin
** PowerPC
** 
**    Rev 1.0   17 Jan 1997 09:21:12   kevin
** Initial revision.
** 
**    Rev 1.4   26 Nov 1996 09:04:40   kevin
** Hera modified
** 
**    Rev 1.3   12 Sep 1996 11:39:36   kevin
** support SDS Ver.6.35
** 
**    Rev 1.2   07 Aug 1996 16:27:32   kevin
** User canceled a load progress while symbols are already in memory, 
** warning messages are shown once rather than twice.
** 
**    Rev 1.1   01 Dec 1995 10:16:30   kevin
** joyce's modification
** 
**    Rev 1.0   07 Sep 1995 10:32:12   gene
** Initial revision.
** 
**    Rev 1.100   29 Mar 1994 14:37:04   nghia
** Fixed bug for PV 2.2:
** - Combined EVENT_START_PC and EVENT_LOAD_COMPLETED to reduce duplicate
** processig.
** - Fixed bug in Shell command load warn - bad bitmask.
** 
**    Rev 1.99   01 Mar 1994 08:55:46   nghia
** Added external interface LdrGetIniDir() to return path string to PWRVIEWS.INI
** 
**    Rev 1.98   18 Jan 1994 16:54:26   nghia
** Revised ldrMangle to have the correct global state.
**  MANGLE - ldrDemangle = TRUE, NOMANGLE - ldrDemangle = FALSE.
** 
**    Rev 1.97   01 Nov 1993 09:54:50   nghia
** Fixed PPR 8975 - Report memory verify error
** Removed MemProcessVerifyError() - LDATA handles this.
** 
**    Rev 1.96   26 Oct 1993 17:43:10   nghia
** Fixed PPR 8998: load without symbol - report old symbol information
** Added check for LOAD_SYMBOL before call SymGetLdrStat().
** Removed dead code.
** Revised ProcessArgs() to remove unreachable code warning.
** 
**    Rev 1.95   11 Oct 1993 10:21:28   nghia
** Added checking for processor name using hardware and loadfile information.
** 
**    Rev 1.94   06 Oct 1993 16:37:02   tom
** 8802, 8929: process memory verify error on load
** 
**    Rev 1.93   30 Sep 1993 14:45:50   nghia
** Revised call to ProcessDbgLines to support blockSize == 0.
** Computed endDebugPart value to be used in error recovery with blockSize == 0.
** 
**    Rev 1.92   23 Sep 1993 17:27:12   nghia
** Changed ldrFlags to U32 to support new load option.
** Remove reload as an encode bit - make a separate flag.
** 
**    Rev 1.91   30 Aug 1993 11:07:36   ron
** Changes for Load Progress dialog
** 
**    Rev 1.90   23 Aug 1993 11:06:18   nghia
** Fixed bugs to support optimized code.
** 1. Revised ProcessDbgLines() to skipover to nextblock when error loading
** line number information.
** 
**    Rev 1.89   10 Aug 1993 11:31:46   courtney
** Shell option 'nomangle' set ldrDemangle flag to incorrect state - should
** be set FALSE so that names are demangled on load.
** 
**    Rev 1.88   10 Aug 1993 09:47:10   nghia
** Added set cursor to normal cursor when loader returns error.
** 
**    Rev 1.87   09 Aug 1993 11:34:46   steve
** toolchainID not defined.
** 
**    Rev 1.86   06 Aug 1993 15:34:18   courtney
** Merge 1.84.1.0 changes to trunk:
** Demangling: previous default was to always demangle on load, which
** turned out to cause problems in Intermetrics (CPU32) loadfiles which
** add double underscores to public symbol names (loader was demangling
** them away).  New default is to only demangle for MRI loadfiles, rest
** of toolchain's loadfiles will remain mangled (commandline load switch
** still available to override and force mangling/demangling).
** 
**    Rev 1.85   06 Aug 1993 15:27:06   mindy
** Added load identification string for Hiware "CPU16".
** 
**    Rev 1.84   03 Aug 1993 17:38:00   nghia
** Fixed PPR 8734:
** - Loader process compilerUsed command and retrieve its section names.
** - Removed LERR_xxxx to use standard error codes.
** - Revised some checkAbort() codes.
** 
**    Rev 1.83   29 Jul 1993 15:24:04   courtney
** Always find .ini file path; restore cursor on err exit.
** 
**    Rev 1.82   28 Jul 1993 15:13:00   nghia
** Revised to abort the load if ProcessEnvPart() return error.
** Reset useDefaultSection when start loading.
** 
**    Rev 1.81   27 Jul 1993 22:33:26   nghia
** Revised to report error other than ER_LOADFILE_HAD_INCOMPATIBLE_TOOLCHAIN
** 
**    Rev 1.80   27 Jul 1993 18:58:56   courtney
** Error return from processing Environment Part of file pops up a
** warning and continues, to allow the system to be open to new
** toolchains.
** 
**    Rev 1.79   27 Jul 1993 11:21:58   courtney
** Re-enabled 'nowarn' load option, which got broken (need to set
** bit for L695_WARNING on return from ProcessArgs, in flags parameter).
** 
**    Rev 1.78   26 Jul 1993 13:48:08   courtney
** Activate call to parse Environment Part of loadfile.
** 
**    Rev 1.77   26 Jul 1993 10:31:30   courtney
** Assembly module loading default is now OFF.
** 
**    Rev 1.76   21 Jul 1993 15:30:14   ernie
** Changed to check return value from BkGetEmulationStatus()
** 
**    Rev 1.75   15 Jul 1993 17:48:36   nghia
** Fixed bug - missing end comment. - Ondemand loading.
** 
**    Rev 1.74   14 Jul 1993 12:44:44   doug
** Use generic syntax error.
** 
**    Rev 1.73   14 Jul 1993 11:55:56   nghia
** Fix bug in ondemand loading:
** revised LdrLoad(), ProcessArgs(), LdrLoadModule().
** added LdrLoadModuleByDesc() to allow the Symbol server call to load ondemand.
** added GetModuleName() to parse module reference to module name.
** 
**    Rev 1.72   13 Jul 1993 15:04:00   courtney
** Added ldrAsm flag to control loading asm blocks (default: TRUE);
** new commandline options [asm|noasm].  Fixed 'Loader once gets invalid
** file error gets stuck' bug (PPR) by clearing initial parse state for
** each new load - this clears any pended pushback characters.  Also,
** close file if symbol load returns error (file was not being closed on
** this abnormal exit).
** 
**    Rev 1.71   13 Jul 1993 10:05:18   ernie
** 1. Added event EVENT_LDR_MEMCHANGED to inform memory server that
**    the loader has changed memory.  This is used to flush memory caches.
** 2. Removed codebufs.  These are no longer needed for loading code.
** 3. Added "isLoading=FALSE" and SetCursor(hOldCursor) in several error
**    cleanup sequences.
** 
**    Rev 1.70   28 Jun 1993 11:34:36   mindy
** Merged release 1.5 to trunk.
** 
**    Rev 1.69   25 Jun 1993 15:53:58   paul
** replace CHECK_ABORT macro with TskCheckAbort function
** This requires use of the new PVTASK.DLL because we have
** new import.
** 
**    Rev 1.68   14 Jun 1993 11:29:56   nghia
** Merged "pv bmwa" and Ernie changes.
** Removed FreeSections if load no-ondemand - causing error for call back.
** 
**    Rev 1.67   10 Jun 1993 13:51:08   steveh
** Revised macros to match coding conventions in lflags.h macros.
** 
**    Rev 1.66   06 Jun 1993 21:58:26   courtney
** Removed calls to ErrExit.
** 
**    Rev 1.65   06 Jun 1993 21:20:38   courtney
** New commandline options: warn, mangle, nomangle (for C++).
** 
**    Rev 1.64   25 May 1993 10:35:00   ernie
** Virtualized _llseek() calls in Seek695File when file buffering added.
** 
**    Rev 1.63.1.1   07 Jun 1993 12:04:28   courtney
** RELEASE PV 1.4
** Revisions for release 1.4.
** 
**    Rev 1.63.1.0   03 Jun 1993 18:45:24   nghia
** Revised to used the new error codes.
** Reporting error code using only Warning() and WarningEx().
** 
**    Rev 1.63   03 May 1993 15:15:24   nghia
** Merged mindy's code to support COSMIC.
** Revised to use the new LXlate's routines.
** 
**    Rev 1.62   20 Apr 1993 12:34:10   courtney
** Warning only, not error for processor string not 332.  MRI C++ 1.0A
** generates processor string "68020" by default, even when -pcpu32 is
** specified.
** 
**    Rev 1.61   16 Apr 1993 13:58:48   nghia
** Added LdrCliMergeSection() to support the MergeSections command.
** 
**    Rev 1.60   12 Apr 1993 16:30:06   nghia
** Revised to use the new Get695BB() call.
** 
**    Rev 1.59   31 Mar 1993 17:49:00   nghia
** Added internal command _sectionNames for auto testing.
** Added hasStackInfo flag to avoid generating stack event when there is no info
** Cleanup for coding standard.
** 
**    Rev 1.58   30 Mar 1993 08:20:38   ernie
** Changed call determining processor type to use the new
** ProcReturnCpu() instead of ProcReturnSpecificProcessor().
** This allows new cpu32 processors to be added without changing
** this file.
** 
**    Rev 1.57   24 Mar 1993 07:30:02   ernie
** Added 68333 to list of valid processors
** 
**    Rev 1.56   23 Mar 1993 18:25:52   courtney
** Remove local prototypes for PSGet* routines (inconsistent to those
** in ldr.h).
** 
**    Rev 1.55   23 Mar 1993 18:10:56   courtney
** Added semaphore to LdrLoadModule.
** 
**    Rev 1.53   22 Mar 1993 16:56:24   nghia
** Revised and fixed bug for CPU id recognizing.
** 
**    Rev 1.52   18 Mar 1993 17:25:36   nghia
** Added _PERFORMANCE_ block to report download time.
** Revised GetToolUsed to call GetSectionNames.
** Updated for bugs (BLOCK3.ABS and Intermec) fixed.
** 
**    Rev 1.51   09 Mar 1993 18:17:04   nghia
** Added HourGlass to LdrLoadWin().
** 
**    Rev 1.50   08 Mar 1993 10:32:58   nghia
** Revised events order to fix bug: load from CLI a loadfile that does not have
** the Start PC will get "Bad memory free" error, if the source window is opened
** Callback routine for ondemand loading corrupt global lbuffer pointer.
** Cleanup names and comments.
** Added routine to handle "CompilerUsed" command.
** 
**    Rev 1.49   25 Feb 1993 17:02:16   nghia
** Cleanup and updated.
** 
**    Rev 1.48   11 Jan 1993 17:03:02   john
** processor #def's were modified, changes made to match
** 
**    Rev 1.47   11 Jan 1993 11:48:56   courtney
** Check semaphore on entry to LdrLoad (CLI entry); return error now
** if load already in progress and new load requested.
** 
**    Rev 1.46   16 Dec 1992 17:51:32   courtney
** Added code to cleanup allocated address descriptors on user abort;
** don't popup load complete if unrecognized loadfile.
** 
**    Rev 1.45   03 Dec 1992 17:24:14   courtney
** . Initialize text buffer for reporting statistics on each initial load.
** . Broke out 2 return codes: SYM_ALREADY and SYM_ALREADY_CLI, by
** popular outcry.
** . Fixed 'sym already' double popup.
** . 68020 loadfiles now issue warning (not completely 332-compatible).
** .
** 
**    Rev 1.44   18 Nov 1992 22:31:52   courtney
** Added else so LdrGetFileStatus doesn't always return GOOD
** as parameter.
** 
**    Rev 1.43   06 Nov 1992 17:24:06   courtney
** Semaphore to prevent reload while already loading; allow runaccess
** load but don't generate events (no register update).
** 
**    Rev 1.42   04 Nov 1992 17:56:16   courtney
** 32-bit retcode to LdrLoad, don't play with it (may be returned
** from local routine, or Srecord loader); add parameter to Srecord
** loader invocation (pass runMode).
** 
**    Rev 1.41   22 Oct 1992 16:48:42   courtney
** Clear parse state after Srecord load.
** 
**    Rev 1.40   21 Oct 1992 16:34:02   courtney
** Cleaned up error reporting to use string resource.
** 
**    Rev 1.39   15 Oct 1992 16:57:06   courtney
** Removed stubbed out code.
** 
**    Rev 1.38   07 Oct 1992 18:53:06   courtney
** No events generated for module-only load from CLI; save loadfile
** timestamp in STATIC for callback; LdrGetFileStatus callback.
** 
**    Rev 1.37   25 Sep 1992 20:56:12   courtney
** Revise Srecord loader interface.
** 
**    Rev 1.36   18 Sep 1992 09:50:42   courtney
** Correct sense of 680x0 case; make 68030/040 loadfiles issue a
** warning only (not fatal error) - assume the user knows what he/she
** is doing.
** 
**    Rev 1.35   14 Sep 1992 10:35:40   courtney
** Fixed comment.
** 
**    Rev 1.34   14 Sep 1992 10:34:22   courtney
** Report status is now disableable; disable Srecord load till debugged;
** free old sections before new load; hourglass cursor for on-demand
** (module-only) load; check processor string in loadfile - verify
** against proc.dll.
** 
**    Rev 1.33   08 Sep 1992 10:42:46   courtney
** Added include events.h for EVENT definitions.
** 
**    Rev 1.32   03 Sep 1992 12:08:10   courtney
** Generate error if Srecord load attempted (not yet supported).
** Generate STACKTOP event when loading code, not symbols.
** 
**    Rev 1.31   21 Aug 1992 09:34:20   courtney
** Switch on loadfile format (695, Srecord currently supported) for
** 'generic Loader' support.
** Propagate error from loading code.
** Clear parse state if error on load (we are a DLL, remember?).
** 
**    Rev 1.30   14 Aug 1992 15:35:24   courtney
** Modification: stack address returned in callback will now be
** stack base plus stack size (not just stack top as before).
** 
**    Rev 1.29   14 Aug 1992 10:07:24   courtney
** Check all retcodes from server calls; correct return values for
** LdrGetLoadRegions.
** 
**    Rev 1.28   11 Aug 1992 14:01:52   courtney
** Return error codes from LdrGetLoadRegion should be masked
** with module ID bits (TBIRDERR()).
** Also, check return codes from Adr* calls in this function.
** 
**    Rev 1.27   08 Aug 1992 11:25:24   tom
** New CLI registration.
** 
**    Rev 1.26   05 Aug 1992 15:33:36   courtney
** Consolidate event generation to generate a single event,
** for either Windows or CLI invocation, on load complete.
** 
**    Rev 1.25   05 Aug 1992 13:43:48   courtney
** Made the former 'LdrGetCliOptions' entry point generic to
** 'LdrGetOptions', so it can be used by the Source Presenter
** for either CLI or Windows invocation.
** 
**    Rev 1.24   31 Jul 1992 19:17:26   courtney
** Handle 332, configuration file depends on processor.
** 
**    Rev 1.23   22 Jul 1992 16:42:36   doug
** conversion to generic shared data
** 
**    Rev 1.22   03 Jun 1992 10:34:04   courtney
** Added callback, LdrGetLoadRegion.
** 
**    Rev 1.21   01 Jun 1992 20:11:00   courtney
** Added ability to turn off loader warnings; enable abort checking
** (PPR 5344); remove error text test.
** Added altlang commandline flag, for loading non-C modules.  This
** forces the load of all that can be loaded and skips the rest.  Uses
** resync on record type NN to skip unknown attribute types and symbols.
** The 'nowarn' commandline flag was added to support this ('altlang').
** Restored InitCServer symbolic values, comments (!), along with fix
** for successful return(should not or-in TBIRDERR bits).
** 
**    Rev 1.19   18 May 1992 11:50:12   courtney
** Bracketed all standard include files to prevent duplicate defines.
** 
**    Rev 1.18   12 May 1992 09:18:46   courtney
** Reset runMode for interactive invocation; inline build flags for
** FASTBUF and LSTATUS.
** 
**    Rev 1.17   08 May 1992 09:51:22   courtney
** Added event generation at load complete for Project Definition invocation.
** 
**    Rev 1.16   07 May 1992 15:55:02   courtney
** Only generate CLILOAD event if load was successful.
** Implemented file buffering.
** If symbol table is full, CLI should not ask a question!
** Reload option for load from CLI is now implemented.
** Informational messages: number of code bytes loaded, number of modules
**   loaded, Load complete message.
** 
**    Rev 1.15   15 Apr 1992 14:24:42   courtney
** Register translation table loaded once only - 1st loader invocation,
** initial load (was loading into memory for each initial load).  Will
** only be freed when DLL is released (don't know if module-only loads
** will follow).  Fixes PPR #5512.
** 
**    Rev 1.14   03 Apr 1992 17:19:10   courtney
** Copy loadfile name and module name into return parameters for
** LdrGetCliOptions (pointer assignment is not good enough).
** 
**    Rev 1.13   03 Apr 1992 15:17:02   courtney
** Added new STATIC stackSize.  Revised LdrGetSTack callback to return size.
** 
**    Rev 1.12   02 Apr 1992 17:22:22   courtney
** Set up statics for callback in the case of cli load (for Project
** Presenter).  LdrGetCliOptions callback added to return them.
** 
**    Rev 1.11   25 Mar 1992 17:24:10   courtney
** Wrong event was being generated for STARTPC.
** 
**    Rev 1.10   24 Mar 1992 21:07:22   courtney
** Added two callback functions, for startPC and stackTop.  These are
** kept as STATIC data items, when those registered on the events
** finally call back the Loader.
** 
**    Rev 1.9   18 Feb 1992 17:54:52   courtney
** Revised path for 'file.i' (695 -> 68K register translation mapping)
** to be current directory.
** 
**    Rev 1.8   17 Feb 1992 16:31:14   courtney
** Cleanup - removed ifdef brackets.
** 
**    Rev 1.7   05 Feb 1992 13:20:20   courtney
** Now call SymAddTypeOverwriteSize after SymAddLoadStart has been called,
** to be sure symbol table is initialized before the loader mucks with it.
** 
**    Rev 1.6   30 Jan 1992 15:52:36   courtney
** No longer try to clear out shared data code buffers before downloading
** code (this trashes the descriptors).
** 
**    Rev 1.5   27 Jan 1992 15:51:22   courtney
** Revised register map table from S16 to U16.
** 
**    Rev 1.4   25 Jan 1992 14:01:30   courtney
** Added support for variable lifetimes (register mapping).
** 
**    Rev 1.3   15 Jan 1992 10:11:02   courtney
** Support for register translation: 695 -> TBird added.
** 
**    Rev 1.2   09 Jan 1992 16:53:26   courtney
** Numerous changes:
** ProcessHdr error return now checked.  Timestamp obtained for loadfile,
** and propagated to modules.  LoadStart now queries user if symbols are
** already loaded - can remove symbols.  Attempt to stub out error returns
** from SDS routines which register code buffers - these prevent re-load
** (i.e., load twice) due to STATIC data not freed.
** Top-level API's now correctly return TBird-style error.
** Fixed bugs in CLI command-line argument processing, and case-sensitivity.
** Added additional error checks to argument processing.
** More error checks for bad loadfiles.
** Overwrite size of basic types only if symbols are to be loaded.
** 
**    Rev 1.1   31 Dec 1991 10:51:16   courtney
** Numerous changes to fully support ondemand loading, as well as
** loader 'flags' (load only code, only symbols, etc.).
** Revised API names to make them shorter.
** Added call to process Environment Part of 695 file to obtain timestamps.
** 
**    Rev 1.0   12 Dec 1991 13:41:12   courtney
** Initial revision.
**
**  $Header:   S:/tbird/arcppc/l695/ldr.c_v   1.1   22 Jan 1997 17:05:20   kevin  $
**
**  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
                       /****************************
                        *                          *
                        *     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;
HDR695 Hdr695;
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;

/*
** Static data, needs to be retained for event callbacks on code/sym
** load complete - by ToolBar Presenter
*/
U32 startPC;
U32 stackTop, stackSize;
BOOLEAN hasStackInfo;
/*
** 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[IEEE_IDNLEN];
STATIC BOOLEAN isLoading;   /* semaphore to prevent re-entrancy */
U32 ldrFlags;
                       /****************************
                        *                          *
                        *    EXTERNAL VARIABLES    *
                        *                          *
                        ****************************/
extern U16 nSections;
extern U16 curbyte;
extern BOOLEAN hasToolUseDefined;
extern CHAR toolUseName[];
extern CHAR pwrViewsDir[];
extern CHAR *infomsg[];
extern BOOLEAN useDefaultSection;
extern BOOLEAN mergeSections;
extern U32 endDebugPart;

                       /****************************
                        *                          *
                        *     LOCAL PROTOTYPES     *
                        *                          *
                        ****************************/
/* LENV.C routines */
RETCODE ProcessEnvPart(HANDLE, TIMESTAMP_TYPE *, U16 *, U16 *, TOOLCHAIN_TYPE *);

/* 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[IEEE_IDNLEN] = "";
   CHAR lpModuleRef[IEEE_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 = L695_DEF_FLAGS;  /* default */
   memset(lpModule, 0, IEEE_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
**
*****************************************************************************/
RETCODE EXPORT LdrLoadWin(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 LDR695 #defines)
   ** parenthWnd  - Handle of caller's window.
   */
   BOOL oflag;
   EMULATION_STATE emuState;
   OFSTRUCT of;
   RETCODE ret, ldrerr = 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;
   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 we are loading from CLI, module has already been set, and
   ** we leave it alone.  If we are loading from a Window (either
   ** Button Bar or Source Presenter), as indicated by the runMode
   ** flag (which is set at the end of each load to INTERACTIVE
   ** state), we need to clear the module global.
   */
   if (runMode == INTERACTIVE)
      memset(lpModule, 0, IEEE_IDNLEN);
   /* initialize static data */
   InitInfostring();
   startPC = stackTop = 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 = L695_GLOBALS;
   else
      /* load everything */
      lflags = (L695_GLOBALS | L695_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;
   }
   /* clear initial parse state */
   Clear695Byte();

   /* This entry point implies we need to create modules/functions */
   oflag = MCREATE;
   if ((hBinfile = Open695File((LPSTR)lpBinfile, &of)) == -1) {
      err = ER_CANNOT_OPEN;
      isLoading = FALSE;
      if (cursorOn)
         SetCursor(hOldCursor);
      LdrProgressDone(err);
      return(err);
   }
   lstb->loadFileSize = filelength(hBinfile);
   /* Get Header info to determine different load file formats */
   if ((err = ProcessHdr(hBinfile, flags, &loadFileType)) != GOOD) {
      isLoading = FALSE;
      Close695File(hBinfile);
      if (cursorOn)
         SetCursor(hOldCursor);
      LdrProgressDone(err);
      return(err);
   }
   lstb->curLocation = tell(hBinfile); 

   switch (loadFileType) {
      case  LFILE_695:
         /* just continue thru code */
         break;
      case  LFILE_SREC:
         Close695File(hBinfile);
         /* Expect a full retcode error return */
         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;
   }

   /* Verify toolchain in Environment Part of file.  Though we don't
      understand why, if it doesn't match PowerViews current compilerUsed
      setting, the symbol table and stack presenter are messed up.  We
      just pop up a warning and continue, to allow PowerViews to be open
      to as yet undiscovered toolchains. 
   */
   if ((err = ProcessEnvPart(hBinfile, &ts, &status, &hostos, &toolchainID))
     != GOOD) {
      Close695File(hBinfile);
      isLoading = FALSE;
      if (cursorOn)
         SetCursor(hOldCursor);
      LdrProgressDone(err);
      return(err);
   }
   lstb->curLocation = tell(hBinfile); 
   if (toolchainID != MRI_TOOLCHAIN)
      /* default for non-MRI toolchain is to preserve mangled names */
      ldrDemangle = FALSE;
   
   /* NOTES:
   ** Timestamp from loadfile will be propagated to modules on creation
   */
   GetTimestamp(hBinfile, &mtime_disk);
   /* structure assign */
   tsLoadfile = mtime_disk;
   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 {
                  Close695File(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 */
               Close695File(hBinfile);
               isLoading = FALSE;
               if (cursorOn)
                  SetCursor(hOldCursor);
               LdrProgressDone(err);
               return(err);
            }
         } else if (status == QCANCEL) {
            Close695File(hBinfile);
            isLoading = FALSE;
            if (cursorOn)
               SetCursor(hOldCursor);
            LdrProgressDone(ER_LDR_ABORT);
            return(err);
         } else {
            /* Unrecognized error probably from CLI */
            Close695File(hBinfile);
            isLoading = FALSE;
            LdrProgressDone(ER_SYM_ALREADY);
            if (runMode == BATCH) {
               return(ER_SYM_ALREADY_CLI);
            }
            else {
               return(ER_SYM_ALREADY);
            }
         }
      } /* if */
      /* Put up hourglass for a lengthy operation */

      if (!inProgress) {
         hHourGlassCursor = LoadCursor(NULL, IDC_WAIT);
         hOldCursor = SetCursor(hHourGlassCursor);
         cursorOn = TRUE;
         SetCursor(hHourGlassCursor);
      }

      if ((err = ProcDefaultStackSize((U32 FAR *)&stackPushSize)) != GOOD ) {
         LdrProgressDone(err);
         return(err);
      }
      /* define stack-push size to the symbol server
      ** thus far, MRI C defines 'int' as a 4-byte quantity
      ** 68k is the same as 332 in this regard.
      */
      SymAddTypeOverwriteSize((U8)T695_INT, stackPushSize);
      SymAddTypeOverwriteSize((U8)T695_UNS, stackPushSize);
      SymAddTypeOverwriteSize((U8)T695_UNS_INT, stackPushSize);
   }

   /* Set up and allocate sections (deallocate previous, if any)
   ** we now need to do this even if symbols are not loaded, so that
   ** we can provide info to the src/dasm presenter about load regions
   */
   FreeSections(nSections);
   if ((err = ProcessSecPart(hBinfile, LOAD_SYM(flags))) != GOOD) {
      Cleanup(flags, ondemand);
      Close695File(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)) {
      Close695File(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) {
      Close695File(hBinfile);
      Cleanup(flags, ondemand);
      if (cursorOn)
         SetCursor(hOldCursor);
      isLoading = FALSE;
      LdrProgressDone(err);
      return (err);
   }
   /* Do not load symbols if code loading failed */
   if (LOAD_CODE(flags)) {
      if (((err = BkProcessorMustBeHalted()) != GOOD) ||
         /* Process and load code and constant data record */ 
         ((err = ProcessDataPart(hBinfile, &nLoaded)) != GOOD)) {
         /* RunAccess is not on, cannot load */
         Close695File(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);
   }
   /* Now always do this, even if code is not loaded */
   if ((err = ProcessTrailPart(hBinfile, &startPC)) != GOOD)
      Warning(err); /* Report warning and continue */
   lstb->curLocation = tell(hBinfile); 
   /* Check for user abort load process */
   if (((err = TskCheckAbort(&abortFromEsc)) != GOOD) ||
       (abortFromEsc != FALSE)) {
      Close695File(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) {
      Close695File(hBinfile);
      Cleanup(flags, ondemand);
      if (cursorOn)
         SetCursor(hOldCursor);
      isLoading = FALSE;
      LdrProgressDone(err);
      return (err);
   }
   /* Symbol records from user's code
   ** now passes timestamp and propagates to modules as created
   */
   if (LOAD_SYM(flags)) {
      nLoaded = 0L;
      if ((err = ProcessDebugPart(hBinfile, oflag, lflags, &mtime_disk,
            &nLoaded)) != GOOD) {
         if (err == ER_LDR_ABORT) {
            Close695File(hBinfile);
            Cleanup(flags, ondemand);
            if (cursorOn)
               SetCursor(hOldCursor);
            isLoading = FALSE;
            LdrProgressDone(ER_LDR_ABORT);
            return(ER_LDR_ABORT);
         } else {
            /* just report */
            Warning(err);
         }
         /* Don't exit here - need to load publics and report status */
      }
      lstb->curLocation = tell(hBinfile); 
      /* Public symbols from linked-in code, always loaded */
      if ((err = ProcessExtPart(hBinfile)) != GOOD) {
         Cleanup(flags, ondemand);
         /* don't exit, but fall thru to report status */
      }
      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);
   }

LDFinal:
   /* clear parser for next load */
   ClearParseState();
   Close695File(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 LDR695 #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(parenthWnd, LdrLoadWin);
   inProgress = FALSE;
   return (ret);

}  /* LdrLoadProgress */

/********************************************************************************
**
**  LdrLoadModuleByDesc
**
******************************************************************************/
RETCODE EXPORT LdrLoadModuleByDesc(LPSTR lpBinfile, LPSTR moduleName,
                                   SYM_DESCRIPTOR moduleDesc) {
   BOOL hourGlassOn = FALSE;
   CHAR blockName[IEEE_IDNLEN];
   HCURSOR hOldCursor, hHourGlassCursor;
   OFSTRUCT of;
   TIMESTAMP_TYPE mtime_disk;
   U16 blockType, lflags = 0, ondemand;
   U32 blockPos, blockSize, nextBlockPos;
   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 = L695_LOCALS;
   /* We only need to open modules/functions/line blocks (not create them);
   ** they were created by the initial load.
   */
   if ((hBinfile = Open695File((LPSTR)lpBinfile, &of)) == -1) {
      isLoading = FALSE;
      return(ER_CANNOT_OPEN);
   }
   /*
   ** For module load, we aren't going to use the environment stored time,
   ** we'll go directly to the disk file.
   */
   GetTimestamp(hBinfile, &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);
   /* If timestamps don't match, any subsequent attempt to get module offset
   ** from ST is going to be bogus, so quit now.
   */
   if (symerr == ER_TIMESTAMPS_DONT_MATCH) {
      err = ER_MODTIME;
      /* This is fatal, since we only wanted to load this module */
      goto CleanUpAndExit;
   }
   /*
   ** 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 = Seek695File(hBinfile, moduleFileOffset,
         SEEK_SET)) == -1L) {
      err = ER_BAD_SEEK;
      goto CleanUpAndExit;
   }
   lstb->curLocation = moduleFileOffset;
   /* Load only line numbers and local symbols  */
   if (MatchRecord(hBinfile, RT_BB) != GOOD) {
      err = ER_MODULE_NOT_FOUND;
      goto CleanUpAndExit;
   }
   Get695BB(hBinfile, &blockSize, (LPSTR)blockName, &blockType);

   /* NOTES: Compute the next block to process - should be a BB5 */
   nextBlockPos = blockPos + blockSize;

   /* Other error/validity checks */
   if ((blockType != BB_MODULE) ||
       (lstrcmp((LPSTR)blockName, moduleName) != 0)) {
      err = ER_MODULE_NOT_FOUND;
      goto CleanUpAndExit;
   }
   lstb->curLocation = tell(hBinfile); 
   /* 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,
                                       typeOffset)) != 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 = ProcessDbgLines(hBinfile, ondemand, MOPEN, &nextBlockPos,
                         &blockSize, moduleDesc);
   /* Call SymAddLoadEnd to sort symbols */
   if ((symerr = SymAddLoadEnd()) != GOOD) {
      Warning(symerr);
      if (err == GOOD) err = ER_CLOSE_SYM;
   }

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


/********************************************************************************
**
**  LdrLoadModule
**
******************************************************************************/
RETCODE EXPORT LdrLoadModule(LPSTR lpBinfile, LPSTR moduleName) {
   BOOL oflag, cursorOn = FALSE;
   CHAR blockName[IEEE_IDNLEN];
   CHAR moduleRef[1] = "";
   HCURSOR hOldCursor, hHourGlassCursor;
   OFSTRUCT of;
   SYM_DESCRIPTOR moduleDesc;
   TIMESTAMP_TYPE mtime_disk;
   U16 blockType, lflags = 0, ondemand;
   U32 blockPos, blockSize, nextBlockPos;
   U32 moduleOffset, 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 = L695_LOCALS;
   /* We only need to open modules/functions/line blocks (not create them);
   ** they were created by the initial load.
   */
   oflag = MOPEN;
   if ((hBinfile = Open695File((LPSTR)lpBinfile, &of)) == -1) {
      err = ER_CANNOT_OPEN;
      isLoading = FALSE;
      return(err);
   }
   /*
   ** For module load, we aren't going to use the environment stored time,
   ** we'll go directly to the disk file.
   */
   GetTimestamp(hBinfile, &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);
   /* If timestamps don't match, any subsequent attempt to get module offset
   ** from ST is going to be bogus, so quit now.
   */
   if (symerr == ER_TIMESTAMPS_DONT_MATCH) {
      err = ER_MODTIME;
      /* This is fatal, since we only wanted to load this module */
      goto CleanUp;
   }
   /*
   ** 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 = Seek695File(hBinfile, moduleOffset, SEEK_SET)) == -1L) {
      err = ER_BAD_SEEK;
      goto CleanUp;
   }
   lstb->curLocation = moduleOffset;
   /* Load only the following:
   **     line numbers
   **     local symbols
   */
   /* Prepare for calling */
   if (MatchRecord(hBinfile, RT_BB) != GOOD) {
      err = ER_MODULE_NOT_FOUND;
      goto CleanUp;
   }
   Get695BB(hBinfile, &blockSize, (LPSTR)blockName, &blockType);
   lstb->curLocation = tell(hBinfile); 
   /* Other error/validity checks */
   if ((blockType != BB_MODULE) ||
         (lstrcmp((LPSTR)blockName, moduleName) != 0)) {
      err = ER_MODULE_NOT_FOUND;
      goto CleanUp;
   }
   /* Put up hourglass for a lengthy operation */
   if (!inProgress) {
      hHourGlassCursor = LoadCursor(NULL, IDC_WAIT);
      hOldCursor = SetCursor(hHourGlassCursor);
      cursorOn = TRUE;
   }
   /* Obtain module descriptor from ST for adding line numbers */
   /* NOTES: module gets closed inside here */
   if ((err = ProcessDbgModule(hBinfile, (LPSTR)blockName, moduleOffset,
         lflags, typeOffset, &moduleDesc, &mtime_disk)) != 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 */
   nextBlockPos = moduleOffset + blockSize;
   if ((err = ProcessDbgLines(hBinfile, ondemand, oflag, &nextBlockPos,
                              &blockSize, moduleDesc)) != 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);

   Close695File(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 = L695_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 |= L695_ASM;
            else return(ER_CLI_SYNTAX);
            break;
         case 'C':   /* load code */
            if (strnicmp(cparm, "COD", 3) == 0) *ldrflags |= L695_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 &= ~L695_DBG;
            else if (strnicmp(cparm, "NOCOD", 5) == 0)
               *ldrflags &= ~L695_CODE;
            else if (strnicmp(cparm, "NOPRO", 5) == 0)
               *ldrflags &= ~L695_STATUS;
            else if (strnicmp(cparm, "NODEM", 5) == 0)
               *ondemand = FALSE;
            else if (strnicmp(cparm, "NOASM", 5) == 0)
               *ldrflags &= ~L695_ASM;
            /* no more war */
            else if (strnicmp(cparm, "NOWAR", 5) == 0) {
               *ldrflags &= ~L695_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 |= L695_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 |= L695_DBG;
            else if (strnicmp(cparm, "SUPER", 5) == 0)
               *ldrflags &= ~L695_SPACE;
            else return(ER_CLI_SYNTAX);
            break;
         case 'U':   /* User Mode */
            if (strnicmp(cparm, "USER", 4) == 0)
               *ldrflags |= L695_SPACE;
            else return(ER_CLI_SYNTAX);
            break;
         case 'W':   /* warnings */
            if (strnicmp(cparm, "WAR", 3) == 0) {
               *ldrflags |= L695_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);
}

/******************************************************************************
**
**  ProcessHdr
**
******************************************************************************/
RETCODE ProcessHdr(HANDLE hfile, U16 flags, LFILE_TYPE *loadFileType) {
   /* flags - load code or symbols only? */
   LOOP_VAR i;
   RETCODE err = GOOD;

   /* 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 = ProcessModBegin(hfile, flags)) != GOOD) {
      /* Determine what kind of loadfile this is */
      if (IsSrecord(curbyte) == TRUE) {
         *loadFileType = LFILE_SREC;
         return(GOOD);
      }
      return(err);
   }
   ProcessAD(hfile);

   /* Obtain file offsets */
   for (i = 0; i < MAX_PARTS; i++) {
      if ((err = ProcessHdrOffset(hfile)) != GOOD) {
         Warning(err);
         return(err);
      }
   }
   /* Set endDebugPart for error recovery */
   if (Hdr695.hdr_data && (Hdr695.hdr_data > Hdr695.hdr_dbg))
      endDebugPart = Hdr695.hdr_data;
   else
      endDebugPart = 0L;
   
   *loadFileType = LFILE_695;
   return(GOOD);
}  /* ProcessHdr */

/******************************************************************************
**
**  ProcessModBegin
**
******************************************************************************/
#pragma argsused
RETCODE ProcessModBegin(HANDLE hfile, U16 flags) {
   /* flags - load code/symbols only? */
   CHAR pidn[IEEE_IDNLEN], cpuName[20];
   RETCODE err;
   PROCESSOR_FAMILY pFamily;
   PROC_CPU cpu;

   /*
   ** ProcessRecord - module begin get processor type and module name
   ** Returns GOOD on success, error return for failure.
   */
   /* propagate error to caller */
   if (MatchRecord(hfile, RT_MB) != GOOD) {
      /* rewind file so we can determine what type of loadfile it is */
      Unget695Byte(hfile, curbyte);
      return(ER_BAD_HDR);
   }

   Get695Idn(hfile, (LPSTR)pidn);
   /* lets see what our system thinks is set up - be sure it matches the
      Loader's register translation table */
/**** removed by Kevin
   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);
   if ((err = ProcReturnProcessorName((LPSTR)&cpuName)) != GOOD)
      return(err);
   Hdr695.hdr_cpu = cpu;

   /* NOTES: Nghia - 03/22/93
   ** Changes these processor string to support loadfile with new CPU.
   */
   /* Check for Processor type from the MB record */
   /* Only 68000, 010 are 68332 compatible, others are not compatible */
   if ((strstr(cpuName, pidn)       == 0) &&
       (strnicmp(pidn, "CPU32",  5) != 0) &&
       (strnicmp(pidn, "68332",  5) != 0) &&
       (strnicmp(pidn, "CPU16",  5) != 0) &&
       (strnicmp(pidn, "6816",   4) != 0) &&
       (strnicmp(pidn, "68HC16", 6) != 0) &&
       (strnicmp(pidn, "68331",  5) != 0) &&
       (strnicmp(pidn, "68000",  5) != 0) &&
       (strnicmp(pidn, "68010",  5) != 0) ) {
       WarningEx(ER_INCOMPATIBLE_CPU, (LPSTR)pidn);
       /* NOTE: MRI C++ generates 68020 processor string by default (even
       ** when -pcpu32 is used to compile.
       */
   }
   Get695Idn(hfile, (LPSTR)Hdr695.hdr_filename);
   return(GOOD);
}  /* ProcessModBegin */

/******************************************************************************
**
**  ProcessAD - Process Address Description
**
******************************************************************************/
RETCODE ProcessAD(HANDLE hfile) {
   int n;

   MatchRecord(hfile, RT_AD);
   Get695Number(hfile, &Hdr695.ad_info.MAU);
   Get695Number(hfile, &Hdr695.ad_info.MAU_len);
   /* get optional endian value */
   Get695Byte(hfile, (U16 *)&n);
   if (n == BYTE_ORDER_8086 || n == BYTE_ORDER_68K)
      Hdr695.ad_info.endian = n;
   else
      Unget695Byte(hfile, n);
   return(GOOD);
}  /* ProcessAD */

/******************************************************************************
**
**  ProcessHdrOffet
**
******************************************************************************/
RETCODE ProcessHdrOffset(HANDLE hfile) {
   U16 hsection;
   U32 offset;

   if (Match2Record(hfile, RT_AV) == ER_NO_MATCH)
      return(ER_NO_MATCH);
   Get695Number(hfile, &hsection);
   Get695Offset(hfile, &offset);
   switch (hsection) {
      case W0:
         Hdr695.hdr_AD = offset;
         break;
      case W1:
         Hdr695.hdr_env = offset;
         break;
      case W2:
         Hdr695.hdr_sec = offset;
         break;
      case W3:
         Hdr695.hdr_ext = offset;
         break;
      case W4:
         Hdr695.hdr_dbg = offset;
         break;
      case W5:
         Hdr695.hdr_data = offset;
         break;
      case W6:
         Hdr695.hdr_trlr = offset;
         break;
      case W7:
         Hdr695.hdr_modend = offset;
         break;
      default:
         return(ER_UNRECOGNIZED_SEC);
   }
   return(GOOD);
}  /* ProcessHdrOffset */

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

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