/*
	Runtime Engine
		By Craig Kadziolka ( Nov 1999 - Dec 2000 )

	Modifications and copying is permitted providing this header
	is retained
*/

#include "memory.h"
#include "Types.h"
#include "ithreads.h"
#include <strings.h>
#include "Synchronization.h"
#include <assert.h>
#include "create.h"

////////////////////////////////////////////////////////////////////
//  FUNCTION:  CREATE                                             //
// -------------------                                            //
//  PARAMETERS:                                                   //
//     PRIORITY:     This is the priority of the thread. Higher   //
//                   priority threads will always be executed     //
//                   first. Available values are between 0 and    //
//                   PRIORITY_LEVELS where 0 is highest           //
//     STARTPOINT:   This is the function that is to be threaded. //
//                                                                //
//STACK SETUP:                                                    //
//                                                                //
//  ----------------------------------                            //
//  |  CPU Thread Node               |                            // 
//  |--------------------------------|                            //
//  |  Save Area                     |                            //
//  |--------------------------------|                            //
//  |                                |                            //
//  |  Threads Stack                 |                            //
//  |                                |                            //
//                                                                //
////////////////////////////////////////////////////////////////////

int number;

Handle Create_Thread(int priority, int processor, void (*StartPoint) (void))
{
	int * frame_setup;
  	int * block;
	int * save_area, * stack_base;
	struct Thread_Node * thread_node;

	/* Caches for commonly used calculations... */
	unsigned int stack_size_over_4 = STACK_SIZE >> 2;

	
	/* Debug - excessive signal disabling ... */
  	disable_interrupts();

  	/* Destroy any threads that are ready for it */
  	Destroy();

  	// Allocate one big block of memory for the thread. 
  	block = (int *) allocate (STACK_SIZE + 4 + (sizeof(TN)) + SAVE_SIZE);

	/* Set up the appropriate pointers...*/
  	stack_base = block;
	save_area = (int *) ((int) block + STACK_SIZE + 4);
	thread_node = (struct Thread_Node *) 
		((int) block + STACK_SIZE + 4 + SAVE_SIZE);
  	
    	thread_node -> previous = NULL;
    	thread_node -> next = PQT(priority);

	if (PQT(priority) != NULL) {
    		PQT(priority) -> previous =  thread_node;
  	} else {
    		PQH(priority) = thread_node;
	}
	
    	PQT(priority) = thread_node;

	/* By now the thread node has been linked into the list of
	 * threads at the appropriate level */
	
	thread_node -> Priority = priority;

  	thread_node -> save_area_base = save_area;
  
  	// Initialize the save area to zeros
  	memset (save_area, 0, SAVE_SIZE);

  	// Fill in the details of the thread we are creating.
  	/* Put in the address of enable interrupts here
     		and put StartPoint on the stack, so when enable interrupts
     		returns, it jumps to the start of the thread. */
  	save_area [12] = (int) enable_interrupts;

	/* Internal test to ensure the stack top doesnt overlap
	 * (or even come close to) the save area for that thread */
  	assert ( (int) &save_area[0] > (int) &stack_base[stack_size_over_4] );
  
	/* Ensure the stack is word aligned */
      	assert( ((int) &stack_base[stack_size_over_4])%4 == 0);
  
	/* Set up the frame for the new thread */
	save_area[0] = (int) &stack_base[stack_size_over_4];

	/* See stack diagram below for explanation... */	
//  	stack_base[stack_size_over_4] = (int) &save_area[0]; 
  	stack_base[stack_size_over_4 - 1] = 0;	/* The null frame */
  	stack_base[stack_size_over_4 - 2] = (int) Thread_Closing;
 	stack_base[stack_size_over_4 - 3] = (int) StartPoint;
  
  	save_area[0] -= 4;
  	save_area[1] = save_area[0] - 8;

  	/*
  	/////////////////////////////////////////////////////
  	// The stack layout for the new thread should now  //
  	// look as follows:                                //
  	//  |                        |                     //
  	//  --------------------------                     //
  	//  | Save Area Pointer      |                     //
  	//  --------------------------                     //
  	//  | Null Frame Pointer     |   Initial EBP       //
  	//  --------------------------                     //
  	//  | Thread Return Address  |                     //
  	//  --------------------------                     //
  	//  | Threads Initial EIP    |   Initial ESP       //
  	//  |                        |                     //
  	*/

#ifdef DEBUG
  	Invariant();
#endif

  	enable_interrupts();

  	thread_node->ThreadID = (Handle) save_area;
  	return (Handle) save_area;
	
}

