            MULTITASKING SCHEDULER FOR MICROPROCESSORS

Multitasking is the capability for a microprocessor based system to 
run more than one programs at a time, such that, it appears to  the 
user that the program are running simultaneously. In the main frame 
world, the goal of the multitasking multiuser real time support  is 
provided  by realtime OS. But when the need is different,  where  a 
small scheduler is required, for example, one may want to process a 
data at the same time monitor the status of the sub system all more 
or less simultaneously, this scheduler can be used.

CPU Scheduling Strategies

The   scheduling  strategy  uses  the  a  mix  of  preemptive   and 
nonpreemptive   methods.   A  periodic  timer   interrupt   routine 
(contextswitcher)  is used to switch between the tasks.  The  tasks 
required  to  run simultaneously on this  contextswitcher  need  no 
special features designed into them to give up control of the  CPU. 
The  contextswitcher  also  provides  some  of  the   nonpreemptive 
features such as suspend, kill, resume and crate task.

The software

The  software is written under Turbo C. The program uses  IBM  PC's 
timer  interrupt (int 8) as system tick (systick). No MSDOS  system 
calls are used to implement this contextswitcher, hence the program 
will  run with any Intel 86 family processors. To make the  program 
portable, two home made programs are also provided to set a  vector 
and get a vector. The software has two parts one for the  scheduler 
and other for the testing program.

The scheduler consists of following functions

1) void far interrupt contextswitch(void)

The  contextswitching process. This core software will  first  save 
the  stack  corresponding to the nonpreempted task.  Then  it  will 
compute  the next task to be executed from the ready list. If  this 
task is not executed before, then a new stack area is allocated for 
this  task.  The process status are saved into the  stack  when  an 
interrupt  is  occurred. Finally it initialize the stack  with  the 
state of the next process to be chosen and transfer control to that 
task.

2) void far interrupt dummy(void)

This interrupt program will load current register contents into the 
stack (all interrupt program will push all the registers in to  the 
stack). It is a null statement program to do just this.

3) int request_create_task( void(far *fp)(),priority pt)

This function will create a task by making process status as  ready 
or  run  state and compute and set the task's start  address.  This 
function has two modes of operation

    1) create low priority task.
    2) create high priority task. 

High  priority task means it will set the interrupt vector for  the 
contextswitcher  and  start running of the that task. So  when  you 
need  to  start context switcher and start execution of  the  first 
task call this function with priority high. The low priority   will 
set  only the start address of the task. The process state  of  all 
tasks are made nonexist  when first call to this function is  made. 
Also  it  will not allow you to create more than  MAXTASK  and  two 
requests with high priority.

4) int request_kill_task(int task_token, relation rel)

This request is to kill a running, ready, or suspended task. If the 
task  suicide then the enumerated relation must be  self  otherwise 
other.  This  is required because, after suicide  task  might  have 
executed little more instructions before the next systick. Hence in 
suicide  request_kill_task  will  go to a forever  loop  till  next 
systick.

5) int request_suspend_task(int task_token)

Request  to suspend a running or ready task. The task_token is  the 
token obtained when the task is created with request_create_task.

6) int request_resume_task(int task_token)

Request  to  resume a suspended task. The task_token is  the  token 
obtained when the task is created with request_create_task.

Test programs

The main() will create seven tasks and execute them simultaneously. 
Each task is to display some text in its exclusive window.  Opening 
of  this  window  is down by the function  call  open_window()  and 
writing to each window by window_str() function.

int open_window(int x1, int y1, int x2,int y2)

This  program will open a window with given corner points.  And  it 
will  return a window-token to this window. Writing to this  window 
is  by  referring  this  window-token  and  using  window_str()  or 
window_char() functions.

void window_char(int wno, int chr)

write  a  character with corresponding attribute in  the  specified 
window wno.

void window_str(int wno,char *str,unsigned atbt)

Write a string on the window specified window wno.

void writechar(int ch)

write  a  character with corresponding attribute in  the  specified 
window wno.

void writestr(char *str,unsigned atbt)

Write a string on the screen


