Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

The iRMX Family Of Operating Systems


January 1991/The iRMX Family Of Operating Systems/Listing 1

Listing 1 (clockint.c)

/*0*********************************************************
*   Richard Carver                            July 1990
*
*   This program demonstrates interprocess communication
*   between several tasks using the iRMX II Operating
*   System.
*
*   Two tasks are used for resource control. The
*   clock_task() controls use of the hardware clock and the
*   crt_task() controls use of the display screen.
*
*   The count_task() competes with the clock_task() for
*   use of the crt_task() services. The timer_task() uses
*   the services of the clock_task() for a software timer.
*
*   The initial task, main(), starts every thing up and,
*   when the timer_task() finishes, shuts everything down.
***********************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <rmxc.h>       /* iRMX OS calls & structures  */
#include <rmxerr.h>     /* iRMX status codes (defines) */
#include <delay.h>      /* Hardcoded delay loop macros */

#define ERROR   (0xFFFF)   /* Error Condition */

#define FALSE   (0)
#define TRUE    (!FALSE)

#define MAX_CLOCK_RETRIES  (4)

/* The NULL token (similar to NULL pointer) */

#define NUL_TKN   ((selector)0)

/* Options for rqgettasktokens() */

#define THIS_TASK (0x00)
#define THIS_JOB  (0x01)
#define PARAM_OBJ (0x02)
#define ROOT_JOB  (0x03)

/* Options for rqcreatemailbox() */

#define FIFO_OBJ_MBX       (0x0000)
#define PRIORITY_OBJ_HBX   (0x0001)
#define FIFO_DATA_MBX      (0x0020)
#define PRIORITY_DATA_MBX  (0x0021)

/* Options for rqcreatesemaphore() */

#define FIFO_SEM      (0x0000)
#define PRIORITY_SEM  (0x0001)

#define SEM_INIT_0    (0)
#define SEM_MAX_1     (1)

/* Common wait times for iRMX System Calls */

#define NO_WAIT       (0x0000)
#define WAIT_FOREVER  (0xFFFF)

/* Options for rqcreatetask() */

#define NO_FLOAT   (0x0000)
#define DEFAULT_STACK_SIZE     ((unsigned int)2048)

/* Task Priorities  */

#define CLOCK_TASK_PRIORITY    ((unsigned char)48)
#define TIMER_TASK_PRIORITY        ((unsigned char)150)
#define CRT_TASK_PRIORITY      ((unsigned char)200)
#define COUNT_TASK_PRIORITY    ((unsigned char)200)

/* Defines For Hardware Clock */
/* Clock Interrupt Level */

#define CLOCK_INT_LEVEL (0x26)

/* Clock Ports */

#define ADR_PORT  ((unsigned short)0xA060)
#define DAT_PORT  ((unsigned short)0xA062)
#define CNT_PORT  ((unsigned short)0xA064)
#define PIO_PORT  ((unsigned short)0xA066)

/* Clock Port I/O Operations */

#define PIO_READ  ((unsigned char)0x82)
#define PIO_WRITE ((unsigned char)0x80)

/* Clock Registers */

#define SEC01_REG (0x00)
#define SEC10_REG (0x01)
#define MIN01_REG (0x02)
#define MIN10_REG (0x03)
#define HRS01_REG (0x04)
#define HRS10_REG (0x05)
#define DAT01_REG (0x07)
#define DAT10_REG (0x08)
#define MON01_REG (0x09)
#define MON10_REG (0x0A)
#define YRS01_REG (0x0B)
#define YRS10_REG (0x0C)

/* Clock Control Lines */

#define CNT_RST     ((unsigned char)0x00)
#define INT_ENB     ((unsigned char)0x20)
#define HOLD_HIGH   ((unsigned char)0x10)
#define HOLD_READ   ((unsigned char)0x30)
#define HOLD_WRITE  ((unsigned char)0x50)

/* Defines for months */

#define JANUARY     (1)
#define FEBRUARY    (2)
#define MARCH       (3)
#define DECEMBER    (12)

/* Time/Date and Timers Data Structure  */

#define MAX_TIMERS    (10)

struct SYS_TIME_STR
  {
  unsigned char change_flg;
  unsigned char second;
  unsigned char minute;
  unsigned char hour;
  unsigned char date;
  unsigned char month;
  unsigned char year;
  
  struct TIMER_STR
    {
    unsigned char in_use;
    unsigned int count;
    unsigned int timeout;
    selector    sem_tkn;
    }timer[MAX_TIMERS];
  };

/* Request Message format for the Display Task */

struct CRT_REQ_STR
  {
   unsigned char *msg_ptr;
   unsigned int  row;
   unsigned int  col;
   };
 
 /* Object Catalog Names */
 
 const unsigned char
   clock_sem_name[] = "\011CLOCK_SEM",
   init_sem_name[] = "\010INIT_SEM";
 
 const unsigned char
   time_seg_name[] = "\010TIME_SEG";
 
 const unsigned char
   crt_req_mbx_name[] = "\007CRT_MBX";
 
 const unsigned char
   crt_shtdwn_sem_name[] = "\012CRT_SHTDWN",
   clock_shtdwn_sem_name[] = "\014CLOCK_SHTDWN",
   count_shtdwn_sem_name[] = "\014COUNT_SHTDWN";
 
 /* Days Per Month */
 
 const unsigned char
   days_in_month[12] =
   {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};

