/*
 *
 * $Log: nwpre3x.c,v $
 * Revision 1.4  1998/09/07 21:23:01  jlawson
 * fixed line breaks
 *
 * Revision 1.3  1998/06/26 08:39:27  cyruspatel
 * Modified netware.cpp to use new GetTimesliceBaseline() function. Added
 * netware.h to repository.
 *
 * Revision 1.1  1998/06/14 08:13:02  jlawson
 * Incorporated Cyrus' NetWare specific stuff into the cvs tree
 *
 */

/*
 * This file contains startup code similar to what crt0 does elsewhere.
 * Written August 1997 Cyrus Patel <cyp@fb14.uni-mainz.de>
 *
 * Unlike Novell's prelude, its in source, is backwards compatible with 
 * NetWare 3.x and supports Watcom c/cpp lib startup/exit code (requires 
 * externals). It does not handle redirection.
 *
 * Unlike Watcom's prelude, its in source and it doesn't have half a ton 
 * of gunk in it to get most of the pseudo-static clib to get pulled in.
 * (This prelude helps make Watcom's static lib (plib[mt]3s) unneccesary)
 */

#ifdef __cplusplus
  #error Error: This needs to be a 'C' not 'CPP' file.
#endif   

/*
 * Try very hard to minimize #including anything. 
 * (Just in case Novell changes even this simple api)
 */

typedef long wchar_t;           /* unicode char */
typedef unsigned int size_t;    

#define WATCOM_ATEXIT   /* add support for watcom exit code */

/*
  Both "_Prelude" and "_Stop" are simply the default names. Other names
  can be defined via linker commands. _StartNLM(), _SetupArgV() and 
  _TerminateNLM() are in the standard CLib. 

       NLM Loader (OS)                       NLM Unloader (OS)
        |         |                              |     |
       "_Prelude(...)"                          "_Stop()"
        |         |                              |     |
       _StartNLM(...)                            |     `---<----,
    (BeginThreadGroup())                         |              |
           |                                     |              |
           |   ,-----ExitThread() -- _exit() -,  |              |
           |   |                |,-----'      |  |              |
       _cstart_()               ||        _TerminateNLM()       |
        |      |                ||            |  |              |
    ,-<-'      `---<-----------,||          raise(6);           |
    |                          |||        DestroyThreads        |
    `- main() -> (by return;) -'||     DestroyThreadGroup       |
         | `->- (by ExitThread)-'|           atexit             |
         `->- (by exit())---->---'            |  `--- atunload -'
                                            KillMe()
*/                           

/* ---------------------------------------------------------------------- */

#define TRADITIONAL_NLM_INFO_SIGNATURE 0x00000000
#define NLM_INFO_SIGNATURE             0x494D4C4E /* 'NLMI' */

#define TRADITIONAL_FLAVOR             0

/* versions within the traditional flavor: */
#define TRADITIONAL_VERSION            0
#define LIBERTY_VERSION                1

typedef struct NLMInfo
{
  union { unsigned long signature; int ID; } sig;
                                           /* 0 = old NLM, 0x494D4C4E = new */
  unsigned long flavor;                    /* major modality differences */
  unsigned long version;                   /* minor deviation within flavor */
  size_t   sizeof_long_double;
  size_t   sizeof_wchar_t;
} NLMInfo;

static NLMInfo kNLMInfo = 
                {{TRADITIONAL_NLM_INFO_SIGNATURE}, /* or 0x494D4C4E 'NLMI' */
                  TRADITIONAL_FLAVOR,   /* 0 */
                  LIBERTY_VERSION,   /* 1, 0=TRADITIONAL_VERSION */
                  sizeof(long double),  /* 10 */
                  sizeof(wchar_t) };    /* 4 */

/* ---------------------------------------------------------------------- */

/* The following is for compatability with Watcom's libraries. The Watcom 
   compiler adds references to an external '_argc' to every object file. */   

extern void __FiniRtns(void);
extern void __InitRtns(void);
extern void atexit( void (*)(void) );
int _argc=0; char **_argv=0;

#ifdef WATCOM_ATEXIT
static void _watlib_exit(void)
{
  _asm mov al,0
  _asm mov dl,0ffh  
  _asm call __FiniRtns
  return;
}
#endif

static void _watlib_init(int argc, char *argv[])
{
 _argc = argc; _argv = argv;
 #ifdef WATCOM_ATEXIT
   atexit( _watlib_exit );
   _asm mov al,0ffh
   _asm call __InitRtns
 #endif
 return;
}

/* ---------------------------------------------------------------------- */

