/* ------------------------------------------------------------------------------------------------ */
/* FILE :		Demo_3_AVR32A.c																		*/
/*																									*/
/* CONTENTS :																						*/
/*				Demo for the AVR32A																	*/
/*				Operation of the RTOS is shown on the serial port									*/
/*				Tasks info & configuration through the serial port									*/
/*				Serial port settings:																*/
/*							Baud Rate: 115200														*/
/*							Data bits:      8														*/
/*							Stop bits:      1														*/
/*							Parity:      none														*/
/*							Flow Ctrl:   none														*/
/*							Emulation: VT-100														*/
/*																									*/
/* INFO:																							*/
/*       How this demo works:																		*/
/*       6 Tasks are running, and also Adam & Eve (main) which is not displayed.					*/
/*       The RTOS timer tick interrupt callback handler goes through a state machine to control the	*/
/*       operations & triggers state changes of the 6 tasks.  The information is sent to the tasks	*/
/*       either by mailboxes or by events (see the defintion for USE_EVENTS).						*/
/*       Adam & Eve is periodically unblocked (semaphore) by the RTOS timertick callback.			*/
/*       Adam & Eve looks at the state opf all 6 tasks and send strings to print on stdout			*/
/*       through a mailbox.																			*/
/*       All 6 tasks monitor this mailbox and perform the printing on stdout.						*/
/*       Although a mutex should be used to provided exclusive access to stdout, interrupt			*/
/*       disabling & enabling is needed instead.  This is because if a mutex was used, the priority	*/
/*       inheritance would most likely kick in and change the priority of the printfing task.		*/
/*       That would create a weird display giving the impression tasks chenge priority for no		*/
/*       reasons.																					*/
/*																									*/
/*       For a full description of what is displayed, see:											*/
/*                    www.code-time.com/demo3.html													*/
/*																									*/
/* Copyright (c) 2012-2017, 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: 2017/04/29 16:38:38 $																	*/
/*																									*/
/* ------------------------------------------------------------------------------------------------ */

#if (((OS_DEMO) != 3) && ((OS_DEMO) != -3))
	#error "Wrong OS_DEMO value" 
#endif

#include "Abassi.h"
#include "SysCall.h"								/* System call layer definitions				*/
#include "Platform.h"								/* Everything about the target platform is here	*/
#include "HWinfo.h"									/* Everything about the target board is here	*/

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

#include "avr32_uart.h"
#define UART_FILTER		(UART_FILT_OUT_LF_CRLF | UART_FILT_IN_CR_LF)

#include "avr32/io.h"								/* From Atmel library							*/
#include "cycle_counter.h"							/* From Atmel library							*/

/* ------------------------------------------------------------------------------------------------ */
/* App definitions																					*/

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

#ifndef USE_EVENTS
 #if ((OX_MAILBOX) == 0)
  #define USE_EVENTS		1						/* No mailbox, has to use events				*/
 #else
  #define USE_EVENTS		0						/* Mailboxes instead of events inform tasks		*/
 #endif
#endif
#define OP_ZERO_CNT			1						/* Operation requested by the ISR state machine	*/
#define OP_LOCK_MTX			2						/* These are the messages / events sent to the	*/
#define OP_UNLOCK_MTX		4						/* 6 tasks										*/

#define TSK_HIGH			0						/* Index of task info/descriptot 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						/* These are what to modify in a task			*/
#define OP_STARVE_WAIT		2
#define OP_STARVE_PRIO		3
#define OP_PRIO				4

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

/* ------------------------------------------------------------------------------------------------ */
/* App variables																					*/

int G_UartDevIn  = UART_DEV;						/* Needed by the system call layer				*/
int G_UartDevOut = UART_DEV;						/* Needed by the system call layer				*/
int G_UartDevErr = UART_DEV;						/* Needed by the system call layer				*/

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       "
                      };
#define LAST_DISP		6							/* Number of lines / tasks to display			*/
#define CURSOR_START()	printf("\033[15;0H");		/* Cursor movements								*/
#define CURSOR_NEXT(x)	printf("\033[15;%02dH", (x)+15);
#define CURSOR_BACK()	printf("\033[15;15H");
#define CURSOR_ADD()	printf("\033[15;16H");

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[LAST_DISP];

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;										/* It is sent to UpdateDisp()					*/

DispInfo_t DispInfo[MBX_DISP_SIZE];