SOURCE LISTING
/*
   Program for task scheduling for embedded microprocessors
   applications. This program is written for IBM PC   using
   timer interrupt as system tick. It uses both preemptive
   and nonpreemptive scheduling strategy. No MSDOS system
   calls are used to implement this contextswitcher, hence
   the program will run with any Intel 86 family processors.
*/


/*------------ Following are definitions used for scheduler ---------*/

#include <dos.h>                       /* for enable and disable     */
#define MAXTASK 7                      /* Maximum number of tasks.   */
enum priority { low,high};         /* priority of task. low -> create
		  only. high -> start contextswitch and run the task */
enum state{run,ready,nonexist,suspend,resume,kill};
       /* current status of the any task may be any one of the state */
enum relation{other,self};/* relation for killing a task             */
struct {
	unsigned    t_sp;
	unsigned    t_cs;
	unsigned    t_ip;       /* structure to store  task's status */
	state       t_state;
       } process_sts[MAXTASK];
void   far interrupt(*oldaddress)();

/*------------- prototype of the scheduler programs -----------------*/

void   far interrupt dummy(void);   /* reload registers in the stack */
void   far interrupt contextswitch(void);
int        request_create_task( void(far *fp)(),priority pt);
int        request_resume_task( int task_token);
int        request_suspend_task( int task_token);
int        request_kill_task( int task_token,relation s_o);
void       request_setvect(void interrupt (far *fp)(),int intno);
void       interrupt (far * request_getvect(int intno))();

/*----- Following are definitions used for tasks. (test program) ----*/

#define MAXWINDOW 20                   /* No. user windows at a time */
struct {
	 int length,width,status,current_point;
	 char far *buf;
       }window_info[MAXWINDOW];         /* for creating task windows */

/*-----            prototype for the testing program            -----*/

#include <stdio.h>                                   /* for sprintf  */
int        open_window(int x1, int y1, int x2,int y2);
void       window_char(int wno, int chr);
void       window_str(int wno,char *str,unsigned atbt);
void       writechar(int ch);
void       writestr(char *str,unsigned atbt);

void   far task0(void);             /* tasks to be run concurrently  */
void   far task1(void);
void   far task2(void);
void   far task3(void);
void   far task4(void);
void   far task5(void);
void   far task6(void);
/*___________________________________________________________________*/

void far interrupt dummy(void)
/*    this interrupt program will load current register
      contents in the stack. It is a null statement
      program to just to do this.
*/
{;}
/*___________________________________________________________________*/

void far interrupt contextswitch(void)

/*   The contextswitching process. It will first save the running
     tasks status and then it compute the next task to be executed
     from the ready list. Then it initialize the CPU registers with
     the state of the next process to chosen up and transferring
     control to that process.
*/

{
     static running_task = 0;
     static unsigned tfg,tsp;
     static unsigned far *tptr;
     /* now compute the stack just before the interrupt. The pushing
     sequence is: flag,cs,ip,ax,bx,cx,dx, es,ds,si,di and bp.
     Then bp is made as current sp.                                  */
     tptr = (unsigned int far *)(((long)_SS*0x10000) + (long)(_BP+0x18));
     process_sts[running_task].t_sp = _BP + 0x18;
     oldaddress();                           /* execute the old isr  */
     while (1)                           /* find the next ready task */
     {
	      running_task++;
	      running_task%=MAXTASK;
	      if((process_sts[running_task].t_state==nonexist)||
	      (process_sts[running_task].t_state==suspend) ||
	      (process_sts[running_task].t_state==kill))   continue;
	      break;
     }
     if(process_sts[running_task].t_state==ready)
     {  /* is is first execution of the task. put it into run list   */
	   process_sts[running_task].t_state=run;
	   _SP = _SP-0x100;                  /* give new stack area  */
	   enable(); /* enable interrupt  flag for further interrupt */
	   dummy();                 /* reload registers in the stack */
	   tptr = (unsigned int far *)(((long)_SS*0x10000) + (long)(_SP));
	   _SP = _SP-0x18;                  /* Adjust stack for IRET */
	   *(tptr-2) = process_sts[running_task].t_cs ;/* new task's */
	   *(tptr-3) = process_sts[running_task].t_ip ;/* cs and ip  */
	    return; /*load all registers from stack and run new task */
     }
     tsp = process_sts[running_task].t_sp;/* get task's stack pointer*/
     _SP = tsp-0x18;                        /* Adjust stack for IRET */
	/* IRET. reload all registers from stack  and  run this task */
}


