/*
	File:		Startup.c

	Contains:	Main startup for the Process Manager.

	Written by:	Erich Ringewald

	Copyright:	© 1986-1993 by Apple Computer, Inc., all rights reserved.

	Change History (most recent first):

	  <SM28>	 7/21/93	joe		Back out <SM27>.
		<27>	10/28/92	DTY		Use new Get/Set macros to access ExpandMem.
		<26>	 9/25/92	DRF		Get rid of MyGestalt, since inline glue exists
		<25>	 8/26/92	DTY		Change ModSquadÕs funky conditional to use #ifdefs like everyone
									else.
		<24>	 8/26/92	DTY		Roll in latest changes for A/UX.
		<23>	 8/17/92	hjcr	Adding support for OCE's "defer drag" feature.  Conditionally
									compiled under MODSQUAD
		<22>	 5/11/92	JSM		#1029437 <DC>: Set emProcessMgrExists to true after calling
									MyNewGestalt().
		<21>	 3/30/92	DTY		#1025416,<DDG>: Look for Skia in InitConfig and set skiaExists
									accordingly.
		<20>	 3/23/92	JSM		OSEvents.h is obsolete, use Events.h.
		<19>	 1/11/92	YK		Initialize new globals for the TSM.
		<18>	11/22/91	DTY		Initialize allocateGMBlkInSysHeap to be false. GMBlk normally
									tries to allocate itÕs block in the Process Manager zone first,
									except when an 'obit' event is being sent.
		<17>	 11/1/91	DTY		Set TOPMAPHANDLE to point to the first system override map
									instead of SYSMAPHANDLE in main().
		<16>	10/15/91	YK		Added initializing code for the Text Service Manager.
		<15>	 9/16/91	DFH		Set initialProcessSR in InitializeScheduler. Needed for
									NuKernel.
		<14>	 5/23/91	dba		do a style of coercion that MPW 3.2 C likes
		<13>	 1/28/91	DFH		JSM,#81425: Removed inhibit_DAs mechanism.
		<12>	 1/15/91	DFH		(VL) Conditionalize out FloatAUXUp.
		<11>	 1/14/91	DFH		(dba) Make system mode per process.
		<10>	 1/14/91	DFH		(JDR) Conditionalize out AUX support.
		 <8>	  1/8/91	DFH		(rdd) Got rid of pErrantProcess since it is no longer needed.
		 <7>	12/15/90	DFH		Main now initializes srcMyScrapInfo.pScrapOwner.
		 <6>	12/14/90	DFH		InitializeScheduler now initializes kernelMode.
		 <5>	12/13/90	JWM		For 7.0B3, sending the AppleEvent message to the network
									extension (Network Setup) in the shell program (Finder 7.0)
									about the PPCToolbox not initializing was removed.
		 <4>	 12/5/90	DFH		Integrated AUX support.
		 <3>	11/26/90	DFH		Change trespass() to dbmsg().
		 <0>	  9/2/86	ELR		New Today.

*/

#pragma segment INIT

#include <types.h>
#include <memory.h>
#include <menus.h>
#include <files.h>
#include <quickdraw.h>
#include <events.h>
#include <resources.h>
#include <scrap.h>
#include <osutils.h>
#include <retrace.h>
#include <fonts.h>
#include <dialogs.h>
#include <textedit.h>
#include <segload.h>
#include <errors.h>
#include <GestaltEqu.h>
#include <EPPC.h>
#include <AppleEventsInternal.h>
#include <MFPrivate.h>
#include <ResourceMgrPriv.h>

#include "Lomem.h"
#include "SysMisc.h"
#include "Glue.h"
#include "Data.h"
#include "OSDispatchPrivate.h"
#include "Patches.h"
#include "Aux.h"
#include "Puppet.h"
#include "HList.h"
#include "Zone.h"
#include "EppcPrivate.h"
#include "AppleEventExtensions.h"

/* Some function prototypes that should be in (yet another) header file */
void			MyNewGestalt(void);
void			InitExceptions(void);
void			GetSwitchTab(void);
void			InitSystemMenus(void);
void			VNInit(void);
void			FMKill(Boolean);