/* Global System Time/Date Structure */

selector  sys_time_seg;
struct SYS_TIME_STR *sys_time;

/* Task declarations  */

void clock_task(void);
void crt_task(void);
void timer_task(void);
void count_task(void);

/* Task objects (global for debugging purposes) */

selector  clock_tsk;
selector  crt_tsk;
selector  timer_tsk;
selector  count_tsk;

/*1*********************************************************
* Task:     main()  (the initial task)
*
* Summary:  Starts it all up and shuts it all down.
*
* Caveats:  None
***********************************************************/

void main(void)
  {
  selector  current_job;       /* current job            */
  selector  init_sem;          /* initilaization sem     */
  selector  clock_sem;         /* time/date shared memory*/
  selector  clock_shtdwn_sem;  /* clock_task() shutdown  */
  selector  count_shtdwn_sem;  /* count_task() shutdown  */
  selector  crt_shtdwn_sem;    /* crt_task() shutdown    */
  selector  sys_time_seg;      /* time/date data segment */
  
  unsigned  int   status;      /* iRMX condition code    */
  
  struct EXCEPTIONSTRUCT exceptioninfo;

/*  Disable current task's exception handler  */

  rqgetexceptionhandler(&exceptioninfo, &status);
  exceptioninfo.exceptionmode = 0;
  rqsetexceptionhandler(&exceptioninfo, &status);
/*  Get Token For Current Job */

  current_job = rqgettasktokens(THIS_JOB, &status);

/*  Create and catalog the semaphore used for */
/*  accessing the shared clock data.          */


  clock_sem = rqcreatesemaphore(SEM_INIT_0,
    SEM_MAX_1, PRIORITY_SEM, &status);
  rqcatalogobject(current_job,
    clock_sem, &clock_sem_name, &status);

/*  Create and catalog the semaphore used for */
/*  synchronizing tasks during initialization */
/*  and shutdown.                             */

  init_sem = rqcreatesemaphore(SEM_INIT_0,
    99, FIFO_SEM, &status);
  rqcatalogobject(current_job,
    init_sem, &init_sem_name, &status);

/*  Create and catalog the segment of memory  */
/*  used to hold the shared time/date data.   */

  sys_time_seg = rqcreatesegment(
    sizeof(struct SYS_TIME_STR), &status);
  rqcatalogobject(current_job,
    sys_time_seg, &time_seg_name, &status);

/*  Create and catalog the semaphores used to */
/*  signal task shutdown.                     */

  clock_shtdwn_sem = rqcreatesemaphore(SEM_INIT_0,
    SEM_MAX_1, FIFO_SEM, &status);
  rqcatalogobject(current_job,
    clock_shtdwn_sem, &clock_shtdwn_sem_name, &status);

  crt_shtdwn_sem = rqcreatesemaphore(SEM_INIT_0,
    SEM_MAX_1, FIFO_SEM, &status);
  rqcatalogobject(current_job,
    crt_shtdwn_sem, &crt_shtdwn_sem_name, &status);

  count_shtdwn_sem = rqcreatesemaphore(SEM_INIT_0,
    SEM_MAX_1, FIFO_SEM, &status);
  rqcatalogobject(current_job,
    count_shtdwn_sem, &count_shtdwn_sem_name, &status);

/*  Create the crt_task(). Wait for the task to  */
/*  indicate it has completed initialization.    */

  crt_tsk = rqcreatetask(CRT_TASK_PRIORITY, &crt_task,
    NUL_TKN, NULL, DEFAULT_STACK_SIZE, NO_FLOAT, &status);
  
  rqreceiveunits(init_sem, 1, WAIT_FOREVER, &status);

/*  Create the clock_task(). Wait for the task    */
/*  to indicate it has completed initialization.  */

  clock_tsk = rqcreatetask(CLOCK_TASK_PRIORITY, &clock_task,
    NUL_TKN, NULL, DEFAULT_STACK_SIZE, NO_FLOAT, &status);
  
  rqreceiveunits(init_sem, 1, WAIT_FOREVER, &status);

/*  Create the count_task(). Wait for the task    */
/*  to indicate it has completed initialization.  */

  count_tsk = rqcreatetask(COUNT_TASK_PRIORITY, &count_task,
    NUL_TKN, NULL, DEFAULT_STACK_SIZE, NO_FLOAT, &status);
  
  rqreceiveunits(init_sem, 1, WAIT_FOREVER, &status);

/*  Create the timer_task(). Wait for the task    */
/*  to indicate it has completed initialization.  */
/*  This also signals the shutdown process.       */
  timer_tsk = rqcreatetask(TIMER_TASK_PRIORITY, &timer_task,
    NUL_TKN, NULL, DEFAULT_STACK_SIZE, NO_FLOAT, &status);
  
  rqreceiveunits(init_sem, 1, WAIT_FOREVER, &status);

/*  Begin shutdown by first disabling any further  */
/*  interrupts from the hardware clock. This task  */
/*  suspends itself for 2/100th of a second to     */
/*  ensure interrupt is disabled.                  */

  rqdisable(CLOCK_INT_LEVEL, &status);
  rqsleep(2, &status);

/*  Send shutdown signals to all tasks and wait    */
/*  for all tasks to complete shutdown.            */

  rqsendunits(count_shtdwn_sem, 1, &status);
  rqsendunits(crt_shtdwn_sem, 1, &status);
  rqsendunits(clock_shtdwn_sem, 1, &status);
  
  rqreceiveunits(init_sem, 3, WAIT_FOREVER, &status);

/*  Cleanup before terminating. This includes      */
/*  deleting tasks and uncataloging/deleteing      */
/*  all objects created.                           */

  rqdeletetask(crt_tsk, &status);
  rqdeletetask(clock_tsk, &status);
  rqdeletetask(count_tsk, &status);
  rqdeletetask(timer_tsk, &status);
  
  rquncatalogobject(current_job,
    &clock_sem_name, &status);
  rquncatalogobject(current_job,
    &init_sem_name, &status);
  rquncatalogobject(current_job,
    &time_seg_name, &status);
  rquncatalogobject(current_job,
    &crt_req_mbx_name, &status);
  
  rquncatalogobject(current_job,
    &clock_shtdwn_sem_name, &status);
  rquncatalogobject(current_job,
    &count_shtdwn_sem_name, &status);
  rquncatalogobject(current_job,
    &crt_shtdwn_sem_name, &status);
  
  rqdeletesemaphore(clock_sem, &status);
  rqdeletesemaphore(init_sem, &status);
  
  rqdeletesemaphore(clock_shtdwn_sem, &status);
  rqdeletesemaphore(count_shtdwn_sem, &status);
  rqdeletesemaphore(crt_shtdwn_sem, &status);
  
  rqdeletesegment(sys_time_seg, &status);
  
  cursor_on();
  printf("\nAll Done!\n");
  
  exit(0);
  }
  
