============================================================================= Programmierung des * programmierbaren-Intervall-Timers (PIT) 8253/8254, des * Systemtimers und der * CMOS Echtzeituhr (RTC) mit Beispiel-Quelltexten fuer Basic unter DOS. Autor: Frank Steinberg Stand: 07.10.2001 INTERNET: http://home.arcor.de/steini63 ============================================================================= I N H A L T : 0. Aenderungen 1. Was gehoert dazu ? 1.1 Unterverzeichnis DELAYS\ 1.2 Unterverzeichnis TIMER\ 1.3 Unterverzeichnis HARDDOK\ 1.4 Unterverzeichnis PCTIM003\ 2. Haftung ? 3. Fuer welche BASIC-Varianten ist der Quelltext geeignet ? 4. Wo wird im PC Zeit gemessen ? 4.1 Der Systemtimer 4.2 Der Programmierbare-Intervall-Timer (PIT) 8253/8254 4.2.1 Geht das mit jedem PC ? 4.2.2 Mit welchen Werten arbeitet der PIT ? 4.2.3 Was behindert genaue Messungen mit dem PIT ? 4.2.4 Wie erreicht man genaue Messungen ? 4.3 Die CMOS-Echtzeituhr (RTC) 4.3.1 Der BIOS-Interrupt 15hex; Funktionen 83hex und 86hex 5. Anmerkungen zu den BASIC-Beispielprogrammen 5.1 8253timr.bas 5.2 8253dlay.bas 5.3 8253timr.bas und 8253dly.bas 5.4 mikrotmr.bas 6. Wo kann man mehr erfahren ? 0. Aenderungen -------------- 07.10.2001 Die Interrupt Routine in wait1583.bas und intdelay.bas funktioniert nicht, wenn das Programm compiliert wird. Grund: Der Compiler ignoriert die ASCII- Zeichen 1 und 2, bei ASCII 26 (End-Of-File) bricht der Compiler ab. Im Maschinencode-String (MC$) kamen ASCII 1 und 2 vor. Das wurde behoben. Hinweis: Absolute.bas (erzeugt Maschinencode-Strings aus ASM-Quelltexten) beruecksichtigt das selbstverstaendlich und ersetzt solche Zeichen durch CHR$(xx). Der ASM-Quelltext (interr.asm) ist jetzt beigefuegt. 1. Was gehoert dazu ? --------------------- Folgende Dateien befinden sich im ZIP-Archiv: timing.txt - Diese Datei. 1.1 Unterverzeichnis DELAYS\ ---------------------------- 8253dlay.bas - Erzeugung eines Delays im Millisekunden-Bereich mit Kompensation des eigenen Zeitverbrauchs (supergenau). intdelay.bas - Kurzzeit-Delay mit Interrupt 15hex, Funktion 86hex. interr.asm - Quelltext der in wait1583.bas benutzten Interrupt-Routine lopdelay.bas - Kurzzeit-Delay mit einer geschwindigkeitskompensierten DO-LOOP Schleife. pitdelay.bas - Drei Moeglichkeiten fuer Kurzzeit-Delays mit dem PIT 8253/8254, darunter auch mit Maschinencode (CALL ABSOLUTE). stdelay.bas - Einfach-Delay mit dem Systemtimer (Aufloesung 55 Millisek.). wait1583.bas - Wartezeit ueber Interrupt 15hex, Funktion 83hex. ***> Diese Dateien/Programme sind PUBLIC DOMAIN. 1.2 Unterverzeichnis TIMER\ --------------------------- 8253timr.bas - Zeitmessung mit dem PIT Zaehler 0 und 2. 8253mini.bas - Zeitmessung mit dem PIT Zaehler 0; Minimalbeispiel. mikrotmr.bas - Zeitmessung mit dem PIT Zaehler 0 und dem Systemtimer; dadurch ist eine maximale Messzeit von 14 Sekunden moeglich. rtc2tmr.bas - Systemtimer mit Echtzeituhr (RTC) korrigieren. stimer.bas - Auslesen des Systemtimers anhand der Zaehler im Hauptspeicher. tmrspeed.bas - Beschleunigung des Systemtimers und Zeitkorrektur mit der RTC. ***> Diese Dateien/Programme sind PUBLIC DOMAIN. 1.3 Unterverzeichnis HARDDOK\ ----------------------------- file_id.diz - Infos zum Dateipaket liesmich.txt - - * - kbd.txt - Infotext zur Tastatur pit.txt - Infotext zum PIT 8253/8254 rtc.txt - Infotext zur Echtzeituhr zip.txt - Infotext zu Zip-Archiven ***> Diese Dateien stammen nicht von mir, sondern von Peter Weigel. Sie sind Shareware und komplett in deutsch. 1.4 Unterverzeichnis PCTIM003\ ------------------------------ figures.zip - GIF-Bilder zum Text file_id.diz - Infos zum Dateipaket pctim003.txt - Ein sehr ausfuehrlicher Text (643.540 Bytes !) zu allen Belangen des Timings auf PCs (englisch). samples.zip - Beispielprogramme zum Text ***> Diese Dateien stammen nicht von mir, sondern von Kris Heidenstrom. Sie sind Freeware und komplett in englisch. 2. Haftung ? ------------ Die Benutzung der Programme geschieht auf eigene Gefahr! Die ueblichen Sicherheitsvorkehrungen sollten unbedingt eingehalten werden: - Alle Datendateien speichern; am besten schliessen. - Regelmaessig Backups machen. 3. Fuer welche Basic-Varianten ist der Quelltext geeignet ? ---------------------------------------------------------- Fuer QBasic, Quick-Basic, Basic PDS, Visual-Basic fuer DOS, Power-Basic 3.x. Bei Power-Basic 2.x, First-Basic passen lediglich die Zeilen DECLARE ... nicht; einfach auskommentieren, SLEEP ist durch DELAY zu ersetzen. Interrupts werden ueber eigene CALL ABSOLUTE - Routinen angesprochen; deshalb auch fuer QBasic kein Problem. 4. Wo wird im PC Zeit gemessen ? -------------------------------- 4.1 Der Systemtimer ------------------- PCs besitzen einen Timer, der alle 54,925 Millisekunden aktualisiert wird. Diesen Timer nutzt z.B. die Funktion TIMER von Basic. Obwohl viele Nach- kommastellen angezeigt werden, wird in 54,925 Millisekunden-Schritten aktualisiert. Diesen Intervall kann man allerdings verkuerzen (siehe 4.2). Will man die langsame, weil Fliesskommaarithmetik benutzende TIMER Funktion von BASIC umgehen, kann man auch die Speicherstellen im BIOS-Datenbereich des Hauptspeichers auslesen. Wie das geht, zeigen stdelay.bas und stimer.bas. Aber auch lopdelay.bas nutzt diese Moeglichkeit, um die Zahl von Schleifen in einer 54,925 Milisekunden-Periode zu ermitteln. In rtc2tmr.bas werden die Specherstellen beschrieben, um den Systemtimer zu verstellen, ohne die Echt- zeituhr zu behelligen. Wie man mit hoeherer Aufloesung messen kann, zeigen 4.2 und 4.3. Zwei Sachen sind zur Programmierung des Systemtimers noch anzumerken: * Der niederwertige Zaehler bei (46Chex) laeuft in der letzten Periode vor Mitternacht nur bis 175, nicht bis 255! In dieser Periode (und nur in der) steht der hoechstwertige (46Ehex) Zaehler auf 24hex. * Man kann den Mitternachts-Zaehlersprung auch ueber die Speicherstelle 470hex abfragen. Die BASIC-Funktion TIME$ loescht dieses Ueberlauf-Flag allerdings ungefragt. Die Mitternachts-Zaehlersprung-Erkennung ist in meinen Programmen deshalb anders realisiert (siehe z.B. stimer.bas). * Wichtig ist sicherzustellen, dass alle Zaehlerstaende zueinander gehoeren. Liest man waehrend eines Zaehlerueberlaufs die drei Speicher- stellen aus, ist das ggf. nicht der Fall. Strategie: Niederwertigen Zaehler am Beginn UND am Ende des Auslesens lesen. Ist der Wert ungleich, wird nochmal alles gelesen. 4.2 Der Programmierbare-Intervall-Timer (PIT) 8253/8254 ------------------------------------------------------- Die Aktualisierung des Systemtimers erledigt der PIT. Den Anstoss fuer die Aktualisierung erledigt der Zaehler 0 des PIT. Der PIT hat insgesamt drei Zaehler: Zaehler 0 - Der erzeugt den Timer-Interrupt, den koennen wir nutzen. Zaehler 1 - Verantwortlich fuer den Speicher-Refresh; FINGER WEG ! Zaehler 2 - Verantwortlich fuer die Tonerzeugung des PC-Lautsprechers, den koennen wir ebenfalls nutzen. Das Aktualisierungsintervall des Systemtimers kann man beschleunigen, indem man den PIT Zaehler 0 mit einem neuen Startwert laedt. Alle Routinen, die auf dem Systemtimer basieren koennen damit beschleunigt werden bzw. liefern eine hoehere Aufloesung (bei BASIC z.B. TIME$, TIMER, ON TIMER, SLEEP). Wie es geht, zeigt tmrspeed.bas (funktioniert bei mir NICHT unter Windows (98)). Dieses Beispiel zeigt auch, wie man die vorausgeeilte Uhrzeit mit der Echtzeituhr wieder korrigiert. Will man kuerzere Zeiten messen oder braucht man eine feinere Aufloesung, kann man die Zaehler des PIT auslesen. Deren Aufloesung betraegt 0,838 Mikrosekunden. Das machen die Programme 8253*.bas, pitdelay.bas und mikrotmr.bas. Das Problem dass man waehrend eines Zaehlersprungs nicht zueinander gehoerende Werte liest, ist durch die Conter-Latch Funktion des PIT leicht zu umgehen. Gibt man die entsprechende Bitkombination in das Steuerregister ein, werden hoeher- und niederwertiges Byte in einem PIT-internen Speicher verriegelt und koennen ohne Zeitdruck nacheinander ausgelesen werden. Was die einzelnen Register zu Ansteuerung des PIT bedeuten, kann den Info- texten in den Unterverzeichnissen HARDDOK oder PCTIM003 entnommen werden. 4.2.1 Geht das mit jedem PC ? ----------------------------- Jeder PC hat einen oder sogar zwei 8253-kompatible PIT. Ab AT wird meist das Nachfolgemodell 8254 eingesetzt. Der 8254 bietet zusaetzlich die Moeglichkeit, den Status auszulesen (Write Back). Diese Funktion wird in den beiliegenden Programmen jedoch nicht benutzt. Es muesste also jeder PC geeignet sein. 4.2.2 Mit welchen Werten arbeitet der PIT ? ------------------------------------------- Der PIT 8253/8254 wird mit 1.193.180 Hz angesteuert, also alle 0,838096515 Mikrosekunden (1.000.000 / 1.193.180). Alle 0,838096515 Mikrosekunden zaehlt ein 16-Bit Zaehler abwaerts (65535,65534,...,2,1,0) das sind 65536 Schritte. Die Zeitdauer fuer einen Durchlauf von 0 bis 0 betraegt also 54.925,49322 Mikrosekunden (65536 * 0,838096515). In diesem Zeittakt wird der Systemtimer vom Zaehler 0 des PIT aktualisiert. Benutzt man nur das hoeherwertige Byte, hat man einen 8-Bit Zaehler. Die 54.925,49322 Mikrosekunden werden in nur 256 Schritte zerlegt, also wird alle 214,5527079 Mikrosekunden gezaehlt (54.925,49322 / 256). Maximal kann eine Zeitspanne von 54,925 Millisekunden gemessen werden. In dieser Zeit wird von 65535 bis Null abwaerts gezaehlt. Das ergibt dann die oben genannte Aufloesung von 0,838 Mikrosekunden. 4.2.3 Was behindert genaue Messungen mit dem PIT ? -------------------------------------------------- * Alle Multitasking-Umgebungen, also praktisch alle modernen Betriebs- systeme (Win 95/98/ME, Linux, OS2 usw.). So richtig Sinn hat's nur unter blankem DOS. In der DOS-Box von Win95 ist Zaehler 0 unbrauchbar. Zaehler 2 macht hohe Streuungen bei den Messwerten (8253tmr.bas). Vermutlich laesst sich der Timer-Interrupt nicht unterbinden. Im DOS- Modus laeuft alle prima (ist ja auch reines DOS). Win98/ME habe ich nicht getestet. Zu Linux/OS 2 kann ich nichts sagen. * Interrupts; jeder Interrupt benoetigt so viel Zeit, dass unser Mess- ergebnis verfaelscht wird. Das kann mit 8253timr.bas leicht ueberprueft werden, indem man waehrend der Messungen mal Tasten drueckt. Zaehler 0 wird reagieren. Unser Hauptfeind ist aber der Timer-Interrupt. Der tritt in Abstaenden der beruehmten 54,925 Millisekunden auf, also ziemlich oft! 4.2.4 Wie erreicht man genaue Messungen ? ----------------------------------------- Zwei Zaehler; zwei Strategien: Zaehler 0 erzeugt ja selber den uns stoerenden Timer-Interrupt, wenn er (von 65535) die Null erreicht hat. Setzen wir den Zaehler beim Initialisieren auf 65535, wird es innerhalb der maximalen Messzeit keinen Timer-Interrupt geben. Die Programmier-Strategie ist also: * Initialisierung: - Konfigurationsbyte ins Steuerregister (Port 43hex) geben - Low- und Highbyte mit Maximalwert 255 in das Zaehl- register (Port 40hex) schreiben. * Irgendeinen Code ausfuehren, dessen Zeitbedarf gemessen wird. * Auslesen: - Messwerte PIT-intern speichern (Counter Latch) durch Schreiben des entsprechenden Bytes in das Steuer- register (Port 43hex). - Lowbyte und Highbyte lesen. - An den Standardeinstellungen von Zaehler 0 wird nichts geaendert, wir brauchen also auch nichts zuruecksetzen. Zaehler 2 Hier kann uns jederzeit der Timer-Interrupt dazwischenfunken. Der Einfach- heit halber unterbinden (maskieren) wir alle Interrupts, bei denen das moeglich ist. Nebeneffekt: Auch Tastendruecke waehrend der Messung aendern das Ergebnis nicht. Damit Zaehler 2 funktioniert, muss ausserdem der Lautsprecher-Kanal aktiviert sein. * Initialisierung: - Lautsprecher aktivieren durch Setzen von Bit0 bei Port 61hex. - Alle Interrupts maskieren durch Setzen aller Bits bei Port 21hex. - Konfigurationsbyte ins Steuerregister (Port 43hex) geben - Low- und Highbyte mit Maximalwert 255 in das Zaehl- register (Port 40hex) schreiben. * Irgendeinen Code ausfuehren, dessen Zeitbedarf gemessen wird. * Auslesen: - Messwerte PIT-intern speichern (Counter Latch) durch Schreiben des entsprechenden Bytes in das Steuer- register (Port 43hex). - Die Messwerte haben wir jetzt im Sack, wir koennen die Interrupts wieder zulassen (alle Bits bei Port 21hex loeschen). - Lowbyte und Highbyte lesen. Wie das alles im Detail gemacht wird, kann den Quelltexten entnommen werden (SUBs Z0init/Z2init, FUNCTION Z0lesen&/Z2lesen& in 8253timr.bas/8253mini.bas). 4.3 Die CMOS-Echtzeituhr (RTC) ------------------------------ RTC ist die Abkuerzung fuer Real-Time-Clock. Die wird AT-Zeiten in PCs ein- gebaut und bewahrt uns davor, nach jedem Booten Datum und Zeit manuell einzutippen. Die Funktionen des BIOS-Interrupts 15hex, die in den Programmen intdelay.bas und wait1583 benutzt werden, bedienen sich der RTC, deshalb laufen sie z.B. nicht auf meinen heissgeliebten DOS-Palmtops (Highscreen- Handy-Organizer, HP LX-200). Die RTC wird nur in Sekundentakt aktualisiert. Da ist nichts revolutionaeres zu programmieren. Man kann die RTC zwar auch beschleunigen, naemlich ueber das Statusregister A des CMOS-RAM, aber da reagiert jeder meiner PCs anders und einige garnicht. Ich habe das deshalb nicht weiter verfolgt. Der eigentliche Grund, warum ich mich mit der RTC beschaeftigt habe, ist der, dass der Systemtimer bei vielen hier vorgestellten Programmen manipuliert wird (beschleunigt, durch Interruptmaskierung gestoppt usw.). Ein Programm, dass eine verstellte Uhrzeit zuruecklaesst ist aber irgendwie uncool. Also benutzt man die RTC, um den Systemtimer zu korrigieren (siehe rtc2tmr.bas und tmrspeed.bas). 4.3.1 Der BIOS-Interrupt 15hex; Funktionen 83hex und 86hex ----------------------------------------------------------- Diese Funktionen benutzen die RTC, um Wartezeiten zu realisieren. Wie das geht, zeigen die Programme intdelay.bas und wait1583.bas. Zeiten messen in dem Sinn, dass eine Zeit angezeigt wird, geht damit nicht. Die RTC wird normalerweise mit einer Freqenz vom 1024 Hz getaktet. Das ergibt eine Aufloesung von 976,56 Mikrosekunden. An beide Funktionen des Interrupts 15hex muss eine Wartezeit in Mikrosekunden uebergeben werden. muss. Eine Aenderung der niederwertigen Stellen der Wartezeit bleibt deshalb ggf. wirkungslos. 5. Anmerkungen zu den BASIC-Beispielprogrammen ---------------------------------------------- 5.1 8253timr.bas ---------------- Ziel des Beispielprogramms war es, die Qualitaet der Messungen anzuzeigen. Dazu wird in der SUB Test eine Anweisung mehrmals ausgefuehrt. Die verstrichene Zeit wird mit Zaehler 0 und Zaehler 2 gemessen und angezeigt. Die Messungen werden permanent wiederholt, bis Escape gedrueckt wird. Wann ist die Messqualitaet gut ? Die Messwerte sollten fuer Zaehler 0 und 2 moeglichst gleich sein. Ausserdem sollten die Messwerte mit moeglichst geringen Abweichungen reproduziert werden. Das erste Kriterium kann durch den Vergleich der Anzeigen von Zaehler 0 und 2 geprueft werden. Das zweite Kriterium wird ueberprueft, indem die "Ausschlaege" der Messwerte in MINimal- und MAXimal-Werten gespeichert und angezeigt werden. Bitte selbst ausprobieren. Das Ergebnis wird beim Compilieren besser sein, als im Interpretermodus. Damit das Programm sofort auf allen Rechnern einsatzbereit ist, muss die Anzahl der zu messenden Anweisungen an die Rechnergeschwindigkeit angepasst werden. Sie muss so dimensioniert sein, dass der maximale Messbereich von 54,925 Millisekunden nicht ueberschritten wird. Da macht die FUNCTION AnzahlAuto&. Sie zaehlt, wie oft innerhalb der 54,925 Millisekunden geschleift wird. Die Haelfte der gemessenen Scheifenzahl wird verwendet. Der Wert der PIT-Messungen liegt dann immer bei etwas unter 25 Millisekunden (25000 Mikrosekunden). Nochmal zur Klarstellung: Die Zeitmessung ist unabhaengig von der Rechner- geschwindigkeit. WAS ich jedoch zu Testzwecken messe, wird natuerlich mal langsamer oder mal schneller ausgefuehrt. 5.2 8253dlay.bas ---------------- 8253dlay.bas soll von der Rechnergeschwindigkeit unabhaengige Delays im Be- reich von Millisekunden erzeugen, und zwar genau! Auf langsamen Rechnern verbraucht der erforderliche Code aber vielleicht schon 0,3 Millisekunden. Deshalb sollte EINMAL im Programm die FUNCTION MKalibrier& aufgerufen werden. Sie ermittelt den Zeitbedarf des Codes und wuerde bewirken, dass (um einen Delay von 1 Millisekunde zu erhalten) nur noch 0,7 Millisekunden geschleift wird. Auf meinem Pentium 100 war das Kalibrieren nicht noetig; beim 386/25 Laptop aber schon. Als šbergabewert erwartet die SUB MDelay& den Zaehlerstand, bei dem mit den Schleifen abgebrochen werden soll. Wir wissen, dass der PIT in 54,925 Millisekunden von 65535 bis Null abwaerts zaehlt. Fuer einen 10 Millisekun- den Delay ist also zu uebergeben: 53603. Warum? Darum: 65536 / 54,925 = 1193,19 (fuer eine Millisekunde) 1193,19 * 10 = 11932 (fuer 10 Millisekunden) 65535 - 11932 = 53603 (gezaehlt wird abwaerts) Warum rechnet das die SUB nicht automatisch aus ? Weil das die moegliche Aufloesung durch die zusaetzlichen Anweisungen ver- schlechtern wuerde (langsame Rechner). Ist das wirklich alles rechnerunabhaengig ? Naja, wenn der Code zum Ausfuehren der SUB MDelay bei langsamen Rechnern schon 2 Millisekunden benoetigt, kann man natuerlich keinen Delay von 1 Millisekunde erzeugen. Wie gross der Eigenbedarf ist, zeigt das Programm an (šberschrift: WERTE DER KALIBRIRUNG). Wie kann man die Qualitaet des Programms ueberpruefen ? Anzahl% auf 30000 erhoehen, das erzeugt 30000 Delays vom 1 Millisekunde, (macht 30 Sekunden), Laufzeit mit der Stoppuhr messen. 5.3 8253timr.bas und 8253dly.bas --------------------------------- * Warum laeuft die Zeit nicht weiter ? Wir aendern bei Zaehler 0 den Stand des Zaehlers, der den Timer aktualisiert. Verwenden wir Zaehler 2, unterdruecken wir den Timer-Interrupt. Bei den Test- programmen tun wir das in Endlosschleifen dauernd hintereinander; bei schnellen Rechnern so schnell hintereinander, dass zu der Zeit wo der Timer- Interrupt dran waere, wir ihn schon wieder unterbinden. Aber: Die Echtzeituhr bleibt unberuehrt. Nach dem Booten stimmt die Uhrzeit wieder. Man kann natuerlich die SUB RTC2Timer aus tmrspeed.bas ergaenzen, die den Timer nach dem Stand der Echtzeit-Uhr aktualisiert. 5.4 mikrotmr.bas ---------------- Das Ding hat echt Nerven gekostet! Das Problem war, dafuer zu sorgen, dass die PIT-Werte den PASSENDEN Systemtimer-Werten zuzuordnen. Meine Versuche haben ergeben, dass der Systemtimer-Zaehler nicht aktualisiert wird, wenn der PIT auf 0 gezaehlt hat oder auf 65535, sondern >>irgendwann<< im Laufe der Timer-Periode. Man weiss nie, ob der PIT-Zaehlerwert zum aktuellen Systemtimer-Zaehlerstand gehoert oder schon zum naechsten. Das hatte zur Folge, dass der Messwert um bis zu knapp 55 Millisekunden schwankte. Das ist der gesamte PIT-Messbereich! Dann man's auch gleich lassen. In einem vergleichbaren Turbo-Pascal Programm wird das Interrupt-Request- Register abgefragt. Liegt ein Interrupt-Request an, wird der Systemtimer inkrementiert. Klingt gut, funktioniert nach meinen Erfahrungen bloss nicht. Die (bis jetzt einzige) Loesung war, vor Messbeginn einen Systemtimer-Sprung abzuwarten. Dann passte der PIT-Wert offensichtlich zum Systemtimer-Zaehler; interessanterweise auch am Ende der Messung! Damit ist das Programm fuer Delays natuerlich nicht mehr geeignet. Beim Start wird ja eine unkalkulier- bare Zeitspanne (max. 55 Millisek.) gewartet. 5.5 intdelay.bas und wait1583.bas --------------------------------- Beide Programme rufen Interrupts auf. Bekanntlich ist das mit QBasic erstmal nicht moeglich. Per CALL ABSOLUTE werden deshalb Maschinencode-Routinen gestartet, die Interrupts aufrufen. Zum Thema Maschinencode mit BASIC gibt's demnaechst mehr auf http://home.arcor.de/steini63. 6. Wo kann man mehr erfahren ? ------------------------------- * Buecher Leider sind Buecher zur Systemprogrammierung rar gesaeht. Ich konnte nur eins ergattern: Hans-Peter Messmer, PC-Hardwarebuch, Addison Wesley, 4. Auflage von 1997, ISBN 3-8273-1086-5, DM 99,90 * Zeitschriften Elektor-Extra 9/98, Seite 12-13: Dipl. Ing. J. C. Felters, Zeitmessung in Visual Basic * Infotext in HARDDOK Im ZIP-Archiv, in dem sich diese Datei befindet, findet sich das Unter- verzeichnis HARDDOK. Dort enthalten sind 4 Textdateien von Peter Weigel. In pit.txt wird der PIT genau beschrieben; auch die Bedeutung der Kontroll- register. Die Textdateien sind Shareware! * Infotext in PCTIM003 Im ZIP-Archiv, in dem sich diese Datei befindet, findet sich das Unter- verzeichnis PCTIM003. Dort enthalten ist die riesige Textdatei pctim003.txt in englischer Sprache von Kris Heidenstrom. Dazu gehoeren noch Beispiel- Programme und Bilder; alles Freeware! Viel Spass!