[comp.os.minix] Clock setting program for AST Six Pack

tsp@killer.UUCP (04/13/87)

Attached is a clock setting program for an AST Six Pack card.  I use this
in /etc/rc as:

  /etc/astclock
  /usr/bin/echo -n "Current time is: "
  /usr/bin/date

No makefile is included, just: cc -o astclock astclock.c port_io.s


Known bugs:  I couldn't seem to get the ten-year count from the AST 
clock chip.  Instead, I just add a constant DECADE to the years value.
If anyone knows how to read the ten-year count, please make the changes
and post the diffs.


Tom Poindexter			April 13, 1987



---------cut here---------------------------------------------------------
echo x - astclock.c
gres '^X' '' > astclock.c << '/'
X/* astclock.c  - set MINIX's system clock from an AST Six Pack board */
X/* should be run in /etc/rc as /etc/astclock                         */
X/* date values to seconds calculation borrowed from date.c           */
X/* port_in() & port_out() in port_io.s were grafted from klib88.s    */
X/* author:  Tom Poindexter, April 9, 1987                            */
X/* I hereby place this software in the public domain                 */
X
X#include "stdio.h"
X
Xint days_per_month[] =
X  { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
X
Xstruct tm {
X  int year, month, day, hour, min, sec;
X};
X
X/* define the current decade */
X#define DECADE          80
X
X/* ports for the ast clock; first is the index, next is the data */
X
X#define AST_IDX_REG     0x02c0
X#define AST_VAL_REG     0x02c1
X
X/* registers on the ast clock; data returned in lowest 4 bits */
X
X#define SEC_1s          0
X#define SEC_10s         1
X#define MIN_1s          2
X#define MIN_10s         3
X#define HR_1s           4
X#define HR_10s          5
X#define DAY_1s          7
X#define DAY_10s         8
X#define MON_1s          9
X#define MON_10s         10
X#define YR_1s           11
X#define YR_10s          12
X#define CLK_MODE        13
X#define CLK_ENABLE      0x0c
X
X#define LOW_4           0x000f
X
X#define TRIES           10
X
X
X
X/******************************************************************/
X
Xmain()
X{
X  struct tm tm1;
X  struct tm tm2;
X  int  i;
X  long t;
X  long calc_sec();
X  int stime();		/* stime() sets the date in minix */
X
X  /* try several times to read the clock */
X
X  for (i = 0; i < TRIES; i++) {
X
X    read_clk(&tm1);			/* read the clock twice */
X    read_clk(&tm2);
X    
X    if (tm1.year  == tm2.year  &&	/* and compare the results */
X        tm1.month == tm2.month &&
X        tm1.day   == tm2.day   &&
X        tm1.hour  == tm2.hour  &&
X        tm1.min   == tm2.min   &&
X        tm1.sec   == tm2.sec) {
X
X      t = calc_sec(&tm1);      		/* calculate the seconds */
X
X      if (stime(&t) == -1) {		/* and set the time */
X        die("astclock: can't set stime(), are you super user?\n");
X      }
X
X      exit(0);                  /* normal termination */
X
X    }
X
X  }
X
X  /* too many errors in reading the ast clock chip, give up */
X
X  die("astclock: can't get a consistent time from the ast clock");
X
X}
X/* end of main */
X
X
X/* calculate seconds since epoch; this code borrowed from `date.c' */
X
Xlong calc_sec(tp)
Xstruct tm *tp;
X{
X  int i = 0;
X  long ct;
X  long s_p_min;
X  long s_p_hour;
X  long s_p_day;
X  long s_p_year;
X
X  /* first, calculate seconds per known intervals */
X  s_p_min  = 60;
X  s_p_hour = 60  * s_p_min;
X  s_p_day  = 24  * s_p_hour;
X  s_p_year = 365 * s_p_day;
X
X  /* calculate seconds from epoch until the start of this year */
X  tp->year -= 70;			/* deal with years since 1970   */
X  ct = tp->year * s_p_year;		/* seconds per year since epoch */
X  ct += ((tp->year + 1) / 4) * s_p_day;	/* add in the leap years and    */
X  if (((tp->year + 2) % 4) == 0) {	/* check if this year is a leap */
X	days_per_month[1]++;		/* and make feb one more day    */
X  }
X
X  /* calculate seconds from january until the start of this month */
X  tp->month--;
X  while (i < tp->month) {
X	ct += days_per_month[i++] * s_p_day;
X  }
X   
X  /* calculate seconds of the days this month */
X  ct += --tp->day * s_p_day;
X
X  /* calculate seconds of the hour this day */
X  ct += tp->hour * s_p_hour;
X
X  /* calculate seconds of the minutes this hour */
X  ct += tp->min * s_p_min;
X 
X  /* and finally add in the seconds so far this minute */
X  ct += tp->sec;
X
X  return (ct);
X }
X
X
X/* read the ast clock and fill values into a tm structure */
X
Xread_clk(tp)
Xstruct tm *tp;
X{
X  
X/*  can't seem to get the 10 year count from the clock chip - the
X    following should work, but YR_10s always returns 0
X  tp->year  = (rd_ast_clk(YR_10s) * 10) + rd_ast_clk(YR_1s);
X*/
X
X  tp->year  = DECADE + rd_ast_clk(YR_1s);
X  tp->month = (rd_ast_clk(MON_10s) * 10) + rd_ast_clk(MON_1s);
X  tp->day   = (rd_ast_clk(DAY_10s) * 10) + rd_ast_clk(DAY_1s);
X  tp->hour  = (rd_ast_clk(HR_10s)  * 10) + rd_ast_clk(HR_1s);
X  tp->min   = (rd_ast_clk(MIN_10s) * 10) + rd_ast_clk(MIN_1s);
X  tp->sec   = (rd_ast_clk(SEC_10s) * 10) + rd_ast_clk(SEC_1s);
X
X}
X/* end of read_clk */
X
X
X/* rd_ast_clk() - read a register from the ricoh rp5c15 clock chip */
X
Xint rd_ast_clk(reg)
Xint reg;
X{
X  int val;
X
X  port_out(AST_IDX_REG, CLK_MODE);	/* index on the clock mode reg */
X  port_out(AST_VAL_REG, CLK_ENABLE);	/* make sure timers are enabled */
X  port_out(AST_IDX_REG, reg);		/* index on the register desired */
X  port_in (AST_VAL_REG, &val);		/* and get the value of the reg */
X 
X  return (val & LOW_4);			/* make sure lower 4 bits only */
X  
X}
X/* end of rd_ast_clk */
X
X
X/* die() - print a message on stderr and exit with false code */
X
Xdie(msg)
Xchar *msg;
X{
X  fprintf(stderr,msg);
X  fflush(stderr);
X  _cleanup();
X  exit(1);
X
X}
X/* end of die */
X
X
X/* end of astclock.c */
/
echo x - port_io.s
gres '^X' '' > port_io.s << '/'
X| this file was grafted from klib88.s 
X
X.globl _port_out, _port_in
X
X
X
X|*===========================================================================*
X|*				port_out				     *
X|*===========================================================================*
X| port_out(port, value) writes 'value' on the I/O port 'port'.
X
X_port_out:
X	push bx			| save bx
X	mov bx,sp		| index off bx
X	push ax			| save ax
X	push dx			| save dx
X	mov dx,4(bx)		| dx = port
X	mov ax,6(bx)		| ax = value
X	out			| output 1 byte
X	pop dx			| restore dx
X	pop ax			| restore ax
X	pop bx			| restore bx
X	ret			| return to caller
X
X
X|*===========================================================================*
X|*				port_in					     *
X|*===========================================================================*
X| port_in(port, &value) reads from port 'port' and puts the result in 'value'.
X_port_in:
X	push bx			| save bx
X	mov bx,sp		| index off bx
X	push ax			| save ax
X	push dx			| save dx
X	mov dx,4(bx)		| dx = port
X	in			| input 1 byte
X	xorb ah,ah		| clear ah
X	mov bx,6(bx)		| fetch address where byte is to go
X	mov (bx),ax		| return byte to caller in param
X	pop dx			| restore dx
X	pop ax			| restore ax
X	pop bx			| restore bx
X	ret			| return to caller
X
X
/

Tom Poindexter
..!ihnp4!killer!tsp

diamant@hpfclp.UUCP (04/20/87)

I have also written an astclock replacement program (for a MegaPlus, but it
probably works the same for the other AST cards).  I also took the port_in
routine the same as Tom.  The compilation command is identical. Tom, mine
is considerably simpler than yours.  There are two main reasons I see for this:

1) You copied the code for date into astclock.  I just called date.
2) You seem to have bullet proofed your code against conditions I don't
   understand (such as why do you need to call the clock twice -- do you
   get garbage back sometimes -- I never have).