/*1*********************************************************
* Function: start_timer
*
* Summary:  Create and start a timer.
*
* Invocation: timer_sem = start_timer(timeout);
*
* Inputs:   timeout (unsigned int) - timer interval
*              in seconds
*
* Outputs:  timer_sem (selector) - the semaphore that will
*              receive a unit at each timer interval
*
* Caveats:  Accesses the time/date data segment that is
*           shared with the clock_task().
***********************************************************/

selector start_timer(unsigned int timeout)
  {
  unsigned int    status;
  unsigned int    indx;
  unsigned int    done;
  unsigned int    no_more_timers;
  
  selector    timer_sem;
  selector    current_job;
  selector    clock_sem;
  selector    sys_time_seg;
  
  struct SYS_TIME_STR   *sys_time;
  
  indx = 0;
  done = FALSE;
  no_more_timers = TRUE;
  timer_sem = NUL_TKN;

/*  Get objects for access semaphore and */
/*  time/date data segment               */

  current_job = rqgettasktokens(THIS_JOB, &status);
  
  clock_sem = rqlookupobject(current_job,
    &clock_sem_name, NO_WAIT, &status);
  sys_time_seg = rqlookupobject(current_job,
    &time_seg_name, NO_WAIT, &status);
  
  sys_time = buildptr(sys_time_seg, 0);

/* Access time/date memory  */

  rqreceiveunits(clock_sem, 1, WAIT_FOREVER, &status);

/* Find an empty timer slot  */

  while (!done)
    {
    if (!sys_time->timer[indx].in_use)
      {
      done = TRUE;
      no_more_timers = FALSE;
      }
    else
      {
      indx++;
      
      if (indx == MAX_TIMERS)
        {
        done = TRUE;
        }
      }
    }

/* Establish a timer  */

  if (no_more_timers == FALSE)
    {
    if (timeout ! = 0)
      {
      timer_sem = rqcreatesemaphore(SEM_INIT_0,
        999, FIFO_SEM, &status);
      sys_time->timer[indx].in_use = TRUE;
      sys_time->timer[indx].count = 0;
      sys_time->timer[indx].timeout = timeout;
      sys_time->timer[indx].sem_tkn = timer_sem;
      }
   }
/*  Release time/date memory */

  rqsendunits(clock_sem, 1, &status);

/*  Return timer semaphore to caller */

  return(timer_sem);
  }

/*1*********************************************************
* Function:  stop_timer
*
* Summary:   Delete a timer.
*
* Invocation:  timer_sem = start_timer(timeout);
*
* Inputs:    timeout (unsigned int) - timer interval
*              in seconds
*
* Outputs:   timer_sem (selector) - the semaphore that will
*              receive a unit at each timer interval
*
* Caveats:   Accesses the time/date data segment that is
*            shared with the clock_task().
***********************************************************/

