DEFINITION TimerSupport; CONST timerName = "timer.device"; addRequest = Exec.nonstd + 0; getSysTime = Exec.nonstd + 1; setSysTime = Exec.nonstd + 2; (* modes for OpenTimer *) microHz = 0; vBlank = 1; TYPE TimeVal = STRUCT secs : LONGINT; micro : LONGINT END; TimeRequest = STRUCT END; TimeRequestPtr = POINTER TO TimeRequest; PROCEDURE AddTime(VAR dest, source: TimeVal); PROCEDURE CmpTime(VAR tv1, tv2: TimeVal): INTEGER; PROCEDURE SubTime(VAR dest, source: TimeVal); PROCEDURE OpenTimer(mode: LONGINT): TimeRequestPtr; PROCEDURE CloseTimer(timeReqPtr: TimeRequestPtr); PROCEDURE Delay(timeReqPtr: TimeRequestPtr; secs, micro: LONGINT); PROCEDURE SendIO(timeReqPtr: TimeRequestPtr; secs, micro: LONGINT); PROCEDURE WaitIO(timeReqPtr: TimeRequestPtr); PROCEDURE GetSysTime(VAR t: TimeVal); PROCEDURE SetSysTime(t: TimeVal); PROCEDURE StartClock; PROCEDURE ReadClock(VAR t: TimeVal); PROCEDURE WriteTime(t: TimeVal); PROCEDURE SysToNormalTime(SysTime: TimeVal; VAR Year, Month, Day: LONGINT; VAR Hour, Minute, Second: LONGINT); PROCEDURE NormalToSysTime(Year, Month, Day: LONGINT; Hour, Minute, Second: LONGINT; VAR SysTime: TimeVal); END TimerSupport. Dieses Modul vereinfacht etwas die Benutzung des Timer.device. Zur Kommunikation mit dem Timer.device wird in der Regel die Struktur TimeVal benutzt. Darin bezeichnet secs die seit dem 1.1.1978 vergangenen Sekunden. micro bezeichnet den Bruchteil einer Sekunde in Mikrosekunden. (1 Mikrosekunde = 1 µs = 1E-6 Sekunden). (micro sollte immer normiert sein, also micro < 1E6. Ebenso sollten secs und micro nie negativ sein.) Die Prozeduren StartClock, ReadClock und WriteTime simulieren eine Stopuhr. StartClock setzt die Uhr auf Null und startet sie, ReadClock liest die Uhr aus, ohne sie anzuhalten. WriteTime gibt den Wert, den ReadClock geliefert hat, aus. Damit kann man sehr einfach messen, wie viel Zeit die Ausführung eines bestimmten Programmsegments benötigt. Allerdings ist die Auflösung der Uhr auf 1/50-Sekunde beschränkt. Die Prozeduren SysToNormalTime und NormalToSysTime (© 1990 by Höhn & Benezan) habe ich unverändert von Amok#25 übernommen. Sie wandeln die in der Struktur TimeVal in Sekunden angegebene Zeit in die konventionelle Zeit um bzw. umgekehrt. Die Parameter sollten nur positive Werte enthalten. Die Prozeduren AddTime, CmpTime und SubTime führen einige arithmetische Operationen durch. PROCEDURE AddTime(VAR dest, source: TimeVal); dest := dest + source PROCEDURE CmpTime(VAR tv1, tv2: TimeVal): INTEGER; wenn tv1 = tv1 : 0 wenn tv1 < tv2 : -1 wenn tv1 > tv2 : +1 PROCEDURE SubTime(VAR dest, source: TimeVal); dest := dest - source Die Prozedur GetSysTime liefert die Systemzeit. Diese kann bei Bedarf mit SysToNormalTime in die konventionelle Form gebracht werden. Die Auflösung von GetSysTime ist auf 1/50 Sekunde beschränkt. Ruft man allerdings GetSysTime mehrmals hintereinander auf, so unterscheiden sich die Ergebnisse nur um 1 oder 2 Mikrosekunden. Dadurch kann der Eindruck entstehen, man könne mit der Uhr des Timer.device auch extrem kurze Zeitintervalle von nur wenigen Mikrosekunden messen. Das ist aber nicht so: Bei jedem Aufruf von GetSysTime erhöht das Timer.device die Systemzeit willkürlich um einige Microsekunden. Das wird aber nur gemacht, damit jeder Aufruf von GetSysTime einen anderen Wert liefert. So kann man diesen Zeitwert z.B. in einen String umwandeln und dann als unverwechselbaren Namen für eine temporäre Datei verwenden. SetSysTime kann benutzt werden, um die Systemzeit einzustellen. Für die Amiga-Uhr gilt das gleiche was auch für alte mechanische Uhren gilt: Man sollte sie besser nicht zurückdrehen! Anderenfalls kann das Betriebssystem irritiert werden! Bevor man die folgenden 3 Prozeduren benutzen kann, muß man die Prozedur OpenTimer aufrufen. Der Grund ist, daß es von dem Timer.device zwei Units gibt: Unit_MicroHz und Unit_VBlank. Bei den obigen Prozeduren ist es egal, welche Unit man benutzt. (Insbesondere scheint es auch bei GetSysTime egal zu sein, welche Unit benutzt wird: Die Auflösung beträgt immer nur 1/50 Sekunde.) Aber wenn man das Timer.device benutzen will, um einige Zeit zu warten, dann unterscheiden sich die beiden Units. Bei Benutzung von Unit_VBlank (Vertical_Blank) hat die Uhr eine Auflösung von 1/50s (Pal) bzw. 1/60s (NTSC). Man kann also nur n/50s (n>0) warten. Damit verhält sich TimerSupport.Delay wie Dos.Delay. (1/50s = 20000µs) Versucht man z.B. 22000 µs zu warten, so wird der Zeitintervall von der Unit_VBlank auf 40000µs = 2/50s aufgerundet. Benutzt man dagegen Unit_MicroHz beim öffnen des TimerDevice, so kann man die Wartezeit tatsächlich bis auf einige µs genau angeben. Im alten RKM: Libraries und Devices wird gesagt, das die Benutzung der Unit_MicroHz einen großen System-Overhead erfordert, also viel Rechenzeit verbraucht. Deshalb sollte man die Unit_MicroHz nur benutzen, wenn man wirklich nur einige µs warten will. (Ich habe bei meinen Experimenten allerdings nichts von diesem Systemoverhead bemerkt.) PROCEDURE OpenTimer(mode: LONGINT): TimeRequestPtr; Mode darf nur die beiden Werte vBlank oder microHz haben. Die Funktion liefert als Ergebnis einen Pointer, den man für die drei folgenden Prozeduren benötigt. OpenTimer kann mehrmals aufgerufen werden, so kann bei Bedarf die Unit_VBlank und Unit_MicroHz abwechselnd benutzt werden. PROCEDURE Delay(timeReqPtr: TimeRequestPtr; secs, micro: LONGINT); Mit dieser Prozedur kann man eine bestimmte Zeit warten. Die Prozedur entspricht Dos.Delay, nur daß man bei Benutzung des Modus Unit_MicroHz auch wenige Microsekunden warten kann. PROCEDURE SendIO(timeReqPtr: TimeRequestPtr; secs, micro: LONGINT); PROCEDURE WaitIO(timeReqPtr: TimeRequestPtr); SendIO und WaitIO gehören zusammen und müßen immer paarweise benutzt werden. SendIO und WaitIO direkt hintereinander aufgerufen entsprechen Delay. Allerdings können zwischen dem Aufruf von SendIO und WaitIO noch beliebige Anweisungen stehen. SendIO startet praktisch einen CountDown. WaitIO wartet dann, bis die Uhr einen Wert <= 0 anzeigt. Beispiel: Es soll 10 s gewartet werden um einen Text anzuzeigen. Während dieser Zeit soll aber eine Prozedur abgearbeitet werden. Die Zeit, die die Prozedur benötigt, ist nicht genau bekannt, sie liege aber zwischen 3 und 20 Sekunden. Dann kann das Programmfragment etwa so aussehen: trPtr:=OpenTimer(vBlank); IF trPtr=NIL THEN HALT(0) END; ZeigeTextAn; (* Text am Bildschirm anzeigen *) SendIO(trPtr,10,0); (* mindestens 10 Sekunden warten *) BerechneIrgendwas; (* während dieser Zeit etwas sinnvolles tun *) WaitIO(trPtr); (* ist die Zeit schon um? wenn ja, dann weiter *) Der Text wird also mindestens 10 Sekunden angezeigt. Wenn die Prozedur BerechneIrgendwas länger als 10 Sekunden rechnet, dann wird der Text eben etwas länger gezeigt. PROCEDURE CloseTimer(timeReqPtr: TimeRequestPtr); Hat man den Timer mit OpenTimer geöffnet, so sollte man ihn wieder schließen, sobald man ihn nicht mehr benötigt. Zu jeden Aufruf von OpenTimer gehört ein entsprechender Aufruf von CloseTimer. (Bei Programmabbruch werden alle Timer automatisch geschlossen.) Stefan Salewski, 18.1.91 PS: Das Modul TimerSupport wurde auf einem Amiga 1000 (PAL) mit KickStart und Workbench 1.3 und dem Amiga-Oberon-Compiler entwickelt. Alle Angaben beziehen sich auf diese System- konfiguration. Bei Benutzung der Betriebssystemversion 2.0 und insbesondere auf dem Amiga 3000 kann es Unterschiede geben!