/* ------------------------------------------------------------------------------------------------ */
/* FILE :		Demo_2_EKLM3S1968_ATOLLIC.c															*/
/*																									*/
/* CONTENTS :																						*/
/*				Demo for the Stellaris EKLM3S1968 evaluation board									*/
/*				Operation of the RTOS shown on the oLED display										*/
/*				Control of the tasks parameters through the serial port								*/
/*				Serial port settings:																*/
/*							Baud Rate: 115200														*/
/*							Data bits:      8														*/
/*							Stop bits:      1														*/
/*							Parity:      none														*/
/*							Flow Ctrl:   none														*/
/*							Emulation: VT-100														*/
/*																									*/
/*																									*/
/* Copyright (c) 2012-2014, Code-Time Technologies Inc. All rights reserved.						*/
/*																									*/
/* Code-Time Technologies retains all right, title, and interest in and to this work				*/
/*																									*/
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS							*/
/* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF										*/
/* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL							*/
/* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR								*/
/* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,							*/
/* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR							*/
/* OTHER DEALINGS IN THE SOFTWARE.																	*/
/*																									*/
/*																									*/
/*	$Revision: 1.6 $																				*/
/*	$Date: 2013/06/21 17:45:30 $																	*/
/*																									*/
/* ------------------------------------------------------------------------------------------------ */

#if OS_DEMO != 2
	#error "Wrong OS_DEMO value" 
#endif

#include <stdio.h>
#include <ctype.h>
#include "Abassi.h"

/* ------------------------------------------------ */

#include "inc/hw_ints.h"
#include "inc/hw_memmap.h"
#include "inc/hw_types.h"
#include "inc/lm3s1968.h"
#include "driverlib/debug.h"
#include "driverlib/gpio.h"
#include "driverlib/interrupt.h"
#include "driverlib/sysctl.h"
#include "driverlib/timer.h"
#include "driverlib/uart.h"
#include "drivers/rit128x96x4.h"

#define BAUDRATE	115200						/* Serial port baud rate						*/

/* ------------------------------------------------ */

#define TSK_REFRESH_RATE	10						/* Display refresh rate: 1/TSK_REFRESH_RATE sec	*/
#define RTC_REFRESH_RATE	1						/* RTC refresh rate related to TSK_REFRESH_RATE	*/

#define USE_EVENTS			0						/* Events or mailboxes used to inform task 		*/

#define OP_ZERO_CNT			1						/* Operation requested by the ISR state machine	*/
#define OP_LOCK_MTX			2
#define OP_UNLOCK_MTX		4

#define TSK_HIGH			0						/* Index of the task info in TskDsc[]			*/
#define TSK_RRA				1
#define TSK_RRB				2
#define TSK_RRC				3
#define TSK_STRVA			4
#define TSK_STRVB			5

#define OP_ROUND_ROBIN		0						/* Command line configuration operation			*/
#define OP_STARVE_RUN		1
#define OP_STARVE_WAIT		2
#define OP_STARVE_PRIO		3
#define OP_PRIO				4

#define MBX_DISP_SIZE		32						/* Depth of the mailbox for the display			*/

/* ------------------------------------------------ */

typedef struct {									/* Hold the descriptions for the task monitored	*/
	TSK_t *Task;									/* Task descriptor								*/
  #if ((USE_EVENTS) == 0)
	MBX_t *Mailbox;									/* Mailbox when ISR state machine uses mailboxes*/
  #endif
	int    Running;									/* If this is the running task (ignoring A&E	*/
	int    WasReset;								/* To inform task to reset running time counter	*/
	int    CPUcnt;									/* Number of timer ticks task has been running	*/
	int    WillBlk;									/* If the task is next to block on the mutex	*/
	int    WillMtx;									/* If the task is next to lock the mutex		*/
} TskDsc_t;
TskDsc_t TskDsc[6];

typedef struct {									/* Info sent to the task to update the display	*/
	char *Str;										/* String to print on the display				*/
	int  X;											/* X coordinate									*/
	int  Y;											/* Y coordinate									*/
} DispInfo_t;
DispInfo_t DispInfo[31];

volatile int g_DispIdx=0;							/* Entry in DispInfo[] is next to be filled		*/

char Display[7][20] = { "00.0s  Px      ",
                        "00.0s  Px      ",
                        "00.0s  Px      ",
                        "00.0s  Px      ",
                        "00.0s  Px      ",
                        "00.0s  Px      ",
						"00:00:00   "
                      };
volatile int g_CmdIdx;								/* Index where to write next character from kyb	*/
TSK_t *AdamEve;
MTX_t *Mutex;										/* Mutex used by the task to show inheritance	*/
MBX_t *MbxDisp;										/* Mailbox to send display updates to the tasks	*/
SEM_t *SemaAE;										/* Semaphore to awake Adam & Eve				*/

#if ((OX_STARVE_RUN_MAX) != 0)
  extern TSK_t *g_StrvList;							/* Imported from Abassi.c						*/
