/************************************************************************
 *
 *	Example.c	This is the example portion of the multitasking 
 *			tutorial for the Zen System Monitor.
 *
 *	By Dwight Moody, Zen Software, 1986
 *
 ************************************************************************
 *
 *	NOTE:		This example program is the heart of the 
 *			tutorial. I've found that there's little that
 *			can compete with examples when it comes to
 *			learning something about programming. Thus, the
 *			text in the manual is meant to accompany this
 *			program and not the other way around.
 *
 *	DISCLAIMER:	This program provided for your education and
 *			may be incorporated into your programs for
 *			personal or comercial use. Zen Software makes
 *			no warranties, explicit or implied, as to this 
 *			programs correctness or reliability, and
 *			cannot be held liable for any loss of data,
 *			personal time or resources whatsoever.
 *
 ************************************************************************/

#include <stdio.h>
#include <exec/types.h>
#include <exec/tasks.h>
#include <exec/memory.h>
#include <exec/io.h>
#include "shtuff.h"

#ifdef	AZTEC
#include <functions.h>
#endif

#include "sometask.h"
  

#define	STACKSIZ	2048L

/************************************************************************
 *	G L O B A L     V A R I A B L E S
 ************************************************************************/

	/*** The Library base pointers used by most of this program. ***/

struct	IntuitionBase	*IntuitionBase	= NULL;
struct	ExecBase	*ExecBase 	= NULL;
struct	GfxBase		*GfxBase	= NULL;
struct	DosLibrary	*DOSBase	= NULL;

	/*** The message struct used to tell the tasks what to do.
	     See mystructs.h for SomeMsg definition. ***/

struct	SomeMsg		*SM = NULL;

	/*** All messages sent to the tasks we spawn return here. ***/
struct	MsgPort		*ReplyPort = NULL;

/************************************************************************
 *	B A R F
 ************************************************************************/

	/*** This function should be called whenever a fatal error occurs 
	     in the kernal portion of the monitor. But not from any task. ***/
 
void	barf(msg)
char	*msg;
{
	printf("ACK!: %s\n",msg);
	cleanup(20);
} /* barf */

/************************************************************************
 *	M A K E    T A S K 
 ************************************************************************/

int
MakeTask(name, pri, start_pc, stksiz, task)
char	*name;
ULONG	pri,
	start_pc,
	stksiz;
struct	Task	*task;
{
	APTR			stack;

	stksiz = (stksiz + 3) & ~3;

	if (!(stack = AllocMem(stksiz, (LONG) MEMF_CLEAR)))
	 	return	(int) FALSE;

	task->tc_SPLower = stack;

	task->tc_SPUpper = (APTR) ((ULONG)stack + stksiz);
	task->tc_SPReg   = task->tc_SPUpper;

	task->tc_Node.ln_Type = (UBYTE) NT_TASK;
	task->tc_Node.ln_Pri = (BYTE) pri;
	task->tc_Node.ln_Name = name;

	AddTask(task, start_pc, NULL);

	return (int) TRUE;
} /* MakeTask */

/************************************************************************
 *	N E W    U N I T
 ************************************************************************/

/*** NewUnit is basically the equivalent of init_all for some of the
     initialization for a new task, the rest of the initialization 
     occurs in the task itself. Variations of this are usefull when
     creating more than one simple task from the startup process. ***/

struct	TaskInfo 
*NewUnit()
{
	struct	TaskInfo	*ti,	/* our new unit			*/
				*temp_ti;

		/*** Allocate our new TaskInfo structure. ***/
	if (!(ti = AllocMem((LONG) sizeof(struct TaskInfo),
		(LONG) (MEMF_CLEAR | MEMF_PUBLIC))))
		barf("Can't allocate TaskInfo struct.");

		/*** Create the appropriate task. ***/
	MakeTask("Some Task", 0L, SomeTask, STACKSIZ, ti);

	return	ti;

} /* NewUnit */

/************************************************************************
 *	N U K E    U N I T
 ************************************************************************/

