jxh@cup.portal.com (Jim - Hickstein) (12/29/88)
Here is the much-requested source for my stupid little program that reads
the CMOS clock chip in an AT&T PC6300 and feeds the date and time to DOS.
I use it at boot time, since IBM PC-DOS 3.30 doesn't have the AT&T-modified
IO system (any 86-DOS old-timers out there?) that "knows" what to do when the
DATE and TIME commands are issued, i.e. call the Olivetti BIOS to touch the
clock chip directly. I have a big binder full of 7th-generation photocopies of
the BIOS listing from an Olivetti M24 (what a 6300 is, really), and managed to
glean the following kernels of trivia therefrom. Note that it was written to
compile under Logitech Modula-2 release 2, but the gist of it is certainly
portable to whatever environment you happen to be suffering from at the moment.
Voila!
------
MODULE ATTClock;
(* Insofar as I wrote this while an employee of Telcom General Corporation,
it is Copyright 1986 Telcom General Corporation, but they're defunct,
so draw your own conclusions.
AT&T PC 6300 BIOS Interrupt Service Routine
INT 1A
Read Time:
ENTRY (AH) = FE (-2)
EXIT (BX) = days since 1984-Jan-01 (1=1984-Jan-01)
(CH) = hour
(CL) = minute
(DH) = second
(DL) = hundredth
MS-DOS Set Date/Time Function(s)
INT 21H
al=0 OK, AL=FF invalid
Set Date 2B cx year dh mo1-12 dl day 1-31
Set Time 2D ch hour, cl minute, dh seconds, dl hundredths
Get Date 2A cx year dh mo1-12 dl day 1-31 al wkdy 0N-6S
Get Time 2C ch hour, cl minute, dh seconds, dl hundredths
*)
FROM SYSTEM IMPORT GETREG, SETREG, SWI, AX, BX, CX, DX;
FROM InOut IMPORT Write, WriteCard, WriteString, WriteLn;
TYPE
Months = (Nul, Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec);
VAR
year, date, hour, minute, second, hundredth,
daysThisYear: CARDINAL;
MonthLength: ARRAY Months OF CARDINAL;
MonthNames: ARRAY [0..3*13] OF CHAR;
month: Months;
PROCEDURE Initialize;
VAR
m: Months;
BEGIN
year := 1984;
MonthNames := '***JanFebMarAprMayJunJulAugSepOctNovDec ';
FOR m := Jan TO Dec DO MonthLength[m] := 31 END;
MonthLength[Feb] := 28;
MonthLength[Apr] := 30;
MonthLength[Jun] := 30;
MonthLength[Sep] := 30;
MonthLength[Nov] := 30;
END Initialize;
PROCEDURE SetDate;
VAR
tmp, tmp2: CARDINAL;
BEGIN
tmp := ORD(month)*256 + date;
SETREG(AX, 2B00H) (* Set Date *);
SETREG(CX, year);
SETREG(DX, tmp);
SWI(21H);
tmp := hour*256 + minute;
tmp2 := second*256 + hundredth;
SETREG(AX, 2D00H) (* Set time *);
SETREG(CX, tmp);
SETREG(DX, tmp2);
SWI(21H);
END SetDate;
PROCEDURE DisplayDate;
PROCEDURE Write2Digits(num: CARDINAL);
BEGIN
IF num < 10 THEN Write('0') END;
WriteCard(num, 1)
END Write2Digits;
VAR
i, j: CARDINAL;
BEGIN
WriteCard(date, 1); Write('-');
i := ORD(month) * 3;
FOR j := i TO i+2 DO Write(MonthNames[j]) END; Write('-');
WriteCard(year, 1); Write(' ');
Write2Digits(hour); Write(':');
Write2Digits(minute); Write(':');
Write2Digits(second); Write('.');
Write2Digits(hundredth); WriteLn
END DisplayDate;
PROCEDURE GetTimeFromATT;
VAR
bx, cx, dx: CARDINAL;
BEGIN
SETREG(AX, 0FE00H);
SWI(1AH);
GETREG(BX, bx);
GETREG(CX, cx);
GETREG(DX, dx);
date := bx;
hour := cx DIV 256;
minute := cx MOD 256;
second := dx DIV 256;
hundredth := dx MOD 256
END GetTimeFromATT;
BEGIN
Initialize;
GetTimeFromATT;
LOOP
IF (((year MOD 4) = 0) AND
(((year MOD 100) # 0) OR ((year MOD 400) = 0))) THEN
daysThisYear := 366;
MonthLength[Feb] := 29
ELSE
daysThisYear := 365;
MonthLength[Feb] := 28
END;
IF date >= daysThisYear THEN
DEC(date, daysThisYear);
INC(year)
ELSE
EXIT
END
END;
month := Jan;
WHILE MonthLength[month] <= date DO
DEC(date, MonthLength[month]);
INC(month);
END;
INC(date) (* first date is 1, not 0 *);
SetDate;
DisplayDate
END ATTClock.
------
Jim Hickstein
jxh@cup.portal.com
...!sun!portal!cup.portal.com!jxh