/**********************************************************
 *
 * The standard PowerUP ELFLoadSeg patch and API Definition.
 * The initial PPC function is called as
 *
 *                    init(Argument,WBMsg)
 *
 * where Argument is NULL for a Workbench start and WBMsg is
 * NULL for a CLI start.
 * Argument is a concatination of the "ProgramName"+"Argument"
 * and NP_Argument is also set for ReadArgs on a CLI start
 *
 * Ralph Schmidt, Phase5 1998
 *
 * Thanks to
 * Steve Krueger for initial runelf source and concept input.
 * Volker Barthelsmann for the VBCC runppc sources
 *
 **********************************************************/
#include <exec/execbase.h>
#include <exec/memory.h>
#include <exec/libraries.h>
#include <dos/dos.h>
#include <dos/dosextens.h>
#include <dos/dostags.h>
#include <utility/tagitem.h>
#include <workbench/startup.h>
#include <workbench/workbench.h>
#include <workbench/icon.h>
#include <powerup/ppclib/object.h>
#include <powerup/proto/ppc.h>
#include <powerup/ppclib/tasks.h>
#include <proto/exec.h>
#include <proto/dos.h>
#include <string.h>
#include "compiler.h"
#include "ELFLoadSeg_VERSION.h"

#define	D(x)		;

#define	INTERNALFUNC	FALSE

#define	LVO_LoadSeg		(-150)
#define	LVO_UnLoadSeg		(-156)
#define	LVO_InternalLoadSeg	(-756)
#define	LVO_InternalUnLoadSeg	(-762)
#define	LVO_NewLoadSeg		(-768)

#define MSGID_EXIT		0x44584954
#define	DEFAULTPPCSTACK		0x10000
#define	PATCHNAME		"ELFLoadSeg-Patch"


void	SegmentCode(void);
void	SegmentCode_Object(void);
void	SegmentCode_ID(void);
void	SegmentCode_End(void);
void	kprintf(UBYTE *fmt,...);

BPTR	ASM	NEW_LoadSeg(REG_D1 char			*MyName,
                            REG_A6 struct Library	*DosBase);
BPTR	ASM	NEW_NewLoadSeg(REG_D1 char		*MyName,
                               REG_D2 struct TagItem	*MyTags,
                               REG_A6 struct Library	*DosBase);
BOOL	ASM	NEW_UnLoadSeg(REG_D1 BPTR			MySegList,
                              REG_A6 struct Library		*DosBase);

BPTR	ASM	NEW_InternalLoadSeg(REG_D0 BPTR			MyFile,
                                    REG_A0 BPTR			Table,
                                    REG_A1 ULONG		*FuncArray,
                                    REG_A2 ULONG		*Stack,
                                    REG_A6 struct Library	*DosBase);
BOOL	ASM	NEW_InternalUnLoadSeg(REG_D1 BPTR			MySegList,
                                      REG_A1 void 			(*FreeFunc)(STRPTR,ULONG),
                                      REG_A6 struct Library		*DosBase);

struct HunkSegment
{
	ULONG			Size;
	struct HunkSegment	*Next;
};

/* VBCC compatibility
 * extension
 * The CTRL-D feature of the
 * runppc should be put into
 * PPCTool as i think it`s too
 * dangerous for normal usage.
 * The signal handling in the old
 * runppc was also not 100% as
 * some shell(s) set the last
 * DosPacket`s Task as the receiver
 * of the signal.
 * Don`t remember now if it was
 * WShell or Shell.
 * With the PPCTASKTAG_BREAKSIGNAL
 * parameter the ppclib waits on
 * both tasks.
 * The creator task and the PPC 68k
 * msg task.
 */

struct StartupData
{
	void	*M68kPort;	/* the PowerPC task can send messages to this port */
	BPTR	std_in;		/* standard input handle */
	BPTR	std_out;	/* standard output handle */
	BPTR	std_err;	/* standard error handle */
	LONG	ReturnCode;	/* here we will find the return code from the PPC task */
	ULONG	Flags;		/* additional flags (currently unused) */
};

#define STARTUPF_ELFLOADSEG	0x1