unsigned int stop_timer(selector timer_sem)
  {
  unsigned int    status;
  unsigned int    indx;
  unsigned int    done;
  unsigned int    invalid_timer;
  
  selector    current_job;
  selector    clock_sem;
  selector    sys_time_seg;
  
  struct SYS_TIME_STR   *sys_time;
  
  indx = 0;
  done = FALSE;
  invalid_timer = TRUE;
  
/*  Get objects for access semaphore and */
/*  time/date data segment               */

  current_job = rqgettasktokens(THIS_JOB, &status);
  
  clock_sem = rqlookupobject(current_job,
    &clock_sem_name, NO_WAIT, &status);
  sys_time_seg = rqlookupobject(current_job,
    &time_seg_name, NO_WAIT, &status);
  
  sys_time = buildptr(sys_time_seg, 0);

/*  Access time/date memory.  */

  rqreceiveunits(clock_sem, 1, WAIT_FOREVER, &status);

/* Locate and remove this timer  */

  while (!done)
    {
    if (sys_time->timer[indx].in_use &&
      (sys_time->timer[indx].sem_tkn == timer_sem))
      {
      rqdeletesemaphore(timer_sem, &status);
      sys_time->timer[indx].in_use = FALSE;
      sys_time->timer[indx].sem_tkn = NUL_TKN;
      sys_time->timer[indx].count = 0;
      sys_time->timer[indx].timeout = 0;
      done = TRUE;
      invalid_timer = FALSE;
      }
    else
      {
      indx++;
      
      if (indx == MAX_TIMERS)
        {
        done = TRUE;
        }
      }
    }

/*  Release time/date memory  */
  
  rqsendunits(clock_sem, 1, &status);
  
/*  Return with result of call  */
  
  return (invalid_timer ? ERROR : EOK);
  }

/*1*********************************************************
* Task:     count_task
*
* Summary:  Continuously counts and displays a value from
*           0-65535.
*
* Caveats:  Communicates with the crt_task() for displaying
*           counter values.
***********************************************************/

void count_task(void)
  {
  unsigned char counter_buf[6];
  
  selector  current_job;
  selector  rsp_mbx;
  selector  crt_req_mbx;
  selector  init_sem;
  selector  count_shtdwn_sem;
  selector  req_seg;
  
  unsigned int  counter;
  unsigned int    status;
  
  struct CRT_REQ_STR    *req_msg;
  
  struct EXCEPTIONSTRUCT    exceptioninfo;
  
/*  Disable this task's exception handler  */

  rqgetexceptionhandler(&exceptioninfo, &status);
  exceptioninfo.exceptionmode = 0;
  rqsetexceptionhandler(&exceptioninfo, &status);

/*  Lookup/Create needed objects  */

  current_job = rqgettasktokens(THIS_JOB, &status);
  
  init_sem = rqlookupobject(current_job,
    &init_sem_name, NO_WAIT, &status);
  
  count_shtdwn_sem = rqlookupobject(current_job,
    &count_shtdwn_sem_name, NO_WAIT, &status);
  
  rsp_mbx = rqcreatemailbox(FIFO_OBJ_MBX, &status);
  
  crt_req_mbx = rqlookupobject(current_job,
    &crt_req_mbx_name, NO_WAIT, &status);

/*  Initialize request message  */

  req_seg = rqcreatesegment(
    sizeof(struct CRT_REQ_STR), &status);
  
  req_msg = buildptr(req_seg, 0);
  
  req_msg->msg_ptr = counter_buf;
  req_msg->row = 12;
  req_msg->col = 38;

/*  Singal that initialization is completed.  */

  rqsendunits(init_sem, 1, &status);

/*  Continuously count 0-65535 and display the  */
/*  value until signalled to stop.               */

  counter = 0;
  
  rqreceiveunits(count_shtdwn_sem, 1, NO_WAIT, &status);
  
  while (status == ETIME) /* while no signal received */
    {
    sprintf(counter_buf, "%d", counter);
    rqsendmessage(crt_req_mbx,
      req_seg, rsp_mbx, &status);
    req_seg = rqreceivemessage(rsp_mbx,
      WAIT_FOREVER, NUL_TKN, &status);
    counter++;
    rqreceiveunits(count_shtdwn_sem, 1, NO_WAIT, &status);
    }

/*  Cleanup objects created by this task. */

  rqdeletemailbox(rsp_mbx, &status);
  rqdeletesegment(req_seg, &status);

/*  Signal shutdown complete and suspend execution. */

  rqsendunits(init_sem, 1, &status);
  
  rqsuspendtask(NUL_TKN, &status);  /* suspend this task  */
  }

/*1************************************************************
* Task:     timer_task
*
* Summary:
*
* Caveats:
**************************************************************/

void timer_task(void)
  {
  selector    current_job;
  selector    init_sem;
  selector    clock_sem;
  selector    timer_sem;
  
  unsigned  int   status;
  
  struct EXCEPTIONSTRUCT  exceptioninfo;

/*  Disable this task's exception handler  */

  rqgetexceptionhandler(&exceptioninfo, &status);
  exceptioninfo.exceptionmode = 0;
  rqsetexceptionhandler(&exceptioninfo, &status);

/* Lookup needed objects  */

  current_job = rqgettasktokens(THIS_JOB, &status);
  
  init_sem = rqlookupobject(current_job,
    &init_sem_name, NO_WAIT, &status);

/*  Start a one-second timer. If the timer was      */
/*  created, wait for 20 seconds (units) then stop  */
/*  stop the timer.                                 */
  timer_sem = start_timer(1);
  
  if (timer_sem != NUL_TKN)
    {
    rqreceiveunits(timer_sem, 20, WAIT_FOREVER, &status);
    stop_timer(timer_sem);
    }

/*  Signal task finished and suspend execution.  */

  rqsendunits(init_sem, 1, &status);
  
  rqsuspendtask(NUL_TKN, &status); /* suspend this task */
  }