#else
  TSK_t *g_StrvList = (TSK_t *)NULL;
#endif

#if ((OX_SEARCH_ALGO) >= 0)							/* Different way to get task list				*/
  extern TSK_t *g_TaskRdy[(OX_PRIO_MIN)+2];			/* Imported from Abassi.c						*/
#else
  extern TSK_t *g_PrioList;							/* Imported from Abassi.c						*/
#endif

/* ------------------------------------------------ */

void   Fct(void);									/* A single function implements all the tasks	*/
int    GetKey(void);								/* Non-blocking keyboard input					*/

/* ------------------------------------------------------------------------------------------------ */
/* ------------------------------------------------------------------------------------------------ */

int main(void)
{
int   ccc;											/* Character read from the keyboard				*/
int   Done;											/* If command decoding is done					*/
int   Error;										/* If error during command decoding				*/
int   ii;											/* General purpose								*/
int   jj;											/* General purpose								*/
int   kk;											/* General purpose								*/
int   Op;											/* Operation requested by the command			*/
int   Prio = 0;										/* Priority of the timer tick interrupt			*/
int   RdIdx;										/* Read index in the command line string		*/
int   Row;											/* Row of the display where to print the sting	*/
char *String;										/* Display sting being created					*/
int   TaskIdx;										/* Index of the task specified on command line	*/
int  *TimerBase;									/* Systick timer registers base address			*/
int   Toggle;										/* Toggle use to control refresh of RTC			*/

static char Cmd[64];								/* Command line from keyboard					*/

/* ------------------------------------------------ */
/* Processor clock set-up							*/

	SysCtlClockSet(SYSCTL_SYSDIV_4|SYSCTL_USE_PLL|SYSCTL_OSC_MAIN|SYSCTL_XTAL_8MHZ);

/* ------------------------------------------------ */
/* Start the RTOS									*/

	OSstart();

	AdamEve = TSKmyID();

/* ------------------------------------------------ */
/* UART0 set-up										*/

	SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0);
	SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);

	GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_0 | GPIO_PIN_1);
	UARTConfigSetExpClk(UART0_BASE, SysCtlClockGet(), BAUDRATE,
	                    (UART_CONFIG_WLEN_8 | UART_CONFIG_STOP_ONE | UART_CONFIG_PAR_NONE));

	setvbuf(stdin,  NULL, _IONBF, 0);				/* By default, Atollic library flush the I/O	*/
	setvbuf(stdout, NULL, _IONBF, 0);				/* buffer when full or when new-line			*/

/* ------------------------------------------------ */
/* SysTick Set-up									*/

	TimerBase = (int *)0xE000E000;
	((volatile int *)0xE000Ed20)[0] = (((volatile int *)0xE000Ed20)[0] & 0x1FFFFFFF)
	                                |   ((Prio & 7) << 29);
	OSisrInstall(-1, TIMtick);						/* Install the OS timer tick function			*/
	TimerBase[5] = (((SysCtlClockGet()/100000) * OS_TIMER_US)/10)-1;
	TimerBase[4] = 0x7;								/* internal clock / int & enable				*/

/* ------------------------------------------------ */
/* Application set-up								*/
													/* Need to create all services before allowing	*/
	MbxDisp                  = MBXopen("Disp", MBX_DISP_SIZE);
	Mutex                    = MTXopen("Mutex");
	SemaAE                   = SEMopen("Sem A&E"); 

	TskDsc[TSK_HIGH].Task    = TSKcreate("HI   ", 2, TC_TSK_STACK, &Fct, 0);
	TskDsc[TSK_RRA].Task     = TSKcreate("RRA  ", 4, TC_TSK_STACK, &Fct, 0);
	TskDsc[TSK_RRB].Task     = TSKcreate("RRB  ", 4, TC_TSK_STACK, &Fct, 0);
	TskDsc[TSK_RRC].Task     = TSKcreate("RRC  ", 4, TC_TSK_STACK, &Fct, 0);

	TskDsc[TSK_STRVA].Task   = TSKcreate("STRVA", 7, TC_TSK_STACK, &Fct, 0);
	TskDsc[TSK_STRVB].Task   = TSKcreate("STRVB", 9, TC_TSK_STACK, &Fct, 0);

  #if ((OX_ROUND_ROBIN) < 0)
	TSKsetRR(TskDsc[TSK_RRA].Task, 4*OX_RR_TICK);
	TSKsetRR(TskDsc[TSK_RRB].Task, 6*OX_RR_TICK);
	TSKsetRR(TskDsc[TSK_RRC].Task, 8*OX_RR_TICK);
  #endif

  #if ((OX_STARVE_RUN_MAX) < 0)
	TSKsetStrvRunMax (TskDsc[TSK_STRVA].Task,   5*OS_TICK_PER_SEC);
	TSKsetStrvRunMax (TskDsc[TSK_STRVB].Task,   5*OS_TICK_PER_SEC);
  #endif

  #if ((OX_STARVE_WAIT_MAX) < 0)
	TSKsetStrvWaitMax(TskDsc[TSK_STRVA].Task, (25*OS_TICK_PER_SEC)/10);
	TSKsetStrvWaitMax(TskDsc[TSK_STRVB].Task, (10*OS_TICK_PER_SEC)/10);
  #endif

	for (ii=0 ; ii<sizeof(TskDsc)/sizeof(TskDsc[0]) ; ii++) {
		TSKsetArg(TskDsc[ii].Task, &TskDsc[ii]);
	  #if ((USE_EVENTS) == 0)
		TskDsc[ii].Mailbox  = MBXopen(NULL, 32);
	  #endif
		TskDsc[ii].CPUcnt   = 0;
		TskDsc[ii].Running  = 0;
		TskDsc[ii].WasReset = 0;
		TskDsc[ii].WillBlk  = 0;
		TskDsc[ii].WillMtx  = 0;
		TSKresume(TskDsc[ii].Task);
	}