struct Library		*SysBase;
struct DosLibrary	*DOSBase;
struct Library		*PPCLibBase;
APTR			OldNewLoadSeg;
APTR			OldLoadSeg;
APTR			OldUnLoadSeg;
APTR			OldInternalLoadSeg;
APTR			OldInternalUnLoadSeg;
static const UBYTE	vers[] = VERSTAG;

ULONG	main(void)
{
struct SignalSemaphore	*MySemaphore;
  SysBase = *(struct Library **)4;

  if (DOSBase=(struct DosLibrary*) OpenLibrary("dos.library",0))
  {
    if (PPCLibBase=OpenLibrary("ppc.library",42))
    {
      if ((PPCLibBase->lib_Version > 45) ||
          ((PPCLibBase->lib_Version == 45) && (PPCLibBase->lib_Revision >= 11)))
      {
        if (FindSemaphore(PATCHNAME)==NULL)
        {
          if (MySemaphore=(struct SignalSemaphore*) AllocVec(sizeof(struct SignalSemaphore) + sizeof(PATCHNAME) + 1,
                                                             MEMF_ANY|MEMF_CLEAR))
          {
            MySemaphore->ss_Link.ln_Name	=	(char*) &MySemaphore[1];
            MySemaphore->ss_Link.ln_Pri	=	-127;
            strcpy((char*) &MySemaphore[1],
                   PATCHNAME);
            InitSemaphore(MySemaphore);
            AddSemaphore(MySemaphore);

            OldNewLoadSeg=SetFunction((struct Library*) DOSBase,  
                                   LVO_NewLoadSeg,  
                                   (ULONG (*)(void)) NEW_NewLoadSeg);
      
            D(kprintf("OldNewLoadSeg=0x%08lx NewNewLoadSeg=0x%08lx\n",  
                      OldNewLoadSeg,  
                      NEW_NewLoadSeg));
      
            OldLoadSeg=SetFunction((struct Library*) DOSBase,  
                                   LVO_LoadSeg,  
                                   (ULONG (*)(void)) NEW_LoadSeg);
      
            D(kprintf("OldLoadSeg=0x%08lx NewLoadSeg=0x%08lx\n",  
                      OldLoadSeg,  
                      NEW_LoadSeg));
      
            OldUnLoadSeg=SetFunction((struct Library*) DOSBase,  
                                     LVO_UnLoadSeg,  
                                     (ULONG (*)(void)) NEW_UnLoadSeg);
      
            D(kprintf("OldUnLoadSeg=0x%08lx NewUnLoadSeg=0x%08lx\n",  
                      OldUnLoadSeg,  
                      NEW_UnLoadSeg));
      
#if INTERNALFUNC
            OldInternalLoadSeg=SetFunction((struct Library*) DOSBase,  
                                           LVO_InternalLoadSeg,  
                                           (ULONG (*)(void)) NEW_InternalLoadSeg);
      
            D(kprintf("OldInternalLoadSeg=0x%08lx NewInternalLoadSeg=0x%08lx\  n",
                      OldInternalLoadSeg,  
                      NEW_InternalLoadSeg));
      
            OldInternalUnLoadSeg=SetFunction((struct Library*) DOSBase,  
                                             LVO_InternalUnLoadSeg,  
                                             (ULONG (*)(void)) NEW_InternalUnLoadSeg);
      
            D(kprintf("OldInternalUnLoadSeg=0x%08lx NewInternalUnLoadSeg=0x%0  8lx\n",
                      OldInternalUnLoadSeg,  
                      NEW_InternalUnLoadSeg));
#endif

            ((struct CommandLineInterface*) BADDR((((struct Process*) FindTask(NULL))->pr_CLI)))->cli_Module	=	NULL;
            return(0);
          }
          else
          {
            SetIoErr(ERROR_NO_FREE_STORE);
          }
        }
        else
        {
          SetIoErr(ERROR_OBJECT_EXISTS);
        }
      }
      else
      {
        Printf("You need at least ppc.library V45.11\n");
      }
      CloseLibrary(PPCLibBase);
    }
    else
    {
      Printf("Can`t open ppc.library\n");
    }
  }
  return(RETURN_FAIL);
}


/*****************************************************************************************/
/*****************************************************************************************/
/*****************************************************************************************/
/*****************************************************************************************/
/*****************************************************************************************/
/*****************************************************************************************/
/*****************************************************************************************/


