//kernel.c

#include <912d60.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "kernel.h"


extern void (*task_ptr[MAXTASKS])(void);



// GLOBAL VARIABLE DEFINITIONS
unsigned int current;		   	 		    // current task id number
unsigned long int system_tick;
unsigned int cop_cycle;



// task control block
typedef struct task_block {
	 	void (*address)();			   		// Address of the task
	 	unsigned char id; 			  		// ID of task
		char name[9];	  					// Name
		enum task_state state;				// State
		unsigned char priority;				// Priority
		unsigned long int period_tick;		// for determining if deadline is up
		unsigned int interrupt_msg_box;		// flags for pending interrupts
		enum message_box message;			// misc flags
		unsigned char message_data[2];		// data for misc_msg_box flags
	 	unsigned char *heap_ptr;			// heap addr while not current task
		unsigned int heap_size;				// heap size
		unsigned char *frame_ptr;			// CCR pointer
		};


// resource control block
typedef struct resource_block {
		unsigned char id;	   				// ID of resource
		char name[5];	  					// Name of resource
		enum resource_state state;			// State (busy, free...)
		unsigned char owner;				// Current resource owner
		signed char queue[3];				// Tasks waiting on resource
		unsigned char queue_ptr;			// Next free spot in queue
		};

		
struct task_block *task[MAXTASKS];
struct resource_block resource[NUMRESOURCES];


// global interrupt flags
//unsigned int interrupt_flags_ADC;
//unsigned int interrupt_flags_TC;


char kdb_trace = 0;


// error massages
const char error_msg[][25] = {"unimplimented function",	// error 0 
	 				  	"syntax error",				// error 1 
						"illegal task ID",			// error 2 
						"illegal task state",		// error 3 
						"illegal task priority",	// error 4 
						"out of memory",			// error 5 
						"illegal resource ID",		// error 6
						"illegal resource state",	// error 7
						"cannot create task"		// error 8
						};
const char error_src[][18] = {"kernel error:",			// source 0
	 				  	"kernel.c error:",			// source 1
						"kernel RTI error:",		// source 2
						"shell error:",				// source 3
						"semlib.c error:"			// source 4
						};



// FUNCTIONS

int get_task_address(char id) {

	return (int)task[id]->address;

}




char get_task_id() { 
	 
	 return task[current]->id;
}



char *get_task_name(unsigned char id) {
	 if (task[id] == NULL)
	 	return NULL;
	 else
	 	return task[id]->name; 
}




int get_task_state(unsigned char id) { 

	if (task[id] == NULL)
	   return -1;
	else if (id < MAXTASKS)
	 	return task[id]->state;
	else {
	 	  #ifdef KERNEL_ERROR_MSG
	 	  puts(error_src[1]); puts(error_msg[3]);
		  #endif
		  return -1;
		  }
		  
}



int get_task_messages(unsigned char id) {

	if (task[id] == NULL)
	   return -1;
	else if (id < MAXTASKS)
	   return task[id]->message;
	else {
		 #ifdef KERNEL_ERROR_MSG
	 	 puts(error_src[1]); puts(error_msg[3]);
		 #endif
		 return -1;
		 }

}



int get_task_priority(unsigned char id) { 

		 INTR_OFF();
		 
		 if (id < MAXTASKS) {
		 	INTR_ON();
		 	return task[id]->priority;
			}
		 else {
	 	 	#ifdef KERNEL_ERROR_MSG
	 	 	puts(error_src[1]); puts(error_msg[4]);
			#endif
			INTR_ON();
			return -1;
		 	}

}



