Embedded Code

High Level Code generation in C, C++, Visual C++
MyRTOS by Orgler: the complete program with two tasks

This short program can run on every microcontroller.
Only the STACK must be adapted to your controller.
For this controller the stack address for TASK1 begins with 0x22FF
and the stack address for TASK2 with 0x23FF
We need less than 16 Bytes for every stack.

to adapt this program to your own µC see the step by step prorgrams
beginning with the FirstStep
#pragma DATA_SEG VAR_PAG_0

unsigned long cAddrTask1;
unsigned long cAddrTask2;

unsigned char *pSPtask1; // 1
unsigned char *pSPtask2; // 2
unsigned char cTaskNr;

void task1(void);
void task2(void);


#pragma CODE_SEG DEFAULT

void main(void)
{

//----prepare timer interrupt-------------
  PITCE     = 0x01;
  PITINTE   = 0x01;
  PITLD0    =  200;
  PITCFLMT  = 0xA0;
//---- init port as output ---------------
  DDRP  = 0xFF;
  PTP   = 0;
//----------------------------------------

  cAddrTask1 = (unsigned long)&task1;
  cAddrTask2 = (unsigned long)&task2;

  //------- prepare task2------------------------------------
  //task2();
    pSPtask2  = (unsigned char*) 0x23FF;
   *pSPtask2-- = (cAddrTask2 >> 8)   & 0xFF;
   *pSPtask2-- = (cAddrTask2 >> 16) & 0xFF;
   *pSPtask2--  = 0xFF;
   *pSPtask2--  = 0xEE;
   *pSPtask2--  = 0xDD;
   *pSPtask2--  = 0xCC;

   *pSPtask2--  = 0xAA;
   *pSPtask2--  = 0xBB;

   *pSPtask2--  = 0;  // CCR with I Bit cleared
   *pSPtask2    = 0;  // CCR
  //-------------------------------------------


  //------------- prepare task1 ------------------------------
  //task1();
    pSPtask1   = (unsigned char*) 0x22FF;
   *pSPtask1-- = (cAddrTask1 >> 8)   & 0xFF;
   *pSPtask1-- = (cAddrTask1 >> 16) & 0xFF;
   *pSPtask1--  = 0xFF;
   *pSPtask1--  = 0xEE;
   *pSPtask1--  = 0xDD;
   *pSPtask1--  = 0xCC;

   *pSPtask1--  = 0xAA;
   *pSPtask1--  = 0xBB;

   *pSPtask1--  = 0;  // CCR:H with I Bit cleared
   *pSPtask1    = 0;  // CCR
   cTaskNr      = 1;
  //-------------------------------------------
    asm
      {
      LDS   pSPtask1
      RTI
      }


  //------
  for(;;)
  {
  }// loop

}



// --------------------------------------------------------------------------------------
// PIT0 Timer Interrupt Service Routine
/////////////////////////////////////////////////////////////////////////////////////////
#pragma CODE_SEG __NEAR_SEG NON_BANKED

interrupt void PIT0_ISR(void)      //  xx µsec
{
 PTP ^= 0x40;        //interrupt timing control

 PITTF = 0x01;

  if(cTaskNr == 1)
  {
   cTaskNr = 2;
asm      STS   pSPtask1    // save from last task
asm      LDS   pSPtask2    // load for the new task
  }
  else
  {
   cTaskNr = 1;
asm    STS   pSPtask2     // save from last task
asm    LDS   pSPtask1     // load for the new task
  }
}
//---------------- finished interrupt ----------



#pragma CODE_SEG DEFAULT

void task1(void)
{
int xCount;

 xCount = 0;
  for(;;)
  {
   if(xCount++ >= 30000)
    {
    xCount = 0;
    PTP ^= BIT5;
    }

  }//==== loop task1 ========
}


void task2(void)
{
int xCount;

  xCount = 0;
  for(;;)
  {
   xCount++;
   if(xCount == 5000)
    {
    xCount = 0;
    PTP ^= BIT1;
    }
  }//=== loop task 2 ============
}
MyRTOS by Orgler: two tasks

C1 yellow: interrupt timing or scheduler timing
C4 green: task0
C2 magenta: task1




Each task is a program with an entry point and an infinite loop.
The entry point is like the main if we have only one task.