Tom, maybe you'd care to comment on why you do as much as you do in your
version.

-------- astclock.c (not shar) -- cut here:

/* Written by:  John Diamant  04/15/1987
   Contributed to the public domain

   This was written for the AST MegaPlus card, but it probably works for the
   other AST cards.

   Should be called in /etc/rc as follows:  "date `astclock`" where path
   information has been deleted.  This only works within the range inputtable
   by date (what happens in 2000?) and will be a few seconds off because of
   the time to call date after astclock returns its answer.  The algorithm
   is also only tested within the years specifiable by date in MS-DOS (the
   way the clock is set on the board), which means 1980-2099.  Since I had
   virtually no documentation for the clock chip, I could only figure out
   the representation for the dates I could set */

#include <stdio.h>
#define CLOCK_PORT 0x2C0
#define SECOND 2
#define MINUTE 3
#define HOUR 4
#define DAY 6
#define MONTH 7
#define Y1 8
#define Y2 9
#define Y3 10

main()
{
	int month, day, year, hour, minute, second;

	port_in(CLOCK_PORT + SECOND, &second);
	port_in(CLOCK_PORT + MINUTE, &minute);
	port_in(CLOCK_PORT + HOUR, &hour);
	port_in(CLOCK_PORT + DAY, &day);
	port_in(CLOCK_PORT + MONTH, &month);
	port_in(CLOCK_PORT + Y3, &year);
	year += 80;

	printf("%02x%02x%02d%02x%02x%02x", month, day, year, hour, minute,
		 second); 
}