BPTR	ASM	NEW_NewLoadSeg(REG_D1 char		*MyName,
                               REG_D2 struct TagItem	*MyTags,
                               REG_A6 struct Library	*DosBase)
{
BPTR	SegList;

BPTR ASM (*MyFunc)(REG_D1 char*,
                   REG_D2 struct TagItem*,
                   REG_A6 struct Library*);

  MyFunc=(BPTR ASM (*) (char*,
                        struct TagItem*,
                        struct Library*))
          OldNewLoadSeg;

  SegList=(*MyFunc)(MyName,
                    MyTags,
                    DosBase);

  D(kprintf("NEW_NewLoadSeg: MyName %s MyTags 0x%08lx\n",
             MyName,
             MyTags));

  if (SegList==NULL)
  {
    struct TagItem	MyTagItem[2];
    void		*MyObject;
    struct HunkSegment	*MySegment;
    ULONG		AllocSize;
    ULONG		CodeSize;
    ULONG		*ObjectPtr;

    MyTagItem[0].ti_Tag		=	PPCELFLOADTAG_ELFNAME;
    MyTagItem[0].ti_Data	=	(ULONG) MyName,
    MyTagItem[1].ti_Tag		=	TAG_END;
//__builtin_emit(0x4afc);
    if (MyObject=PPCLoadObjectTagList(&MyTagItem[0]))
    {
      D(kprintf("NEW_NewLoadSeg: MyObject 0x%08lx\n",
                MyObject));

      CodeSize	=	(ULONG) &SegmentCode_End - (ULONG) &SegmentCode;
      AllocSize	=	CodeSize + sizeof(struct HunkSegment);
      if (MySegment=(struct HunkSegment*) AllocMem(AllocSize,
                                                   MEMF_PUBLIC))
      {
        D(kprintf("NEW_NewLoadSeg: MySegMent 0x%08lx AllocSize 0x%08lx\n",
                  MySegment,
                  AllocSize));

        MySegment->Size	=	AllocSize;
        MySegment->Next	=	NULL;
        CopyMemQuick((void*) &SegmentCode,
                     &MySegment[1],
                     CodeSize);

        ObjectPtr	=(ULONG*) ((ULONG) &MySegment[1] + ((ULONG) &SegmentCode_Object - (ULONG) &SegmentCode + 2));
        *ObjectPtr 	=(ULONG) MyObject;
        SegList	=	MKBADDR(&MySegment->Next);
        CacheClearE(MySegment,
                    AllocSize,
                    CACRF_ClearI|CACRF_ClearD);
                    
        SetIoErr(RETURN_OK);
      }
      else
      {
        D(kprintf("NEW_NewLoadSeg: MySegment alloc failed\n"));
        PPCUnLoadObject(MyObject);
        SetIoErr(ERROR_NO_FREE_STORE);
      }
    }
    else
    {
      SetIoErr(ERROR_OBJECT_WRONG_TYPE);
      D(kprintf("NEW_NewLoadSeg: no elf object\n"));
    }
  }
  D(kprintf("NEW_NewLoadSeg: SegList 0x%08lx\n",
            SegList));

  return(SegList);
}

/*****************************************************************************************/
/*****************************************************************************************/
/*****************************************************************************************/
/*****************************************************************************************/
/*****************************************************************************************/
/*****************************************************************************************/
/*****************************************************************************************/