The variables declared after the entry point are only visible in the same task.
They are located on the stack and in this task always valid.
Other possible variables are global variables.
The scheduler uses global variables, lists, queues and semaphores are global
variables.
You can make your own global variables to control data exchange from one task
to another or from interrupt routines to a task.
Example for a simple semaphore:
Variable cByteControl1 equal NULL or equal TWO: task0 write datas and than sets cByteControl1=1;
task1 can read if "cByteControl1==1" and after reading sets to zero or better to two.
MyRTOS by Orgler: first step a simple main loop
this simple main loop must run on your µC board
instead of port PTP you can use your preferred port

#pragma CODE_SEG DEFAULT

void main(void)
{
unsigned int xCount;

//---- init port as output ---------------
  DDRP  = 0xFF;
  PTP   = 0;
//----------------------------------------

   xCount = 0;
  //---- main loop -----
  for(;;)
  {
   if(xCount++ > 20000)
    {
    xCount = 0;
    PTP ^= 0x80;  // toggle Bit7 form Port PTP
    }
  }// loop
}

MyRTOS by Orgler: second step: add a timer interupt routine
we need an interrupt as our scheduler
you must add the interrupt vector to your vector table
the output on our PORT PTP BIT6 must have a about 1/2 Vdd
#pragma CODE_SEG DEFAULT

void main(void)
{
unsigned int xCount;

 //----prepare timer interrupt-------------
  PITCE     = 0x01;
  PITINTE   = 0x01;
  PITLD0    =  200;
  PITCFLMT  = 0xA0;

//---- init port as output ---------------
  DDRP  = 0xFF;
  PTP   = 0;
//----------------------------------------
   EnableInterrupts;
   
   xCount = 0;
  //---- main loop -----
  for(;;)
  {
   if(xCount++ > 20000)
    {
    xCount = 0;
    PTP ^= 0x80;  // toggle Bit7 form Port PTP
    }
  }// loop
}


// --------------------------------------------------------------------------------------
// PIT0 Timer Interrupt Service Routine
/////////////////////////////////////////////////////////////////////////////////////////
#pragma CODE_SEG __NEAR_SEG NON_BANKED

interrupt void PIT0_ISR(void)      //  xx µsec
{
 PTP ^= 0x40;        //BIT 6 interrupt timing control

 PITTF = 0x01;       // reset interrupt flag
 }
//---------------- finished interrupt ----------
MyRTOS by Orgler: stack operations: the secret of a scheduler
We need one pointer for every task for the stack operation.
The address (in this example 0x22FF) must be located in our RAM-Space
As a first step we save the address of the task function.
Be aware of the memory model of your microcontroller.
For the HCS12 a function address had 3 bytes. One Byte is for the global page.
The byte for global page we need only for banked model.

After the address we insert dummy values for the registers. The first time
we jump in the task function this values can be any.
 //------------- prepare task1 ------------------------------
  //task1();
    pSPtask1   = (unsigned char*) 0x22FF;
   *pSPtask1-- = (cAddrTask1 >> 8)   & 0xFF;
   *pSPtask1-- = (cAddrTask1 >> 16) & 0xFF;
   *pSPtask1--  = 0xFF;
   *pSPtask1--  = 0xEE;
   *pSPtask1--  = 0xDD;
   *pSPtask1--  = 0xCC;

   *pSPtask1--  = 0xAA;
   *pSPtask1--  = 0xBB;

   *pSPtask1--  = 0;  // CCR:H with I Bit cleared
   *pSPtask1    = 0;  // CCR
   cTaskNr      = 1;
  //-------------------------------------------
    asm
      {
      LDS   pSPtask1
      RTI
      }
Now we need same assembler instructions. We load the StackPointer (SP) with
the content of pSPtask1 ( in this example 0x22F6)

The RTI now is magic. This command (return from interrupt) does restore all registers
with the values saved on the actual stack.
Because CCR is set to zero, the I Bit is also cleared and the interrupts are enabled.
We don't need any extra command like "EnableInterrupts" to start interrupts.
The PC (ProgramCounter) the HCS12 has the IP (Instructions Pointer) is loaded with the start
address of the task1 function, after the RTI command we continue with the task1 function.
We never return in the main function. The task1 function has a loop forever and we remain
inside this loop until the first interrupt is issued.
We said that an interrupt first saves all registers on the stack, set the I-Bit in CCR
to one and blocks all other possible interrupts. We don't have any.
Now we change the Stack Pointer to the stack of the other task and the last command of
an interrupt function is the RTI.
This RTI is not visible in the program code, but the compiler inserts at the end of an
interrupt function always an RTI.
At the end of a normal function is we have always a RTS.
interrupt void PIT0_ISR(void)      //  xx µsec
{
 PTP ^= 0x40;        //interrupt timing control

 PITTF = 0x01;

  if(cTaskNr == 1)
  {
   cTaskNr = 2;
asm      STS   pSPtask1    // save from last task
asm      LDS   pSPtask2    // load for the new task
  }
  else
  {
   cTaskNr = 1;
asm    STS   pSPtask2     // save from last task
asm    LDS   pSPtask1     // load for the new task
  }
}
MyRTOS by Orgler: insert the DELAY FUNCTION

