/*
 *  This file is part of ixemul.library for the Amiga.
 *  Copyright (C) 1991, 1992  Markus M. Wild
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Library General Public
 *  License as published by the Free Software Foundation; either
 *  version 2 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Library General Public License for more details.
 *
 *  You should have received a copy of the GNU Library General Public
 *  License along with this library; if not, write to the Free
 *  Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 *  createtask.c,v 1.1.1.1 1994/04/04 04:30:45 amiga Exp
 *
 *  createtask.c,v
 * Revision 1.1.1.1  1994/04/04  04:30:45  amiga
 * Initial CVS check in.
 *
 *  Revision 1.1  1992/05/14  19:55:40  mwild
 *  Initial revision
 *
 *
 *  Code is based on the example given in the RKM Libraries & Devices.
 */

#define _KERNEL
#include "ixemul.h"

#include <exec/tasks.h>
#include <exec/memory.h>

/* the template for the mementries. Unfortunately, this is hard to
 * do from C; mementries have unions, and they cannot be statically
 * initialized...
 *
 * In the interest of simplicity I recreate the mem entry structures
 * here with appropriate sizes. We will copy this to a local
 * variable and set the stack size to what the user spedified,
 * then attempt to actually allocate the memory.
 *
 * NOTE: static data here ok with shared library, since never changed
 *       directly
 */
#define ME_TASK    0
#define ME_STACK   1
#define NUMENTRIES 2

struct FakeMemEntry {
  ULONG fme_Reqs;
  ULONG fme_Length;
};

static 
struct FakeMemList {
  struct Node fml_Node;
  UWORD fml_NumEntries;
  struct FakeMemEntry fml_ME[NUMENTRIES];
} TaskMemTemplate = {
  { 0, },
  NUMENTRIES,
  {
    { MEMF_PUBLIC | MEMF_CLEAR, sizeof(struct Task) }, /* task */
    { MEMF_CLEAR, 0 },				       /* stack */
  }
};

struct Task *
CreateTask (unsigned char *name, long pri, void *initPC, u_long stackSize)
{
  struct Task *newTask;
  struct FakeMemList fakememlist;
  struct MemList *ml;
  
  /* round the stack up to longwords... */
  stackSize = (stackSize + 3) & ~3;

  /* this will allocate two chunks of memory, task of PUBLIC and
   * stack of PRIVATE
   */
  fakememlist = TaskMemTemplate;
  fakememlist.fml_ME[ME_STACK].fme_Length = stackSize;

  ml = (struct MemList *) AllocEntry ((struct MemList *)&fakememlist);
  
  /* strange way of returning an error... */
  if ((u_int)ml & (1<<31)) return NULL;
  
  /* set the stack accounting stuff */
  newTask = (struct Task *) ml->ml_ME[ME_TASK].me_Addr;
  
  newTask->tc_SPLower = ml->ml_ME[ME_STACK].me_Addr;
  newTask->tc_SPUpper = (APTR)((ULONG)(newTask->tc_SPLower) + stackSize);
  newTask->tc_SPReg = newTask->tc_SPUpper;
  
  /* misc task data structures */
  newTask->tc_Node.ln_Type = NT_TASK;
  newTask->tc_Node.ln_Pri = pri;
  newTask->tc_Node.ln_Name = name;
  
  /* add it to the tasks memory list */
  NewList (&newTask->tc_MemEntry);
  AddHead (&newTask->tc_MemEntry, (struct Node *)ml);
  
  /* add the task to the system -- use the default final PC */
  AddTask (newTask, initPC, 0);
  
  /* something has changed here for after V36, but I stick to what I know.. */
  return newTask;
}