/* ------------------------------------------------ */
/* Display the basic template on the LCD			*/

	RIT128x96x4Init(1000000);
	RIT128x96x4StringDraw("     Abassi Demo",     0, 16, 15);
	RIT128x96x4StringDraw("High  00.0s  P2     ", 0, 32, 15);
	RIT128x96x4StringDraw("RR-A  00.0s  P4     ", 0, 40, 15);
	RIT128x96x4StringDraw("RR-B  00.0s  P4     ", 0, 48, 15);
	RIT128x96x4StringDraw("RR-C  00.0s  P4     ", 0, 56, 15);
	RIT128x96x4StringDraw("ST-A  00.0s  P7     ", 0, 64, 15);
	RIT128x96x4StringDraw("ST-B  00.0s  P9     ", 0, 72, 15);


/* ------------------------------------------------ */
/* Interrupts ON, the application runs				*/

	OSeint(1);

	g_CmdIdx = 0;
	Toggle   = 0;

	printf("\033[H\033[J");							/* Clear screen									*/
	printf("\033[0;0H");							/* Set cursor where desired						*/
	printf("Abassi Demo > ");						/* Prompt on Serial for the command line		*/

	G_OStimCnt = 0;

	for (;;) {

		SEMwait(SemaAE, -1);

		if (++Toggle >= RTC_REFRESH_RATE) { 		/* RTC is not always refreshed					*/
			Toggle = 0;
			String = &Display[6][0];				/* Display line to update						*/
													/* RTC display is up to 99:59:59 so				*/
			jj = OSdint();							/* roll back 00:00:00 when max reached			*/
			if (G_OStimCnt >= (360000L*(long)OS_TICK_PER_SEC)) {
				G_OStimCnt -= (360000L*(long)OS_TICK_PER_SEC);
			}
			OSeint(jj);
			jj = G_OStimCnt;

			kk = jj/(OS_TICK_PER_SEC*3600*10);		/* MS digit for the hours						*/
			String[0] = '0'+kk;
			jj -= kk*(OS_TICK_PER_SEC*3600*10);

			kk  =jj/(OS_TICK_PER_SEC*3600);			/* LS digits for the hours						*/
			String[1] = '0'+kk;
			jj -= kk*(OS_TICK_PER_SEC*3600);

			kk  = jj/(OS_TICK_PER_SEC*600);			/* MS digits for the minutes					*/
			String[3] = '0'+kk;
			jj -= kk*(OS_TICK_PER_SEC*600);

			kk  = jj/(OS_TICK_PER_SEC*60);			/* LS digits for the minutes					*/
			String[4] = '0'+kk;
			jj -= kk*(OS_TICK_PER_SEC*60);

			kk  = jj/(OS_TICK_PER_SEC*10);			/* MS digits for the seconds					*/
			String[6] = '0'+kk;
			jj -= kk*(OS_TICK_PER_SEC*10);

			kk = jj/(OS_TICK_PER_SEC);				/* LS digits for the seconds					*/
			String[7] = '0'+kk;
			Row = 40+(6<<3);

			DispInfo[g_DispIdx].Str = String;
			DispInfo[g_DispIdx].X   = Row;			/* Send the string to the running task			*/
			DispInfo[g_DispIdx].Y   = 7*6-3;
			MBXput(MbxDisp, (intptr_t)&DispInfo[g_DispIdx], 0);
			if (++g_DispIdx >= (sizeof(DispInfo)/sizeof(DispInfo[0]))) {
				g_DispIdx = 0;
			}
		}

		for (ii=0 ; ii<sizeof(TskDsc)/sizeof(TskDsc[0]) ; ii++) {	/* Update all tasks status		*/
			String = &Display[ii][0];				/* Display line to update						*/
			jj = (TskDsc[ii].CPUcnt*100)/(OS_TICK_PER_SEC);
			if (jj > 9999) {						/* Can't display more than 99.9 (is /10)		*/
				jj = 9999;
			}
			kk = jj/1000;							/* Running time MS digit						*/
			String[0] = '0'+kk;

			jj = jj - (kk*1000);					/* Running time LS digit						*/
			kk = jj/100;
			String[1] = '0'+kk;

			jj = jj - (kk*100);						/* Running time fraction						*/
			kk = jj/10;
			String[3] = '0'+kk;

			if (TSKisSusp(TskDsc[ii].Task)) {		/* If the task is suspended, the priority		*/
				String[7] = 'S';					/* is replaced by "S "							*/
				String[8] = ' ';
			}
			else {									/* Regular priority field						*/
				String[7] = 'P';
				if (TskDsc[ii].Task->SuspRqst != 0) {
					String[8] = 'S';				/* When the task will be suspended, indicate so	*/
				}
				else {								/* Priority numerical value						*/
					String[8] = '0'+TSKgetPrio(TskDsc[ii].Task);
				}
			}
			if (TskDsc[ii].Running) {				/* If this is the running task, indicate so		*/
				String[11] = 'R';
			}
			else {									/* Not a running task, nothing in this field	*/
				String[11] = ' ';
			}
			if (TskDsc[ii].Task == g_StrvList) {	/* If the task is under starvation protection	*/
				String[12] = 'A';					/* Show the "A"									*/
			}
			else {									/* Not under starvation protection, nothing to	*/
				String[12] = ' ';					/* shown in this field							*/
			}

			if (TskDsc[ii].Task->MyMutex == Mutex) {
				String[13] = 'M';					/* If this task locks the mutex, show it		*/
			}
			else if (TskDsc[ii].WillMtx  != 0) {
				String[13] = 'm';					/* If the task will lock the mutex, show it		*/
			}
			else if (TskDsc[ii].Task->Blocker == Mutex) {
				String[13] = 'B';					/* If the task is blocked by the mutex, show it	*/
			}
			else if (TskDsc[ii].WillBlk  != 0) {
				String[13] = 'b';					/* if the task will block on the mutex, show it	*/
			}
			else {
				String[13] = ' ';					/* If the task not involved with the mutex, 	*/
			}										/* nothing to show in this field				*/

			if ((TSKgetPrio(TskDsc[ii].Task) != TskDsc[ii].Task->OriPrio)
			&&  (TskDsc[ii].Task != g_StrvList)
			&&  (TskDsc[ii].Task->MyMutex == Mutex)) {
				String[14] = 'I';					/* If the task has it's priority increased due	*/
			}										/* to priority inheritance, show this			*/
			else {
				String[14] = ' ';
			}
			Row = 32+(ii<<3);
			DispInfo[g_DispIdx].Str = String;
			DispInfo[g_DispIdx].X   = Row;
			DispInfo[g_DispIdx].Y   = 6*6;
													/* Send the string to display to running task	*/
			MBXput(MbxDisp, (intptr_t)&DispInfo[g_DispIdx], 0);
			if (++g_DispIdx >= (sizeof(DispInfo)/sizeof(DispInfo[0]))) {
				g_DispIdx = 0;
			}
		}

		ccc = GetKey();								/* Check for a new key pressed on the keyboard 	*/
		if (ccc != 0) {
			if (g_CmdIdx == 0) {
				for (ii=0 ; ii<sizeof(Cmd) ; ii++) {
					putchar(' ');
				}
				printf("\033[0;15H");
			}
			if (g_CmdIdx < (sizeof(Cmd)/sizeof(Cmd[0]))-2) {
				putchar(ccc);						/* A key was pressed , echo it					*/
			}										/* If command line buffer full, don't echo		*/
			if (ccc == '\r') {						/* Need to remap CR to '\n'						*/
				putchar('\n');
			}
			else if (ccc == '\b') {					/* Backspace pressed, to erase previous key		*/
				if (g_CmdIdx != 0) {
					g_CmdIdx--;
					putchar(' ');
					putchar('\b');
				}
			}
			else {
				if (g_CmdIdx < (sizeof(Cmd)/sizeof(Cmd[0]))-2) {
					Cmd[g_CmdIdx] = toupper(ccc);
					g_CmdIdx++;
				}
			}

			if (ccc == '\r') {						/* CR pressed, then process the command			*/
				Cmd[g_CmdIdx] = '\0';
				TaskIdx = -1;
				RdIdx   = 0;
				while ((Cmd[RdIdx] == ' ')			/* Skip start of line white spaces				*/
				||     (Cmd[RdIdx] == ',')
				||     (Cmd[RdIdx] == '\t')) {
					RdIdx++;
				}

				Done  = 0;
				Error = 0;

				if (g_CmdIdx == RdIdx) {			/* When everything white space, nothing to do	*/
					Done = 1;
				}

				g_CmdIdx = 0;

				printf("\033[0;0H");

				if ((0 == strncmp(&Cmd[RdIdx], "?", 1))
				||  (0 == strncmp(&Cmd[RdIdx], "INFO", 4))) {
					putchar('\n');
					putchar('\n');
					for (ii=0 ; ii<sizeof(TskDsc)/sizeof(TskDsc[0]) ; ii++) {
						printf("%s  -->  ",  TskDsc[ii].Task->TskName);
					  #if ((OX_ROUND_ROBIN) != 0)
						printf("RR %3d  |  ", TSKgetRR(TskDsc[ii].Task));
					  #endif
					  #if ((OX_STARVE_RUN_MAX) != 0)
						printf("SP %1d  |  ", TSKgetStrvPrio(TskDsc[ii].Task));
						printf("SR %3d  |  ", TSKgetStrvRunMax(TskDsc[ii].Task));
						printf("SW %3d  |  ", TSKgetStrvWaitMax(TskDsc[ii].Task));
					  #endif
						printf("P %d      \n",  TskDsc[ii].Task->OriPrio);
					}
					putchar('\n');
					Done = 1;
					printf("\033[0;15H       ");
				}

				if (0 == strncmp(&Cmd[RdIdx], "HI", 2)) {
					TaskIdx = TSK_HIGH;
					RdIdx += 2;
				}
				if (0 == strncmp(&Cmd[RdIdx], "RRA", 3)) {
					TaskIdx = TSK_RRA;
					RdIdx += 3;
				}
				if (0 == strncmp(&Cmd[RdIdx], "RRB", 3)) {
					TaskIdx = TSK_RRB;
					RdIdx += 3;
				}
				if (0 == strncmp(&Cmd[RdIdx], "RRC", 3)) {
					TaskIdx = TSK_RRC;
					RdIdx += 3;
				}
				if (0 == strncmp(&Cmd[RdIdx], "STRVA", 5)) {
					TaskIdx = TSK_STRVA;
					RdIdx += 5;
				}
				if (0 == strncmp(&Cmd[RdIdx], "STRVB", 5)) {
					TaskIdx = TSK_STRVB;
					RdIdx += 5;
				}
				if (Done == 0) {
					if (TaskIdx < 0) {
						printf("\033[0;15H");		/* Set cursor after Abassi Demo >				*/
						ii = printf("Unrecognized task name");
						for( ; ii<sizeof(Cmd) ; ii++) {
							putchar(' ');
						}
						Error=1;
					}
				}

				Op = -1;							/* 2nd token if the service, check all			*/
				if ((Error == 0)
				&&  (Done == 0)) {
					while ((Cmd[RdIdx] == ' ')		/* Skip white spaces between 1st and 2nd token	*/
					||     (Cmd[RdIdx] == ',')
					||     (Cmd[RdIdx] == '\t')) {
						RdIdx++;
					}
					if (0 == strncmp(&Cmd[RdIdx], "RR", 2)) {
						Op = OP_ROUND_ROBIN;		/* Token names are re-mapped to operations #	*/
						RdIdx += 2;
					}
					if (0 == strncmp(&Cmd[RdIdx], "SR", 2)) {
						Op = OP_STARVE_RUN;
						RdIdx += 2;
					}
					if (0 == strncmp(&Cmd[RdIdx], "SP", 2)) {
						Op = OP_STARVE_PRIO;
						RdIdx += 2;
					}
					if (0 == strncmp(&Cmd[RdIdx], "SW", 2)) {
						Op = OP_STARVE_WAIT;
						RdIdx += 2;
					}
					if (0 == strncmp(&Cmd[RdIdx], "P", 1)) {
						Op = OP_PRIO;
						RdIdx += 1;
					}
					if (Op < 0) {
						Error = 1;
						printf("\033[0;15H");		/* Set cursor after Abassi Demo >				*/
						ii = printf("Unrecognized property        ");
						for( ; ii<sizeof(Cmd) ; ii++) {
							putchar(' ');
						}
					}
				}

				if ((Error == 0)
				&&  (Done == 0)) {
					while ((Cmd[RdIdx] == ' ')		/* Skip white spaces between 2nd and 3rd token	*/
					||     (Cmd[RdIdx] == ',')
					||     (Cmd[RdIdx] == '\t')) {
						RdIdx++;
					}
					ii = 0;
					if (Cmd[RdIdx] == '\0') {
						Error = 1;
						printf("\033[0;15H");		/* Set cursor after Abassi Demo >				*/
						ii = printf("Invalid number");
						for( ; ii<sizeof(Cmd) ; ii++) {
							putchar(' ');
						}
					}
					do {							/* 3rd token is the service numerical value		*/
						if ((Cmd[RdIdx] >= '0')
						&&  (Cmd[RdIdx] <= '9')) {
							ii = ii*10;
							ii += Cmd[RdIdx] - '0';
							RdIdx++;
						}
						else if (Cmd[RdIdx] == '\0') {
							Done = 1;
						}
						else {
							Error = 1;
							printf("\033[0;15H");	/* Set cursor after Abassi Demo >				*/
							ii = printf("Invalid number");
							for( ; ii<sizeof(Cmd) ; ii++) {
								putchar(' ');
							}
						}
					} while ((Error == 0)
					  &&     (Done  == 0));

					if (Error == 0) {				/* When everything OK, set the numerical value	*/
						switch(Op) {				/* to the service in the task					*/
						case OP_ROUND_ROBIN:
						  #if ((OX_ROUND_ROBIN) < 0)
							TSKsetRR(TskDsc[TaskIdx].Task, ii);
						  #endif
							break;
						case OP_STARVE_PRIO:
						  #if ((OX_STARVE_PRIO) < 0)
							TSKsetStrvPrio(TskDsc[TaskIdx].Task, ii);
						  #endif
							break;
						case OP_STARVE_RUN:
						  #if ((OX_STARVE_RUN_MAX) < 0)
							TSKsetStrvRunMax(TskDsc[TaskIdx].Task, ii);
						  #endif
							break;
						case OP_STARVE_WAIT:
						  #if ((OX_STARVE_WAIT_MAX) < 0)
							TSKsetStrvWaitMax(TskDsc[TaskIdx].Task, ii);
						  #endif
							break;
						default:
							TSKsetPrio(TskDsc[TaskIdx].Task, ii);
							break;
						}
					}
				}
				if (Error == 0) {
					printf("\033[0;0H");			/* Set cursor at beginning of line				*/
					printf("Abassi Demo > ");
					for (ii=0 ; ii<sizeof(Cmd) ; ii++) {
						putchar(' ');
					}
				}
				printf("\033[0;15H");
			}
		}
	}
}