/* Function prototypes internal to this file */
void			InitializeEnvironment(void);
void			InitializeScheduler(void);
void			InitConfig(void);
void			FloatMainUp(void);
void			FloatKernelUp(void);
void			FloatEppcUp(void);
void			FloatAUXUp(void);

main(void)
	{
	LaunchResults			finderLaunchResults;
	Handle					ourScrapHdl;
	HighLevelEventRecord	theEvent;
	FSSpec					finderSpec;

	_DataInit();							/* Initialize our global data */
	InitializeEnvironment();				/* Adjust machine environment to our liking */
	InstallPatches();						/* Install our patches */
	InitializeScheduler();					/* Initialize our global structures */

	/* Locate Process Mgr's scrap handle BEFORE launches */
	ourScrapHdl = SCRAPHANDLE;

	/* In case BG app dies before we launch FINDERNAME */
	pShellProcess = ((Ptr) -1);

	/* Launch startup background applications */
	if ((EXTENSIONSENABLEBYTE & (1 << ExtensionsEnabledBit)) != 0)
		LaunchFacelessTasks();

	THEZONE = ProcessMgrZone;
	if ((finderLaunchResults.LaunchError = FSMakeFSSpec(BOOTVOL, 0, FINDERNAME, &finderSpec)) == noErr)
		StandardLaunch(&finderSpec, launchUseMinimum, 0, nil, &finderLaunchResults, nil);

	/* Quick Restore:  Have to set zone to sys to do _UnloadSeg correctly */
	THEZONE = SYSZONE; CURMAP = SYSMAP; TOPMAPHANDLE = GetOverrideMap(SYSMAPHANDLE);

	/* Bomb box if the Finder launch failed */
	if (finderLaunchResults.LaunchError != noErr)
		SysError(dsFinderErr);

	/* Remember who we launched from this special place of honor */
	pShellProcess = finderLaunchResults.pNewProcess;
	pShellProcess->p_dadProcess = nil;
	ShellWasFinderType = IS_FINDER(pShellProcess);

	/* Dump scrap since we don't use it */
	if (ourScrapHdl != nil)
		DisposHandle(ourScrapHdl);

	/* Next Process Mgr call should use Process Mgr stack as kernel stack */
	kernelstack = initCurStackBase;

	/* Prepare to execute Finder */
	coercionState = CS_INIT;
	srcMyScrapInfo.pScrapOwner = pFrontProcess = pShellProcess;
	pFrontProcess->p_state = PRRUN;

	/* Unload initialization code before leaving, and also
	 * remove the handle from the system res map.
	 * NOTE: This leaves us running in a dead code segment.  But the current
	 * Resource Manager and Memory Manager let us get away with this
	 * for now.  All we need to do is make the switch_task call, since its
	 * code is in another segment, and will never return.
	 */
	{
	CodeEntry		*pCodeEntry;	/* Needed explicitly because of C compiler bug */

	pCodeEntry = InitializeEnvironment;
	UnloadSeg(pCodeEntry);
	ReleaseResource(GET_SCOD_HDL_FROM_FNC_PTR(pCodeEntry));
	}

	/* Send Finder AppleEvents a) to say that machine was just booted, and b) to give a
	 * message to the network extension if PPC wouldn't initialize.
	 */
	SetAppleEvent(aeStartup, &theEvent);
	(void) BeginSystemMode();
	(void) PostHighLevelEvent((EventRecord *) &theEvent, (u_long) &pShellProcess->p_serialNumber, 0, nil, 0, receiverIDisPSN);

	/* For 7.0B3 the message to the network extension in the shell program about the
	 * PPCToolbox not initializing was removed.  No one to support Responder, so it
	 * was rewritten.
	 */

	(void) EndSystemMode();

	/* Switch to the Finder we launched, never to return (hopefully) */
	switch_task(pFrontProcess);
	dbmsg("Illegal return to the Process Manager.");
	}


/* Dummy routines to force load of CODE segments */
#pragma segment Main

void
FloatMainUp(void)
	{
	}

#pragma segment kernel_segment

void
FloatKernelUp(void)
	{
	}

#pragma segment eppc_segment

void
FloatEppcUp(void)
	{
	}