/* _Stop is the default "name" of stop function called from the OS 
   The OS doesn't load by name, but by whatever the linker set the "stop"
   (see linker OPTION STOP) function address to. */

#define TERMINATE_BY_EXTERNAL_THREAD   0
#define TERMINATE_BY_UNLOAD            5

extern unsigned long _TerminateNLM( int NLMID, int threadID, int status );

void _Stop( void )
{
   _TerminateNLM( kNLMInfo.sig.ID, TERMINATE_BY_EXTERNAL_THREAD /* 0 */ , 
                                    TERMINATE_BY_UNLOAD  /* 5 */ );
   return;
}                                      

/* ---------------------------------------------------------------------- */

/* _cstart_ (called userStartFunc() in Novell documentation) is called 
   from _StartNLM. It in turn calls the first program function, which 
   in the ANSI C "hosted" execution model is main(int argc, char *argv[]).

   Since the NetWare CLIB has a function for setting up argc and argv, 
   there is no need to set the command line here, and we call the CLIB
   function (_SetupArgV()) to do the work for us. There is a special
   _SetupArgV() in NetWare 4.11 that handles STDIN/STDOUT redirection 
   called _SetupArgV_411, otherwise we have to handle redirection ourselves. 
   
   _SetupArgV() calls main() (or whatever) with argc and argv and then
   returns the errorcode returned by main().
*/

extern int _SetupArgv ( int (*main)(int arg, char *argv[]) );
extern int _SetupArgV_411 ( int (*main)(int arg, char *argv[]) );
extern int main( int argc, char *argv[] );

static int _pre_main(  int argc, char *argv[] )
{
  _watlib_init( argc, argv ); 
  return main( argc, argv );
}  

int _cstart_()  
{
  return _SetupArgv( _pre_main );
}

/* ---------------------------------------------------------------------- */

/* _Prelude is the default "name" of start function called from the OS 
   The OS doesn't load by name, but by whatever the linker set the "start"
   (see linker OPTION START) function address to. 
   
   Special note on "custom data":  NetWare allows modules to read custom 
   data into system memory during initialization. This data can be anything 
   that might be required. For example, the driver may need to read in 
   firmware to be loaded into a co-processor board. To define the custom 
   file, use the CUSTOM keyword in the driver's definition file followed by 
   the file's name (custom files are simply appended to the NLM/DSK/NAM/LAN
   file). NetWare passes the custom data file's handle, starting offset, 
   size, and the Read routine address to the Initialize routine, where it 
   should be saved upon entry. Initialize Driver can read the file into 
   memory by calling the Read routine using the syntax shown below:
   ReadCustomDataRoutine( LONG CustomDataFileHandle, LONG CustomDataOffset,
                           BYTE *CustomDataDestination, LONG CustomDataSize);
   Note: the CustomDataFileHandle is the same as the NLM file handle.

   Of Interest: The stack frame size is [ebp-esp] (at the start of _Prelude).
*/

extern unsigned long _StartNLM( void *NLMHandle, void *initErrorScreenID, 
   unsigned char *cmdLineP, unsigned char *loadDirectoryPath,
   unsigned long uninitializedDataLength, unsigned long NLMFileHandle,
   unsigned long (*readRoutineP)(), unsigned long customDataOffset,
   unsigned long customDataSize, NLMInfo *NLMInformation,
   int (*userStartFunc)() );

extern char _edata;
extern char _end;
void *memset( void *__s, int __c, unsigned int __n );

unsigned long _Prelude( void *NLMHandle, void *initErrorScreenID,
   unsigned char *cmdLineP, unsigned char *loadDirectoryPath,
   unsigned long uninitializedDataLength, unsigned long NLMFileHandle,
   unsigned long (*readRoutineP)(), unsigned long customDataOffset,
   unsigned long customDataSize )
{
   //register unsigned char *p = &_end, *q=&_edata;
   //while (p < q) *p++=0;
   //memset( ((void *)(&_edata)), 0, ((&_end)-(&_edata)) );

   _asm push edi
   _asm mov ecx,offset _end
   _asm mov edi,offset _edata
   _asm xor eax,eax
   _asm sub ecx,edi
   _asm mov edx,ecx
   _asm cld
   _asm shr ecx,2
   _asm repnz stosd
   _asm and edx,3
   _asm mov ecx,edx
   _asm repnz stosb
   _asm pop edi

   return _StartNLM( NLMHandle, initErrorScreenID, cmdLineP, 
     loadDirectoryPath,  uninitializedDataLength, NLMFileHandle, readRoutineP,
     customDataOffset, customDataSize, &kNLMInfo, _cstart_ );
}