/* ------------------------------------------------------------------------------------------------ */
/*																									*/
/* Idle Task																						*/
/*																									*/
/* ------------------------------------------------------------------------------------------------ */

void IdleTask(void)
{
DispInfo_t *Disp;
int         ii;
intptr_t    Msg;

 #if defined(OS_NEWLIB_REENT)						/* In case the reentrance is set on per task	*/
  #if OS_NEWLIB_REENT > 0
	setvbuf(stdin,  NULL, _IONBF, 0);
	setvbuf(stdout, NULL, _IONBF, 0);
  #endif
 #endif

	MbxDisp = MBXopen("Disp", MBX_DISP_SIZE);		/* Example of run time safe creation			*/

	for(;;) {
		ii = OSdint();								/* Will print, don't want to be pre-empted		*/
		if (0 ==  MBXget(MbxDisp, &Msg, 0)) {		/* Check if A&E has sent a string to display	*/
			Disp = (void *)Msg;						/* on the LCD									*/
			RIT128x96x4StringDraw(Disp->Str, Disp->Y, Disp->X, 15);
		}
		OSeint(ii);
	}
}

/* ------------------------------------------------------------------------------------------------ */
/*																									*/
/* Tasks function																					*/
/*																									*/
/* All tasks use the same function.																	*/
/* The Task Argument is the custom task description data structure									*/
/*																									*/
/* ------------------------------------------------------------------------------------------------ */

