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

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

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include "ithreads.h"
#include "Types.h"
#include "idle.h"
#include "context.h"
#include "memory.h"
#include "findme.h"
#include <assert.h>
#include "Synchronization.h"

Handle idle_thread;
Handle main_thread; /* The thread with a system stack must be treated
		       differently in many instances */
int * top_of_main_stack;

extern void * current_thread;

/////////////////////////////////////////////////////////////////
//  FUNCTION INITIALIZE_THREADS                                //
//  RETURNS    : None                                          //
//  PARAMETERS : None                                          //
//  DESCRIPTION: The way this function works is simple. It sets//
//               up the threading queue mechanisim, which means//
//               creating a node to hold the CURRENT context   //
//               and then returns.  the next call to suspend   //
//               aka Sync_Save_Context will do the             //
//               initialization for us.                        //
/////////////////////////////////////////////////////////////////
void inline Initialize_Threads()
{

  Address block_ptr;
  struct null_frame *boot_null_frame;
  Address frame_pointer;
  int a;

#ifdef DEBUG
  printf("WARNING: DEBUG DEFINED.\n");
  printf("-----------------------\n");
  printf("Performance will be a fraction of standard speed\n");
  printf("Remove the DEBUG define and recompile for optimal\n");
  printf("performance.\n");
#endif
  
  disable_interrupts();

  /* Initialize All Priority Levels To No Threads */
  for (a=0; a<PRIORITY_LEVELS; a++) {
    PQH(a) = PQT(a) = NULL;
  }

  /* Put the Thread_Node for the main thread immediatly above the
   * save area.  Only one call to Allocate required.
   * There is no stack, as the main thread uses the system
   * allocated stack */
  
  block_ptr = (Address) allocate ((sizeof (TN)) + SAVE_SIZE);

#ifdef DEBUG
  printf("Result of block_ptr allocation is %d \n", block_ptr);
#endif

  boot_null_frame = (struct null_frame *) allocate (sizeof(struct null_frame));

#ifdef DEBUG
  printf("DEFAULT PRIORITY = %u\n", DEFAULT_PRIORITY);
#endif

  /* The boot code goes in the DEFAULT priority, as defined */
  PQH(DEFAULT_PRIORITY) = (TNPTR) (block_ptr + SAVE_SIZE);
  PQH(DEFAULT_PRIORITY) -> save_area_base = block_ptr;
  PQT(DEFAULT_PRIORITY) = PQH(DEFAULT_PRIORITY);
  PQT(DEFAULT_PRIORITY) -> Priority = DEFAULT_PRIORITY;
  PQT(DEFAULT_PRIORITY) -> ThreadID = (Handle) block_ptr;
  PQT(DEFAULT_PRIORITY) -> previous = NULL;
  PQH(DEFAULT_PRIORITY) -> next = NULL;
  main_thread = (Handle) block_ptr;  

	current_thread = PQH(DEFAULT_PRIORITY) -> save_area_base;
  
#ifdef DEBUG
  printf("Created main thread %d with priority %u \n", PQH(DEFAULT_PRIORITY),
		PQH(DEFAULT_PRIORITY)->Priority);
  
#endif

  // This sets the freshly allocated Thread Node up with
  // the default values.
  // Returns immediately.
  Sync_Save_Context();

  // This creates our IDLE thread.
  idle_thread = Create_Thread(PRIORITY_LEVELS-1, 0, IDLE);

  enable_interrupts();

}
