/* Res.C - Autodetach code for Manx in C
**
** Thanks to Eddy Carrol for his groundwork in assembler. I rewrote it
** in C 'cause assembly isn't really my favourite language..
** (Eddy's code also contains a few potential bugs..)
**
** Eddy writes good documentation, so get "CPUBlit" from Fred Fish and
** study the source code and docs there..
**
** Synopsis for res:
**
** struct Task *pid = res(char *name, int pri, int (*kickoff)(), int stack)
**
** kickoff() will be spawned as a completely selfcontained process. Caller's
** code will not be UnLoadSeg'ed until kickoff() terminates (exit() is a no-no
** in kickoff(), use return(doscode)!)
**
** There's more, so read Eddy's docs!
**
** This file is part of the Powerpacker Patcher project.
** Copyright 1991, Michael Berg. (Thanx Eddy!)
*/

#include <pragmas.h>
#include <exec/types.h>
#include <libraries/dos.h>
#include <libraries/dosextens.h>
#include <exec/memory.h>

struct FakeSeg
{
	ULONG segsize;
	ULONG segptr;
	UWORD op_moveim_a4;	ULONG reg_a4;
	UWORD op_jsrabs;	int (*func)();
	UWORD op_moveim_a6;	ULONG reg_a6;
	UWORD op_moveim_d1;	ULONG reg_d1;
	UWORD op_jmpindir_a6;	UWORD lvo_unloadseg;
};

#define SEGSIZE	sizeof(struct FakeSeg)

typedef struct Process PROCESS;

extern struct DOSBase *DOSBase;

/* Doesn't Manx have another way to get at a register..? */
fetch_a4()
{
#asm
	move.l	a4,d0
#endasm
}

/* Well, here it is! */
struct Task *res(char *name,int pri,int (*fptr)(),int stacksize)
{
	register struct FakeSeg *fs;

	if (fs = (struct FakeSeg *)AllocMem(SEGSIZE,MEMF_PUBLIC))
	{
		register PROCESS *ThatsMe = (PROCESS *)FindTask(0);
		BPTR ourseg,CLI;
		struct Task *created;

		ourseg = ThatsMe->pr_CLI;
		CLI = ((struct CommandLineInterface *)BADDR(ourseg))->cli_Module;
		((struct CommandLineInterface *)BADDR(ourseg))->cli_Module = (BPTR)0;

		fs->segptr  = (ULONG)CLI;
		fs->segsize = SEGSIZE;

		fs->op_moveim_a4 = 0x287C;
		fs->reg_a4	 = fetch_a4();
		fs->op_jsrabs	 = 0x4EB9;
		fs->func	 = fptr;
		fs->op_moveim_a6 = 0x2C7C;
		fs->reg_a6       = (ULONG)DOSBase;
		fs->op_moveim_d1 = 0x223C;
		fs->reg_d1	 = ((ULONG)(&fs->segptr)) >> 2;
		fs->op_jmpindir_a6 = 0x4EEE;
		fs->lvo_unloadseg  = 0xFF64;

		if (created = (struct Task *)CreateProc(name,pri,fs->reg_d1,stacksize))
			return(created);
		else
		{
			((struct CommandLineInterface *)BADDR(ourseg))->cli_Module = CLI;
			FreeMem(fs,SEGSIZE);
		}
	}

	return(NULL);
}