volatile int g_AppRdy = 0;							/* To inform RTOS timer callback when the		*/
volatile int g_CmdIdx;								/* Index where to write next character from kyb	*/
volatile int g_DispIdx = 0;							/* Next entry in DispInfo[] to fill				*/
													/* application is fully configured				*/
TSK_t *AdamEve;										/* Adam & Eve task descriptor					*/
MTX_t *Mutex;										/* Mutex used by the task to show inheritance	*/
#if ((OS_MAILBOX) != 0)
  MBX_t *MbxDisp;									/* Mailbox to send display updates to the tasks	*/
#else												/* No mailboxes, use a circulare buffer			*/
  int g_DispRdIdx=0;								/* Index used by Fct to grab the display test	*/
#endif
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

#ifdef DEMO_XTRA_VARS								/* Platform specific variables					*/
  DEMO_XTRA_VARS
#endif

/* ------------------------------------------------------------------------------------------------ */
/* Apps functions																					*/

void Fct(void);										/* A single function implements all the tasks	*/
void UpdateDisp(DispInfo_t *Disp);

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

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   IsrState;
int   jj;											/* General purpose								*/
int   kk;											/* General purpose								*/
int   Op;											/* Operation requested by the command			*/
int   RdIdx;										/* Read index in the command line string		*/
char *String;										/* Display sting being created					*/
int   TaskIdx;										/* Index of the task specified on command line	*/
int   Toggle;										/* Toggle use to control refresh of RTC			*/

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


/* ------------------------------------------------ */
/* Set buffering here: tasks inherit main's stdios	*/
/* when Newlib reent is used & stdio are shared		*/ 

  {
  char SetV[3];										/* IAR needs non-NULL buf with _IONBF			*/
	setvbuf(stdin,  &SetV[0], _IONBF, 0);			/* By default, libraries flush the I/O			*/
	setvbuf(stdout, &SetV[1], _IONBF, 0);			/* buffer when full or when new-line			*/
	setvbuf(stderr, &SetV[2], _IONBF, 0);			/* Done before OSstart() to have all tasks with	*/
  }													/* the same stdio set-up						*/

/* ------------------------------------------------ */
/* Start Abassi										*/

	OSstart();										/* Start Abassi									*/

	SysCallInit();									/* Initialize the System Call layer				*/

	OSintOn();										/* Enable the interrupts						*/

	AdamEve = TSKmyID();							/* This demo needs to know when A&E is running	*/

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

	OSisrInstall(OSavr32aISRmap(0, 0), &TIMtick);
	OSavr32aISRprio(0, 3);							/* SYSREG compare is in group 0, line 0			*/
													/* Set to highest priority: 3					*/
	Set_sys_count(0);
	Set_sys_compare((((SYS_FREQ/100000)*(OS_TIMER_US))/10)-1);

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

   #if (0U == ((UART_LIST_DEVICE) & (1U << (UART_DEV))))
	#error "Selected UART device is not in UART_LIST_DEVICE"
   #endif

   #if (((UART_LIST_DEVICE) & 0x01U) != 0U)			/* Install the UART interrupt handler			*/
	OSisrInstall(OSavr32aISRmap(UART0_INT>>8, UART0_INT & 0xFF), &UARTintHndl_0);
	OSavr32aISRprio(UART0_INT>>8, 2);
  #endif
   #if (((UART_LIST_DEVICE) & 0x02U) != 0U)			/* Install the UART interrupt handler			*/
	OSisrInstall(OSavr32aISRmap(UART1_INT>>8, UART1_INT & 0xFF), &UARTintHndl_1);
	OSavr32aISRprio(UART1_INT>>8, 2);
  #endif
   #if (((UART_LIST_DEVICE) & 0x04U) != 0U)			/* Install the UART interrupt handler			*/
	OSisrInstall(OSavr32aISRmap(UART2_INT>>8, UART2_INT & 0xFF), &UARTintHndl_2);
	OSavr32aISRprio(UART2_INT>>8, 2);
  #endif
   #if (((UART_LIST_DEVICE) & 0x08U) != 0U)			/* Install the UART interrupt handler			*/
	OSisrInstall(OSavr32aISRmap(UART3_INT>>8, UART3_INT & 0xFF), &UARTintHndl_3);
	OSavr32aISRprio(UART3_INT>>8, 2);
  #endif
   #if (((UART_LIST_DEVICE) & 0x010U) != 0U)		/* Install the UART interrupt handler			*/
	OSisrInstall(OSavr32aISRmap(UART4_INT>>8, UART4_INT & 0xFF), &UARTintHndl_4);
	OSavr32aISRprio(UART4_INT>>8, 2);
  #endif
   #if (((UART_LIST_DEVICE) & 0x20U) != 0U)			/* Install the UART interrupt handler			*/
	OSisrInstall(OSavr32aISRmap(UART5_INT>>8, UART5_INT & 0xFF), &UARTintHndl_5);
	OSavr32aISRprio(UART5_INT>>8, 2);
  #endif
													/* UART_Q_SIZE_?X are set in Platform.h			*/
	uart_init(UART_DEV, BAUDRATE, 8, 0, 10, UART_Q_SIZE_RX, UART_Q_SIZE_TX, UART_FILTER);

