[comp.os.minix] Setting minix date from Multi I/O card

subelman@ttidca.TTI.COM (Ed Subelman) (06/03/87)

My XT Clone has a Calendar/Clock on a card labelled "Multi I/O" that
also contains the Floppy Disk controller, a parallel port, a serial port
and a joystick controller port. I tried to use the programs posted on
this group to read an AST card and set the minix date at boot time. They
failed miserably. I then poked around the program that came with my machine
and figured out how to read the clock. I also figured out why you need to
read the hard card more than once to get the time right. See the comments.
Until the shell can handle shar files, this file should be cut into two
pieces: timedate.c and port_io.s.

--------- begin of timedate.c -------------- cut here --------
/* timedate.c  - get date and time from Multi I/O Card
 * and print on standard out. Format is MMDDYYHHMMSS
 * as required by "date".
 * Should be run in /etc/rc as:  date `/usr/bin/timedate`
 * Compile as:   cc -o timedate timedate.c port_io.s
 *
 * Author: Ed Subelman 5/87
*/

#include <stdio.h>

struct tm {
	int year;
	int month;
	int day;
	int hour;
	int min;
	int sec;
};

/* ports for the hard clock; first is the index, next is the data */

#define CLK_CNTL	0x033A
#define CLK_COMD	0x0338

/* registers on the hard clock; data returned in lowest 4 bits */

#define SEC_1s		0x50
#define SEC_10s		0x51
#define MIN_1s		0x52
#define MIN_10s		0x53
#define HR_1s		0x54
#define HR_10s		0x55
#define WK_DAY		0x56
#define DAY_1s		0x57
#define DAY_10s		0x58
#define MON_1s		0x59
#define MON_10s		0x5A
#define YR_1s		0x5B
#define YR_10s		0x5C

/* Commands for index port. I just disassembled the program that came with
   the card. Seems to work */

#define COM0		0x00
#define COM1		0x10
#define COM2		0x40

/* Bit masks */

#define LOW_4		0x0f
#define LOW_2		0x03
#define AM_PM		0x04

#define TRIES		10	/* number of times to read clock */


main(argc, argv)
	int argc;
	char *argv[];
{
	struct tm tm1;
	struct tm tm2;
	int i;

	/* try several times to read the clock. This is needed to avoid
	   the following sequence of events:
		time is 03:59:59.99
		you read the hour: 03
		the hard clock ticks: time is 04:00:00.00
		you read the minutes: 00. You are off by 1 hour!
	*/

	for (i = 0; i < TRIES; i++)
	{
		read_clk(&tm1);			/* read the clock twice */
		read_clk(&tm2);

		if (tm1.year == tm2.year	/* and compare the results */
				&& tm1.month == tm2.month
				&& tm1.day   == tm2.day
				&& tm1.hour  == tm2.hour
				&& tm1.min   == tm2.min
				&& tm1.sec   == tm2.sec)
		{
			printf("%02d%02d%02d%02d%02d%02d\n",
				tm1.month, tm1.day, tm1.year,
				tm1.hour,  tm1.min, tm1.sec);
			exit(0);		/* normal termination */
		}
	}

	/* too many errors in reading the clock give up */
	fprintf(stderr, "%s: hard clock unstable\n", argv[0]);
	fflush(stderr);
	_cleanup();
	exit(1);
}

/* read the hard clock and fill values into a tm structure */

read_clk(tp)
	struct tm *tp;
{
	int tmp;

	port_out(CLK_CNTL, COM1);	/* prepare clock */
	port_out(CLK_COMD, COM2);

	tp->year  = get_val(YR_10s) * 10 + get_val(YR_1s);
	tp->month = get_val(MON_10s) * 10 + get_val(MON_1s);
	tp->day   = (get_val(DAY_10s) & LOW_2) * 10 + get_val(DAY_1s);

	/* Card runs on 12 hour clock. Must convert to 24 hour */
	tmp	  = get_val(HR_10s);		/* save AM/PM indicator */
	tp->hour  = (tmp & LOW_2) * 10 + get_val(HR_1s);
	if (tp->hour == 12)
		tp->hour = 0;
	if (tmp & AM_PM)
		tp->hour += 12;

	tp->min   = get_val(MIN_10s) * 10 + get_val(MIN_1s);
	tp->sec   = get_val(SEC_10s) * 10 + get_val(SEC_1s);

	port_out(CLK_COMD, COM2);	/* restart clock? */
	port_out(CLK_COMD, COM0);
	port_out(CLK_CNTL, COM0);
}

/* get_val() - read a register from the hard clock */

int get_val(reg)
	int reg;
{
	int val;

	port_out(CLK_COMD, reg);	/* index on the register desired */
	port_in (CLK_COMD, &val);	/* and get the value of the reg */
 
	return (val & LOW_4);		/* make sure lower 4 bits only */
}

------------ begin of port_io.s ----------- cut here --------
| this file was grafted from klib88.s 

.globl _port_out, _port_in



|*===========================================================================*
|*				port_out				     *
|*===========================================================================*
| port_out(port, value) writes 'value' on the I/O port 'port'.

_port_out:
	push bx			| save bx
	mov bx,sp		| index off bx
	push ax			| save ax
	push dx			| save dx
	mov dx,4(bx)		| dx = port
	mov ax,6(bx)		| ax = value
	out			| output 1 byte
	pop dx			| restore dx
	pop ax			| restore ax
	pop bx			| restore bx
	ret			| return to caller


|*===========================================================================*
|*				port_in					     *
|*===========================================================================*
| port_in(port, &value) reads from port 'port' and puts the result in 'value'.
_port_in:
	push bx			| save bx
	mov bx,sp		| index off bx
	push ax			| save ax
	push dx			| save dx
	mov dx,4(bx)		| dx = port
	in			| input 1 byte
	xorb ah,ah		| clear ah
	mov bx,6(bx)		| fetch address where byte is to go
	mov (bx),ax		| return byte to caller in param
	pop dx			| restore dx
	pop ax			| restore ax
	pop bx			| restore bx
	ret			| return to caller

------------ end of port_io.s ------- cut here ---------
-- 
Ed Subelman		{csun|philabs|psivax|trwrb}!ttidca!subelman
Citicorp(+)TTI		(213) 450-9111, x2972
3100 Ocean Park Blvd.,	Santa Monica, CA  90405