/*1**********************************************************
* Functions:   clrscrn, cursor_on, cursor_off, print_at
*
* Summary:   Functions for basic display control.
*
* Invocation:    clrscrn( );
*                curson_on( );
*                cursoff_on( );
*
* Invocation:    print_at(row, col, msg_ptr)
*
* Inputs:  row (unsigned int) - row position
*          col (unsigned int) - column position
*          msg_ptr (unsigned char *) - message string pointer
*
* Caveats: Escape sequences are for a WYSE60 terminal.
**************************************************************/

void clrscrn(void)
  {
  fprintf(stdout, "\x1B+");
  fflush(stdout);
  return;
  }

void cursor_on(void)
  {
  fprintf(stdout, "\x1B`1");
  fflush(stdout);
  return;
  }

void cursor_off(void)
  {
  fprintf(stdout, "\x1B'0");
  fflush(stdout);
  return;
  }

void print_at(unsigned int row,
  unsigned int col, unsigned char *msg_ptr)
  {
  fprintf(stdout, "\x1B=%c%c%s",
    (row + 31), (col + 31), msg_ptr);
  fflush(stdout);
  return;
  }

/*1*********************************************************
* Task:     crt_task
*
* Summary:
*
* Caveats:
***********************************************************/

void crt_task(void)
  {
  selector    current_job;
  selector    req_mbx;
  selector    rsp_mbx;
  selector    rec_seg;
  selector    init_sem;
  selector    crt_shtdwn_sem;
  
  unsigned int    status;
  
  struct CRT_REQ_STR    *req_msg;
  
  struct EXCEPTIONSTRUCT    exceptioninfo;
  
/*  Disable this task's exception handler */

  rqgetexceptionhandler(&exceptioninfo, &status);
  exceptioninfo.exceptionmode = 0;
  rqsetexceptionhandler(&exceptioninfo, &status);

/*  Lookup/Create needed objects  */

  current_job = rqgettasktokens(THIS_JOB, &status);
  
  init_sem = rqlookupobject(current_job,
    &init_sem_name, WAIT_FOREVER, &status);
  
  crt_shtdwn_sem = rqlookupobject(current_job,
    &crt_shtdwn_sem_name, WAIT_FOREVER, &status);
  
  req_mbx = rqcreatemailbox(FIFO_OBJ_MBX, &status);
  rqcatalogobject(current_job,
    req_mbx, &crt_req_mbx_name, &status);

/*  Initialize display  */

  cursor_off( );
  clrscrn( );

/*  Signal initialization complete  */

  rqsendunits(init_sem, 1, &status);
  
  
  rqreceiveunits(crt_shtdwn_sem, 1, NO_WAIT, &status);
  
  while (status == ETIME)
    {
    req_seg = rqreceivemessage(req_mbx,
      WAIT_FOREVER, &rsp_mbx, &status);
    
    req_msg = buildptr(req_seg, 0);
    
    print_at(req_msg->row,
      req_msg->col, req_msg->msg_ptr);
    
    if (rsp_mbx != NUL_TKN)
      {
      rqsendmessage(rsp_mbx, req_seg, NUL_TKN, &status);
      }
    else
      {
      rqdeletesegment(req_seg, &status);
      }
      
    rqreceiveunits(crt_shtdwn_sem, 1, NO_WAIT, &status);
    }
    
  rqsendunits(init_sem, 1, &status);
  
  for(;;)
    {
    req_seg = rqreceivemessage(req_mbx,
      WAIT_FOREVER, &rsp_mbx, &status);
      
    if (rsp_mbx != NUL_TKN)
      {
      rqsendmessage(rsp_mbx, req_seg, NUL_TKN, &status);
      }
    else
      {
      rqdeletesegment(req_seg, &status);
      }
    }
  }

/*1*********************************************************
* Function:  read_time
*
* Summary:   Read a specified time/date register of the
*            hardware clock.
*
* Invocation:  in_byte = read_time(reg);
*
* Inputs:    reg (unsigned char) - the hardware time/date
*              register to be read
*
* Outputs:   in_byte (unsigned char) - the value of the
*              register read
*
* Caveats:   None
***********************************************************/

unsigned char read_time(unsigned char reg)
  {
  unsigned char in_byte;
  
  outbyte(CNT_PORT, HOLD_HIGH);
  delay(3);
  outbyte(CNT_PORT, HOLD_READ);
  delay(1);
  outbyte(ADR_PORT, reg);
  delay(1);
  in_byte = inbyte(DAT_PORT);
  outbyte(CNT_PORT, CNT_RST);
  delay(1);
  
  return (in_byte);
  }

/*1*********************************************************
* Function:  write_time
*
* Summary:   Write a byte to the specified time/date register
*            of the hardware clock.
*
* Invocation:  write_time(reg, out_byte);
*
* Inputs:    reg (unsigned char) - the hardware time/date
*              register
*
*           out_byte (unsigned char) - the value to be
*             written to the hardware clock register
*
* Caveats:  None
***********************************************************/