/* ------------------------------------------------ */
/* Application set-up								*/
													/* Need to create all services before allowing	*/
  #if ((OS_MAILBOX) != 0)							/* any task to run								*/
	MbxDisp                  = MBXopen("Disp", MBX_DISP_SIZE);
  #endif
	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_HIGH].Task,  1*OS_TICK_PER_SEC);
	TSKsetRR(TskDsc[TSK_RRA].Task,   4*OS_TICK_PER_SEC);
	TSKsetRR(TskDsc[TSK_RRB].Task,   6*OS_TICK_PER_SEC);
	TSKsetRR(TskDsc[TSK_RRC].Task,   8*OS_TICK_PER_SEC);
	TSKsetRR(TskDsc[TSK_STRVA].Task, 1*OS_TICK_PER_SEC);
	TSKsetRR(TskDsc[TSK_STRVB].Task, 1*OS_TICK_PER_SEC);
  #endif

  #if ((OX_STARVE_RUN_MAX) < 0)
	TSKsetStrvRunMax(TskDsc[TSK_HIGH].Task,    2*OS_TICK_PER_SEC);
	TSKsetStrvRunMax(TskDsc[TSK_RRA].Task,     2*OS_TICK_PER_SEC);
	TSKsetStrvRunMax(TskDsc[TSK_RRB].Task,     2*OS_TICK_PER_SEC);
	TSKsetStrvRunMax(TskDsc[TSK_RRC].Task,     2*OS_TICK_PER_SEC);
	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_HIGH].Task,  (50*OS_TICK_PER_SEC)/10);
	TSKsetStrvWaitMax(TskDsc[TSK_RRA].Task,   (50*OS_TICK_PER_SEC)/10);
	TSKsetStrvWaitMax(TskDsc[TSK_RRB].Task,   (50*OS_TICK_PER_SEC)/10);
	TSKsetStrvWaitMax(TskDsc[TSK_RRC].Task,   (50*OS_TICK_PER_SEC)/10);
	TSKsetStrvWaitMax(TskDsc[TSK_STRVA].Task, (25*OS_TICK_PER_SEC)/10);
	TSKsetStrvWaitMax(TskDsc[TSK_STRVB].Task, (10*OS_TICK_PER_SEC)/10);
  #endif

  #if ((OX_STARVE_PRIO) < 0)
	TSKsetStrvPrio(TskDsc[TSK_HIGH].Task,  3);
	TSKsetStrvPrio(TskDsc[TSK_RRA].Task,   3);
	TSKsetStrvPrio(TskDsc[TSK_RRB].Task,   3);
	TSKsetStrvPrio(TskDsc[TSK_RRC].Task,   3);
	TSKsetStrvPrio(TskDsc[TSK_STRVA].Task, 3);
	TSKsetStrvPrio(TskDsc[TSK_STRVB].Task, 3);
  #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);
	}