void Fct(void)
{
DispInfo_t *Disp;
int         ii;
intptr_t    Msg;
int         NoNewMsg;
TskDsc_t   *MyInfo;


	MyInfo = (TskDsc_t *)TSKgetArg();				/* Get the argument for this task				*/

 #if defined(OS_NEWLIB_REENT)						/* In case the reentrance is set on per task	*/
  #if OS_NEWLIB_REENT > 0
	setvbuf(stdin,  NULL, _IONBF, 0);
	setvbuf(stdout, NULL, _IONBF, 0);
  #endif
 #endif

	for ( ; ; ) {
	  #if USE_EVENTS								/* Info from ISR state machine comes from 		*/
		NoNewMsg = EVTwait(0x0000, 0xFFFF, 0);		/* either an event flag or a mailbox			*/
		Msg      = EVTget();
	  #else
		NoNewMsg = MBXget(MyInfo->Mailbox, &Msg, 0);
	  #endif

		if (NoNewMsg == 0) {						/* When a valid sync trigger was received		*/

			if ((int)Msg & OP_ZERO_CNT) {			/* Request to zero the running time				*/
				MyInfo->CPUcnt   = 0;
				MyInfo->WasReset = 0;
			}
			if (((int)Msg & OP_LOCK_MTX)			/* Request from ISR to lock the mutex			*/
			&&  (MTXowner(Mutex) != TSKmyID())) {	/* Safety in case state machine has issues		*/
				MyInfo->WillBlk = 0;
				MyInfo->WillMtx = 0;
				MTXlock(Mutex, -1);
			}
			if (((int)Msg & OP_UNLOCK_MTX)			/* Request from ISR to unlock the mutex			*/
			&&  (MTXowner(Mutex) == TSKmyID())) {	/* Safety in case state machine has issues		*/
				MTXunlock(Mutex);
			}
		}
		ii = OSdint();								/* Will print, don't want to be pre-empted		*/
		if (0 ==  MBXget(MbxDisp, &Msg, 0)) {		/* Check if A&E has sent a string to display	*/
			Disp = (void *)Msg;						/* on the LCD									*/
			RIT128x96x4StringDraw(Disp->Str, Disp->Y, Disp->X, 15);
		}
		OSeint(ii);
	}
}