/*___________________________________________________________________*/

int request_create_task( void(far *fp)(),priority pt)

/*
    Function to create task. This function has two modes of operation
    1) create low priority task. 2) create high priority task. High
    priority task means it is the starting of the contextswitcher and
    start running of the that task. So when you need to start context
    switcher and start execution of the first task call this function
    with priority high. The process state of all tasks are made
    nonexist  when first call to this function is made. Also it will
    not allow you to create more than MAXTASK and two requests with
    high priority.
*/

{
    static task_token=0xff,i;
    if(task_token == 0xff) /* first call to this function make       */
    for(i=0;i<MAXTASK;i++) /* status nonexist                        */
    {
	  process_sts[i].t_state =nonexist;
	  task_token = 0;
    }
    if(pt == low)
    {    /* load start address of the task in the table              */
	 task_token++;
	 if(task_token >= MAXTASK)return(0);
	 process_sts[task_token].t_state=ready;
	 process_sts[task_token].t_cs = (unsigned long)fp/0x10000;
	 process_sts[task_token].t_ip = (unsigned)fp;
	 return(task_token);
    }
    if(pt == high)
    {  /* get old vector, put contextswitcher and execute this task  */
       unsigned int vect;
       static done =0;
       if(!done)
       {
	   vect =0x8;
	   done = 1;
	   oldaddress = request_getvect (vect);
	   request_setvect (contextswitch,vect);
	   process_sts[0].t_state=run; /* index 0 for high priority */
	   fp();
       }
       else return(0); /* not succssful. contextswitcher running     */
    }
}

/*___________________________________________________________________*/

int kill_task( int task_token)
{
    if((task_token+1) >= MAXTASK)return(0);
    if(process_sts[task_token].t_state == nonexist)return(0);
    process_sts[task_token].t_state = kill;
    return(1);
}

/*___________________________________________________________________*/

int request_resume_task( int task_token)
{
    if((task_token+1) >= MAXTASK)return(0);
    if(process_sts[task_token].t_state == suspend)
    {
	process_sts[task_token].t_state = run;
	return(1);
    }
    return(0);
}

/*___________________________________________________________________*/

int request_suspend_task( int task_token)
{
    if((task_token+1) >= MAXTASK)return(0);
    if(process_sts[task_token].t_state == run)
    {
	process_sts[task_token].t_state = suspend;
	return(1);
    }
    return(0);
}

/*___________________________________________________________________*/

int request_kill_task( int task_token,relation s_o)
{
   int result;
   result = kill_task(task_token);
   while(s_o);                     /* if it is self delete wait here */
   return(result);                  /* else return result of killing */
}
/*___________________________________________________________________*/

void request_setvect(void interrupt (far *fp)(),int intno)
{
/*
   home made program to set a vector at specified
   interrupt vector table
*/
   struct addr{
      unsigned offset;
      unsigned base;
      };
   union{
      void interrupt(far *function)();
      struct addr function_addr;
      }intrpt;

   unsigned far * vet_table =(unsigned far *)(4*intno);
   intrpt.function = fp;
   disable();
   *vet_table     = intrpt.function_addr.offset;
   *(vet_table+1) = intrpt.function_addr.base;
   enable();
}

/*___________________________________________________________________*/
void interrupt (far * request_getvect(int intno))()
{
/*  home made program to get a vector from specified
    interrupt vector table
*/
   struct addr{
      unsigned offset;
      unsigned base;
      };
   union{
      void interrupt(far *function)();
      struct addr function_addr;
      }intrpt;

   unsigned far * vet_table =(unsigned far *)(4*intno);
   disable();
   intrpt.function_addr.offset=*vet_table    ;
   intrpt.function_addr.base  =*(vet_table+1)  ;
   enable();
   return(intrpt.function);
}
/*___________________________________________________________________*/