/* ------------------------------------------------ */
/* Here we are										*/

	g_AppRdy = 1;									/* Inform the timer callback everything is OK	*/

	printf("\033[H\033[J");							/* Clear screen									*/
	puts("\n    Abassi Demo 3\n");

	puts("High  00.0s  P2     ");
	puts("RR-A  00.0s  P4     ");
	puts("RR-B  00.0s  P4     ");
	puts("RR-C  00.0s  P4     ");
	puts("ST-A  00.0s  P7     ");
	puts("ST-B  00.0s  P9     ");

	g_CmdIdx = 0;
	Toggle   = 0;

	CURSOR_START();
	printf("Abassi Demo > ");						/* Prompt on Serial for the command line		*/

	for (;;) {

		while(0 != SEMwait(SemaAE, -1));			/* while only useful in eval version			*/

		if (++Toggle >= RTC_REFRESH_RATE) { 		/* RTC is not always refreshed					*/
			Toggle = 0;
			String = &Display[LAST_DISP][0];		/* Display line to update						*/
													/* RTC display is up to 99:59:59 so				*/
			IsrState = OSintOff();					/* 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);
			}
			OSintBack(IsrState);
			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;

			DispInfo[g_DispIdx].Str = String;		/* Send the string to the running task			*/
			DispInfo[g_DispIdx].X   = (LAST_DISP)+5;
			DispInfo[g_DispIdx].Y   = 7;
		  #if ((OS_MAILBOX) != 0)
			MBXput(MbxDisp, (intptr_t)&DispInfo[g_DispIdx], 0);
		  #endif
			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 in /10s)	*/
				jj = 9999;
			}

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

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

			kk = jj/10;								/* Running time fraction						*/
			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 ((OS_MTX_INVERSION) != 0)
			if ((TSKgetPrio(TskDsc[ii].Task) != TskDsc[ii].Task->OriPrio)
		  #if ((OX_STARVE_RUN_MAX) != 0)
			&&  ((TskDsc[ii].Task != g_StrvList)
			  || (TskDsc[ii].Task->OriPrio != TskDsc[ii].Task->StrvPrio))
		  #endif
			&&  (TskDsc[ii].Task->MyMutex == Mutex)) {
				String[14] = 'I';					/* If the task has it's priority increased due	*/
			}										/* to priority inheritance, show this			*/
			else
		 #endif
			{
				String[14] = ' ';
			}
			DispInfo[g_DispIdx].Str = String;
			DispInfo[g_DispIdx].X   = 4+ii;
			DispInfo[g_DispIdx].Y   = 7;
													/* Send the string to display to running task	*/
		  #if ((OS_MAILBOX) != 0)
			MBXput(MbxDisp, (intptr_t)&DispInfo[g_DispIdx], 0);
		  #endif
			if (++g_DispIdx >= (sizeof(DispInfo)/sizeof(DispInfo[0]))) {
				g_DispIdx = 0;
			}
		}

		while (0 != (ccc = GetKey())) {				/* Check for a new key pressed on the keyboard 	*/
			IsrState = OSintOff();					/* When A&E is printing, don't clash with other	*/
			if (g_CmdIdx == 0) {					/* tasks										*/
				for (ii=0 ; ii<sizeof(Cmd) ; ii++) {
					putchar(' ');
				}
			}
			CURSOR_NEXT(g_CmdIdx);

			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');						/* Kept in case uart driver filter not set		*/
			}
			else if (ccc == '\b') {					/* Backspace pressed, to erase previous key		*/
				if (g_CmdIdx != 0) {
					g_CmdIdx--;
					putchar(' ');
					putchar('\b');
				}
			}
			else {
				if (g_CmdIdx == 0) {				/* Add to the command line without overflow		*/
					CURSOR_ADD();
					for (ii=1 ; ii<sizeof(Cmd) ; ii++) {
						putchar(' ');
					}
					CURSOR_ADD();
				}
				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			*/
			||  (ccc == '\n')) {
				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;

				CURSOR_START();

				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
					  #if (((OS_MTX_INVERSION) != 0) || ((OS_STARVE_RUN_MAX) != 0))
						printf("  |  P %d",  TskDsc[ii].Task->OriPrio);
					  #endif
						putchar('\n');
					}
					putchar('\n');
					Done = 1;
					CURSOR_BACK();
				}

				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) {
						CURSOR_BACK();				/* 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;
						CURSOR_BACK();				/* 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;
						CURSOR_BACK();				/* 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')
						     ||  (Cmd[RdIdx] == '\r')
						     ||  (Cmd[RdIdx] == '\n')) {
							Done = 1;
						}
						else {
							Error = 1;
							CURSOR_BACK();			/* 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) {
					CURSOR_START();					/* Set cursor at beginning of line				*/
					printf("Abassi Demo > ");
					for (ii=0 ; ii<sizeof(Cmd) ; ii++) {
						putchar(' ');
					}
					CURSOR_BACK();
				}
				else {
					CURSOR_NEXT(g_CmdIdx);
				}
			}
			OSintBack(IsrState);
		}
	}
}

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