/* ------------------------------------------------------------------------------------------------ */
/*																									*/
/* RTOS Timer call back function																	*/
/*																									*/
/* ------------------------------------------------------------------------------------------------ */
#if USE_EVENTS
	#define SEND_INFO(_Idx, _Info)	EVTset(TskDsc[_Idx].Task, _Info);
#else
	#define SEND_INFO(_Idx, _Info)	MBXput(TskDsc[_Idx].Mailbox, _Info, 0)
#endif

void TIMcallBack(void)
{
int    AErunning;
int    ii;
int    jj;
int    RunIdx;
static int Cnt       =  0;
static int MtxCnt    =  0;
static int MtxOwner  =  5;
static int MtxSelect =  5;
static int MtxBlk    =  1;
static int MtxState  =  0;
static int SuspCnt   =  0;

	Cnt++;											/* Check if A&E is running as we can't update	*/
	AErunning = (G_OStaskNow == AdamEve);			/* everything when it does						*/
													/* Update the CPU of the tasks					*/
	RunIdx = -1;									/* Invalidate running status of the  task		*/
	if (AErunning == 0) {							/* When A&E is running, don't update			*/
		for (ii=0 ; ii<sizeof(TskDsc)/sizeof(TskDsc[0]) ; ii++) {
			TskDsc[ii].Running = 0;
			if (G_OStaskNow == TskDsc[ii].Task) {
				TskDsc[ii].Running  = 1;
				RunIdx = ii;
			}
			if (TskDsc[ii].Task->Blocker != (SEM_t *)NULL) {
				if (TskDsc[ii].WasReset == 0) {
					SEND_INFO(ii, OP_ZERO_CNT);
					TskDsc[ii].WasReset = 1;
				}
			}
			if (TskDsc[ii].Task->RRmax != 0U) {
				if (TskDsc[ii].Task->RRcnt <= 1U) {
					if (TskDsc[ii].WasReset == 0) {
						SEND_INFO(ii, OP_ZERO_CNT);
						TskDsc[ii].WasReset = 1;
					}
				}
			}
		}
	}

	for (ii=0 ; ii<sizeof(TskDsc)/sizeof(TskDsc[0]) ; ii++) {	/* Excluding A&E & Idles			*/
		if (TskDsc[ii].Running != 0) {				/* It is supposed to be running, check if so	*/
			TskDsc[ii].CPUcnt++;
		}
	}

	if (AErunning == 0) {
		switch(MtxState) {
		case 0:										/* State #0 : new mutex owner					*/
			if (++MtxSelect >= sizeof(TskDsc)/sizeof(TskDsc[0])) {
				MtxSelect = 0;
			}
			if (MtxSelect == 2) {					/* RRB if not the first locker of the mutex		*/
				MtxSelect = 4;
			}

			MtxOwner = MtxSelect;

			SEND_INFO(MtxOwner, OP_LOCK_MTX);		/* Tell new owner to lock the mutex				*/

			if (++MtxBlk >= 4) {					/* Select the task that will block				*/
				MtxBlk = 0;							/* Only Hi & RR can block on the mutex			*/
			}

			if (MtxBlk == MtxOwner) {				/* Get another task to block on the mutex		*/
				if (++MtxBlk >= 4) {
					MtxBlk = 0;
				}
			}
			TskDsc[MtxOwner].WillMtx = 1;			/* Display it will lock the mutex soon			*/
			TskDsc[MtxBlk].WillBlk = 1;				/* And this will be blocked 					*/
			MtxState++;
			break;
		case 1:										/* Waiting for the owner to lock the mutex		*/
			if (TskDsc[MtxOwner].Task->MyMutex == Mutex) {
				MtxState++;
			}
			break;
		case 2:										/* OK, the mutex owner is locking the mutex		*/
			SEND_INFO(MtxBlk, OP_LOCK_MTX);			/* Tell the task to be blocked to lock it		*/
			MtxState++;
			break;
		case 3:										/* Make sure the task gets blocked				*/
			if (TskDsc[MtxOwner].Task->Prio == TskDsc[MtxBlk].Task->Prio) {
				if (TSKisBlk(TskDsc[MtxBlk].Task)) {/* OK, the task is blocked on the mutex now		*/
					MtxState++;						/* This may take a while as it needs to run		*/
					MtxCnt = 0;
				}
			}
			else {									/* If to be suspended, must skip a few states	*/
				if (TskDsc[MtxOwner].Task->SuspRqst != 0) {	/* as != prio can't run at same time	*/
					MtxState = 4;
					MtxCnt   = 0;
					TskDsc[MtxOwner].WillMtx = 0;
					TskDsc[MtxBlk].WillBlk   = 0;
					TskDsc[MtxBlk].WillMtx   = 1;
				}
			}
			break;
		case 4:										/* State #4: Task is blocked on the mutex		*/
			if (MtxOwner == RunIdx) {				/* Make sure the owner is running long enough	*/
				jj = (MtxOwner == 0)				/* When HI task, is getting suspended			*/
				   ? (((15*OS_TICK_PER_SEC)+1) - TskDsc[0].CPUcnt)
				   :   ((5*OS_TICK_PER_SEC)+1);
				if (++MtxCnt > jj) {				/* Tell the owner to release the mutex			*/
					SEND_INFO(MtxOwner, OP_UNLOCK_MTX);
					MtxState++;
				}
			}
			break;
		case 5:										/* Wait for owner to unlock the mutex			*/
			if (TskDsc[MtxOwner].Task->MyMutex != Mutex) {
				MtxOwner = MtxBlk;					/* The new owner is the one that was blocked	*/
				MtxState++;
			}
			break;
		case 6:										/* Wait for new owner to lock the mutex			*/
			if (TskDsc[MtxOwner].Task->MyMutex == Mutex) {
				MtxState++;
				MtxCnt = 0;
			}
			break;
		case 7:
			if (MtxOwner == RunIdx) {				/* Wait a while before telling to release		*/
				jj = (MtxOwner == 0)				/* When HI task, is getting suspended			*/
				   ? (((15*OS_TICK_PER_SEC)+1) - TskDsc[0].CPUcnt)
				   :   ((5*OS_TICK_PER_SEC)+1);
				if (++MtxCnt > jj) {				/* Tell the owner to release the mutex			*/
					SEND_INFO(MtxOwner, OP_UNLOCK_MTX);
					MtxState++;
				}
			}
			break;
		default:									/* Wait for blocked to unlock					*/
			if (TskDsc[MtxOwner].Task->MyMutex != Mutex) {
				MtxState =  0;
				MtxOwner = -1;
			}
			break;
  		}

		if ((Cnt >= OS_TICK_PER_SEC/TSK_REFRESH_RATE)
		&&  (TSKisBlk(AdamEve))) {					/* Make sure A&E does not use all the CPU		*/
			SEMpost(SemaAE);						/* Tell A & E to refresh the display			*/
			Cnt = 0;
		}
	}

	SuspCnt++;
	if (SuspCnt == 10*OS_TICK_PER_SEC+3) {			/* Suspend / resume the high priority			*/
		TSKsuspend(TskDsc[TSK_HIGH].Task);
	}
	else if (SuspCnt > 30*OS_TICK_PER_SEC+3) {
		TSKresume(TskDsc[TSK_HIGH].Task);
		SuspCnt = 0;
	}

	return;
}