#ifdef HAS_AUX_PROCESSMGR
#pragma segment AUX_SEGMENT

/* null procedure so that segment can get loaded appropriately */
void FloatAUXUp(void)
	{
	}
#endif HAS_AUX_PROCESSMGR

#pragma segment INIT

/* This is the size we give the system heap at startup.  It's exact value is somewhat
 * unimportant.  It mainly determines how many times we'll have to call the System
 * zone or Process Mgr zone GrowZone procedures before things settle out.  Make sure
 * it's small enough that the initial Finder launch goes smoothly.
 */
#define INIT_SYS_HEAP_SIZE	(96*1024)

/* InitializeEnvironment.  Set up the initial world (i.e. heap, qd globals, etc.). */
void
InitializeEnvironment(void)
	{
	CodeEntry					*pCodeEntry;
	register OSDispatchEntry	*pDispatchEntry;

	/* Set up our own lomem A5 ptr */
	PROCESSMGRGLOBALS = CURRENTA5;

	/* Save initial location zero before we call any traps that might change it! */
	initLocationZero = LOCATIONZERO;

	/* Get configuration information */
	InitConfig();

	/* Get table of switchable low memory locations (in System Heap) */
	GetSwitchTab();

	/* Expand the zone up to the APPLLIMIT */
	MaxApplZone();

	/* initialize the scrap, if necessary */
	THEZONE = SYSZONE;
	if (SCRAPSTATE == (-1))
		(void) ZeroScrap();
	THEZONE = APPLZONE;

	/* Unload data init seg and release its CODE resource (to get it out of map). */
	pCodeEntry = _DataInit;
	UnloadSeg(pCodeEntry);
	ReleaseResource(GET_SCOD_HDL_FROM_FNC_PTR(pCodeEntry));

	/* Set up dynamic heaps */
	InitHeaps();

	THEZONE = SYSZONE;

	/* Load in the code segments now that zone expansion code is in place */
	FloatMainUp();
	FloatKernelUp();
	FloatEppcUp();
#ifdef HAS_AUX_PROCESSMGR
	if (AUXIsPresent)
		FloatAUXUp();
#endif HAS_AUX_PROCESSMGR

	/* Alter dispatch table to go directly to routine, bypassing the jump table */
	pDispatchEntry = &OSDispatchTable[0];
	while (pDispatchEntry->dispatchAddr != nil)
		{
		pDispatchEntry->dispatchAddr = ROUTINE_ADDR(pDispatchEntry->dispatchAddr);
		pDispatchEntry += 1;
		};

	/* Make sure PROCESSMGRGLOBALS world is reasonable */
	InitGraf(&qd.thePort);
#ifdef MM_DEBUG
	{
	pascal void			DebugNotifyPurge(void);
	extern Ptr			oldPurgeProc;

	oldPurgeProc = (Ptr)SYSZONE->purgeProc;
	SYSZONE->purgeProc = (ProcPtr)DebugNotifyPurge;
	}
#endif MM_DEBUG

	/* Initialize window manager in system heap ('cause it's shared in our patch)
	 * NOTE: Calls InitFonts for InitWindow's sake on older machines.
	 */
	InitFonts();
	InitWindows();
	MFLayer = GetCurLayer();
	IMLayer	= nil;
	THEZONE = APPLZONE;

#ifdef HAS_AUX_PROCESSMGR
	/* Adjust ourselves to running under AUX */
	if (AUXIsPresent)
		AUX_Setup();
#endif HAS_AUX_PROCESSMGR

	/* We want to run from the boot volume. */
	SetVol(nil, BOOTVOL);

	/* Set up volume notification mechanism */
	VNInit();
	}

/* GetKernelMode.  Inline to fetch the processor's SR and save it sans all bits
 * except the supervisor/user mode one.
 */
#pragma parameter GetKernelMode(__A0)
void
GetKernelMode(short *storage)
	= {0x40C0, 0x0240, 0x2000, 0x3080};
/*	MOVE       SR,D0
 *	ANDI.W     #$2000,D0
 *	MOVE.W     D0,(A0)
 */

/* InitializeScheduler.  Now that our machine environment looks good, prepare our data
 * structures so we can run applications.
 */