void write_time(unsigned char reg, unsigned char out_byte)
  {
  outbyte(CNT_PORT, HOLD_HIGH);
  delay(3);
  outbyte(ADR_PORT, reg);
  delay(1);
  outbyte(DAT_PORT, out_byte);
  delay(1);
  outbyte(CNT_PORT, HOLD_WRITE);
  delay(1);
  outbyte(CNT_PORT, CNT_RST);
  delay(1);
  
  return;
  }

/*1*********************************************************
* Function:  read_clock
*
* Summary:   Read the time/date from the hardware clock and
*            stores it in the global time/date structure.
*
* Invocation:  read_clock();
*
* Caveats:   Requires the caller to obtain access the shared
*            time/date data segment.
***********************************************************/

void read_clock(void)
  {
  unsigned char time_buf[13];
  unsigned int i;

/*  Set mode */

  outbyte(PIO_PORT, PIO_READ);
  delay(1);

/*  Set control lines to disable interrupts */

  outbyte(CNT_PORT, CNT_RST);
  delay(1);

/*  Read time/date */

  for (i = 0 ; (i <= 12); i++)
    {
    time_buf[i] = read_time(i);
    }
  
  sys_time->second =
    (10 * (time_buf[SEC10_REG] & 0x07)) +
    (time_buf[SEC01_REG] & 0x0F);
  sys_time->minute =
    (10 * (time_buf[MIN10_REG] & 0x07)) +
    (time_buf[MIN01_REG] & 0x0F);
  sys_time->hour =
    (10 * (time_buf[HRS10_REG] & 0x03)) +
    (time_buf[HRS01_REG] & 0x0F);
  sys_time->date =
    (10 * (time_buf[DAT10_REG] & 0x03)) +
    (time_buf[DAT01_REG] & 0x0F);
  sys_time->month =
    (10 * (time_buf[MON10_REG] & 0x01)) +
    (time_buf[MON01_REG] & 0x0F);
  sys_time->year =
    (10 * (time_buf[YRS10_REG] & 0x0F)) +
    (time_buf[YRS01_REG] & 0x0F);

/*  Reset control lines to enable interrupts */

  outbyte(CNT_PORT, INT_ENB);
  delay(1);
  
  outbyte(ADR_PORT, 0x0F);
  delay(1);
  
  return;
  }

/*1*********************************************************
* Function:  write_clock
*
* Summary:   Uses the global time/date structure to write a
*            new time/date to the hardware clock.
*
* Invocation:  write_clock();
*
* Caveats:   Requires the caller to obtain access the shared
*            time/date data segment.
************************************************************/

void write_clock(void)
  {
  unsigned char temp_byte;

/*  Set mode */

  outbyte(PIO_PORT, PIO_WRITE);
  delay(1);

/*  Set control lines to disable interrupts */

  outbyte(CNT_PORT, CNT_RST);
  delay(1);

/*  Write time/date. Seconds always set to 0 */

  sys_time->second = 0;
  write_time(SEC01_REG, 0);
  write_time(SEC10_REG, 0);
  
  temp_byte =
    sys_time->minute - (10 * (sys_time->minute / 10));
  write_time(MIN01_REG, temp_byte);
  temp_byte = sys_time->minute / 10;
  write_time(MIN10_REG, temp_byte);
  
  temp_byte =
    sys_time->hour - (10 * (sys_time->hour / 10));
  write_time(HRS01_REG, temp_byte);
  temp_byte =
    (sys_time->hour / 10) | 0x08;     /* 24hr format */
  write_time(HRS10_REG, temp_byte);
  
  temp_byte =
    sys_time->date - (10 * (sys_time->date / 10));
  write_time(DAT01_REG, temp_byte);
  temp_byte = sys_time->date / 10;
  write_time(DAT10_REG, temp_byte);
  
  temp_byte =
    sys_time->month - (10 * (sys_time->month / 10));
  write_time(MON01_REG, temp_byte);
  temp_byte = sys_time->month / 10;
  write_time(MON10_REG, temp_byte);
  
  temp_byte =
    sys_time->year - (10 *(sys_time->year / 10));
  write_time(YRS01_REG, temp_byte);
  temp_byte = sys_time->year / 10;
  write_time(YRS10_REG, temp_byte);

/*  Reset mode*/

  outbyte(PIO_PORT, PIO_READ);
  delay(1);

/*  Reset control lines to enable interrupts */

  outbyte(CNT_PORT, INT_ENB);
  delay(1);
  
  outbyte(ADR_PORT, 0x0F);
  delay(1);
  
  return;
  }

/*1*********************************************************
* Function:  reset_timers
*
* Summary:   Initializes (zero out) all timer slots of the
*            shared time/date data segment.
*
* Invocation:  reset_timers();
*
* Caveats:   Requires the caller to obtain access the shared
*            time/date data segment.
************************************************************/