void	NukeUnit(ti)
struct	TaskInfo	*ti;
{
	struct	TaskInfo	*blat_ti;
	struct	Task		*task;

	task = (struct Task *) ti;	/* make things a bit more readable */

		/*** Tell the task to commit suicide. ***/
	SM->sm_Command = (WORD) CMD_CURLUPANDDIE;
	PutMsg(ti->ti_MsgPort, SM);
	WaitPort(ReplyPort);
	GetMsg(ReplyPort);

		/*** Deallocate the stack. ***/
	FreeMem( task->tc_SPLower, (long) ((STACKSIZ + 3) & ~3));
	
		/*** Finish the thing off for good. ***/
	RemTask(ti);

		/*** Free up the memory it was taking ***/
	FreeMem(ti, (LONG) sizeof(struct TaskInfo));

} /* NukeUnit */

/************************************************************************
 *	I N I T _ A L L
 ************************************************************************/

struct	TaskInfo	
*init_all()
{
		/*** Save the pointer into the data segment for other tasks
		     to obtain with geta4 at startup. ***/
#ifdef	AZTEC
	sava4();
#endif

		/*** Open some libraries ***/

	if (!(ExecBase = (struct ExecBase *)
			OpenLibrary("exec.library",NULL)))
		barf("can't open exec.library");

	if (!(IntuitionBase = (struct IntuitionBase *)
			OpenLibrary("intuition.library",NULL))) 
		barf("can't open intuition.library");

	if (!(GfxBase = (struct GfxBase *)
			OpenLibrary("graphics.library",NULL)))
		barf("can't open graphics.library");

	if (!(DOSBase = (struct DosLibrary *)
			OpenLibrary("dos.library",NULL)))
		barf("can't open dos.library");

		/*** Message Passing Shtuff ***/

	if (!(ReplyPort = CreatePort("Reply Port", (LONG) 0 )))
		barf("Can't create Master Reply");

	if (!(SM = AllocMem((long)sizeof(struct SomeMsg),NULL)))
		barf("can't allocmem some message");

	SM->sm_Message.mn_Node.ln_Type = (UBYTE) NT_MESSAGE;
	SM->sm_Message.mn_Node.ln_Pri  = (BYTE) 0;
	SM->sm_Message.mn_ReplyPort    = ReplyPort;
	SM->sm_Command 		       = (WORD) CMD_DOYOURTHING;

	return	(NewUnit()); 
	
} /* init_all */

/************************************************************************
 *	C L E A N U P
 ************************************************************************/

cleanup(val)
WORD	val;
{

	if (ReplyPort)
		DeletePort(ReplyPort);

	if (SM)
		FreeMem(SM, (LONG) sizeof(struct SomeMsg));

	if (ExecBase)
		CloseLibrary(ExecBase);

	if (IntuitionBase)
		CloseLibrary(IntuitionBase);

	if (GfxBase)
		CloseLibrary(GfxBase);

	if (DOSBase)
		CloseLibrary(DOSBase);

	exit(val);
} /* cleanup */
 
/************************************************************************
 *	M A I N
 ************************************************************************/

main()
{
	struct	SomeMsg		*sMsg;	/* The message we send between */
	struct	TaskInfo	*ti;	/* Pointer to the info for task */
	char			buf[80],/* Temporary storage for line */
				*ptr, 	/* Random pointer for function
						returns. */
				*gets();/* Something to keep the compiler
						happy */

	ti = init_all();	/* Get everything ready */
	SM->sm_Whatever = buf;	/* Let other task know where buffer is */

		/* while the user hasn't pressed return. */
	for (;;) {

			/* Get a line from stdin */
		ptr = gets(buf);

			/* If they pressed return, the first char equals zero */
		if (buf[0] == 0)
			break;	/* Drop out of the loop */

		PutMsg(ti->ti_MsgPort, SM);
		WaitPort(ReplyPort);
		GetMsg(ReplyPort);
	}

	NukeUnit(ti);

	cleanup(0);
} /* main */