tsp@killer.UUCP (Tom Poindexter) (04/27/87)

[ In <9490005@hpfclp.HP.COM>, diamant@hpfclp.HP.COM (John Diamant) writes: ]

>I have also written an astclock replacement program (for a MegaPlus, but it
>probably works the same for the other AST cards).  I also took the port_in
>routine the same as Tom.  The compilation command is identical. Tom, mine
>is considerably simpler than yours.  There are two main reasons I see for this:
>
>1) You copied the code for date into astclock.  I just called date.
>2) You seem to have bullet proofed your code against conditions I don't
>   understand (such as why do you need to call the clock twice -- do you
>   get garbage back sometimes -- I never have).
>
>Tom, maybe you'd care to comment on why you do as much as you do in your
>version.

John, here are the reasons for writing my version of astclock.c as I did:

1.  The code from `date.c' to compute the number of seconds since
epoch and calling stime() was used because of a comment to an earlier posting
of an AT clock reading & setting program.  The commentator cautioned against
using pipes during init becase of possible (or prior) damage to the file 
system.  I don't have the posting any longer to pinpoint the possible hazards,
but I took the advice anyway.  I will also have to hack both date.c and my
astclock.c in Dec. 2099, assuming I still have my PC & AST board and I am 
not {a higher life form/a frog/dust}, since date.c doesn't figure leap
years quite correctly. :-)

2.  I read the clock chip twice based on the programming suggestions in my
AST Six Pack manual.  I never experienced a situation where two readings were
different in my testing, but just in case.....

3.  I didn't experiment with the clock chip much, so I didn't know one 
could read directly from the clock registers.  The valid clock i/o addressing
is listed as 0x02c0 through 0x02c7 in the manual; your program addresses
ports beyond 0x02c7 (CLOCK_PORT + Y3 to address year, where CLOCK_PORT is
0x02c0 and Y3 is 10).  Could be that my documenation doesn't tell all or 
your MegaPlus is wired up differently, or uses a different clock chip.
My Six Pack clock chip always returned 'F' as the high nibble, so I and'ed
the value returned with LOW_4 (0x0f) after reading it.

4.  As a matter of my programming style, I tend to bullet-proof, write 
verbosely, add comments, etc., especially on code that I release 
publicly, so that others can make changes, find bugs, etc.  My previous 
iterations of the program were rather terse and clunky.


In any case, thanks for your comments and code; I always look for easier
ways of doing things. Perhaps someone can verify if the Six Pack Plus
clock chip & i/o addressing are the same for the MegaPlus.  -Tom
  
  Tom Poindexter     ..!ihnp4!killer!tsp