void reset_timers(void)
  {
  unsigned int    i;
  
  for (i = 0 ; i < MAX_TIMERS; i++)
    {
    sys_time->timer[i].in_use = FALSE;
    sys_time->timer[i].sem_tkn = NUL_TKN;
    sys_time->timer[i].count = 0;
    sys_time->timer[i].timeout = 0;
    }
  
  return;
  }

/*1*********************************************************
* Function:  advance_timers
*
* Summary:   Increments all active timers in the shared
*            time/date data segment by one second.
*
* Invocation:  advance_timers();
*
* Caveats:   Requires the caller to obtain access the shared
*            time/date data segment.
***********************************************************/

void advance_timers(void)
  {
  unsigned int    i;
  unsigned int    status;
  
  
  for (i = 0 ; i < MAX_TIMERS; i++)
    {
    if (sys_time->timer[i].in_use)
      {
      sys_time->timer[i].count++;
      if (sys_time->timer[i].count >=
        sys_time->timer[i].timeout)
        {
        rqsendunits(
          sys_time->timer[i].sem_tkn, 1, &status);
        sys_time->timer[i].count = 0;
        }
      }
    }
  
  return;
  }


/*1**********************************************************
* Function:  advance_time_date
*
* Summary:   Increments the time/date (hh:mm:ss mm/dd/yy) in
*            the shared time/date data segment by one second.
*
* Invocation:  advance_time_date();
*
* Caveats:   Requires the caller to obtain access the shared
*            time/date data segment.
************************************************************/


void advance_time_date(void)
  {
  if (sys_time->second < 59)
    {
    sys_time->second++;
    
    }
  else
    {
    sys_time->second = 0;
    
    if (sys_time->minute < 59)
      {
      sys_time->minute++;
      }
    else
      {
      sys_time->minute = 0;
      
      if (sys_time->hour < 23)
        {
        sys_time->hour++;
        }
      else
        {
        sys_time->hour = 0;
        
        if (sys_time->date <
          days_in_month[sys_time->month - 1])
          {
          sys_time->date++;
          }
        else
          {
          if (sys_time->month == FEBRUARY)
            {
/*  Check for leap year (only good through 2100)  */

            if (((sys_time->year % 4) == 0) &&
              (sys_time->date == 28))
              {
              sys_time->date = 29;
              }
            else
              {
              sys_time->month = MARCH;
              sys_time->date = 1;
              }
            }
          else
            {
            sys_time->date = 1;
            
            if (sys_time->month < DECEMBER)
              {
              sys_time->month++;
              }
            else
              {
              sys_time->month = JANUARY;
              
              if (sys_time->year < 99)
                {
                sys_time->year++;
                }
              else
                {
                sys_time->year = 0;
                }
              }
            }
          }
        }
      }
    }
  
  return;
  }


/*1*********************************************************
* Function:  start_timer
*
* Summary:   Create and start a timer.
*
* Invocation:  timer_sem = start_timer(timeout);
*
* Inputs:    timeout (unsigned int) - timer interval
*             in seconds
*
* Outputs:   timer_sem (selector) - the semaphore that will
*             receive a unit at each timer interval
*
* Caveats:   The interrupt handler will signal the
*            interrupt task to finish servicing the
*            interrupt.
************************************************************/

/* Tell the compiler that this function is an interrupt    */
/* routine so it can generate proper cede for entering     */
/* and exiting the interrupt routine.                      */

#pragma interrupt("clockint_handler")

void clockint_handler(void)
  {
  unsigned int status;

/*  Signal the interrupt task */

  rqsignal interrupt(CLOCK_INT_LEVEL, &status);
  
  return;
  }

/*1*********************************************************
* Task:     clock_task
*
* Summary:  Services interrupts from the hardware clock.
*           Also maintains/updates the global time/date
*           data and services software timers.
*
* Caveats:  Communicates with the crt_task() to display the
*           current time/date.
***********************************************************/

