home *** CD-ROM | disk | FTP | other *** search
/ The C Users' Group Library 1994 August / wc-cdrom-cusersgrouplibrary-1994-08.iso / listings / v_09_01 / 9n01114a < prev    next >
Text File  |  1990-11-29  |  37KB  |  1,472 lines

  1. /*0*********************************************************
  2. *   Richard Carver                            July 1990
  3. *   This program demonstrates interprocess communication
  4. *   between several tasks using the iRMX II Operating
  5. *   System.
  6. *
  7. *   Two tasks are used for resource control.  The
  8. *   clock_task() controls use of the hardware clock and the
  9. *   crt_task() controls use of the display screen.
  10. *
  11. *   The count_task() competes with the clock_task() for
  12. *   use of the crt_task() services.  The timer_task() uses
  13. *   the services of the clock_task() for a software timer.
  14. *
  15. *   The initial task, main(), starts every thing up and,
  16. *   when the timer_task() finishes, shuts everything down.
  17. ***********************************************************/
  18.  
  19. #include <stdio.h>
  20. #include <stdlib.h>
  21. #include <string.h>
  22. #include <rmxc.h>         /* iRMX OS calls & structures   */
  23. #include <rmxerr.h>       /* iRMX status codes (defines)  */
  24. #include <delay.h>        /* Hardcoded delay loop macros  */
  25.  
  26. #define ERROR   (0xFFFF)    /* Error Condition  */
  27.  
  28. #define FALSE   (0)
  29. #define TRUE    (!FALSE)
  30.  
  31. #define MAX_CLOCK_RETRIES   (4)
  32.  
  33. /* The NULL token (similar to NULL pointer) */
  34.  
  35. #define NUL_TKN   ((selector)0)
  36.  
  37. /* Options for rqgettasktokens()  */
  38.  
  39. #define THIS_TASK (0x00)
  40. #define THIS_JOB  (0x01)
  41. #define PARAM_OBJ (0x02)
  42. #define ROOT_JOB  (0x03)
  43.  
  44. /* Options for rqcreatemailbox()  */
  45.  
  46. #define FIFO_OBJ_MBX      (0x0000)
  47. #define PRIORITY_OBJ_MBX  (0x0001)
  48. #define FIFO_DATA_MBX     (0x0020)
  49. #define PRIORITY_DATA_MBX (0x0021)
  50.  
  51. /* Options for rqcreatesemaphore()  */
  52.  
  53. #define FIFO_SEM      (0x0000)
  54. #define PRIORITY_SEM  (0x0001)
  55.  
  56. #define SEM_INIT_0    (0)
  57. #define SEM_MAX_1     (1)
  58.  
  59. /* Common wait times for iRMX System Calls  */
  60.  
  61. #define NO_WAIT       (0x0000)
  62. #define WAIT_FOREVER  (0xFFFF)
  63.  
  64. /* Options for rqcreatetask() */
  65.  
  66. #define NO_FLOAT    (0x0000)
  67. #define DEFAULT_STACK_SIZE    ((unsigned int)2048)
  68.  
  69. /* Task Priorities  */
  70.  
  71. #define CLOCK_TASK_PRIORITY   ((unsigned char)48)
  72. #define TIMER_TASK_PRIORITY       ((unsigned char)150)
  73. #define CRT_TASK_PRIORITY     ((unsigned char)200)
  74. #define COUNT_TASK_PRIORITY   ((unsigned char)200)
  75.  
  76. /* Defines For Hardware Clock */
  77. /* Clock Interrupt Level */
  78.  
  79. #define CLOCK_INT_LEVEL (0x26)
  80.  
  81. /* Clock Ports */
  82.  
  83. #define ADR_PORT  ((unsigned short)0xA060)
  84. #define DAT_PORT  ((unsigned short)0xA062)
  85. #define CNT_PORT  ((unsigned short)0xA064)
  86. #define PIO_PORT  ((unsigned short)0xA066)
  87.  
  88. /* Clock Port I/O Operations */
  89.  
  90. #define PIO_READ  ((unsigned char)0x82)
  91. #define PIO_WRITE ((unsigned char)0x80)
  92.  
  93. /* Clock Registers */
  94.  
  95. #define SEC01_REG (0x00)
  96. #define SEC10_REG (0x01)
  97. #define MIN01_REG (0x02)
  98. #define MIN10_REG (0x03)
  99. #define HRS01_REG (0x04)
  100. #define HRS10_REG (0x05)
  101. #define DAT01_REG (0x07)
  102. #define DAT10_REG (0x08)
  103. #define MON01_REG (0x09)
  104. #define MON10_REG (0x0A)
  105. #define YRS01_REG (0x0B)
  106. #define YRS10_REG (0x0C)
  107.  
  108. /* Clock Control Lines */
  109.  
  110. #define CNT_RST     ((unsigned char)0x00)
  111. #define INT_ENB     ((unsigned char)0x20)
  112. #define HOLD_HIGH   ((unsigned char)0x10)
  113. #define HOLD_READ   ((unsigned char)0x30)
  114. #define HOLD_WRITE  ((unsigned char)0x50)
  115.  
  116. /* Defines for months */
  117.  
  118. #define JANUARY     (1)
  119. #define FEBRUARY    (2)
  120. #define MARCH       (3)
  121. #define DECEMBER    (12)
  122.  
  123. /* Time/Date and Timers Data Structure  */
  124.  
  125. #define MAX_TIMERS    (10)
  126.  
  127. struct SYS_TIME_STR
  128.   {
  129.   unsigned char change_flg;
  130.   unsigned char second;
  131.   unsigned char minute;
  132.   unsigned char hour;
  133.   unsigned char date;
  134.   unsigned char month;
  135.   unsigned char year;
  136.  
  137.   struct TIMER_STR
  138.     {
  139.     unsigned char in_use;
  140.     unsigned int  count;
  141.     unsigned int  timeout;
  142.     selector    sem_tkn;
  143.     }timer[MAX_TIMERS];
  144.   };
  145.  
  146. /* Request Message format for the Display Task  */
  147.  
  148. struct CRT_REQ_STR
  149.   {
  150.   unsigned char *msg_ptr;
  151.   unsigned int  row;
  152.   unsigned int  col;
  153.   };
  154.  
  155. /* Object Catalog Names */
  156.  
  157. const unsigned char
  158.   clock_sem_name[] = "\011CLOCK_SEM",
  159.   init_sem_name[] = "\010INIT_SEM";
  160.  
  161. const unsigned char
  162.   time_seg_name[] = "\010TIME_SEG";
  163.  
  164. const unsigned char
  165.   crt_req_mbx_name[] = "\007CRT_MBX";
  166.  
  167. const unsigned char
  168.   crt_shtdwn_sem_name[] = "\012CRT_SHTDWN",
  169.   clock_shtdwn_sem_name[] = "\014CLOCK_SHTDWN",
  170.   count_shtdwn_sem_name[] = "\014COUNT_SHTDWN";
  171.  
  172. /* Days Per Month */
  173.  
  174. const unsigned char
  175.   days_in_month[12] =
  176.   {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
  177.  
  178. /* Global System Time/Date Structure */
  179.  
  180. selector  sys_time_seg;
  181. struct SYS_TIME_STR *sys_time;
  182.  
  183. /* Task declarations  */
  184.  
  185. void clock_task(void);
  186. void crt_task(void);
  187. void timer_task(void);
  188. void count_task(void);
  189.  
  190. /* Task objects (global for debugging purposes) */
  191.  
  192. selector    clock_tsk;
  193. selector    crt_tsk;
  194. selector    timer_tsk;
  195. selector    count_tsk;
  196.  
  197. /*1*********************************************************
  198. * Task:     main()  (the initial task)
  199. *
  200. * Summary:  Starts it all up and shuts it all down.
  201. *
  202. * Caveats:  None
  203. ***********************************************************/
  204.  
  205. void main(void)
  206.   {
  207.   selector  current_job;      /* current job            */
  208.   selector  init_sem;         /* initilaization sem     */
  209.   selector  clock_sem;        /* time/date shared memory*/
  210.   selector  clock_shtdwn_sem; /* clock_task() shutdown  */
  211.   selector  count_shtdwn_sem; /* count_task() shutdown  */
  212.   selector  crt_shtdwn_sem;   /* crt_task() shutdown    */
  213.   selector  sys_time_seg;     /* time/date data segment */
  214.  
  215.   unsigned int    status;     /* iRMX condition code    */
  216.  
  217.   struct EXCEPTIONSTRUCT  exceptioninfo;
  218.  
  219.  
  220. /*  Disable current task's exception handler  */
  221.  
  222.   rqgetexceptionhandler(&exceptioninfo, &status);
  223.   exceptioninfo.exceptionmode = 0;
  224.   rqsetexceptionhandler(&exceptioninfo, &status);
  225.  
  226. /*  Get Token For Current Job */
  227.  
  228.   current_job = rqgettasktokens(THIS_JOB, &status);
  229.  
  230. /*  Create and catalog the semaphore used for */
  231. /*  accessing the shared clock data.          */
  232.  
  233.   clock_sem = rqcreatesemaphore(SEM_INIT_0,
  234.     SEM_MAX_1, PRIORITY_SEM, &status);
  235.   rqcatalogobject(current_job,
  236.     clock_sem, &clock_sem_name, &status);
  237.  
  238. /*  Create and catalog the semaphore used for */
  239. /*  synchronizing tasks during initialization */
  240. /*  and shutdown.                             */
  241.  
  242.   init_sem = rqcreatesemaphore(SEM_INIT_0,
  243.     99, FIFO_SEM, &status);
  244.   rqcatalogobject(current_job,
  245.     init_sem, &init_sem_name, &status);
  246.  
  247. /*  Create and catalog the segment of memory  */
  248. /*  used to hold the shared time/date data.   */
  249.  
  250.   sys_time_seg = rqcreatesegment(
  251.     sizeof(struct SYS_TIME_STR), &status);
  252.   rqcatalogobject(current_job,
  253.     sys_time_seg, &time_seg_name, &status);
  254.  
  255. /*  Create and catalog the semaphores used to */
  256. /*  signal task shutdown.                     */
  257.  
  258.   clock_shtdwn_sem = rqcreatesemaphore(SEM_INIT_0,
  259.     SEM_MAX_1, FIFO_SEM, &status);
  260.   rqcatalogobject(current_job,
  261.     clock_shtdwn_sem, &clock_shtdwn_sem_name, &status);
  262.  
  263.   crt_shtdwn_sem = rqcreatesemaphore(SEM_INIT_0,
  264.     SEM_MAX_1, FIFO_SEM, &status);
  265.   rqcatalogobject(current_job,
  266.     crt_shtdwn_sem, &crt_shtdwn_sem_name, &status);
  267.  
  268.   count_shtdwn_sem = rqcreatesemaphore(SEM_INIT_0,
  269.     SEM_MAX_1, FIFO_SEM, &status);
  270.   rqcatalogobject(current_job,
  271.     count_shtdwn_sem, &count_shtdwn_sem_name, &status);
  272.  
  273. /*  Create the crt_task().  Wait for the task to  */
  274. /*  indicate it has completed initialization.     */
  275.  
  276.   crt_tsk = rqcreatetask(CRT_TASK_PRIORITY, &crt_task,
  277.     NUL_TKN, NULL, DEFAULT_STACK_SIZE, NO_FLOAT, &status);
  278.  
  279.   rqreceiveunits(init_sem, 1, WAIT_FOREVER, &status);
  280.  
  281. /*  Create the clock_task().  Wait for the task   */
  282. /*  to indicate it has completed initialization.  */
  283.  
  284.   clock_tsk = rqcreatetask(CLOCK_TASK_PRIORITY, &clock_task,
  285.     NUL_TKN, NULL, DEFAULT_STACK_SIZE, NO_FLOAT, &status);
  286.  
  287.   rqreceiveunits(init_sem, 1, WAIT_FOREVER, &status);
  288.  
  289. /*  Create the count_task().  Wait for the task   */
  290. /*  to indicate it has completed initialization.  */
  291.  
  292.   count_tsk = rqcreatetask(COUNT_TASK_PRIORITY, &count_task,
  293.     NUL_TKN, NULL, DEFAULT_STACK_SIZE, NO_FLOAT, &status);
  294.  
  295.   rqreceiveunits(init_sem, 1, WAIT_FOREVER, &status);
  296.  
  297. /*  Create the timer_task().  Wait for the task   */
  298. /*  to indicate it has completed initialization.  */
  299. /*  This also signals the shutdown process.       */
  300.  
  301.   timer_tsk = rqcreatetask(TIMER_TASK_PRIORITY, &timer_task,
  302.     NUL_TKN, NULL, DEFAULT_STACK_SIZE, NO_FLOAT, &status);
  303.  
  304.   rqreceiveunits(init_sem, 1, WAIT_FOREVER, &status);
  305.  
  306. /*  Begin shutdown by first disabling any further   */
  307. /*  interrupts from the hardware clock.  This task  */
  308. /*  suspends itself for 2/100th of a second to      */
  309. /*  ensure interrupt is disabled.                   */
  310.  
  311.   rqdisable(CLOCK_INT_LEVEL, &status);
  312.   rqsleep(2, &status);
  313.  
  314. /*  Send shutdown signals to all tasks and wait */
  315. /*  for all tasks to complete shutdown.         */
  316.  
  317.   rqsendunits(count_shtdwn_sem, 1, &status);
  318.   rqsendunits(crt_shtdwn_sem, 1, &status);
  319.   rqsendunits(clock_shtdwn_sem, 1, &status);
  320.  
  321.   rqreceiveunits(init_sem, 3, WAIT_FOREVER, &status);
  322.  
  323. /*  Cleanup before terminating.  This includes  */
  324. /*  deleting tasks and uncataloging/deleteing   */
  325. /*  all objects created.                        */
  326.  
  327.   rqdeletetask(crt_tsk, &status);
  328.   rqdeletetask(clock_tsk, &status);
  329.   rqdeletetask(count_tsk, &status);
  330.   rqdeletetask(timer_tsk, &status);
  331.  
  332.   rquncatalogobject(current_job,
  333.     &clock_sem_name, &status);
  334.   rquncatalogobject(current_job,
  335.     &init_sem_name, &status);
  336.   rquncatalogobject(current_job,
  337.     &time_seg_name, &status);
  338.   rquncatalogobject(current_job,
  339.     &crt_req_mbx_name, &status);
  340.  
  341.   rquncatalogobject(current_job,
  342.     &clock_shtdwn_sem_name, &status);
  343.   rquncatalogobject(current_job,
  344.     &count_shtdwn_sem_name, &status);
  345.   rquncatalogobject(current_job,
  346.     &crt_shtdwn_sem_name, &status);
  347.  
  348.   rqdeletesemaphore(clock_sem, &status);
  349.   rqdeletesemaphore(init_sem, &status);
  350.  
  351.   rqdeletesemaphore(clock_shtdwn_sem, &status);
  352.   rqdeletesemaphore(count_shtdwn_sem, &status);
  353.   rqdeletesemaphore(crt_shtdwn_sem, &status);
  354.  
  355.   rqdeletesegment(sys_time_seg, &status);
  356.  
  357.   cursor_on();
  358.   printf("\nAll Done!\n");
  359.  
  360.   exit(0);
  361.   }
  362.  
  363. /*1*********************************************************
  364. * Function: start_timer
  365. *
  366. * Summary:  Create and start a timer.
  367. *
  368. * Invocation: timer_sem = start_timer(timeout);
  369. *
  370. * Inputs:   timeout (unsigned int) - timer interval
  371. *             in seconds
  372. *
  373. * Outputs:  timer_sem (selector) - the semaphore that will
  374. *             receive a unit at each timer interval
  375. *
  376. * Caveats:  Accesses the time/date data segment that is
  377. *           shared with the clock_task().
  378. ***********************************************************/
  379.  
  380. selector start_timer(unsigned int timeout)
  381.   {
  382.   unsigned int    status;
  383.   unsigned int    indx;
  384.   unsigned int    done;
  385.   unsigned int    no_more_timers;
  386.  
  387.   selector    timer_sem;
  388.   selector    current_job;
  389.   selector    clock_sem;
  390.   selector    sys_time_seg;
  391.  
  392.   struct SYS_TIME_STR   *sys_time;
  393.  
  394.   indx = 0;
  395.   done = FALSE;
  396.   no_more_timers = TRUE;
  397.   timer_sem = NUL_TKN;
  398.  
  399. /*  Get objects for access semaphore and  */
  400. /*  time/date data segment                */
  401.  
  402.   current_job = rqgettasktokens(THIS_JOB, &status);
  403.  
  404.   clock_sem = rqlookupobject(current_job,
  405.     &clock_sem_name, NO_WAIT, &status);
  406.   sys_time_seg = rqlookupobject(current_job,
  407.     &time_seg_name, NO_WAIT, &status);
  408.  
  409.   sys_time = buildptr(sys_time_seg, 0);
  410.  
  411. /* Access time/date memory  */
  412.  
  413.   rqreceiveunits(clock_sem, 1, WAIT_FOREVER, &status);
  414.  
  415. /*  Find an empty timer slot  */
  416.  
  417.   while (!done)
  418.     {
  419.     if (!sys_time->timer[indx].in_use)
  420.       {
  421.       done = TRUE;
  422.       no_more_timers = FALSE;
  423.       }
  424.     else
  425.       {
  426.       indx++;
  427.  
  428.       if (indx == MAX_TIMERS)
  429.         {
  430.         done = TRUE;
  431.         }
  432.       }
  433.     }
  434.  
  435. /* Establish a timer */
  436.  
  437.   if (no_more_timers == FALSE)
  438.     {
  439.     if (timeout != 0)
  440.       {
  441.       timer_sem = rqcreatesemaphore(SEM_INIT_0,
  442.         999, FIFO_SEM, &status);
  443.       sys_time->timer[indx].in_use = TRUE;
  444.       sys_time->timer[indx].count = 0;
  445.       sys_time->timer[indx].timeout = timeout;
  446.       sys_time->timer[indx].sem_tkn = timer_sem;
  447.       }
  448.     }
  449.  
  450. /*  Release time/date memory */
  451.  
  452.   rqsendunits(clock_sem, 1, &status);
  453.  
  454. /*  Return timer semaphore to caller  */
  455.  
  456.   return(timer_sem);
  457.   }
  458.  
  459. /*1*********************************************************
  460. * Function: stop_timer
  461. *
  462. * Summary:  Delete a timer.
  463. *
  464. * Invocation: timer_sem = start_timer(timeout);
  465. *
  466. * Inputs:   timeout (unsigned int) - timer interval
  467. *             in seconds
  468. *
  469. * Outputs:  timer_sem (selector) - the semaphore that will
  470. *             receive a unit at each timer interval
  471. *
  472. * Caveats:  Accesses the time/date data segment that is
  473. *           shared with the clock_task().
  474. ***********************************************************/
  475.  
  476. unsigned int stop_timer(selector timer_sem)
  477.   {
  478.   unsigned int    status;
  479.   unsigned int    indx;
  480.   unsigned int    done;
  481.   unsigned int    invalid_timer;
  482.  
  483.   selector    current_job;
  484.   selector    clock_sem;
  485.   selector    sys_time_seg;
  486.  
  487.   struct SYS_TIME_STR   *sys_time;
  488.  
  489.  
  490.   indx = 0;
  491.   done = FALSE;
  492.   invalid_timer = TRUE;
  493.  
  494. /*  Get objects for access semaphore and  */
  495. /*  time/date data segment                */
  496.  
  497.   current_job = rqgettasktokens(THIS_JOB, &status);
  498.  
  499.   clock_sem = rqlookupobject(current_job,
  500.     &clock_sem_name, NO_WAIT, &status);
  501.   sys_time_seg = rqlookupobject(current_job,
  502.     &time_seg_name, NO_WAIT, &status);
  503.  
  504.   sys_time = buildptr(sys_time_seg, 0);
  505.  
  506. /*  Access time/date memory.  */
  507.  
  508.   rqreceiveunits(clock_sem, 1, WAIT_FOREVER, &status);
  509.  
  510. /*  Locate and remove this timer  */
  511.  
  512.   while (!done)
  513.     {
  514.     if (sys_time->timer[indx].in_use &&
  515.       (sys_time->timer[indx].sem_tkn == timer_sem))
  516.       {
  517.       rqdeletesemaphore(timer_sem, &status);
  518.       sys_time->timer[indx].in_use = FALSE;
  519.       sys_time->timer[indx].sem_tkn = NUL_TKN;
  520.       sys_time->timer[indx].count = 0;
  521.       sys_time->timer[indx].timeout = 0;
  522.       done = TRUE;
  523.       invalid_timer = FALSE;
  524.       }
  525.     else
  526.       {
  527.       indx++;
  528.  
  529.       if (indx == MAX_TIMERS)
  530.         {
  531.         done = TRUE;
  532.         }
  533.       }
  534.     }
  535.  
  536. /*  Release time/date memory  */
  537.  
  538.   rqsendunits(clock_sem, 1, &status);
  539.  
  540. /*  Return with result of call  */
  541.  
  542.   return (invalid_timer ? ERROR : EOK);
  543.   }
  544.  
  545.  
  546. /*1*********************************************************
  547. * Task:     count_task
  548. *
  549. * Summary:  Continuously counts and displays a value from
  550. *           0-65535.
  551. *
  552. * Caveats:  Communicates with the crt_task() for displaying
  553. *           counter values.
  554. ***********************************************************/
  555.  
  556. void count_task(void)
  557.   {
  558.   unsigned char counter_buf[6];
  559.  
  560.   selector  current_job;
  561.   selector  rsp_mbx;
  562.   selector  crt_req_mbx;
  563.   selector  init_sem;
  564.   selector  count_shtdwn_sem;
  565.   selector  req_seg;
  566.  
  567.   unsigned int  counter;
  568.   unsigned int    status;
  569.  
  570.   struct CRT_REQ_STR    *req_msg;
  571.  
  572.   struct EXCEPTIONSTRUCT    exceptioninfo;
  573.  
  574. /*  Disable this task's exception handler */
  575.  
  576.   rqgetexceptionhandler(&exceptioninfo, &status);
  577.   exceptioninfo.exceptionmode = 0;
  578.   rqsetexceptionhandler(&exceptioninfo, &status);
  579.  
  580. /*  Lookup/Create needed objects */
  581.  
  582.   current_job = rqgettasktokens(THIS_JOB, &status);
  583.  
  584.   init_sem = rqlookupobject(current_job,
  585.     &init_sem_name, NO_WAIT, &status);
  586.  
  587.   count_shtdwn_sem = rqlookupobject(current_job,
  588.     &count_shtdwn_sem_name, NO_WAIT, &status);
  589.  
  590.   rsp_mbx = rqcreatemailbox(FIFO_OBJ_MBX, &status);
  591.  
  592.   crt_req_mbx = rqlookupobject(current_job,
  593.     &crt_req_mbx_name, NO_WAIT, &status);
  594.  
  595. /*  Initialize request message */
  596.  
  597.   req_seg = rqcreatesegment(
  598.     sizeof(struct CRT_REQ_STR), &status);
  599.  
  600.   req_msg = buildptr(req_seg, 0);
  601.  
  602.   req_msg->msg_ptr = counter_buf;
  603.   req_msg->row = 12;
  604.   req_msg->col = 38;
  605.  
  606. /*  Singal that initialization is completed.  */
  607.  
  608.   rqsendunits(init_sem, 1, &status);
  609.  
  610. /*  Continuously count 0-65535 and display the  */
  611. /*  value until signalled to stop.              */
  612.  
  613.   counter = 0;
  614.  
  615.   rqreceiveunits(count_shtdwn_sem, 1, NO_WAIT, &status);
  616.  
  617.   while (status == ETIME)   /* while no signal received */
  618.     {
  619.     sprintf(counter_buf, "%d", counter);
  620.     rqsendmessage(crt_req_mbx,
  621.       req_seg, rsp_mbx, &status);
  622.     req_seg = rqreceivemessage(rsp_mbx,
  623.       WAIT_FOREVER, NUL_TKN, &status);
  624.     counter++;
  625.     rqreceiveunits(count_shtdwn_sem, 1, NO_WAIT, &status);
  626.     }
  627.  
  628. /*  Cleanup objects created by this task. */
  629.  
  630.   rqdeletemailbox(rsp_mbx, &status);
  631.   rqdeletesegment(req_seg, &status);
  632.  
  633. /*  Signal shutdown complete and suspend execution. */
  634.  
  635.   rqsendunits(init_sem, 1, &status);
  636.  
  637.   rqsuspendtask(NUL_TKN, &status);  /* suspend this task  */
  638.   }
  639.  
  640. /*1*********************************************************
  641. * Task:     timer_task
  642. *
  643. * Summary:  
  644. *
  645. * Caveats:  
  646. ***********************************************************/
  647.  
  648. void timer_task(void)
  649.   {
  650.   selector    current_job;
  651.   selector    init_sem;
  652.   selector    clock_sem;
  653.   selector    timer_sem;
  654.  
  655.   unsigned int    status;
  656.  
  657.   struct EXCEPTIONSTRUCT    exceptioninfo;
  658.  
  659. /*  Disable this task's exception handler */
  660.  
  661.   rqgetexceptionhandler(&exceptioninfo, &status);
  662.   exceptioninfo.exceptionmode = 0;
  663.   rqsetexceptionhandler(&exceptioninfo, &status);
  664.  
  665. /*  Lookup needed objects */
  666.  
  667.   current_job = rqgettasktokens(THIS_JOB, &status);
  668.  
  669.   init_sem = rqlookupobject(current_job,
  670.     &init_sem_name, NO_WAIT, &status);
  671.  
  672. /*  Start a one-second timer.  If the timer was     */
  673. /*  created, wait for 20 seconds (units) then stop  */
  674. /*  stop the timer.                                 */
  675.  
  676.   timer_sem = start_timer(1);
  677.  
  678.   if (timer_sem != NUL_TKN)
  679.     {
  680.     rqreceiveunits(timer_sem, 20, WAIT_FOREVER, &status);
  681.     stop_timer(timer_sem);
  682.     }
  683.  
  684. /*  Signal task finished and suspend execution.   */
  685.  
  686.   rqsendunits(init_sem, 1, &status);
  687.  
  688.   rqsuspendtask(NUL_TKN, &status);  /* suspend this task  */
  689.   }
  690.  
  691. /*1*********************************************************
  692. * Functions:  clrscrn, cursor_on, cursor_off, print_at
  693. *
  694. * Summary:  Functions for basic display control.
  695. *
  696. * Invocation:   clrscrn();
  697. *               curson_on();
  698. *               cursoff_on();
  699. *
  700. * Invocation:   print_at(row, col, msg_ptr)
  701. *
  702. * Inputs: row (unsigned int) - row position
  703. *         col (unsigned int) - column position
  704. *         msg_ptr (unsigned char *) - message string pointer
  705. *
  706. * Caveats:  Escape sequences are for a WYSE60 terminal.
  707. ***********************************************************/
  708.  
  709. void clrscrn(void)
  710.   {
  711.   fprintf(stdout, "\x1B+");
  712.   fflush(stdout);
  713.   return;
  714.   }
  715.  
  716. void cursor_on(void)
  717.   {
  718.   fprintf(stdout, "\x1B`1");
  719.   fflush(stdout);
  720.   return;
  721.   }
  722.  
  723. void cursor_off(void)
  724.   {
  725.   fprintf(stdout, "\x1B`0");
  726.   fflush(stdout);
  727.   return;
  728.   }
  729.  
  730. void print_at(unsigned int row,
  731.   unsigned int col, unsigned char *msg_ptr)
  732.   {
  733.   fprintf(stdout, "\x1B=%c%c%s",
  734.     (row + 31), (col + 31), msg_ptr);
  735.   fflush(stdout);
  736.   return;
  737.   }
  738.  
  739. /*1*********************************************************
  740. * Task:     crt_task
  741. *
  742. * Summary:  
  743. *
  744. * Caveats:  
  745. ***********************************************************/
  746.  
  747. void crt_task(void)
  748.   {
  749.   selector    current_job;
  750.   selector    req_mbx;
  751.   selector    rsp_mbx;
  752.   selector    req_seg;
  753.   selector    init_sem;
  754.   selector    crt_shtdwn_sem;
  755.  
  756.   unsigned int    status;
  757.  
  758.   struct CRT_REQ_STR    *req_msg;
  759.  
  760.   struct EXCEPTIONSTRUCT    exceptioninfo;
  761.  
  762. /*  Disable this task's exception handler */
  763.  
  764.   rqgetexceptionhandler(&exceptioninfo, &status);
  765.   exceptioninfo.exceptionmode = 0;
  766.   rqsetexceptionhandler(&exceptioninfo, &status);
  767.  
  768. /*  Lookup/Create needed objects */
  769.  
  770.   current_job = rqgettasktokens(THIS_JOB, &status);
  771.  
  772.   init_sem = rqlookupobject(current_job,
  773.     &init_sem_name, WAIT_FOREVER, &status);
  774.  
  775.   crt_shtdwn_sem = rqlookupobject(current_job,
  776.     &crt_shtdwn_sem_name, WAIT_FOREVER, &status);
  777.  
  778.   req_mbx = rqcreatemailbox(FIFO_OBJ_MBX, &status);
  779.   rqcatalogobject(current_job,
  780.     req_mbx, &crt_req_mbx_name, &status);
  781.   
  782. /*  Initialize display  */
  783.  
  784.   cursor_off();
  785.   clrscrn();
  786.  
  787. /*  Signal initialization complete  */
  788.  
  789.   rqsendunits(init_sem, 1, &status);
  790.  
  791.  
  792.   rqreceiveunits(crt_shtdwn_sem, 1, NO_WAIT, &status);
  793.  
  794.   while (status == ETIME)
  795.     {
  796.     req_seg = rqreceivemessage(req_mbx,
  797.       WAIT_FOREVER, &rsp_mbx, &status);
  798.  
  799.     req_msg = buildptr(req_seg, 0);
  800.  
  801.     print_at(req_msg->row,
  802.       req_msg->col, req_msg->msg_ptr);
  803.  
  804.     if (rsp_mbx != NUL_TKN)
  805.       {
  806.       rqsendmessage(rsp_mbx, req_seg, NUL_TKN, &status);
  807.       }
  808.     else
  809.       {
  810.       rqdeletesegment(req_seg, &status);
  811.       }
  812.  
  813.     rqreceiveunits(crt_shtdwn_sem, 1, NO_WAIT, &status);
  814.     }
  815.  
  816.   rqsendunits(init_sem, 1, &status);
  817.  
  818.   for(;;)
  819.     {
  820.     req_seg = rqreceivemessage(req_mbx,
  821.       WAIT_FOREVER, &rsp_mbx, &status);
  822.  
  823.     if (rsp_mbx != NUL_TKN)
  824.       {
  825.       rqsendmessage(rsp_mbx, req_seg, NUL_TKN, &status);
  826.       }
  827.     else
  828.       {
  829.       rqdeletesegment(req_seg, &status);
  830.       }
  831.     }
  832.   }
  833.  
  834. /*1*********************************************************
  835. * Function: read_time
  836. *
  837. * Summary:  Read a specified time/date register of the
  838. *           hardware clock.
  839. *
  840. * Invocation: in_byte = read_time(reg);
  841. *
  842. * Inputs:   reg (unsigned char) - the hardware time/date
  843. *             register to be read
  844. *
  845. * Outputs:  in_byte (unsigned char) - the value of the
  846. *             register read
  847. *
  848. * Caveats:  None
  849. ***********************************************************/
  850.  
  851. unsigned char read_time(unsigned char reg)
  852.   {
  853.   unsigned char in_byte;
  854.  
  855.   outbyte(CNT_PORT, HOLD_HIGH);
  856.   delay(3);
  857.   outbyte(CNT_PORT, HOLD_READ);
  858.   delay(1);
  859.   outbyte(ADR_PORT, reg);
  860.   delay(1);
  861.   in_byte = inbyte(DAT_PORT);
  862.   outbyte(CNT_PORT, CNT_RST);
  863.   delay(1);
  864.  
  865.   return (in_byte);
  866.   }
  867.  
  868. /*1*********************************************************
  869. * Function: write_time
  870. *
  871. * Summary:  Write a byte to the specified time/date register
  872. *           of the hardware clock.
  873. *
  874. * Invocation: write_time(reg, out_byte);
  875. *
  876. * Inputs:   reg (unsigned char) - the hardware time/date
  877. *             register
  878. *
  879. *           out_byte (unsigned char) - the value to be
  880. *             written to the hardware clock register
  881. *
  882. * Caveats:  None
  883. ***********************************************************/
  884.  
  885. void write_time(unsigned char reg, unsigned char out_byte)
  886.   {
  887.   outbyte(CNT_PORT, HOLD_HIGH);
  888.   delay(3);
  889.   outbyte(ADR_PORT, reg);
  890.   delay(1);
  891.   outbyte(DAT_PORT, out_byte);
  892.   delay(1);
  893.   outbyte(CNT_PORT, HOLD_WRITE);
  894.   delay(1);
  895.   outbyte(CNT_PORT, CNT_RST);
  896.   delay(1);
  897.  
  898.   return;
  899.   }
  900.  
  901. /*1*********************************************************
  902. * Function: read_clock
  903. *
  904. * Summary:  Read the time/date from the hardware clock and
  905. *           stores it in the global time/date structure.
  906. *
  907. * Invocation: read_clock();
  908. *
  909. * Caveats:  Requires the caller to obtain access the shared
  910. *           time/date data segment.
  911. ***********************************************************/
  912.  
  913. void read_clock(void)
  914.   {
  915.   unsigned char time_buf[13];
  916.   unsigned int  i;
  917.  
  918. /*  Set mode  */
  919.  
  920.   outbyte(PIO_PORT, PIO_READ);
  921.   delay(1);
  922.  
  923. /*  Set control lines to disable interrupts */
  924.  
  925.   outbyte(CNT_PORT, CNT_RST);
  926.   delay(1);
  927.  
  928. /*  Read time/date  */
  929.  
  930.   for (i = 0 ; (i <= 12); i++)
  931.     {
  932.     time_buf[i] = read_time(i);
  933.     }
  934.  
  935.   sys_time->second =
  936.     (10 * (time_buf[SEC10_REG] & 0x07)) +
  937.     (time_buf[SEC01_REG] & 0x0F);
  938.   sys_time->minute =
  939.     (10 * (time_buf[MIN10_REG] & 0x07)) +
  940.     (time_buf[MIN01_REG] & 0x0F);
  941.   sys_time->hour =
  942.     (10 * (time_buf[HRS10_REG] & 0x03)) +
  943.     (time_buf[HRS01_REG] & 0x0F);
  944.   sys_time->date =
  945.     (10 * (time_buf[DAT10_REG] & 0x03)) +
  946.     (time_buf[DAT01_REG] & 0x0F);
  947.   sys_time->month =
  948.     (10 * (time_buf[MON10_REG] & 0x01)) +
  949.     (time_buf[MON01_REG] & 0x0F);
  950.   sys_time->year =
  951.     (10 * (time_buf[YRS10_REG] & 0x0F)) +
  952.     (time_buf[YRS01_REG] & 0x0F);
  953.  
  954. /*  Reset control lines to enable interrupts */
  955.  
  956.   outbyte(CNT_PORT, INT_ENB);
  957.   delay(1);
  958.  
  959.   outbyte(ADR_PORT, 0x0F);
  960.   delay(1);
  961.  
  962.   return;
  963.   }
  964.  
  965. /*1*********************************************************
  966. * Function: write_clock
  967. *
  968. * Summary:  Uses the global time/date structure to write a
  969. *           new time/date to the hardware clock.
  970. *
  971. * Invocation: write_clock();
  972. *
  973. * Caveats:  Requires the caller to obtain access the shared
  974. *           time/date data segment.
  975. ***********************************************************/
  976.  
  977. void write_clock(void)
  978.   {
  979.   unsigned char temp_byte;
  980.  
  981. /*  Set mode  */
  982.  
  983.   outbyte(PIO_PORT, PIO_WRITE);
  984.   delay(1);
  985.  
  986. /*  Set control lines to disable interrupts */
  987.  
  988.   outbyte(CNT_PORT, CNT_RST);
  989.   delay(1);
  990.  
  991. /*  Write time/date.  Seconds always set to 0 */
  992.  
  993.   sys_time->second = 0;
  994.   write_time(SEC01_REG, 0);
  995.   write_time(SEC10_REG, 0);
  996.  
  997.   temp_byte =
  998.     sys_time->minute - (10 * (sys_time->minute / 10));
  999.   write_time(MIN01_REG, temp_byte);
  1000.   temp_byte = sys_time->minute / 10;
  1001.   write_time(MIN10_REG, temp_byte);
  1002.  
  1003.   temp_byte =
  1004.     sys_time->hour - (10 * (sys_time->hour / 10));
  1005.   write_time(HRS01_REG, temp_byte);
  1006.   temp_byte =
  1007.     (sys_time->hour / 10) | 0x08;     /* 24hr format */
  1008.   write_time(HRS10_REG, temp_byte);
  1009.  
  1010.   temp_byte =
  1011.     sys_time->date - (10 * (sys_time->date / 10));
  1012.   write_time(DAT01_REG, temp_byte);
  1013.   temp_byte = sys_time->date / 10;
  1014.   write_time(DAT10_REG, temp_byte);
  1015.  
  1016.   temp_byte =
  1017.     sys_time->month - (10 * (sys_time->month / 10));
  1018.   write_time(MON01_REG, temp_byte);
  1019.   temp_byte = sys_time->month / 10;
  1020.   write_time(MON10_REG, temp_byte);
  1021.  
  1022.   temp_byte =
  1023.     sys_time->year - (10 * (sys_time->year / 10));
  1024.   write_time(YRS01_REG, temp_byte);
  1025.   temp_byte = sys_time->year / 10;
  1026.   write_time(YRS10_REG, temp_byte);
  1027.  
  1028. /*  Reset mode  */
  1029.  
  1030.   outbyte(PIO_PORT, PIO_READ);
  1031.   delay(1);
  1032.  
  1033. /*  Reset control lines to enable interrupts  */
  1034.  
  1035.   outbyte(CNT_PORT, INT_ENB);
  1036.   delay(1);
  1037.  
  1038.   outbyte(ADR_PORT, 0x0F);
  1039.   delay(1);
  1040.  
  1041.   return;
  1042.   }
  1043.  
  1044. /*1*********************************************************
  1045. * Function: reset_timers
  1046. *
  1047. * Summary:  Initializes (zero out) all timer slots of the
  1048. *           shared time/date data segment.
  1049. *
  1050. * Invocation: reset_timers();
  1051. *
  1052. * Caveats:  Requires the caller to obtain access the shared
  1053. *           time/date data segment.
  1054. ***********************************************************/
  1055.  
  1056. void reset_timers(void)
  1057.   {
  1058.   unsigned int    i;
  1059.  
  1060.   for (i = 0 ; i < MAX_TIMERS; i++)
  1061.     {
  1062.     sys_time->timer[i].in_use = FALSE;
  1063.     sys_time->timer[i].sem_tkn = NUL_TKN;
  1064.     sys_time->timer[i].count = 0;
  1065.     sys_time->timer[i].timeout = 0;
  1066.     }
  1067.  
  1068.   return;
  1069.   }
  1070.  
  1071. /*1*********************************************************
  1072. * Function: advance_timers
  1073. *
  1074. * Summary:  Increments all active timers in the shared
  1075. *           time/date data segment by one second.
  1076. *
  1077. * Invocation: advance_timers();
  1078. *
  1079. * Caveats:  Requires the caller to obtain access the shared
  1080. *           time/date data segment.
  1081. ***********************************************************/
  1082.  
  1083. void advance_timers(void)
  1084.   {
  1085.   unsigned int    i;
  1086.   unsigned int    status;
  1087.  
  1088.  
  1089.   for (i = 0 ; i < MAX_TIMERS; i++)
  1090.     {
  1091.     if (sys_time->timer[i].in_use)
  1092.       {
  1093.       sys_time->timer[i].count++;
  1094.  
  1095.       if (sys_time->timer[i].count >=
  1096.         sys_time->timer[i].timeout)
  1097.         {
  1098.         rqsendunits(
  1099.           sys_time->timer[i].sem_tkn, 1, &status);
  1100.         sys_time->timer[i].count = 0;
  1101.         }
  1102.       }
  1103.     }
  1104.  
  1105.   return;
  1106.   }
  1107.  
  1108. /*1*********************************************************
  1109. * Function: advance_time_date
  1110. *
  1111. * Summary:  Increments the time/date (hh:mm:ss mm/dd/yy) in
  1112. *           the shared time/date data segment by one second.
  1113. *
  1114. * Invocation: advance_time_date();
  1115. *
  1116. * Caveats:  Requires the caller to obtain access the shared
  1117. *           time/date data segment.
  1118. ***********************************************************/
  1119.  
  1120. void advance_time_date(void)
  1121.   {
  1122.   if (sys_time->second < 59)
  1123.     {
  1124.     sys_time->second++;
  1125.     }
  1126.   else
  1127.     {
  1128.     sys_time->second = 0;
  1129.  
  1130.     if (sys_time->minute < 59)
  1131.       {
  1132.       sys_time->minute++;
  1133.       }
  1134.     else
  1135.       {
  1136.       sys_time->minute = 0;
  1137.  
  1138.       if (sys_time->hour < 23)
  1139.         {
  1140.         sys_time->hour++;
  1141.         }
  1142.       else
  1143.         {
  1144.         sys_time->hour = 0;
  1145.  
  1146.         if (sys_time->date <
  1147.           days_in_month[sys_time->month - 1])
  1148.           {
  1149.           sys_time->date++;
  1150.           }
  1151.         else
  1152.           {
  1153.           if (sys_time->month == FEBRUARY)
  1154.             {
  1155. /*  Check for leap year (only good through 2100)  */
  1156.  
  1157.             if (((sys_time->year % 4) == 0) &&
  1158.               (sys_time->date == 28))
  1159.               {
  1160.               sys_time->date = 29;
  1161.               }
  1162.             else
  1163.               {
  1164.               sys_time->month = MARCH;
  1165.               sys_time->date = 1;
  1166.               }
  1167.             }
  1168.           else
  1169.             {
  1170.             sys_time->date = 1;
  1171.  
  1172.             if (sys_time->month < DECEMBER)
  1173.               {
  1174.               sys_time->month++;
  1175.               }
  1176.             else
  1177.               {
  1178.               sys_time->month = JANUARY;
  1179.  
  1180.               if (sys_time->year < 99)
  1181.                 {
  1182.                 sys_time->year++;
  1183.                 }
  1184.               else
  1185.                 {
  1186.                 sys_time->year = 0;
  1187.                 }
  1188.               }
  1189.             }
  1190.           }
  1191.         }
  1192.       }
  1193.     }
  1194.  
  1195.   return;
  1196.   }
  1197.  
  1198. /*1*********************************************************
  1199. * Function: start_timer
  1200. *
  1201. * Summary:  Create and start a timer.
  1202. *
  1203. * Invocation: timer_sem = start_timer(timeout);
  1204. *
  1205. * Inputs:   timeout (unsigned int) - timer interval
  1206. *             in seconds
  1207. *
  1208. * Outputs:  timer_sem (selector) - the semaphore that will
  1209. *             receive a unit at each timer interval
  1210. *
  1211. * Caveats:  The interrupt handler will signal the
  1212. *           interrupt task to finish servicing the
  1213. *           interrupt.
  1214. ***********************************************************/
  1215.  
  1216. /*  Tell the compiler that this function is an interrupt  */
  1217. /*  routine so it can generate proper code for entering   */
  1218. /*  and exiting the interrupt routine.                    */
  1219.  
  1220. #pragma interrupt("clockint_handler")
  1221.  
  1222. void clockint_handler(void)
  1223.   {
  1224.   unsigned int  status;
  1225.  
  1226. /*  Signal the interrupt task */
  1227.  
  1228.   rqsignalinterrupt(CLOCK_INT_LEVEL, &status);
  1229.  
  1230.   return;
  1231.   }
  1232.  
  1233. /*1*********************************************************
  1234. * Task:     clock_task
  1235. *
  1236. * Summary:  Services interrupts from the hardware clock.
  1237. *           Also maintains/updates the global time/date
  1238. *           data and services software timers.
  1239. *
  1240. * Caveats:  Communicates with the crt_task() to display the
  1241. *           current time/date.
  1242. ***********************************************************/
  1243.  
  1244. void clock_task(void)
  1245.   {
  1246.   char    time_string[19];
  1247.  
  1248.   unsigned int  status;
  1249.   unsigned int  retry;
  1250.  
  1251.   selector  current_job;
  1252.   selector  init_sem;
  1253.   selector  clock_sem;
  1254.   selector  clock_shtdwn_sem;
  1255.   selector  rsp_seg;
  1256.   selector  req_seg;
  1257.   selector  crt_req_mbx;
  1258.   selector  rsp_mbx;
  1259.  
  1260.   struct CRT_REQ_STR    *req_msg;
  1261.  
  1262.   struct EXCEPTIONSTRUCT    exceptioninfo;
  1263.  
  1264. /*  Disable this task's exception handler */
  1265.  
  1266.   rqgetexceptionhandler(&exceptioninfo, &status);
  1267.   exceptioninfo.exceptionmode = 0;
  1268.   rqsetexceptionhandler(&exceptioninfo, &status);
  1269.  
  1270. /*  Disable any current interrupt task  */
  1271.  
  1272.   rqresetinterrupt(CLOCK_INT_LEVEL, &status);
  1273.  
  1274. /*  Lookup/Create needed objects */
  1275.  
  1276.   current_job = rqgettasktokens(THIS_JOB, &status);
  1277.  
  1278.   init_sem = rqlookupobject(current_job,
  1279.     &init_sem_name, NO_WAIT, &status);
  1280.  
  1281.   clock_sem = rqlookupobject(current_job,
  1282.     &clock_sem_name, NO_WAIT, &status);
  1283.  
  1284.   sys_time_seg = rqlookupobject(current_job,
  1285.     &time_seg_name, NO_WAIT, &status);
  1286.  
  1287.   clock_shtdwn_sem = rqlookupobject(current_job,
  1288.     &clock_shtdwn_sem_name, NO_WAIT, &status);
  1289.  
  1290.   crt_req_mbx = rqlookupobject(current_job,
  1291.     &crt_req_mbx_name, NO_WAIT, &status);
  1292.  
  1293.   sys_time = buildptr(sys_time_seg, 0);
  1294.  
  1295.   rsp_mbx = rqcreatemailbox(FIFO_OBJ_MBX, &status);
  1296.  
  1297. /*  Initialize time string buffer */
  1298.  
  1299.   memset(&time_string, ' ', sizeof(time_string));
  1300.  
  1301. /*  Initialize crt_task() request message and */
  1302. /*  send it to the response mailbox to setup  */
  1303. /*  for the processing loop.                  */
  1304.  
  1305.   req_seg = rqcreatesegment(
  1306.     sizeof(struct CRT_REQ_STR), &status);
  1307.   req_msg = buildptr(req_seg, 0);
  1308.  
  1309.   req_msg->msg_ptr = &time_string;
  1310.   req_msg->row = 1;
  1311.   req_msg->col = 60;
  1312.  
  1313.   rqsendmessage(rsp_mbx, req_seg, NUL_TKN, &status);
  1314.  
  1315. /*  Initialize retry counter and timers */
  1316.  
  1317.   retry = 0;
  1318.   sys_time->change_flg = FALSE;
  1319.   reset_timers();
  1320.  
  1321. /*  Initialize and read hardware clock  */
  1322.  
  1323.   outbyte(PIO_PORT, PIO_READ);
  1324.   delay(1);
  1325.  
  1326.   outbyte(CNT_PORT, INT_ENB);
  1327.   delay(1);
  1328.  
  1329.   outbyte(ADR_PORT, 0x0F);
  1330.   delay(1);
  1331.  
  1332.   read_clock();
  1333.  
  1334. /*  Allow access to time/date memory  */
  1335.  
  1336.   rqsendunits(clock_sem, 1, &status);
  1337.  
  1338. /*  Set interrupt vector  */
  1339.  
  1340.   rqsetinterrupt(CLOCK_INT_LEVEL, 1,
  1341.     &clockint_handler, NUL_TKN, &status);
  1342.  
  1343. /*  Signal initialization complete  */
  1344.  
  1345.   rqsendunits(init_sem, 1, &status);
  1346.  
  1347. /*  Process interrupt signals until shutdown  */
  1348.  
  1349.   rqreceiveunits(clock_shtdwn_sem, 1, NO_WAIT, &status);
  1350.  
  1351.   while (status == ETIME)   /* while no signal received */
  1352.     {
  1353. /*  Wait no longer than 2 seconds for interrupt signal  */
  1354.  
  1355.     rqetimedinterrupt(CLOCK_INT_LEVEL, 200, &status);
  1356.  
  1357. /*  If an interrupt is NOT received, attempt to reset */
  1358. /*  the hardware clock.                               */
  1359.  
  1360.     if (status == ETIME)      /* if no interrupt  */
  1361.       {
  1362.       retry++;
  1363.  
  1364. /*  Once the maximum number of retries is reached,    */
  1365. /*  give up.  Retrieve outstanding display request    */
  1366. /*  and wait for shutdown.  When shutdown signal is   */
  1367. /*  received, disable this interrupt task, signal     */
  1368. /*  shutdown complete and suspend execution.          */
  1369.  
  1370.       if (retry == MAX_CLOCK_RETRIES)
  1371.         {
  1372.         req_seg = rqreceivemessage(rsp_mbx,
  1373.           WAIT_FOREVER, NUL_TKN, &status);
  1374.         rqreceiveunits(clock_shtdwn_sem, 1,
  1375.           WAIT_FOREVER, &status);
  1376.         rqresetinterrupt(CLOCK_INT_LEVEL, &status);
  1377.         rqsendunits(init_sem, 1, &status);
  1378.         rqsuspendtask(NUL_TKN, &status);
  1379.         }
  1380.  
  1381. /*  Reinitialize hardware clock */
  1382.  
  1383.         outbyte(PIO_PORT, PIO_READ);
  1384.         delay(1);
  1385.  
  1386.         outbyte(CNT_PORT, INT_ENB);
  1387.         delay(1);
  1388.  
  1389.         outbyte(ADR_PORT, 0x0F);
  1390.         delay(1);
  1391.  
  1392. /*  Re-read hardware clock  */
  1393.  
  1394.         rqreceiveunits(clock_sem, 1,
  1395.           WAIT_FOREVER, &status);
  1396.  
  1397.         read_clock();
  1398.  
  1399.         rqsendunits(clock_sem, 1, &status);
  1400.         }
  1401.       else
  1402.         {
  1403. /*  Interrupt signal received, reset retry count  */
  1404.  
  1405.         retry = 0;
  1406.  
  1407. /*  Access time/date memory */
  1408.  
  1409.         rqreceiveunits(clock_sem, 1,
  1410.           WAIT_FOREVER, &status);
  1411.  
  1412. /*  If needed, update hardware clock with new */
  1413. /*  time/date data.  Otherwise, advance the   */
  1414. /*  time/data data by one second.             */
  1415.  
  1416.         if (sys_time->change_flg)
  1417.           {
  1418.           write_clock();
  1419.           sys_time->change_flg = 0;
  1420.           }
  1421.         else
  1422.           {
  1423.           advance_time_date();
  1424.           }
  1425.  
  1426. /*  Advance any timers by one second  */
  1427.  
  1428.         advance_timers();
  1429.  
  1430. /*  Prepare time/date data for display  */
  1431.  
  1432.       sprintf(time_string, "%02d:%02d:%02d  %02d/%02d/%02d",
  1433.         sys_time->hour, sys_time->minute, sys_time->second,
  1434.         sys_time->month, sys_time->date, sys_time->year);
  1435.  
  1436. /*  Release time/date memory  */
  1437.  
  1438.       rqsendunits(clock_sem, 1, &status);
  1439.  
  1440. /*  If previous display request has finished(segment    */
  1441. /*  returned), then request new time/date be displayed  */
  1442.  
  1443.       rsp_seg = rqreceivemessage(rsp_mbx,
  1444.         NO_WAIT, NUL_TKN, &status);
  1445.  
  1446.       if (status != ETIME)
  1447.         {
  1448.         rqsendmessage(crt_req_mbx,
  1449.           req_seg, rsp_mbx, &status);
  1450.         }
  1451.       }
  1452.  
  1453. /*  Check for shutdown  */
  1454.  
  1455.     rqreceiveunits(clock_shtdwn_sem, 1, NO_WAIT, &status);
  1456.     }
  1457.  
  1458. /*  Retrieve outstanding display request, disable   */
  1459. /*  this interrupt task, signal shutdown complete   */
  1460. /*  and suspend execution.                          */
  1461.  
  1462.   req_seg = rqreceivemessage(rsp_mbx,
  1463.     WAIT_FOREVER, NUL_TKN, &status);
  1464.  
  1465.   rqsendunits(init_sem, 1, &status);
  1466.  
  1467.   rqresetinterrupt(CLOCK_INT_LEVEL, &status);
  1468.  
  1469.   rqsuspendtask(NUL_TKN, &status);  /* suspend this task  */
  1470.   }
  1471.