/* ------------------------------------------------------------------------------------------------ */

TSK_t *GetHeadPrio(int Prio)
{

  #if ((OS_SEARCH_ALGO) < 0)
int    ii;
TSK_t *TaskScan;

	ii = OSdint();
	  TaskScan = (TSK_t *)g_PrioList;
	  while ((TaskScan != (TSK_t *)NULL)
	  &&     (TaskScan->Prio != Prio)) {
		  TaskScan = TaskScan->PrioNext;
	  }
	OSeint(ii);

	return(TaskScan);
  #else

	return(g_TaskRdy[Prio]);
  #endif
}

/* ------------------------------------------------------------------------------------------------ */
/*																									*/
/* UART functions needed by the Peripheral library to access the UART								*/
/*																									*/
/* ------------------------------------------------------------------------------------------------ */

#if !defined(_write_r)
_ssize_t _write_r (struct _reent *rreenn, int fd, const void *vbuf, size_t len)
{
const char *buf;
int   size;

	size = len;
	buf  = vbuf;
	while (size > 0) {
		if (*buf == '\n') {
			UARTCharPut(UART0_BASE, '\r');
		}
		UARTCharPut(UART0_BASE, *buf);
		buf++;
		size--;
	}

	return(len);
}
#endif

/* ------------------------------------------------------------------------------------------------ */

#if !defined(_read_r)
_ssize_t  _read_r(struct _reent *rreenn, int fd, void *vbuf, size_t size)
{
char *buf;

	buf = vbuf;
	for (; !(UARTCharsAvail(UART0_BASE)) ; );
	*buf = UARTCharGetNonBlocking(UART0_BASE);

	return(1);
}
#endif

/* ------------------------------------------------------------------------------------------------ */

int GetKey(void)
{
int RetVal;

	RetVal = 0;
	if (UARTCharsAvail(UART0_BASE)) {
		RetVal = UARTCharGetNonBlocking(UART0_BASE);
	}
	return(RetVal);
}

/* EOF */