int set_task_state(unsigned char id, unsigned char newstate) {
	
	
	INTR_OFF();
	
	
	// check id validity
	if (id < MAXTASKS) {

	   if (task[id] == NULL) {
	   	  #ifdef KERNEL_ERROR_MSG
	 	  puts(error_src[1]); puts(error_msg[2]);
		  #endif
		  INTR_ON();
	 	  return -1;
	   	  }
		  
		  
	   // check newstate validity
	   if ((newstate == RUNNING) || 
	   	  (newstate == WAITING)  ||
	   	  (newstate > STOPPED)) { 
	      		 #ifdef KERNEL_ERROR_MSG
	 	 	  	 puts(error_src[1]); puts(error_msg[3]);
			  	 #endif
				 INTR_ON();
	 	  		 return -1;
	   			 }
	 
	 		// referenced to the task's current state:
	   		switch (task[id]->state) {
		   	  	   case IDLE:
				   		if (newstate == IDLE) {	   // no change
						   INTR_ON();
				 		   return 0;
						   }
				 		else if (newstate == PENDING) {
				 		   // pass msg to task[id]->msgbox;
						   task[id]->message |= STATE_FLAG;
		  				   task[id]->message_data[STATE_BOX] = PENDING;
						   INTR_ON();
					  	   return 0;
				 		   }
						else if (newstate == STOPPED) {
							// pass msg to task[id]->msgbox;
						    task[id]->message |= STATE_FLAG;
		  					task[id]->message_data[STATE_BOX] = STOPPED;
							INTR_ON();
					  		return 0; 
						    }
				 		else {	  // nothing else is legal;
						   	 #ifdef KERNEL_ERROR_MSG
	 	 	  			   	 puts(error_src[1]); puts(error_msg[3]);
			  			   	 #endif
							 INTR_ON();
					  		 return -1;
				 			 }
				 		break;
		   			case PENDING:
						 if (newstate == PENDING) {	// no change
						 	INTR_ON();
				 		 	return 0;
							}
						 else if (newstate == IDLE) {
				   		 	  // pass msg to task[id]->msgbox;
						   	  #ifdef KERNEL_ERROR_MSG
	 	 	  			   	  puts(error_src[1]); puts(error_msg[0]);
			  			   	  #endif
							  INTR_ON();
					  		  return -1;
							  }
						 else if (newstate == STOPPED) {
				   		 	  	 // pass msg to task[id]->msgbox;
								 puts("TP");
							  	 task[id]->message |= STATE_FLAG;
		  					  	 task[id]->message_data[STATE_BOX] = STOPPED;
							  	 INTR_ON();
					  		  	 return 0;
						 	  	 }	
						 else {
				   		 	  #ifdef KERNEL_ERROR_MSG
							  puts(error_src[1]); puts(error_msg[3]);
							  #endif
							  INTR_ON();
							  return -1;
						 	  }
						 break;
					case RUNNING:
						 if (newstate == STOPPED) {
						 	// pass msg to task[id]->msgbox;
							task[id]->message |= STATE_FLAG;
		  					task[id]->message_data[STATE_BOX] = STOPPED;
							
							INTR_ON();
					  		return 0;
						 	}
						 else {
				   		 	  #ifdef KERNEL_ERROR_MSG
							  puts(error_src[1]); puts(error_msg[3]);
							  #endif
							  INTR_ON();
							  return -1;
						 	  }
						 break;
		   			case STOPPED:
		   				 if (newstate == STOPPED)	{ 	// no change
				   		 	INTR_ON();
							return 0;
							}
						 else if (newstate == PENDING) {
				   		 	  // pass msg to task[id]->msgbox;
							  task[id]->message |= STATE_FLAG;
		  					  task[id]->message_data[STATE_BOX] = PENDING;
							  INTR_ON();
					  		  return 0;
							  }
						 else {
					 	 	  #ifdef KERNEL_ERROR_MSG
							  puts(error_src[1]); puts(error_msg[3]);
							  #endif
							  INTR_ON();
							  return -1;
							  }
						 break;
		   			default:
		   		   			#ifdef KERNEL_ERROR_MSG
							puts(error_src[1]); puts(error_msg[3]);
							#endif
							INTR_ON();
							return -1;
				   			break;
							
	 				}	// end switch (task[id]->state)
					
	 		}		// if (id < MAXTASKS)
			
		   else {
		   		#ifdef KERNEL_ERROR_MSG
				puts(error_src[1]); puts(error_msg[2]);
				#endif
				INTR_ON();
				return -1;
				}

	 INTR_ON();
	 return 0;

}