the scheduler returns in the red task and continues the elaboaration.
at the end of the loop there is a taskdelay(3);
Thats means that we enter in the function of "taskdelay" but in this function
there is a context switch to the next task( to the task green)
MyRTOS by Orgler: insert the IDLE task

every task returns at the end of the loop to the scheduler over the function
taskDelay and will not called about the number of thicks indicated.
The green task has taskDelay(2), the red task has taskDelay(3);
The scheduler tries to start immediatly the next task.
If there is not another task able to start, the scheduler starts the IDLE task (C3 blue)

MyRTOS by Orgler: the source code

this source has less than 300 lines and is able to start at least 4 tasks.
All the tasks has the same priority and this maybe right for many embedded applications

This code can be easy modified for every microcontroller

#pragma DATA_SEG VAR_PAG_0
unsigned char cTaskNr;
unsigned char cxTaskBlocked[4];
unsigned char cxBITControl[4];
unsigned int  uTaskThicks[4];

unsigned long cAddrIdle;
unsigned long ulAddrTask[4];   // address of task function
unsigned char *pSPtask[4];     // task stack pointer

unsigned char cTaskMax;
unsigned char cTaskCount;      // use for scheduler
unsigned char *pSPIdle;
unsigned char *pSP;

unsigned int  uThickCounter;   //  interrupt counter

#pragma CODE_SEG DEFAULT
void task0(void);
void task1(void);
void taskDelay(unsigned int uThicks);
void taskIdle(void);

//--------- utility --------------
void prepareStack(unsigned int uAddrStack)
{
    pSP    = (unsigned char*) uAddrStack;
   *pSP--  = (ulAddrTask[cTaskMax] >> 8)   & 0xFF;
   *pSP--  = (ulAddrTask[cTaskMax] >> 16) & 0xFF;
   *pSP--  = 0xFF;
   *pSP--  = 0xEE;
   *pSP--  = 0xDD;
   *pSP--  = 0xCC;
   *pSP--  = 0xAA;
   *pSP--  = 0xBB;
   *pSP--  = 0;  // CCR:H with I Bit cleared
   *pSP    = 0;  // CCR
   pSPtask[cTaskMax]     = pSP;
}




void main(void)
{
asm   // clear RAM
  {
  ldx #0x2000;
NX: CLR 0,X
  INX
  CPX #0x4000
  BLO NX
  }
        Sci0_Init();

//--------------prepare timer interrupt-------------
  PITCE     = 0x01;
  PITINTE   = 0x01;
  PITLD0    = 2000;
  PITCFLMT  = 0xA0;
//---- init port as output -------------------------
  DDRA  = 0xFF;
  PORTA = 0;
  DDRP  = 0xFF;
  PTP   = 0;
//--------------------------------------------------

//----------- prepare IDLE task ------------------
   cAddrIdle  = (unsigned long)&taskIdle;

   pSPIdle    = (unsigned char*) 0x24FF;
   *pSPIdle--  = (cAddrIdle >> 8)  & 0xFF;
   *pSPIdle--  = (cAddrIdle >> 16) & 0xFF;
   *pSPIdle--  = 0xFF;
   *pSPIdle--  = 0xEE;
   *pSPIdle--  = 0xDD;
   *pSPIdle--  = 0xCC;
   *pSPIdle--  = 0xAA;
   *pSPIdle--  = 0xBB;
   *pSPIdle--  = 0;  // CCR with I Bit cleared
   *pSPIdle    = 0;  // CCR
//------------------------------------------------
//------------- prepare task0 ----------------------
    cTaskMax = 0;

    ulAddrTask[cTaskMax] = (unsigned long)&task0;
    prepareStack(0x22FF);

    cxBITControl[cTaskMax]=BIT0;
    cTaskMax++;
  //------- prepare task1----------------------------

    ulAddrTask[cTaskMax] = (unsigned long)&task1;
    prepareStack(0x23FF);

   cxBITControl[cTaskMax]=BIT1;
   cTaskMax++;
  //--------------------------------------------------
  //--- we start a task ------------------------------
  cTaskNr    = 0;
  PORTA |=   cxBITControl[cTaskNr];
  pSP    =   pSPtask[cTaskNr];

  asm  LDS     pSP    // load for the new task
  asm  RTI
//-- the RTI restores the context of task0 and enables the interrupts
//-- the programm continues now with the entry point of task0

  //--- loop forever the program never riches this lines--------
  for(;;)
  {
  }// loop
}


