/* ---------------------------------------------------------------------------
| Filename: sema_pre.c
|
| Author :      Thomas Braunl   
|               
|
| Description: 
| This is a RoBiOS demo program demonstrating thread and semaphore usage
| for PREEMPTIVE Multitasking
| ------------------------------------------------------------------------- */

#include "eyebot.h"

#define SLAVES 3
#define SSIZE  8192

/** pointer to thread control blocks */
struct tcb *slave_p[SLAVES], *master_p;

/** semaphores */
struct sem sema[SLAVES];
struct sem lcd;


/* ----------------------------------------------------------------------- */
/**Code for task "slave"
   task is started with id-number (0..SLAVES-1)
   and does mutual exclusion with the semaphor matching its id-no.
   semaphor P and V is also controlled by the master task
   execution is deferred to other processes after each loop iteration
*/
void slave()
{ int id, i, count = 0;
  
  /** read slave id no. as supplied from main program during spawn op. */
  id = OSGetUID(0);
  OSSemP(&lcd);
    LCDPrintf("slave %d start\n", id);
  OSSemV(&lcd);

  while(1)
  { OSSemP(&sema[id]);                        /* occupy semaphore */
      OSSemP(&lcd);
        for (i=0; i<2*id; i++) LCDPrintf("-");   /* space for easy reading */
        LCDPrintf("slave %d:%d\n", id, count);
      OSSemV(&lcd);
      count = (count+1) % 100;                /* max count 99 */
    OSSemV(&sema[id]);                        /* free semaphore */ 
  }
} /* end slave */


/* ----------------------------------------------------------------------- */
int ord(int key)
{ switch(key)
  {
    case KEY1: return 0;
    case KEY2: return 1;
    case KEY3: return 2;
    case KEY4: return 3;
  }
  return 0; /* error */
}

/* ----------------------------------------------------------------------- */
/**Code for task "master"
   master handles user input via keys to occupy and free semaphores,
   which in turn enable slave tasks to work or block them
*/
void master()
{ int i,k;
  int block[SLAVES] = {1,1,1};  /* start status: all slaves blocked */
 
  OSSemP(&lcd);
    LCDPrintf("master start\n");
    LCDMenu("V.0", "V.1", "V.2", "END");
  OSSemV(&lcd);

  while(1)
  { k = ord(KEYGet());
    if (k!=3)
    { block[k] = !block[k];
      OSSemP(&lcd);
        if (block[k]) LCDMenuI(k+1,"V"); else LCDMenuI(k+1,"P");
      OSSemV(&lcd);
      if (block[k])   OSSemP(&sema[k]);  else OSSemV(&sema[k]);
    }
     else /* kill both slaves and then exit master itself */
     { for (i=0; i<SLAVES; i++) OSKill(slave_p[i]);
        OSKill(0);
     }
  } /* end while */
} /* end master */


/* ----------------------------------------------------------------------- */
/** main of demo program */
int main()
{ int i;

  /** initialize multithreading */
  LCDPrintf("Kernel.. ");
  OSMTInit(PREEMPT);
  LCDPrintf("done\n");

  /** initialize semaphores */
  LCDPrintf("Semaphore..");
  for (i=0; i<SLAVES; i++) OSSemInit(&sema[i],0); /* halt all slaves */
  OSSemInit(&lcd,1);  /* allow 1 writer */
  LCDPrintf("done\n");

  /** Init threads
      Spawn needs: a pointer to the control block 
      the name of the thread, a pointer to its stack
      a pointer to its code, the size of its stack; its priority
      and returns: a pointer to the initializes control block
  */
  LCDPrintf("Spawn..");
  for (i=0; i<SLAVES; i++) {
    slave_p[i] = OSSpawn("slave-i", slave, SSIZE, MIN_PRI,i);
    if(!slave_p[i]) OSPanic("spawn for slave failed");
  }
  master_p     = OSSpawn("master", master, SSIZE, MIN_PRI,10);
  if(!master_p) OSPanic("spawn for master failed");
  LCDPrintf("done\n");
  
  /** READY all threads and start multitasking */
  for (i=0; i<SLAVES; i++) OSReady(slave_p[i]);
  OSReady(master_p);
  OSPermit();  /* activate preemptive multitasking */
  OSWait(100); /* startup time */

/* ----------------------------------------------------------------------- */
/** processing will return HERE, when there is no READY thread left */
	
  LCDPrintf("back to main\n");
  LCDMenu (" ", " ", " ", "END");
  KEYWait(KEY4); 
  return 0;
}