void Fct(void)
{
DispInfo_t *Disp;									/* Information to send to UpdateDisp()			*/
int         ii;										/* General purpose								*/
intptr_t    Msg;									/* Message received from timer tick call back	*/
int         NoNewMsg;								/* If there are no new messages					*/
TskDsc_t   *MyInfo;									/* All the information about my task			*/

 #if ((OX_LIB_REENT_PROTECT) > 0)					/* In case reentrance protect is per task		*/
  char SetV[3];										/* IAR needs non-NULL buf with _IONBF			*/
	setvbuf(stdin,  &SetV[0], _IONBF, 0);			/* By default, libraries flush the I/O			*/
	setvbuf(stdout, &SetV[1], _IONBF, 0);			/* buffer when full or when new-line			*/
	setvbuf(stderr, &SetV[2], _IONBF, 0);
 #endif

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

	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 = OSintOff();							/* Will print, don't want to be pre-empted		*/
	  #if ((OS_MAILBOX) != 0)
		if (0 ==  MBXget(MbxDisp, &Msg, 0)) {		/* Check if A&E has sent a string to display	*/
			Disp = (void *)Msg;
	  #else
		if (g_DispIdx != g_DispRdIdx) {
			Disp = &DispInfo[g_DispRdIdx];
			if (++g_DispRdIdx >= (sizeof(DispInfo)/sizeof(DispInfo[0]))) {
				g_DispRdIdx = 0;
			}
	  #endif
			UpdateDisp(Disp);
		}
		OSintBack(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;

	Set_sys_count(0);
	Set_sys_compare((((SYS_FREQ/100000)*(OS_TIMER_US))/10)-1);

	if (g_AppRdy == 0) {							/* If the configuration not done, skip			*/
		return;
	}

	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 ((OX_ROUND_ROBIN) != 0)
		  #if ((OX_ROUND_ROBIN) < 0)
			if (TskDsc[ii].Task->RRmax != 0U)
		  #endif
			 {
				if (TskDsc[ii].Task->RRcnt <= 1U) {
					if (TskDsc[ii].WasReset == 0) {
						SEND_INFO(ii, OP_ZERO_CNT);
						TskDsc[ii].WasReset = 1;
					}
				}
			}
		  #endif
		}
	}

	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;
			}
		  #ifdef TINY_ABASSI						/* TINY_ABASI doesn't have staravation protect	*/
			if (MtxSelect >= 4 ) {					/* Skip ST-A & ST-B								*/
				MtxSelect = 0;
			}
		  #endif

			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;
}

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

void UpdateDisp(DispInfo_t *Disp)
{
int jj;

	if ((Disp->X == 0)								/* Cursor at top of screen						*/
	&&  (Disp->Y == 0)) {
//		printf("\033[H");
		putchar(0x1B);
		putchar('[');
		putchar('H');
	}
	else {
//		printf("\033[%d;%dH%s", Disp->X, Disp->Y, Disp->Str); 
		putchar(0x1B);
		putchar('[');
		if (Disp->X < 10) {
			putchar('0'+Disp->X);
		}
		else {
			putchar('0'+(Disp->X/10));
			putchar('0'+(Disp->X%10));
		}
		putchar(';');
		if (Disp->Y < 10) {
			putchar('0'+Disp->Y);
		}
		else {
			putchar('0'+(Disp->Y/10));
			putchar('0'+(Disp->Y%10));
		}
		putchar('H');

		for (jj=0 ; Disp->Str[jj] != '\0' ; jj++){
			putchar(Disp->Str[jj]);
		}
													/* Set cursor at the next typing location		*/
//		printf("\033[%02d;%02dH", LAST_DISP+9, g_CmdIdx+15);
		putchar(0x1B);
		putchar('[');
		putchar('0'+(((LAST_DISP)+9)/10));
		putchar('0'+(((LAST_DISP)+9)%10));
		putchar(';');
		jj = g_CmdIdx+15;
		if (jj < 10) {
			putchar('0'+jj);
		}
		else {
			putchar('0'+(jj/10));
			putchar('0'+(jj%10));
		}
		putchar('H');
	}

	return;
}

/* EOF */