// --------------------------------------------------------------------------------------
// PIT0 Timer Interrupt Service Routine
/////////////////////////////////////////////////////////////////////////////////////////
#pragma CODE_SEG __NEAR_SEG NON_BANKED

interrupt void PIT0_ISR(void)       //  xx µsec
{
 asm     STS   pSP                  // save from last task

 PORTA = 0;
 PTP   |= BIT6;                     //BIT 6 interrupt timing control
 PITTF  = 1;                        //reset interrupt flag

 uThickCounter++;
 if(uThickCounter ==  uTaskThicks[0]) cxTaskBlocked[0] = 0;
 if(uThickCounter ==  uTaskThicks[1]) cxTaskBlocked[1] = 0;
 if(uThickCounter ==  uTaskThicks[2]) cxTaskBlocked[2] = 0;
 if(uThickCounter ==  uTaskThicks[3]) cxTaskBlocked[3] = 0;


  if(cTaskNr & BIT7) pSPIdle= pSP;     // save from last task
  else
  pSPtask[cTaskNr] = pSP;

  cTaskNr  &= 0x7F;                   // last tasknr

//=================== scheduler1 ================================
  cTaskCount=0;
  while(cTaskCount < cTaskMax)
  {
  cTaskCount++;
  cTaskNr++;
  if(cTaskNr >= cTaskMax) cTaskNr=0;

      if(cxTaskBlocked[cTaskNr]==0)       // check if blocked
      {
      PORTA |=   cxBITControl[cTaskNr];
      pSP    =   pSPtask[cTaskNr];
      asm      LDS     pSP                // load for the new task

      PTP    &= BIT6_INVERS;
      return;
      }
  }

  PORTA   |=  BIT7;
  cTaskNr |=  BIT7;
  asm      LDS   pSPIdle                // load for the new task
  PTP   &= BIT6_INVERS;
//==================== end of scheduler1 ==========================
}
//---------------- finished interrupt ---------------------


#pragma CODE_SEG DEFAULT
void task0(void)
{
int xCount;

 xCount = 0;
  for(;;)
  {
   if(xCount++ >= 1000)
    {
    xCount = 0;
    PTP ^= BIT5;
    }
    taskDelay(3);     // delay for 3 thicks
  }//========== loop task0 =============
}

void task1(void)
{
int xCount;

  xCount = 0;
  for(;;)
  {
   xCount++;

   if(xCount == 500)
    {
    xCount = 0;
    PTP ^= BIT2;
    }
    taskDelay(2);
  }//=========== loop task 2 ============
}


//============this functions don't return ===================
void taskDelay(unsigned int uThicks)
{
DisableInterrupts;
  uTaskThicks[cTaskNr]    = uThickCounter + uThicks;

    asm  TSX
    asm  INX
    asm  INX
    asm  CLRA
    asm  STAA  0,X
    asm  DEX
    asm  STAA  0,X
    asm  DEX
    asm  STAA  0,X
    asm  DEX
    asm  STAA  0,X
    asm  DEX
    asm  STAA  0,X
    asm  DEX
    asm  STAA  0,X
    asm  DEX
    asm  STAA  0,X
    asm  DEX
    asm  STAA  0,X
    asm  STX  pSP

  pSPtask[cTaskNr]      = pSP;
  cxTaskBlocked[cTaskNr]= 1;

  //=== the same scheduler as in the interrupt function====
  PORTA = 0;
  cTaskCount=0;
  while(cTaskCount < cTaskMax)
  {
  cTaskCount++;
  cTaskNr++;
  if(cTaskNr >= cTaskMax) cTaskNr=0;

      if(cxTaskBlocked[cTaskNr]==0)       // check if blocked
      {
      PORTA  |=   cxBITControl[cTaskNr];
      pSP    =   pSPtask[cTaskNr];
      asm      LDS     pSP                // load for the new task

      asm  RTI
      }
  }
PORTA   |=  BIT7;
cTaskNr |=  BIT7;
asm LDS pSPIdle
asm RTI
//============== end of scheduler2 =========================
}