void
InitializeScheduler(void)
	{
	PEntryPtr			pProc;
	Handle				hdl;
	u_size				currSysHeapSize;
	extern StringPtr	SSSPromptpStr, MacOSPortNamepStr;

	/* Initialize these before calling InitExceptions() because the exception
	 * handlers will want to look at them.
	 */
	pProc = pCurrentProcess = pNullProcess;

	/* Install our exception handlers */
	InitExceptions();

	/* initialize the process list globals */
	ProcessNumberSource = LowestNonSystemPSN;
	ProcessIDSource = MAX_PROCESSID;
	pProcessList = nil;
	pFrontProcess = pNullProcess;

	/* we are taking over the nullprocess */
	MemClear(pProc, sizeof(PEntry));
	pProc->p_state = PRNULL;
	pProc->p_signature = NULLPROC_SIGNATURE;
	pProc->p_version = SYS_VERSION;
	SetPSN(kSystemProcess, &pProc->p_serialNumber);
	pProc->p_zone = THEZONE;
	pProc->p_size = (unsigned long) BUFPTR - (unsigned long) THEZONE;

	/* Save useful startup values */
	initialProcessSR = GetCPUStatusRegister() & (kCPUSupervisorMode | kCPUMasterStack);
	initMemTop = MEMTOP;
	initFSQueueHook = FSQUEUEHOOK;

	/* Get the stack for kernel use set up. */
	initCurStackBase = CURSTACKBASE;
	kernelstack = nil;
	kernelstackbase = (unsigned long) APPLZONE->bkLim;
	GetKernelMode(&kernelMode);

 	/* Because app zone can't grow anymore */
	kernelbusy = 0;

	/* initialize the state lists */
	pSleepingStateList = nil;
	pDyingStateList = nil;

	/* Now init some of the more mundane things. */
	pDebugProcess = nil;
	debugKeyTryCount = -1;			/* Based on -1 as start */
	pSystemQuitAllMsg = nil;
	pLastBGProcess = nil;
 	nbacks = 0;
	cutCopyCount = 0;
	dont_switch = 0;
	napOver = false;
	(void) GetDeskPort((GrafPtr *) &desklayer);
	desklayerowner = nil;
	SystemTempMemHList = CreateHList(0);
	
	/* Allocate handle for supporting the Text Service Manager */
	fakeKeyHandle = (FakeKeyHandle)NewHandle( 0);
	fakeKeyCount = 0;
	dontGetFakeKey = 0;
	tsmLayerOwner = nil;
	routeEvent = false;

	/* Get all the resources we need.  Do the Process Mgr ones first.  Wait to do system
	 * ones until we have THEZONE == SYSZONE.  See note below.
	 */
	THEZONE = SYSZONE;

	/* Get menu strings for scrap coercion */
	hdl = Get1Resource('STR#',-16414);
	pStrListCopyNamePrefix = (hdl == nil) ? nil : *((StrListHdl)hdl);

	/* Get the strings for ePPC  */
	SSSPromptpStr = nil;
	MacOSPortNamepStr = nil;
	if ((hdl = Get1Resource('STR#',-16457)) != nil)
		{
		SSSPromptpStr = *hdl+(Ptr)2;
		MacOSPortNamepStr = (Ptr)SSSPromptpStr + (Ptr)(*(char *)SSSPromptpStr);
		MacOSPortNamepStr += (Ptr)1;
		}

	/* <18> This semaphore is only true when an 'obit' event is being sent from
		SendDeathNotice.  Set it to false to start off. */
	allocateGMBlkInSysHeap = false;	
	
	/* Make sure there is some minimum sys heap size so we won't call
	 * the grow zone proc so many times to launch Finder.
	 */
	currSysHeapSize = (Ptr)SYSZONE->bkLim - (Ptr)SYSZONE;
	if (currSysHeapSize < INIT_SYS_HEAP_SIZE)
		(void)ProcessMgrSysGZProc(INIT_SYS_HEAP_SIZE - currSysHeapSize);

	/* Initialize the puppet strings */
	PuppetStringsInit();

	/* Get rid of old system resources that were in the system heap */
	THEZONE = APPLZONE;
	RsrcZoneInit();

	/* Switch to SYSZONE because any of the _GetResource calls below could move
	 * memory within the call, which is a problem for the rsrc mgr routine
	 * CheckLoad().  It saves and restores THEZONE, but our heap can move so restoring
	 * the "original" value is incorrect.
	 */
	THEZONE = SYSZONE;
	{
	/* Get the standard MDEF for the system menus.  We must do this because some apps
	 * (like Finder!) aren't picky about what they get.  We need a good version!
	 */
	long				mdef = 'MDEF';
	short				mdefIndex = 0;
	long				mdefVersionID = 0;
	Handle				realMDefHdl;

#define SMALLEST_GOOD_MDEF_ID (13)

	while (mdefVersionID < SMALLEST_GOOD_MDEF_ID)
		{
		if ((realMDefHdl = GetResource(mdef, mdefIndex)) == nil)
			{
			/* Couldn't find any tables */
			assert(RESERR == memFullErr);
			SysError(dsMemFullErr);
			}

		/* NOTE:  Create struct for this */
		mdefVersionID = *((unsigned long *)(*((Handle)realMDefHdl) + 8));
		mdefIndex++;
		}

	standardMenuDefProcRsrc = (Handle) StripAddress(realMDefHdl);
	}

	/* Insert the system menus now that we have the MDEF */
	InitSystemMenus();

	/* Get rid of useless font width tables, etc.  Must be done after InitSystemMenus,
	 * because menu installers need font info set up.
	 */
	FMKill(false);

	/* Announce our arrival */
	MyNewGestalt();
	
	/* Let other parts of system software know we are here without having to call the gestalt routine we just installed */
	SetExpandMemProcessMgrExists((short)true);

	/* Initialize the Event Program to Program Communication (can load drivers) */
	ePPCInit();

	/* End of sys resources */
	THEZONE = ProcessMgrZone;
	}