BPTR	ASM	NEW_LoadSeg(REG_D1 char			*MyName,
                            REG_A6 struct Library	*DosBase)
{
BPTR	SegList;

BPTR ASM (*MyFunc)(REG_D1 char*,
                   REG_A6 struct Library*);

  MyFunc=(BPTR ASM (*) (char*,
                        struct Library*))
          OldLoadSeg;

  SegList=(*MyFunc)(MyName,
                    DosBase);

  D(kprintf("NEW_LoadSeg: MyName %s\n",
             MyName));

  if (SegList==NULL)
  {
    struct TagItem	MyTagItem[2];
    void		*MyObject;
    struct HunkSegment	*MySegment;
    ULONG		AllocSize;
    ULONG		CodeSize;
    ULONG		*ObjectPtr;

    MyTagItem[0].ti_Tag		=	PPCELFLOADTAG_ELFNAME;
    MyTagItem[0].ti_Data	=	(ULONG) MyName,
    MyTagItem[1].ti_Tag		=	TAG_END;
//__builtin_emit(0x4afc);
    if (MyObject=PPCLoadObjectTagList(&MyTagItem[0]))
    {
      D(kprintf("NEW_LoadSeg: MyObject 0x%08lx\n",
                MyObject));

      CodeSize	=	(ULONG) &SegmentCode_End - (ULONG) &SegmentCode;
      AllocSize	=	CodeSize + sizeof(struct HunkSegment);
      if (MySegment=(struct HunkSegment*) AllocMem(AllocSize,
                                                   MEMF_PUBLIC))
      {
        D(kprintf("NEW_LoadSeg: MySegMent 0x%08lx AllocSize 0x%08lx\n",
                  MySegment,
                  AllocSize));

        MySegment->Size	=	AllocSize;
        MySegment->Next	=	NULL;
        CopyMemQuick((void*) &SegmentCode,
                     &MySegment[1],
                     CodeSize);

        ObjectPtr	=(ULONG*) ((ULONG) &MySegment[1] + ((ULONG) &SegmentCode_Object - (ULONG) &SegmentCode + 2));
        *ObjectPtr 	=(ULONG) MyObject;
        SegList	=	MKBADDR(&MySegment->Next);
        CacheClearE(MySegment,
                    AllocSize,
                    CACRF_ClearI|CACRF_ClearD);
                    
        SetIoErr(RETURN_OK);
      }
      else
      {
        D(kprintf("NEW_LoadSeg: MySegment alloc failed\n"));
        PPCUnLoadObject(MyObject);
        SetIoErr(ERROR_NO_FREE_STORE);
      }
    }
    else
    {
      D(kprintf("NEW_LoadSeg: no elf object\n"));
      SetIoErr(ERROR_OBJECT_WRONG_TYPE);
    }
  }
  D(kprintf("NEW_LoadSeg: SegList 0x%08lx\n",
            SegList));

  return(SegList);
}


/*****************************************************************************************/
/*****************************************************************************************/
/*****************************************************************************************/
/*****************************************************************************************/
/*****************************************************************************************/
/*****************************************************************************************/
/*****************************************************************************************/
#if INTERNALFUNC
/* Really dumb...DOS doesn`t call these through the library
 * removed because of a certain program..authors know which i meant:-)
 */
BPTR	ASM	NEW_InternalLoadSeg(REG_D0 BPTR			MyFile,
                                    REG_A0 BPTR			Table,
                                    REG_A1 ULONG		*FuncArray,
                                    REG_A2 ULONG		*Stack,
                                    REG_A6 struct Library	*DosBase)
{
BPTR	SegList;

BPTR ASM (*MyFunc)(REG_D0 BPTR,
                   REG_A0 BPTR,
                   REG_A1 ULONG*,
                   REG_A2 ULONG*,
                   REG_A6 struct Library*);

  MyFunc=(BPTR ASM (*) (BPTR,
                        BPTR,
                        ULONG*,
                        ULONG*,
                        struct Library*))
          OldInternalLoadSeg;

  SegList=(*MyFunc)(MyFile,
                    Table,
                    FuncArray,
                    Stack,
                    DosBase);

  D(kprintf("NEW_InternalLoadSeg: File 0x%08lx Table 0x%08lx FuncArray 0x%08lx Stack 0x%08lx\n",
             MyFile,
             Table,
             FuncArray,
             Stack));

  if (SegList==NULL)
  {
    struct TagItem	MyTagItem[4];
    void		*MyObject;
    struct HunkSegment	*MySegment;
    ULONG		AllocSize;
    ULONG		CodeSize;
    ULONG		*ObjectPtr;

    MyTagItem[0].ti_Tag		=	PPCELFLOADTAG_FILE;
    MyTagItem[0].ti_Data	=	(ULONG) MyFile;
    MyTagItem[1].ti_Tag		=	PPCELFLOADTAG_ELFNAME;
    MyTagItem[1].ti_Data	=	(ULONG) "ElfPatch";
    MyTagItem[2].ti_Tag		=	TAG_END;
    if (MyObject=PPCLoadObjectTagList(&MyTagItem[0]))
    {
      D(kprintf("NEW_InternalLoadSeg: MyObject 0x%08lx\n",
                MyObject));

      CodeSize	=	(ULONG) &SegmentCode_End - (ULONG) &SegmentCode;
      AllocSize	=	CodeSize + sizeof(struct HunkSegment);
      if (MySegment=(struct HunkSegment*) AllocMem(AllocSize,
                                                   MEMF_PUBLIC))
      {
        D(kprintf("NEW_InternalLoadSeg: MySegment 0x%08lx AllocSize 0x%08lx\n",
                  MySegment,
                  AllocSize));

        MySegment->Size	=	AllocSize;
        MySegment->Next	=	NULL;
        CopyMemQuick((void*) &SegmentCode,
                     &MySegment[1],
                     CodeSize);

        ObjectPtr	=(ULONG*) ((ULONG) &MySegment[1] + ((ULONG) &SegmentCode_Object - (ULONG) &SegmentCode + 2));
        *ObjectPtr 	=(ULONG) MyObject;
        SegList	=	MKBADDR(&MySegment->Next);
        CacheClearE(MySegment,
                    AllocSize,
                    CACRF_ClearI|CACRF_ClearD);
                    
        SetIoErr(RETURN_OK);
      }
      else
      {
        D(kprintf("NEW_InternalLoadSeg: MySegment alloc failed\n"));
        PPCUnLoadObject(MyObject);
        SetIoErr(ERROR_NO_FREE_STORE);
      }
    }
    else
    {
      D(kprintf("NEW_InternalLoadSeg: no elf object\n"));
      SetIoErr(ERROR_OBJECT_WRONG_TYPE);
    }
  }
  D(kprintf("NEW_InternalLoadSeg: SegList 0x%08lx\n",
            SegList));

  return(SegList);
}
#endif

