BIOS.Clock.Mod 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. (* Aos, Copyright 2001, Pieter Muller, ETH Zurich *)
  2. MODULE Clock; (** AUTHOR "pjm"; PURPOSE "Real-time clock driver"; *)
  3. IMPORT SYSTEM, Machine, KernelLog, Kernel, Objects, Modules;
  4. CONST
  5. TraceVerbose = FALSE;
  6. TYPE
  7. Clock = OBJECT
  8. VAR
  9. clockmode: LONGINT;
  10. second, minute, hour, day, month, year: INTEGER;
  11. PROCEDURE ReadClock;
  12. BEGIN
  13. second := ORD(Machine.GetNVByte(0)); minute := ORD(Machine.GetNVByte(2));
  14. hour := ORD(Machine.GetNVByte(4)); day := ORD(Machine.GetNVByte(7));
  15. month := ORD(Machine.GetNVByte(8)); year := ORD(Machine.GetNVByte(9))
  16. END ReadClock;
  17. PROCEDURE HandleInterrupt;
  18. BEGIN {EXCLUSIVE}
  19. INC(Nints);
  20. IF clockmode = 0 THEN
  21. IF 4 IN SYSTEM.VAL(SET, LONG(ORD(Machine.GetNVByte(0CH)))) THEN ReadClock
  22. ELSE INC(Nmissed)
  23. END
  24. ELSE
  25. ReadClock
  26. END
  27. END HandleInterrupt;
  28. PROCEDURE Get(VAR time, date: LONGINT);
  29. VAR y, o, d, h, m, s: LONGINT;
  30. BEGIN {EXCLUSIVE}
  31. IF clockmode = 1 THEN (* poll, ref p. 750 of undocumented pc *)
  32. REPEAT (* until same clock value read twice in a row *)
  33. REPEAT UNTIL ~(7 IN SYSTEM.VAL(SET, LONG(ORD(Machine.GetNVByte(0AH))))); (* no update in progress *)
  34. ReadClock;
  35. h := hour; m := minute; s := second; y := year; o := month; d := day;
  36. ReadClock
  37. UNTIL (h = hour) & (m = minute) & (s = second) & (y = year) & (o = month) & (d = day)
  38. ELSE
  39. h := hour; m := minute; s := second; y := year; o := month; d := day
  40. END;
  41. h := BCDToInt(h); m := BCDToInt(m); s := BCDToInt(s);
  42. y := BCDToInt(y); (* returns 0..99, or possibly 0..165 on "fixed" clocks *)
  43. IF y < 90 THEN INC(y, 100) END; (* if 0..89, modify it to 100..189 *)
  44. (* now y is 90..189, i.e. 1990..2089 *)
  45. o := BCDToInt(o); d := BCDToInt(d);
  46. time := h*4096 + m*64 + s;
  47. date := y*512 + o*32 + d
  48. END Get;
  49. PROCEDURE Set(time, date: LONGINT);
  50. BEGIN {EXCLUSIVE}
  51. Machine.PutNVByte(0BH, 82X); (* disable clock & interrupt *)
  52. second := IntToBCD(time MOD 64);
  53. minute := IntToBCD(time DIV 64 MOD 64);
  54. hour := IntToBCD(time DIV 4096 MOD 32);
  55. day := IntToBCD(date MOD 32);
  56. month := IntToBCD(date DIV 32 MOD 16);
  57. year := IntToBCD(date DIV 512 MOD 100);
  58. Machine.PutNVByte(0, CHR(second));
  59. Machine.PutNVByte(2, CHR(minute));
  60. Machine.PutNVByte(4, CHR(hour));
  61. Machine.PutNVByte(7, CHR(day));
  62. Machine.PutNVByte(8, CHR(month));
  63. Machine.PutNVByte(9, CHR(year));
  64. Machine.PutNVByte(0BH, 12X) (* 24 hour mode & 1 second interrupt *)
  65. END Set;
  66. PROCEDURE GetSecond(): LONGINT;
  67. BEGIN {EXCLUSIVE}
  68. RETURN second
  69. END GetSecond;
  70. PROCEDURE &InitClock*;
  71. CONST Delay = 3;
  72. VAR p: LONGINT; timer: Kernel.MilliTimer; s: ARRAY 8 OF CHAR;
  73. BEGIN
  74. second := -1; p := 0;
  75. Machine.GetConfig("ClockMode", s);
  76. clockmode := Machine.StrToInt(p, s); (* mode 0 - interrupt with test, mode 1 - poll, mode -1 - interrupt without test *)
  77. IF clockmode # 1 THEN
  78. Objects.InstallHandler(SELF.HandleInterrupt, Machine.IRQ0+8);
  79. Machine.PutNVByte(0BH, 12X); (* 24 hour mode & 1 second interrupt *)
  80. Kernel.SetTimer(timer, Delay*1000); (* wait up to 3 seconds *)
  81. REPEAT UNTIL (GetSecond() # -1) OR Kernel.Expired(timer) (* wait for first update *)
  82. END;
  83. IF second = -1 THEN (* clock interrupt not functioning *)
  84. second := 0; minute := 0; hour := 0; day := 0; month := 0; year := 0;
  85. IF clockmode # 1 THEN
  86. Objects.RemoveHandler(SELF.HandleInterrupt, Machine.IRQ0+8)
  87. END;
  88. clockmode := 1 (* poll *)
  89. END;
  90. IF TraceVerbose THEN
  91. KernelLog.String("Clock: ClockMode = "); KernelLog.Int(clockmode, 1); KernelLog.Ln
  92. END
  93. END InitClock;
  94. PROCEDURE Terminate;
  95. BEGIN
  96. IF clockmode # 1 THEN
  97. Objects.RemoveHandler(SELF.HandleInterrupt, Machine.IRQ0+8)
  98. END;
  99. END Terminate;
  100. END Clock;
  101. VAR
  102. tz*: LONGINT; (** system time zone offset in minutes (from -720 to 720) *)
  103. starttime*, startdate*: LONGINT; (** time this module was loaded (usually boot time) *)
  104. clock: Clock;
  105. Nmissed, Nints: LONGINT;
  106. (* BCDToInt/IntToBCD - Decode/Encode 2 BCD digits *)
  107. PROCEDURE BCDToInt(x: LONGINT): LONGINT;
  108. BEGIN
  109. RETURN (x DIV 16) * 10 + x MOD 16
  110. END BCDToInt;
  111. PROCEDURE IntToBCD(x: LONGINT): INTEGER;
  112. BEGIN
  113. RETURN SHORT((x DIV 10) * 16 + x MOD 10)
  114. END IntToBCD;
  115. (** Return the current time and date in Oberon format. *)
  116. PROCEDURE Get*(VAR time, date: LONGINT);
  117. BEGIN
  118. clock.Get(time, date)
  119. END Get;
  120. (** Set the current time and date in Oberon format. *)
  121. PROCEDURE Set*(time, date: LONGINT);
  122. BEGIN
  123. clock.Set(time, date)
  124. END Set;
  125. PROCEDURE Cleanup;
  126. BEGIN
  127. ASSERT (clock # NIL);
  128. clock.Terminate;
  129. clock := NIL;
  130. END Cleanup;
  131. BEGIN
  132. tz := 2*60; (* fixme: configurable *)
  133. NEW(clock); Get(starttime, startdate);
  134. Modules.InstallTermHandler(Cleanup);
  135. END Clock.
  136. (*
  137. 23.08.1999 pjm Split from Aos.Kernel
  138. *)
  139. (**
  140. Notes
  141. 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.
  142. 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.
  143. The time and date are each represented in an encoded LONGINT.
  144. Converting from year, month, day, hour, minute, second to time, date:
  145. time := hour*4096 + minute*64 + second;
  146. date := (year-1900)*512 + month*32 + day;
  147. Converting from time to hour, minute, second:
  148. hour := time DIV 4096 MOD 32;
  149. minute := time DIV 64 MOD 64;
  150. second := time MOD 64;
  151. Converting from date to year, month, day:
  152. year = 1900+date DIV 512;
  153. month = date DIV 32 MOD 16;
  154. day = date MOD 32;
  155. All years in the current millenium can be represented. The 1900 offset is a historical artefact from the Oberon system.
  156. Time and date values (respectively) can be compared with the normal Oberon operators <, <=, =, >=, >, #. Overflow at midnight has to be handled separately.
  157. *)