int set_task_priority(unsigned char id, unsigned char priority) {

	 if (priority == 0) 			 // priority 0 is reserved for the shell
	 	priority = 1;
		
	 if (id >= (MAXTASKS)) {
	 	#ifdef KERNEL_ERROR_MSG
		puts(error_src[1]); puts(error_msg[4]);
		#endif
		return -1;
		}
	 else if ((priority > 255) || (priority < 0)) {
	 	#ifdef KERNEL_ERROR_MSG
		puts(error_src[1]); puts(error_msg[4]);
		#endif
		return -1;
		}
	 else {
	 	 task[id]->priority = priority;
		 return 0;
		 }
}




int create_task(char *name, void (*addr)(), unsigned char priority, int state) {

	// LOCAL VARIABLES
	unsigned char id, i=0;
	
	
	INTR_OFF();	  // critical section	  
	
	
	//if (get_free_memory() > 64) {
	
	// determine lowest free id available to assign to this task
	for (i=0; i<MAXTASKS; i++) {
		if (task_ptr[i] != NULL)
		   continue;
		else
		   break;
		 }
	id = i;
	
	// create task block
	if ((task[id] = malloc(sizeof(struct task_block))) == NULL) {
	   #ifdef KERNEL_ERROR_MSG
	   puts(error_src[1]); puts(error_msg[5]);
	   #endif
	   return -1;
	   }
	else { 
	 	 // put addr entry into task_ptr[]
		 task_ptr[id] = addr;
		 
		 
	 	 // initialize the task structures
	 	 task[id]->address = addr;
		 task[id]->id = id;
		 strcpy(task[id]->name, name);
 		 task[id]->state = state;
		 task[id]->priority = priority;
		 task[id]->period_tick = 0;
		 task[id]->interrupt_msg_box = NULL;
		 task[id]->message = NULL;
		 task[id]->message_data[0] = NULL;
		 task[id]->message_data[1] = NULL;
		 task[id]->heap_ptr = malloc(STACK_SIZE);
		 task[id]->heap_size = 0;
		 task[id]->frame_ptr = NULL;
	
	}
	//}
	
	INTR_ON();
	 
	return id;
	 
}




int get_resource_state(unsigned char rid) {

	if (rid < NUMRESOURCES) {
	 	return resource[rid].state;
		}
	else {
		 #ifdef KERNEL_ERROR_MSG
	 	 puts(error_src[1]); puts(error_msg[6]);
		 #endif
		 return -1;
		 }

}





int get_resource_owner(unsigned char rid) {

	if (rid < NUMRESOURCES) {
	 	return resource[rid].owner;
		}
	else {
		 #ifdef KERNEL_ERROR_MSG
	 	 puts(error_src[1]); puts(error_msg[6]);
		 #endif
		 return -1;
		 }

}




int get_resource_queuelen(unsigned char rid) {

	if (rid < NUMRESOURCES) {
	 	return resource[rid].queue_ptr;
		}
	else {
		 #ifdef KERNEL_ERROR_MSG
	 	 puts(error_src[1]); puts(error_msg[6]);
		 #endif
		 return -1;
		 }

}




int get_free_memory(void) {

	// LOCAL VARIABLES
	char *memory, *last;
	int i;
	
	
	INTR_OFF();
	// check for largest free memory block
	for (i=0; i<INITIAL_HEAP_SIZE; i+=32) {
		if ((memory = malloc(i)) != NULL)
		   last = memory;
		else
		   break;
		free(last);
		}
		
	INTR_ON();
	//printf("%d bytes\n", i);
	
	return (i-32);

}