/* InitConfig.  Set up hardware/software configuration information, based on the
 * word of our wonderboy, Gestalt.  Only gets the info we actually need.  Sets
 * defaults if selector not installed.  The defaults are somewhat impossible to
 * know for FPU and keyboard types, but their selectors should always be safe.
 */
void
InitConfig(void)
	{
	long			gestaltResult;

	/* set defaults in case selector undefined */
	In32BitMode = false;
	Colorized = false;
	MachineHasFPU = false;
	MachineHasMacPlusKbd = false;
#ifdef HAS_AUX_PROCESSMGR
	AUXIsPresent = false;
#endif HAS_AUX_PROCESSMGR

	/* Check processor addressing mode */
	if (Gestalt(gestaltAddressingModeAttr,&gestaltResult) == noErr)
		In32BitMode = ((gestaltResult & (1 << gestalt32BitAddressing)) != 0);

	/* Check QuickDraw version */
	if (Gestalt(gestaltQuickdrawVersion,&gestaltResult) == noErr)
		Colorized = ((gestaltResult & 0xFFFF) >= gestalt8BitQD);

	/* Does this machine have an FPU? */
	if (Gestalt(gestaltFPUType,&gestaltResult) == noErr)
		MachineHasFPU = (gestaltResult != gestaltNoFPU);

	/* Does this machine have an older style keyboard (without an escape key)? */
	if (Gestalt(gestaltKeyboardType,&gestaltResult) == noErr)
		MachineHasMacPlusKbd = (gestaltResult <= gestaltMacPlusKbd);

	if (Gestalt(gestaltGraphicsVersion,&gestaltResult) == noErr)
		skiaExists = true;
	else
		skiaExists = false;
		
#ifdef MODSQUAD
	/* Check whether drag manager is installed. */
	if (Gestalt(gestaltDragMgrVersion, &gestaltResult) == noErr)
		gDragMgrIsAvailable = true;
#endif

#ifdef HAS_AUX_PROCESSMGR
	/* Check whether we're running under AUX. */
	if (Gestalt(gestaltAUXVersion, &gestaltResult) == noErr	&& AUX_EnableCoffLaunch())
		AUXIsPresent = true;
#endif HAS_AUX_PROCESSMGR
	}