void taskIdle(void)
{
 for(;;)
  {
    PTP ^= BIT0;
  }
}
 
MyRTOS by Orgler: see all the variables



MyRTOS by Orgler for Arduino UNO

A simple RTOS with two tasks for the Atmega328P compiled with Atmel Studio 6
and loaded to an Arduino UNO board.

:100000000C9434000C9451000C9451000C94510049
:100010000C9451000C9451000C9451000C9451001C
:100020000C9451000C9451000C9451000C9451000C
:100030000C9451000C9451000C9451000C945100FC
:100040000C947E000C9451000C9451000C945100BF
:100050000C9451000C9451000C9451000C945100DC
:100060000C9451000C94510011241FBECFEFD8E026
:10007000DEBFCDBF11E0A0E0B1E0E8E5F2E002C0F4
:1000800005900D92A030B107D9F711E0A0E0B1E0E2
:1000900001C01D92A131B107E1F70E94F0000C945C
:1000A0002A010C94000040ED5DED66E070E020E276
:1000B000DB01CA010197A109B109E1F780910101B2
:1000C000909102010196909302018093010185B104
:1000D000822785B9EDCF28EE3DEF40E1C9010197B8
:1000E000F1F7809106019091070101969093070125
:1000F0008093060185B1842785B9F0CF1F920F92B6
:100100000FB60F9211248F939F932F923F924F928D
:100110005F926F927F928F929F92AF92BF92CF9297
:10012000DF92EF92FF920F931F932F933F934F9382
:100130005F936F937F93AF93BF93CF93DF93EF93CF
:10014000FF93809104018823A9F48DB78093050162
:100150008EB780930A01809103018DBF80910001C9
:100160008EBF95B182E0892785B9289881E0809378
:10017000040113C08DB7809303018EB780930001F3
:10018000809105018DBF80910A018EBF95B181E0FC
:10019000892785B9299810920401FF91EF91DF9189
:1001A000CF91BF91AF917F916F915F914F913F91AF
:1001B0002F911F910F91FF90EF90DF90CF90BF9004
:1001C000AF909F908F907F906F905F904F903F90F7
:1001D0002F909F918F910F900FBE0F901F901895A9
:1001E0008DE091E020E18030920730F4FC01119223
:1001F00080E1E030F807D9F78FEF84B981E0809390
:100200006E0023E025BD8BE690E090931001809373
:100210000F018093FF04809110018093FE048CED08
:100220008093030184E08093000183E590E0909344
:10023000100180930F018093FF0380911001809340
:10024000FE038DEF8093050120930A018DBF2EBF21
:080250001895FFCFF894FFCFD1
:00000001FF

to load this hex data in the Arduino UNO copy the hex data in a file with the name MyRtosAVR.hex
in the same directory open a black cmd window
write this two lines in one line:

avrdude -C avrdude.conf -q -q -patmega328p -carduino -P\\.\COM17 -b115200 -D -U flash:w:MyRtosAVR.hex:i

you can copy this also in a batch file like xx.bat
you must adapt only the COM port, instead COM17 you must insert your port.

please note: you must have in the same directory this files:
avrdude.exe
avrdude.conf
libusb0.dll
MyRtosAVR.hex

avrdude.exe, avrdude.conf and libusb0.dll you can found in the arduino directories.




the yellow channel is from PORTB BIT4 and the two others are the alterantive running tasks

MyRTOS by Orgler for Arduino UNO the source code


/*
 * MyRtosAVR.c
 *
 * Created: 26.11.2012
 *  Author: Orgler Ludwig
 */
#define BIT0 0x01
#define BIT1 0x02
#define BIT2 0x04
#define BIT3 0x08
#define BIT4 0x10
#define BIT5 0x20
#define BIT6 0x40
#define BIT7 0x80

#include 
#include 

void task0(void);
void task1(void);

unsigned char *ptrx;
unsigned int addrx;
unsigned char *pSPtask0;
unsigned char *pSPtask1;
unsigned char cTaskNr;

unsigned char cSPtask0_L;
unsigned char cSPtask0_H;

unsigned char cSPtask1_L;
unsigned char cSPtask1_H;

unsigned int uCount0;
unsigned int uCount1;