int taskid;
void main(void)
{
    if(!request_create_task(task1,low))writestr("\nUnable to create task",0x71);
    if(!(taskid = request_create_task(task2,low)))writestr("\nUnable to create task",0x72);
    if(!request_create_task(task3,low))writestr("\nUnable to create task",0x71);
    if(!request_create_task(task4,low))writestr("\nUnable to create task",0x71);
    if(!request_create_task(task5,low))writestr("\nUnable to create task",0x71);
    if(!request_create_task(task6,low))writestr("\nUnable to create task",0x71);
    if(!request_create_task(task0,high))writestr("\nUnable to create task",0x72);
}
/*___________________________________________________________________*/

void far task0(void)
{
   int wid = open_window(1, 1,19,9);
   while(1) window_str(wid," A0B0C0D0E0F0",0x4f);
}
/*___________________________________________________________________*/

void far task1(void)
{
   int wid = open_window(20,1,39,9);
   while(1)window_str(wid," a1b1c1d1e1f1",0x3f);
}

/*___________________________________________________________________*/

void far task2(void)
{
    int i;
    int wid = open_window(40,1,59,9);
    for(i=0;i<2000;i++) window_str(wid,"writing window 3",0x2f);
    window_str(wid,"                    Task 2 is deleted.    ",0x2c);
    request_kill_task(taskid,self);
}
/*___________________________________________________________________*/

void far task3(void)
{
    int wid = open_window(60,1,79,9);
    while(1) window_str(wid,"44 44 44 44",0x5f);

}
/*___________________________________________________________________*/

void far task4(void)
{
    int wid = open_window(60,10,79,19);
    while(1) window_str(wid,"0000 0",0x7f);

}
/*___________________________________________________________________*/

void far task5(void)
{
    int wid = open_window(1, 10,19,19);
    while(1) window_str(wid,"11111 1",0x6f);

}

/*___________________________________________________________________*/

void far task6(void)
{
char hsec=0,sec=0,min=0,hour=0;
char tbf[20];
int i;
int wid = open_window(30,20,37,20);
 while(1)
 {
      for(i=0;i<1000;i++);
      hsec++;
      if(hsec>=60){
	 hsec =0;
	 sec++;
	 if(sec>=60)
	 {
	     sec=0;
	     min++;
	     if(min>=60)
	     {
		 min = 0;
		 hour++;
		 if(hour>=12)hour = 0;
	     }
	  }
      }
      sprintf(tbf,"%02d:%02d:%02d",hour,min,sec);
      window_str(wid,tbf,0x1f);
 }
}


/*___________________________________________________________________*/


int open_window(int x1, int y1, int x2,int y2)
{
/*
     this program will open a window with given corner points.
     And it will returns a window token to this window. Writing to
     this window by referring this window token, using window_str()
     or window_char() functions.
*/
  static wno=0;
     if((wno+1) >= MAXWINDOW)return(-1);
     window_info[wno].buf = (char far *)(0xb8000000 + (y1-1)*160 + (x1-1)*2);
     window_info[wno].length    =  x2-x1+1;
     window_info[wno].width     =  y2-y1+1;
     window_info[wno].status    = 1;
     window_info[wno].current_point=0;
     wno++;
     return(wno-1);
}

/*___________________________________________________________________*/

void window_char(int wno, int chr)
{
/*   write a character with corresponding attribute in the
     specified window wno.
*/
     if(!window_info[wno].status)return;
     *(int far *)(window_info[wno].buf+
     ((window_info[wno].current_point/window_info[wno].length)*160)+
     ((window_info[wno].current_point%window_info[wno].length)*2)) = chr;
     window_info[wno].current_point++;
     window_info[wno].current_point%=
     (window_info[wno].length* window_info[wno].width);
}
/*___________________________________________________________________*/

void window_str(int wno,char *str,unsigned atbt)
{
/*      write a string in the window  */
     atbt*=0x100;
     while(*str)window_char(wno,(*str++)+atbt);
}

/*___________________________________________________________________*/

void writechar(int ch)
{
     static int offset=0;
     *(int far *)(0xb8000000 + offset) = ch;
     offset+=2;
     if (offset == 25*80*2) offset = 0;
}
/*___________________________________________________________________*/

void writestr(char *str,unsigned atbt)
{
     atbt*=0x100;
     while(*str)writechar((*str++)+atbt);
}
/*___________________________________________________________________*/