/*****************************************************************************************/
/*****************************************************************************************/
/*****************************************************************************************/
/*****************************************************************************************/
/*****************************************************************************************/
/*****************************************************************************************/
/*****************************************************************************************/

BOOL	ASM	NEW_UnLoadSeg(REG_D1 BPTR			MySegList,
                              REG_A6 struct Library		*DosBase)
{
char	*IDPtr;
UBYTE	*SegmentAddress;
void	*MyObject;

BOOL ASM (*MyFunc)(REG_D1 BPTR,
                   REG_A6 struct Library*);

  D(kprintf("NEW_UnLoadSeg: SegList 0x%08lx\n",
             MySegList));

  if (MySegList)
  {
    SegmentAddress	=(UBYTE*) BADDR(MySegList) + sizeof(ULONG);
    IDPtr		=(char*) &SegmentAddress[(ULONG) &SegmentCode_ID - (ULONG) &SegmentCode];

    D(kprintf("NEW_UnLoadSeg: IDPtr 0x%08lx\n",
              IDPtr));

//__builtin_emit(0x4afc);
    if (strcmp(IDPtr,
               (char*) &SegmentCode_ID)==0)
    {

      MyObject	=(void*)	*((ULONG*) (&SegmentAddress[(ULONG) &SegmentCode_Object - (ULONG) &SegmentCode + 2]));

      D(kprintf("NEW_UnLoadSeg: ELF 0x%08lx\n",
                MyObject));

      if (MyObject)
      {
        PPCUnLoadObject(MyObject);
      }
    }
  }

  MyFunc=(BOOL ASM (*) (BPTR,
                        struct Library*))
         OldUnLoadSeg;

  return((*MyFunc)(MySegList,
                   DosBase));

}

/*****************************************************************************************/
/*****************************************************************************************/
/*****************************************************************************************/
/*****************************************************************************************/
/*****************************************************************************************/
/*****************************************************************************************/
/*****************************************************************************************/
/* Really dumb...DOS doesn`t call these through the library
 */
#if INTERNALFUNC
 * removed because of a certain program..authors know which i meant:-)
