MODULE Clock;
IMPORT SYSTEM, Machine, KernelLog, Kernel, Objects;
CONST
TraceVerbose = FALSE;
TYPE
Clock = OBJECT
VAR
clockmode: LONGINT;
second, minute, hour, day, month, year: INTEGER;
PROCEDURE ReadClock;
BEGIN
second := ORD(Machine.GetNVByte(0)); minute := ORD(Machine.GetNVByte(2));
hour := ORD(Machine.GetNVByte(4)); day := ORD(Machine.GetNVByte(7));
month := ORD(Machine.GetNVByte(8)); year := ORD(Machine.GetNVByte(9))
END ReadClock;
PROCEDURE HandleInterrupt;
BEGIN {EXCLUSIVE}
INC(Nints);
IF clockmode = 0 THEN
IF 4 IN SYSTEM.VAL(SET, LONG(ORD(Machine.GetNVByte(0CH)))) THEN ReadClock
ELSE INC(Nmissed)
END
ELSE
ReadClock
END
END HandleInterrupt;
PROCEDURE Get(VAR time, date: LONGINT);
VAR y, o, d, h, m, s: LONGINT;
BEGIN {EXCLUSIVE}
IF clockmode = 1 THEN
REPEAT
REPEAT UNTIL ~(7 IN SYSTEM.VAL(SET, LONG(ORD(Machine.GetNVByte(0AH)))));
ReadClock;
h := hour; m := minute; s := second; y := year; o := month; d := day;
ReadClock
UNTIL (h = hour) & (m = minute) & (s = second) & (y = year) & (o = month) & (d = day)
ELSE
h := hour; m := minute; s := second; y := year; o := month; d := day
END;
h := BCDToInt(h); m := BCDToInt(m); s := BCDToInt(s);
y := BCDToInt(y);
IF y < 90 THEN INC(y, 100) END;
o := BCDToInt(o); d := BCDToInt(d);
time := h*4096 + m*64 + s;
date := y*512 + o*32 + d
END Get;
PROCEDURE Set(time, date: LONGINT);
BEGIN {EXCLUSIVE}
Machine.PutNVByte(0BH, 82X);
second := IntToBCD(time MOD 64);
minute := IntToBCD(time DIV 64 MOD 64);
hour := IntToBCD(time DIV 4096 MOD 32);
day := IntToBCD(date MOD 32);
month := IntToBCD(date DIV 32 MOD 16);
year := IntToBCD(date DIV 512 MOD 100);
Machine.PutNVByte(0, CHR(second));
Machine.PutNVByte(2, CHR(minute));
Machine.PutNVByte(4, CHR(hour));
Machine.PutNVByte(7, CHR(day));
Machine.PutNVByte(8, CHR(month));
Machine.PutNVByte(9, CHR(year));
Machine.PutNVByte(0BH, 12X)
END Set;
PROCEDURE GetSecond(): LONGINT;
BEGIN {EXCLUSIVE}
RETURN second
END GetSecond;
PROCEDURE &InitClock*;
CONST Delay = 3;
VAR p: LONGINT; timer: Kernel.MilliTimer; s: ARRAY 8 OF CHAR;
BEGIN
second := -1; p := 0;
Machine.GetConfig("ClockMode", s);
clockmode := Machine.StrToInt(p, s);
IF clockmode # 1 THEN
Objects.InstallHandler(SELF.HandleInterrupt, Machine.IRQ0+8);
Machine.PutNVByte(0BH, 12X);
Kernel.SetTimer(timer, Delay*1000);
REPEAT UNTIL (GetSecond() # -1) OR Kernel.Expired(timer)
END;
IF second = -1 THEN
second := 0; minute := 0; hour := 0; day := 0; month := 0; year := 0;
IF clockmode # 1 THEN
Objects.RemoveHandler(SELF.HandleInterrupt, Machine.IRQ0+8)
END;
clockmode := 1
END;
IF TraceVerbose THEN
KernelLog.String("Clock: ClockMode = "); KernelLog.Int(clockmode, 1); KernelLog.Ln
END
END InitClock;
END Clock;
VAR
tz*: LONGINT;
starttime*, startdate*: LONGINT;
clock: Clock;
Nmissed, Nints: LONGINT;
PROCEDURE BCDToInt(x: LONGINT): LONGINT;
BEGIN
RETURN (x DIV 16) * 10 + x MOD 16
END BCDToInt;
PROCEDURE IntToBCD(x: LONGINT): INTEGER;
BEGIN
RETURN SHORT((x DIV 10) * 16 + x MOD 10)
END IntToBCD;
PROCEDURE Get*(VAR time, date: LONGINT);
BEGIN
clock.Get(time, date)
END Get;
PROCEDURE Set*(time, date: LONGINT);
BEGIN
clock.Set(time, date)
END Set;
BEGIN
tz := 2*60;
NEW(clock); Get(starttime, startdate)
END Clock.
(*
23.08.1999 pjm Split from Aos.Kernel
*)
(**
Notes
The time and date are that of the real-time clock of the system, which may be set to universal time, or to some local time zone.
The tz variable indicates the system time zone offset from universal time in minutes. It may be updated at any time due to daylight savings time. E.g. MET DST is 2 * 60 = 120.
The time and date are each represented in an encoded LONGINT.
Converting from year, month, day, hour, minute, second to time, date:
time := hour*4096 + minute*64 + second;
date := (year-1900)*512 + month*32 + day;
Converting from time to hour, minute, second:
hour := time DIV 4096 MOD 32;
minute := time DIV 64 MOD 64;
second := time MOD 64;
Converting from date to year, month, day:
year = 1900+date DIV 512;
month = date DIV 32 MOD 16;
day = date MOD 32;
All years in the current millenium can be represented. The 1900 offset is a historical artefact from the Oberon system.
Time and date values (respectively) can be compared with the normal Oberon operators <, <=, =, >=, >, #. Overflow at midnight has to be handled separately.
*)