void clock_task(void)
  {
  char time_string[19];
  
  unsigned int status;
  unsigned int retry;
  
  selector current_job;
  selector init_sem;
  selector clock_sem;
  selector clock_shtdwn_sem;
  
  selector rsp_seg;
  selector req_seg;
  selector crt_req_mbx;
  selector rsp_mbx;
  
  struct CRT_REQ_STR       *req_msg;
  
  struct EXCEPTIONSTRUCT      exceptioninfo;

/*  Disable this task's exception handler */

  rqgetexceptionhandler(&exceptioninfo, &status);
  exceptioninfo.exceptionmode = 0;
  rqsetexceptionhandler(&exceptioninfo, &status);

/*  Disable any current interrupt task */

  rqresetinterrupt(CLOCK_INT_LEVEL, &status);

/*  Lookup/Create needed objects */

  current job = rqgettasktokens(THIS_JOB, &status);
  
  init_sem = rqlookupobject(current_job,
    &init_sem_name, NO_WAIT, &status);
  
  clock_sem = rqlookupobject(current_job,
    &clock_sem_name, NO_WAIT, &status);
  
  sys_time_seg = rqlookupobject(current_job,
    &time_seg_name, NO_WAIT, &status);
  
  clock_shtdwn_sem = rqlookupobject(current_job,
    &clock_shtdwn_sem_name, NO_WAIT, &status);
  
  crt_req_mbx = rqlookupobject(current_job,
    &crt_req_mbx_name, NO_WAIT, &status);
  
  sys_time = buildptr(sys_time_seg, 0);
  
  rsp_mbx = rqcreatemailbox(FIFO_OBJ_MBX, &status);

/*  Initialize time string buffer  */

  memset(&time_string, ' ' ,sizeof(time_string));

/*  Initialize crt_task() request message and  */
/*  send it to the response mailbox to setup   */
/*  for the processing loop.                   */

  req_seg = rqcreatesegment(
    sizeof(struct CRT_REQ_STR), &status);
  req_msg = buildptr(req_seg, 0);
  
  req_msg->msg_ptr = &time_string;
  req_msg->row = 1;
  req_msg->col = 60;
  
  rqsendmessage(rsp_mbx, req_seg, NUL_TKN, &status);

/*  Initialize retry counter and timers */

  retry = 0;
  sys_time->change_flg = FALSE;
  reset_timers();

/*  Initialize and read hardware clock */

  outbyte(PIO_PORT, PIO_READ);
  delay(1);
  
  outbyte(CNT_PORT, INT_ENB);
  delay(1);
  
  outbyte(ADR_PORT, 0x0F);
  delay(1);
  
  read_clock();

/*  Allow access to time/date memory */

  rqsendunits(clock_sem, 1, &status);

/*  Set interrupt vector  */

  rqsetinterrupt(CLOCK_INT_LEVEL, 1,
    &clockint_handler, NUL_TKN, &status);

/*  Signal initialization complete */

  rqsendunits(init_sem, 1, &status);

/*  Process interrupt signals until shutdown */

  rqreceiveunits(clock_shtdwn_sem, 1, NO_WAIT, &status);
  
  while (status == ETIME)  /* while no signal received */
    {
/*  Wait no longer than 2 seconds for interrupt signal */
   
   rqetimedinterrupt(CLOCK_INT_LEVEL, 200, &status);

/*  If an interrupt is NOT received, attempt to reset  */
/*  the hardware clock.                                */

   if (status == ETIME)      /* if no interrupt  */
     {
     retry++;

/*  Once the maximum number of retries is reached,    */
/*  give up. Retrieve outstanding display request     */
/*  and wait for shutdown. When shutdown signal is    */
/*  received, disable this interrupt task, signal     */
/*  shutdown complete and suspend execution.          */

     if (retry == MAX_CLOCK_RETRIES)
       {
       req_seg = rqreceivemessage(rsp_mbx,
         WAIT_FOREVER, NUL_TKN, &status);
       rqreceiveunits(clock_shtdwn_sem, 1,
         WAIT_FOREVER, &status);
       rqresetinterrupt(CLOCK_INT_LEVEL, &status);
       rqsendunits(init_sem, 1, &status);
       rqsuspendtask(NUL_TKN, &status);
       }

/*  Reinitialize hardware clock */

       outbyte(PIO_PORT, PIO_READ);
       delay(1);
       
       outbyte(CNT_PORT, INT_ENB);
       delay(1);
       
       outbyte(ADR_PORT, 0x0F);
       delay(1);

/*  Re-read hardware crock */

       rqreceiveunits(clock_sem, 1,
         WAIT_FOREVER, &status);
       
       read_clock();
       
       rqsendunits(clock_sem, 1, &status);
       }
     else
       {
/*  Interrupt signal received, reset retry count */

       retry = 0;

/*  Access time/date memory */

       rqreceiveunits(clock_sem, 1,
         WAIT_FOREVER, &status);

/*  If needed, update hardware clock with new  */
/*  time/date data. Otherwise, advance the     */
/*  time/data data by one second.              */

       if (sys_time->change_flg)
         {
         write_clock();
         sys_time->change_flg = 0;
         }
       else
         {
         advance_time_date();
         }

/*  Advance any timers by one second  */

       advance_timers();

/*  Prepare time/date data for display  */

     sprintf(time_string, "%02d:%02d:%02d %02d/%02d/%O2d",
       sys_time->hour, sys_time->minute, sys_time->second,
       sys_time->month, sys_time->date, sys_time->year);

/*  Release time/date memory */

     rqsendunits(clock_sem, 1, &status);

/*  If previous display request has finished(segment    */
/*  returned), then request new time/date be displayed  */

     rsp_seg = rqreceivemessage(rsp_mbx,
       NO_WAIT, NUL_TKN, &status);
     
     if (status != ETIME)
       {
       rqsendmessage(crt_req_mbx,
         req_seg, rsp_mbx, &status);
       }
     }
/*  Check for shutdown  */

   rqreceiveunits(clock_shtdwn_sem, 1, NO_WAIT, &status);
   }

/*  Retrieve outstanding display request, disable   */
/*  this interrupt task, signal shutdown complete   */
/*  and suspend execution.                          */

  req_seg = rqreceivemessage(rsp_mbx,
    WAIT_FOREVER, NUL_TKN, &status);
  
  rqsendunits(init_sem, 1, &status);
  
  rqresetinterrupt(CLOCK_INT_LEVEL, &status);
  
  rqsuspendtask(NUL_TKN, &status);  /* suspend this task  */
  }


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.