ISR(TIMER0_OVF_vect)
{
	asm volatile (\
	"push	r2		\n" \
	"push	r3		\n" \
	"push	r4		\n"	\
	"push	r5		\n"	\
	"push	r6		\n"	\
	"push	r7		\n"	\
	"push	r8		\n"	\
	"push	r9		\n"	\
	"push	r10		\n"	\
	"push	r11		\n"	\
	"push	r12		\n"	\
	"push	r13		\n"	\
	"push	r14		\n"	\
	"push	r15		\n"	\
	"push	r16		\n"	\
	"push	r17		\n"	\
	"push	r18		\n"	\
	"push	r19		\n"	\
	"push	r20		\n"	\
	"push	r21		\n"	\
	"push	r22		\n"	\
	"push	r23		\n"	\
	"push	r26		\n"	\
	"push	r27		\n"	\
	"push	r28		\n"	\
	"push	r29		\n"	\
	"push	r30		\n"	\
	"push	r31		\n"	\
	);


	if(cTaskNr==0)
	{
		cSPtask0_L = SPL;
		cSPtask0_H = SPH;

		SPL = cSPtask1_L;
		SPH = cSPtask1_H;
		PORTB	^=  BIT1;
		PORTB   &= (BIT0^0xFF);
		cTaskNr=1;   //  next task
	}
  	else
	{
		cSPtask1_L = SPL;
		cSPtask1_H = SPH;

		SPL = cSPtask0_L;
		SPH = cSPtask0_H;
		PORTB  ^= BIT0;
		PORTB   &= (BIT1^0xFF);
		cTaskNr=0;    //  next task
	}


	asm volatile (\
	"pop	r31		\n"	\
	"pop	r30		\n"	\
	"pop	r29		\n"	\
	"pop	r28		\n"	\
	"pop	r27		\n"	\
	"pop	r26		\n"	\
	"pop	r23		\n"	\
	"pop	r22		\n"	\
	"pop	r21		\n"	\
	"pop	r20		\n"	\
	"pop	r19		\n"	\
	"pop	r18		\n"	\
	"pop	r17		\n"	\
	"pop	r16		\n"	\
	"pop	r15		\n"	\
	"pop	r14		\n"	\
	"pop	r13		\n"	\
	"pop	r12		\n"	\
	"pop	r11		\n"	\
	"pop	r10		\n"	\
	"pop	r9		\n"	\
	"pop	r8		\n"	\
	"pop	r7		\n"	\
	"pop	r6		\n"	\
	"pop	r5		\n"	\
	"pop	r4		\n"	\
	"pop	r3		\n"	\
	"pop	r2		\n"	\
	);

}// end of ISR(TIMER0_OVF_vect)



int main(void)
{
	ptrx = (unsigned char*) &ptrx;
	ptrx += 2;
	while(ptrx < (unsigned char*) 0x1000) *ptrx++ = 0;

	DDRB   = 0xFF;

	//===== init Timer overflow interrupt routine ===
	TIMSK0 =(1>>TOV0);
	TCCR0B = ( 1 << CS01 ) | ( 1 << CS00 );
    //----------------------------------------------

    addrx = (unsigned int) &task1;
	pSPtask1 = (unsigned char*) 0x04FF;
	*pSPtask1-- =  (addrx  & 0xFF);
	*pSPtask1-- =  ((addrx >> 8) & 0xFF);
	 pSPtask1 -= 33;    //      space of 32 register plus status register
	addrx = (unsigned int) pSPtask1;
	cSPtask1_L = (unsigned char) (addrx & 0xFF);
	cSPtask1_H = (unsigned char) (addrx >> 8);

    addrx = (unsigned int) &task0;
	pSPtask0    = (unsigned char*) 0x03FF;
	*pSPtask0-- =  (addrx  & 0xFF);
	*pSPtask0-- =  ((addrx >> 8) & 0xFF);
	addrx = (unsigned int) pSPtask0;
	cSPtask0_L = (unsigned char) (addrx & 0xFF);
	cSPtask0_H = (unsigned char) (addrx >> 8);

	//------------- start task0 --------------------
	 SPL = cSPtask0_L;
	 SPH = cSPtask0_H;
	asm("reti");  //---->> go to task0 and enable interrupts


    while(1)  //------ this line could not be reached ------
    {
    }
}

void task0(void)
{
unsigned long xCount;

	xCount = 0;
	for(;;)
	{

		xCount++;
		if(xCount == 450000)
		{
		uCount0++;
		xCount=0;
		PORTB ^= BIT5;
		}
	}
}


void task1(void)
{
unsigned int zCount;

	zCount = 0;
	for(;;)
	{
		zCount++;
		if(zCount == 65000)
		{
		uCount1++;
		zCount=0;
		PORTB ^= BIT4;
		}
	}
}