BOOL	ASM	NEW_InternalUnLoadSeg(REG_D1 BPTR			MySegList,
                                      REG_A1 void 			(*FreeFunc)(STRPTR,ULONG),
                                      REG_A6 struct Library		*DosBase)
{
char	*IDPtr;
UBYTE	*SegmentAddress;
void	*MyObject;

BOOL ASM (*MyFunc)(REG_D1 BPTR,
                   REG_A1 void (*)(STRPTR,ULONG),
                   REG_A6 struct Library*);

  D(kprintf("NEW_InternalUnLoadSeg: SegList 0x%08lx FreeFunc 0x%08lx\n",
             MySegList,
             FreeFunc));

  if (MySegList)
  {
    SegmentAddress	=(UBYTE*) BADDR(MySegList) + sizeof(ULONG);
    IDPtr		=(char*) &SegmentAddress[sizeof(ULONG) + (ULONG) &SegmentCode_ID - (ULONG) &SegmentCode];

    D(kprintf("NEW_InternalUnLoadSeg: IDPtr 0x%08lx\n",
              IDPtr));

    if (strcmp(IDPtr,
               (char*) &SegmentCode_ID)==0)
    {
      MyObject	=(void*)	*((ULONG*) (&SegmentAddress[(ULONG) &SegmentCode_Object - (ULONG) &SegmentCode + 2]));

      D(kprintf("NEW_InternalUnLoadSeg: ELF 0x%08lx\n",
                MyObject));

      if (MyObject)
      {
        PPCUnLoadObject(MyObject);
      }
    }
  }

  MyFunc=(BOOL ASM (*) (BPTR,
                        void (*)(STRPTR,ULONG),
                        struct Library*))
         OldInternalUnLoadSeg;

  return((*MyFunc)(MySegList,
                   FreeFunc,
                   DosBase));

}
#endif

/*****************************************************************************************/
/*****************************************************************************************/
/*****************************************************************************************/
/*****************************************************************************************/
/*****************************************************************************************/
/*****************************************************************************************/
/*****************************************************************************************/

ULONG	ASM RunElf(REG_A0 char	*MyCommandLine,
                   REG_A1 void	*MyObject)
{
char				*p;
char				*MyArgument;
const char			*MyCommandName;
ULONG				MyCommandLen;
long				MyStackSize;
struct Process			*MyProcess;
struct CommandLineInterface	*MyCLI;
char				*rdargs_line;
int				MyCommandLinelength;
struct TagItem			MyTags[19];
ULONG				Result;
ULONG				Index;
struct WBStartup		*WBMsg;
struct WBArg			*MyWBArg;
BPTR				NewCurrentDir;
BPTR				OldCurrentDir;
BPTR				MySTDErr;
void				*M68kPort;
void				*MyStartupMsg;
struct StartupData		*MyStartupData;

  MyProcess	= (struct Process*) FindTask(NULL);
  MyCLI		= (struct CommandLineInterface*) BADDR(MyProcess->pr_CLI);
  NewCurrentDir	=	NULL;
  OldCurrentDir	=	NULL;
  Result	=	ERROR_NO_FREE_STORE;
  if (MyCLI)
  {
    MyCommandName	=	BADDR(MyCLI->cli_CommandName);
    MyCommandLen	=	MyCommandName[0];
    MyStackSize		=	MyCLI->cli_DefaultStack * sizeof(ULONG);
    if (MyStackSize < DEFAULTPPCSTACK)
    {
      MyStackSize = DEFAULTPPCSTACK;
    }
    /* strip off the /n the end */
    MyCommandLinelength	=	strlen(MyCommandLine);
    p			=	MyCommandLine + MyCommandLinelength;
    p--;
    if (*p == '\n')
    {
      *p = 0;
    }
    D(kprintf("RunElf: MyCommandLine '%s' Object 0x%08lx\n",
              MyCommandLine,
              MyObject));

    if (MyArgument=(char*) AllocVec(MyCommandLinelength + (MyCommandName ? MyCommandLen + 6 : 0),
                                    MEMF_PUBLIC))
    {
      if (MyCommandName)
      {
        MyArgument[0]			=	'"';
        memcpy(&MyArgument[1],
               &MyCommandName[1],
               MyCommandLen);
        MyArgument[1+MyCommandLen]	=	'"';
        MyArgument[1+1+MyCommandLen]	=	' ';
        Index				=	1+1+1+MyCommandLen;
      }
      else
      {
        Index				=	0;
      }
      memcpy(&MyArgument[Index],
             MyCommandLine,
             MyCommandLinelength);
    
      if (rdargs_line=AllocVec(MyCommandLinelength + 2, MEMF_ANY))
      {
        memcpy(rdargs_line,
               MyCommandLine,
               MyCommandLinelength-1);
        rdargs_line[MyCommandLinelength-1]	=	'\n';
        rdargs_line[MyCommandLinelength]	=	'\0';

        if (M68kPort=PPCCreatePortTags(TAG_END))
        {
          if (MyStartupMsg=PPCCreateMessage(M68kPort,sizeof(struct StartupData)))
          {
            if (MyStartupData=(struct StartupData*)
                              PPCAllocVec(sizeof(struct StartupData),MEMF_CLEAR|MEMF_PUBLIC))
            {
              MyStartupData->M68kPort	=	M68kPort;
              MyStartupData->std_in	=	Input();
              MyStartupData->std_out	=	Output();
              if (MyStartupData->std_err=MyProcess->pr_CES)
              {
                MySTDErr	=	0;
              }
              else
              {
                if (!(MyStartupData->std_err=MySTDErr=Open("CONSOLE:",MODE_NEWFILE)))
                {
                  MyStartupData->std_err	=	MyStartupData->std_out;
                }
              }
              MyStartupData->Flags	=	STARTUPF_ELFLOADSEG;

              MyTags[0].ti_Tag		=	PPCTASKTAG_STOPTASK;
              MyTags[0].ti_Data		=	FALSE;
              MyTags[1].ti_Tag		=	PPCTASKTAG_WAITFINISH;
              MyTags[1].ti_Data		=	TRUE;
              MyTags[2].ti_Tag		=	PPCTASKTAG_INPUTHANDLE;
              MyTags[2].ti_Data		=	(ULONG) Input();
              MyTags[3].ti_Tag		=	PPCTASKTAG_OUTPUTHANDLE;
              MyTags[3].ti_Data		=	(ULONG) Output();
              MyTags[4].ti_Tag		=	PPCTASKTAG_ERRORHANDLE;
              MyTags[4].ti_Data		=	(ULONG) MyStartupData->std_err;
              MyTags[5].ti_Tag		=	PPCTASKTAG_ARG1;
              MyTags[5].ti_Data		=	(ULONG) MyArgument;
              MyTags[6].ti_Tag		=	PPCTASKTAG_STACKSIZE;
              MyTags[6].ti_Data		=	MyStackSize;
              MyTags[7].ti_Tag		=	NP_CloseInput;
              MyTags[7].ti_Data		=	FALSE;
              MyTags[8].ti_Tag		=	NP_CloseOutput;
              MyTags[8].ti_Data		=	FALSE;
              MyTags[9].ti_Tag		=	NP_CloseError;
              MyTags[9].ti_Data		=	FALSE;
              MyTags[10].ti_Tag		=	PPCTASKTAG_BREAKSIGNAL;
              MyTags[10].ti_Data	=	TRUE;
              MyTags[11].ti_Tag		=	NP_Arguments;
              MyTags[11].ti_Data	=	(ULONG)rdargs_line;
              MyTags[12].ti_Tag		=	NP_Name;
              MyTags[12].ti_Data	=	(ULONG) MyCommandName,
              MyTags[13].ti_Tag		=	NP_CommandName;
              MyTags[13].ti_Data	=	(ULONG) MyCommandName,
              MyTags[14].ti_Tag		=	PPCTASKTAG_STARTUP_MSG;
              MyTags[14].ti_Data	=(ULONG)MyStartupMsg;
              MyTags[15].ti_Tag		=	PPCTASKTAG_STARTUP_MSGDATA;
              MyTags[15].ti_Data	=(ULONG)MyStartupData;
              MyTags[16].ti_Tag		=	PPCTASKTAG_STARTUP_MSGLENGTH;
              MyTags[16].ti_Data	=	sizeof(struct StartupData);
              MyTags[17].ti_Tag		=	PPCTASKTAG_STARTUP_MSGID;
              MyTags[17].ti_Data	=	MSGID_EXIT;
              MyTags[18].ti_Tag		=	TAG_END;

              Result=(ULONG) PPCCreateTask(MyObject,
                                           &MyTags[0]);


              /* The startupmsg MUST be replied when PPCCreateTask
               * returns so this is safe.
               * Trust me...:-)
               */
              while (PPCGetMessage(M68kPort));

              if (MySTDErr)
              {
                Close(MySTDErr);
              }
              PPCFreeVec(MyStartupData);
            }
            PPCDeleteMessage(MyStartupMsg);
          }
          PPCDeletePort(M68kPort);
        }
        FreeVec(rdargs_line);
      }
      FreeVec(MyArgument);
    }
  }
  else
  {
    /* WBStartup */
    MyCommandName	=	NULL;
    MyStackSize		=	DEFAULTPPCSTACK;

    WaitPort(&MyProcess->pr_MsgPort);
    if (WBMsg=(struct WBStartup*) GetMsg(&MyProcess->pr_MsgPort))
    {
      if (MyWBArg=WBMsg->sm_ArgList)
      {
        if (NewCurrentDir=DupLock(MyWBArg->wa_Lock))
        {
          OldCurrentDir		=	CurrentDir(NewCurrentDir);
        }
        MyCommandName		=	MyWBArg->wa_Name;
      }
      D(kprintf("RunElf: WBMsg 0x%08lx Object 0x%08lx\n",
                WBMsg,
                MyObject));

      if (M68kPort=PPCCreatePortTags(TAG_END))
      {
        if (MyStartupMsg=PPCCreateMessage(M68kPort,sizeof(struct StartupData)))
        {
          if (MyStartupData=(struct StartupData*)
                            PPCAllocVec(sizeof(struct StartupData),MEMF_CLEAR|MEMF_PUBLIC))
          {
            MyStartupData->M68kPort	=	M68kPort;
            MyStartupData->std_in	=	Input();
            MyStartupData->std_out	=	Output();
            MyStartupData->std_err	=	MyStartupData->std_out;
            MyStartupData->Flags	=	STARTUPF_ELFLOADSEG;

            MyTags[0].ti_Tag		=	PPCTASKTAG_STOPTASK;
            MyTags[0].ti_Data		=	FALSE;
            MyTags[1].ti_Tag		=	PPCTASKTAG_WAITFINISH;
            MyTags[1].ti_Data		=	TRUE;
            MyTags[2].ti_Tag		=	PPCTASKTAG_ARG1;
            MyTags[2].ti_Data		=	(ULONG) NULL;		/* NULL Argument means WBMsg */
            MyTags[3].ti_Tag		=	PPCTASKTAG_ARG2;
            MyTags[3].ti_Data		=	(ULONG) WBMsg;
            MyTags[4].ti_Tag		=	PPCTASKTAG_STACKSIZE;
            MyTags[4].ti_Data		=	MyStackSize;
            MyTags[5].ti_Tag		=	PPCTASKTAG_BREAKSIGNAL;
            MyTags[5].ti_Data		=	TRUE;
            MyTags[6].ti_Tag		=	NP_Name;
            MyTags[6].ti_Data		=	(ULONG) MyCommandName;
            MyTags[7].ti_Tag		=	PPCTASKTAG_STARTUP_MSG;
            MyTags[7].ti_Data		=(ULONG) MyStartupMsg;
            MyTags[8].ti_Tag		=	PPCTASKTAG_STARTUP_MSGDATA;
            MyTags[8].ti_Data		=(ULONG) MyStartupData;
            MyTags[9].ti_Tag		=	PPCTASKTAG_STARTUP_MSGLENGTH;
            MyTags[9].ti_Data		=	sizeof(struct StartupData);
            MyTags[10].ti_Tag		=	PPCTASKTAG_STARTUP_MSGID;
            MyTags[10].ti_Data		=	MSGID_EXIT;
            MyTags[11].ti_Tag		=	TAG_END;

            Result=(ULONG) PPCCreateTask(MyObject,
                                         &MyTags[0]);


            /* The startupmsg MUST be replied when PPCCreateTask
             * returns so this is safe.
             * Trust me...:-)
             */
            while (PPCGetMessage(M68kPort));

            PPCFreeVec(MyStartupData);
          }
          PPCDeleteMessage(MyStartupMsg);
        }
        PPCDeletePort(M68kPort);
      }
      if (NewCurrentDir)
      {
        CurrentDir(OldCurrentDir);
        UnLock(NewCurrentDir);
      }
      Forbid();
      ReplyMsg(&WBMsg->sm_Message);
    }
  }
  return(Result);
}
