Auf der Schwelle zum Licht: Uhrzeit und Datum

Heute werden wir uns um die diversen Uhren kümmern, die in jedem ST ticken, sowie um die zugehörigen Zeitfunktionen von GEMDOS und XBIOS. Dies ist damit die vorerst letzte Folge dieser Serie über die internen Abläufe des GEMDOS.

Drei Uhren in einem Computer

Auch solch einfache Dinge wie eine Uhr können beim ST zu einer komplizierten Angelegenheit werden. Es gibt nämlich zwei (beim MEGA-ST sogar drei) Uhren, die über zwei verschiedene Teile des Betriebssystems (XBIOS und GEMDOS) gesteuert werden. Alle drei Uhren messen sowohl die Uhrzeit als auch das Datum.

Die Existenz einer internen Uhr bemerkt man als Anwender normalerweise an den Erstellungszeiten der Dateien, die in den Verzeichnissen der Massenspeicher vermerkt sind und vom Desktop bei “zeige Info” oder bei der “Textdarstellung” angezeigt werden.

GEMDOS ist für die Dateiverwaltung zuständig, und daher nenne ich die hier verwendete Uhr “GEMDOS-Uhr”. Sie ist,eine reine Software-Uhr, d.h. sie wird interruptgesteuert von GEMDOS hochgezählt und benötigt keine Hardware. Die GEMDOS-Uhr gibt normalerweise die Zeit an, die für den Anwender von Bedeutung ist.

Die zweite Uhr ist im Tastaturprozessor versteckt. Die “intelligente Tastatur” (IKBD) des ST übernimmt auch die Aufgabe einer Hardware-Uhr, von mir kurz “IKBD-Uhr” genannt. Aus der Sicht des Tastaturprozessors ist sie allerdings auch “nur” eine Software-Uhr. Sie hat zunächst einmal nichts mit der GEMDOS-Uhr zu tun und arbeitet im Verborgenen, d.h. ihre Zeit bekommt man i.allg. nicht zu Gesicht.

Die IKBD-Uhr wird beim Reset noch nicht einmal initialisiert, d.h. sie ist sogar reset-resident. Dies scheint selbst bei ATARI in Vergessenheit geraten zu sein, da es von TOS nicht unterstützt wird.

Im MEGA-ST schließlich findet noch ein richtiger batterie-gepufferter Uhrenchip Verwendung, der dafür sorgt, daß der MEGA-ST die einmal eingestellte Zeit nicht “vergißt”. Diese Uhr möchte ich mit “MEGA-Uhr” bezeichnen.

Die Konstrukteure des ST haben mit diesen drei Uhren also die Voraussetzungen für die zu erwartenden Konfusionen bei der Verwaltung durch TOS geschaffen.

Hinzu kommt, daß die Behandlung der Uhren eines der wenigen Dinge ist, worin sich TOS 1.0 (“altes TOS”) und TOS 1.2 (“Blitter-TOS”) voneinander unterscheiden.

Synchronisierung der Uhren

Kommen wir zunächst zu den Zusammenhängen zwischen den Uhren, bevor wir auf sie im einzelnen zu sprechen kommen.

Die verschiedenen Uhren müssen zu bestimmten Zeitpunkten miteinander synchronisiert werden, denn was nützt eine gepufferte MEGA-Uhr, wenn die für den Anwender entscheidende GEMDOS-Uhr nicht die gleiche Zeit liefert?

Beginnen wir beim Systemstart. Gegen Ende der Reset-Routine des BIOS, als Teil der allgemeinen Initialisierung des GEMDOS, wird die GEMDOS-Uhr gestellt. Beim TOS 1.0 wird das Datum stets auf einen festen Default-Wert gesetzt, der dem “system header block” entnommen wird (siehe hierzu [1]). Dieser Wert ist bekannterweise der 6.2.1986 (beim deutschen TOS). Die Uhrzeit wird stets auf 0:00 gesetzt.

Ab TOS 1.2 geschieht dies nur, wenn keine MEGA-Uhr vorhanden ist (hier ist das Default-Datum der 22.4.1987). Ansonsten wird die GEMDOS-Uhr nach der MEGA-Uhr gestellt, wie wohl nicht anders zu erwarten war.

Doch nun zu den TOS-Routinen für die Uhren. GEMDOS stellt vier Funktionen zum Setzen und Holen von Uhrzeit und Datum zur Verfügung. Diese Funktionen wirken primär auf die GEMDOS-Uhr. Auch das XBIOS hat zwei Zeitfunktionen, Datum und Uhrzeit werden hier allerdings stets gemeinsam behandelt.

Bei TOS 1.0 war alles noch ganz einfach: Die GEMDOS-Funktionen waren nur für die GEMDOS-Uhr zuständig und die XBIOS-Funktionen für die IKBD-Uhr.

Ab TOS 1.2 wirken die XBIOS-Funktionen entweder auf die MEGA-Uhr oder auf die IKBD-Uhr, je nachdem, ob die MEGA-Uhr vorhanden ist oder nicht. Daher möchte ich die über das XBIOS ansprechbare Uhr kurz mit “XBIOS-Uhr” bezeichnen. Die XBIOS-Uhr ist demnach keine weitere Uhr, sondern nur eine übergeordnete Bezeichnung, die je nach TOS-Version und Rechner-Typ die IKBD- oder MEGA-Uhr meint.

Aber auch die GEMDOS-Zeitfunktionen, die Uhrzeit und Datum setzen, verhalten sich ab TOS 1.2 etwas anders. Sie stellen nämlich außer der GEMDOS-Uhr auch die XBIOS-Uhr. Hier zeigt sich also eine kleine Inkompatibilität zu TOS 1.0 selbst bei STs ohne MEGA-Uhr, die sich aber nur bei Programmen bemerkbar machen dürfte, die die XBIOS-Uhr unabhängig von der GEMDOS-Uhr für andere Zwecke als die Darstellung der Systemzeit benutzen.

Dies ist aber noch nicht alles, was zur Synchronisierung der Uhren getan wird. Ab TOS 1.2 wird nämlich bei jeder Programmterminierung mit ‘Pterm’/’Pterm0’/’Ptermres’ die MEGA-Uhr in die GEMDOS-Uhr übertragen. Falls keine MEGA-Uhr vorhanden ist, verhält sich TOS 1.2 hier wie TOS 1.0, d.h. die GEMDOS-Uhr bleibt unangetastet.

Dieser Unterschied wird sich demnach bei Programmen auf MEGA-STs bemerkbar machen, die die XBIOS-Uhr verstellen, ohne die GEMDOS-Uhr beeinflussen zu wollen.

Welche Uhr wozu?

Für den Programmierer von Utilities, die die Systemzeit setzen oder anzeigen sollen (z.B. die beliebten “Uhrzeit-in-der-Menüleiste-Programme”), stellt sich nun die Frage, mit welcher Uhr er denn arbeiten soll - der GEMDOS- oder der XBIOS-Uhr?

Meistens werden nur die GEMDOS-Funktionen benutzt, was unter TOS 1.0 zur Folge hat, daß hard- oder softwaremäßig resident gemachte IKBD-Uhren nicht verändert werden. Im Zweifelsfall empfiehlt es sich daher, die Uhr stets mit GEMDOS und XBIOS zu setzen. Beim Lesen sollte man sich nur auf die GEMDOS-Uhr verlassen, da die XBIOS-Uhr Undefiniert sein kann. Genauso arbeitet übrigens auch das Kontrollfeld-Accessory von ATARI.

Abb. 1 gibt eine Übersicht über die eben dargelegten Zusammenhänge zwischen Programmen, Betriebssystem und der Hardware.

Abb. 1: Zusammenhänge zwischen den Uhren

Direkte Programmierung der XBIOS-Uhr

Der aufmerksame Leser wird bemerkt haben, daß ab TOS 1.2 und vorhandener MEGA-Uhr die IKBD-Uhr weder mit den GEMDOS- noch den XBIOS-Funktionen angesprochen werden kann.

Dies wird im allgemeinen wohl auch nicht notwendig sein, da die MEGA-Uhr quasi ein Ersatz für die IKBD-Uhr ist. Wer aber nicht mitansehen kann, wie die IKBD-Uhr im MEGA-ST brachliegt, dem bleiben noch die direkten Kommandos an den Tastaturprozessor, die z.B. mit der XBIOS-Funktion ‘Ikbdws’ abgeschickt werden können. Die Programmierung der IKBD-Uhr ist so aber etwas aufwendig, daher kann hier nicht näher darauf eingegangen werden (siehe [2]).

Auch die MEGA-Uhr kann natürlich direkt über die Hardware-Register des Uhrenchips programmiert werden, was aber nicht im Sinne des Erfinders, sprich der XBIOS-Zeitfunktionen, liegt.

Interessant ist höchstens zu wissen, wie TOS merkt, ob eine MEGA-Uhr vorhanden ist oder nicht.

Dazu muß man wissen, daß der Uhrenchip nämlich auch über eine Alarmfunktion verfügt, die allerdings unter TOS nicht so ohne weiteres nutzbar ist. Das XBIOS schreibt nun in die Register für die Minuten der Alarmzeit unsinnige Werte und überprüft, ob dies geklappt hat. Wenn kein Uhrenchip angeschlossen ist, erhält man beim Lesen immer $FF (hier gibt es ausnahmsweise einmal keinen Bus Error!), ansonsten die gerade hineingeschriebenen Werte.

Eine genaue Beschreibung aller Register und Funktionen des Uhrenchips ist in [2] nachzulesen.

Formate für Uhrzeit und Datum

Uhrzeit und Datum werden in GEMDOS jeweils durch einen 16-Bit-Integer repräsentiert, die Kodierung findet sich in Abb. 2 und 3. Wie manches andere auch sind diese Formate MS-DOS-kompatibel.

Der Wert im Jahresfeld könnte zwischen 0 und 127 liegen, erlaubt sind aber nur welche bis 119, so daß mit dem GEMDOS-Format alle Daten zwischen dem 1.1.1980 und 31.12.2099 dargestellt werden können.

Die Darstellung der Uhrzeit hat leider den Nachteil, daß sie nur auf 2 Sekunden genau zählt, was für Programme, die ständig die aktuelle Zeit benötigen, Komplikationen mit sich bringen kann. Daher kann es hier sinnvoll sein, direkt die IKBD- bzw. MEGA-Uhr anzusprechen, da diese sekundenweise zählt.

Auch die in den Verzeichnissen der Massenspeicher und den internen Datenstrukturen des GEMDOS gespeicherten Zeiten liegen in diesem Format vor, allerdings eventuell im Intel-Format, d.h. mit vertauschten Low und High Bytes.

Die XBIOS-Funktionen verwenden im Prinzip das gleiche Format wie GEMDOS, mit dem Unterschied, daß Uhrzeit und Datum in einem 32-Bit-Integer zusammengefaßt werden (Abb. 4).

Der Tastaturprozessor verwendet bei der Kommunikation mit TOS sein eigenes Format, das von den XBIOS-Funktionen in das GEMDOS-Format umgerechnet werden muß. Abb. 5 zeigt das Format des Statuspakets, das der Tastaturprozessor abschickt, wenn er gefragt wird, wie spät es ist.

Der Uhrenchip hat für jede Ziffer von Datum und Zeit ein eigenes 4-Bit-Register. An dieser Stelle möchte ich wieder auf [2] verweisen. Auch hier übernehmen natürlich die XBIOS-Routinen die Konvertierung ins XBIOS-Format.

IKBD- und MEGA-Uhr kennen nur zweistellige Jahreszahlen, so daß deswegen nur Daten bis zum 31.12.1999 darstellbar sind. Das dürfte aber wenig tragisch sein, da im Jahre 2000 “GEMDOS" bestenfalls im Museum, Abteilung “Mißratene Betriebssysteme”, zu finden sein wird.

Arbeitsweise der GEMDOS-Uhr

Kommen wir nun so langsam auf die Arbeitsweise des GEMDOS zu sprechen, die ja das eigentliche Thema dieser Serie ist.

Die globalen Variablen des GEMDOS, die für die Verwaltung der GEMDOS-Uhr benötigt werden, sind in Abb. 6 zusammengestellt. Die Adressen beziehen sich wie üblich auf TOS 1.0 bzw. TOS 1.2 und ändern sich bei zukünftigen TOS-Versionen.

15...9 8...5 4...0 (Bit)
Jahr - 1980 Monat Tag
0..119 1..12 1..31

Abb. 2: GEMDOS-Format für Datum

15...11 10...5 4 (Bit)
Stunde Minute Sek./2
0..23 0..59 0..29

Abb. 3: GEMDOS-Format für Uhrzeit

31...16 15...0 (Bit)
Datum im GEMDOS-Format Zeit im GEMDOS-Format

Abb. 4: XBIOS-Format für Uhrzeit/Datum

Bei der Initialisierung des GEMDOS beim Systemstart wird mit der BIOS-Funktion ‘Setexc‘ eine Interrupt-Routine im 'etv_timer'-Vektor eingehängt, die somit alle 20 ms aufgerufen wird. Der alte Wert des Vektors wird in ‘otimer‘ festgehalten.

Der Stand der GEMDOS-Uhr, also die aktuelle Uhrzeit und das Datum stehen in 'dos_time‘ und ‘dos_date‘, und zwar beide direkt im GEMDOS-Format.

Die GEMDOS-'etv_timer'-Routine ruft die eigentliche, die Uhr weitersetzende Routine ‘t_timer‘ auf und übergibt ihr als Parameter die seit dem letzten 'etv_timer‘-Interrupt vergangene Zeit in Millisekunden, die sie selbst auf dem Stack übergeben bekommt. Anschließend springt sie über den 'otimer'-Vektor.

't_timer' ist in ‘C’ programmiert, was für eine Interrupt-Routine doch ein wenig ungewöhnlich ist. Glücklicherweise macht sie nicht sehr viel, so daß keine allzu großen Zeitverluste zu befürchten sind.

Die seit dem Start von GEMDOS insgesamt vergangene Zeit wird in 'dosjns mitgezählt. Der Sinn ist nicht ganz klar, da 'dos_ms' sonst nirgendwo im GEMDOS Verwendung findet und auch keine legal zugängliche Variable ist.

Außerdem wird in 'last_ms' die seit dem letzten Weitersetzen der GEMDOS-Uhr verstrichene Zeit vermerkt. Solange dieser Wert unter 2000 (= 2 Sekunden) bleibt, terminiert 't_timer' hier.

Ansonsten wird die GEMDOS-Uhr um einen 2-Sekunden-Schritt weitergesetzt. Dazu wird zunächst ‘last_ms' um 2000 erniedrigt, 'dos_time' wird um Eins erhöht, und wenn das Sekundenfeld noch nicht 30 (entspricht 30*2=60 Sekunden) erreicht hat, beendet sich 't_timer' hier.

Andernfalls werden die Sekunden auf Null zurückgesetzt und die Minuten um Eins erhöht. Nun werden die Minuten geprüft... den Rest kann sich jeder selbst denken; die ganze Routine besteht fast nur aus dem Ausmaskieren und Verändern bestimmter Felder in 'dos_time‘ und 'dos_date'.

Die einzige Schwierigkeit ergibt sich beim Übertrag der Monate, deren Länge bekanntlich unterschiedlich sein kann. GEMDOS hat dazu in seinen statischen Variablen noch das Feld ‘monthl‘ in dem die "normalen" Monatslängen abgelegt sind. Nur die normalen Schaltjahre (“4er-Regel”) werden berücksichtigt, die “100er-Regel” und “400er-Regel” kennt GEMDOS nicht. Da es bis zum Jahr 2099 sowieso keine Ausnahmen von der “4er-Regel” gibt, ist das aber in Ordnung.

Zu ‘t_timer’ gibt es höchstens noch zu bemerken, daß es zwei überflüssige Abfragen enthält, die glücklicherweise aber wenigstens nicht falsch sind.

Es stellt sich die Frage, warum es überhaupt noch eine solche “Software-Uhr” gibt. Die IKBD- bzw. die MEGA-Uhr würde doch vollkommen ausreichen. GEMDOS könnte das Lesen und Setzen der Uhr doch direkt vom XBIOS, das die Hardware ansteuert, erledigen lassen. Dies würde Rechenzeit und Programmkode sparen und die aufgezeigten Probleme, die die zwei bis drei Uhren mit sich bringen, vermeiden. Diese antiquierte Interrupt-Uhr hat mich jedenfalls gleich wieder an den VC-20 und C64 erinnert, die auch schon so arbeiteten.

Wahrscheinlich ist wieder einmal die Tatsache Schuld, daß GEMDOS für PCs entwickelt wurde, bevor man es auf den ST portiert hat. Dort wollte man vielleicht dem BIOS keine eigenen Zeitfunktionen zumuten bzw. Hardware-Uhren waren nicht vorgesehen. ATARI war offensichtlich nicht in der Lage, dies zu verbessern, oder wollte es womöglich gar nicht.

typedef struct
{   char header;    /* $FC, Kennung für Uhrzeit-Statuspaket */
    char jahr;      /* Jahreszahl   in gepackter BCD-Form   */
    char monat;     /* Monatszahl   in gepackter BCD-Form   */
    char tag;       /* Tageszahl    in gepackter BCD-Form   */
    char stunde;    /* Stundenzahl  in gepackter BCD-Form   */
    char minute;    /* Minutenzahl  in gepackter BCD-Form   */
    char sec;       /* Sekundenzahl in gepackter BCD-Form   */
} TIMEOFDAY;

Abb. 5: IKBD-Format für Uhrzeit/Datum

long otimer;            /*$1672/$16d2: 'etv_timer'-Vektor vor GEMDOS-Init.*/
long dos_ms;            /*$5ffa/$879c: ms seit GEMDOS-Initialisierung     */
int last_ms;            /*$415a/$68fc: ms seit letztem Setzen der Uhr     */
unsigned int dos_date;  /*$609e/$8840: akt. Datum im GEMDOS-Format        */
unsigned int dos_time;  /*$4e0e/$75b0: akt. Zeit im GEMDOS-Format         */
int monthl[] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };
                        /* $17b7e/$fd1c8e/$fd30ba                         */

Abb. 6: GEMDOS-Variablen für Zeitfunktionen

GEMDOS-Funktionen (TRAP #1)

Funktion $2a Tgetdate

int Tgetdate()

Liefert das Datum der GEMDOS-Uhr im GEMDOS-Datumformat. Die Funktion ist allerdings eigentlich als dong'-Funktion definiert.

Arbeitsweise

Es wird direkt 'dos_date' zurückgegeben. Da 'Tgetdate' als 'long'-Funktion definiert wurde, wird 'dos_date' vorher noch auf 'long' vorzeichenerweitert. Dies deutet darauf hin, daß ‘dos_date' im Originalquelltext von Digital Research als 'int' und nicht als ‘unsigned int' definiert wurde.

Funktion $2b Tsetdate

int Tsetdate(int date)

Setzt 'date' im GEMDOS-Datumformat als aktuelles Datum der GEMDOS-Uhr.

Rückgabewerte:

-1L (ERROR) 'date' ist keine gültige Datumsangabe im GEMDOS-Datumformat
0L(EOK) alles ok

Arbeitsweise

Als erstes wird überprüft, ob ‘date’ dem GEMDOS-Datumformat entspricht. An Jahreszahlen sollte eigentlich alles über 2099 abgewiesen werden, was aber aufgrund eines Vorzeichenfehlers nicht erfolgt ('date' müßte als 'unsigned int' deklariert sein).

Beim Monat wird 0 und alles über 12 als Fehler erkannt. Der Tag wird korrekt in Abhängigkeit von der Monatslänge und einem eventuellen Schaltjahr überprüft, nur 0 wird nicht als Fehler angesehen.

Nun wird 'date' nach 'dos_date' übertragen. Ab TOS 1.2 werden 'dos_date' und 'dos_time' mit XBIOS-'Settime' auch in der XBIOS-Uhr gesetzt.

Funktion $2c Tgettime

int Tgettime()

Liefert die Uhrzeit der GEMDOS-Uhr im GEMDOS-Zeitformat. Die Funktion ist allerdings eigentlich als ‘long’-Funktion definiert.

Arbeitsweise

Es wird direkt 'dos_time' zurückgegeben. Wie bei 'Tgetdate' erfolgt eine Vorzeichenerweiterung auf ‘long’.

Funktion $2d Tsettime

int Tsettime(int time)

Setzt 'time' im GEMDOS-Zeitformat als aktuelle Zeit der GEMDOS-Uhr.

Rückgabewerte:

-1L (ERROR) 'time' ist keine gültige Zeitangabe im GEMDOS-Zeitformat
0L (EOK) alles ok

Arbeitsweise

Zunächst wird geprüft, ob 'time' dem GEMDOS-Zeitformat entspricht. Werte im Sekundenfeld größer/gleich 30 und Minuten über 59 werden als Fehler abgewiesen.

Stunden größer als 23 sollten ebenfalls mit einer Fehlermeldung quittiert werden, doch ein Vorzeichenfehler verhindert dies (Sie erraten es sicher schon: 'time' müßte als 'unsigned int' deklariert werden).

Nun wird 'time' 'dos time' zugewiesen. Ab TOS 1.2 werden 'dos_date' und 'dos_time' mit XBIOS-Settime auch ans XBIOS weitergegeben.

XBIOS-Funktionen (TRAP #14)

Funktion $16 Settime

void Settime(unsigned long datetime)

Setzt Uhrzeit und Datum 'datetime' im XBIOS-Format als aktuelle Uhrzeit und Datum in der XBIOS-Uhr.

Arbeitsweise

Ab TOS 1.2 und vorhandener MEGA-Uhr wird der Uhrenchip entsprechend programmiert (siehe [2]).

Ansonsten wird 'datetime' in das IKBD-Format konvertiert und der Kommandoblock zum Setzen der IKBD-Uhr an den Tastaturprozessor ('time-of-day clock set', Kommando $1b) wird abgeschickt.

Da beim IKBD-Format ausschließlich zweistellige Jahreszahlen möglich sind, werden nur Jahreszahlen bis 1999 zugelassen. Durch eine ungeschickte Programmierung der Konvertierroutine im XBIOS führen größere Jahreszahlen i.allg. zu einer unzulässigen Jahresangabe im IKBD-Format.

Anschließend wird die IKBD-Uhr nochmals gelesen (wie bei ‘Gettime'), allerdings wird die Ausführung nicht abgewartet und die Zeit wird auch nicht zurückgegeben, daher ist mir der Sinn dessen nicht klar.

Für die Gültigkeitskontrolle ist allein der Tastaturprozessor zuständig. Laut [2] wird jeder Parameter nur einzeln geprüft, so daß für die Tagesangabe des Datums pauschal alle Werte von 1 bis 31 zugelassen werden. Ungültige Parameter werden einfach nicht verändert.

Funktion $17 Gettime

unsigned long Gettime()

Liefert die Uhrzeit und das Datum der XBIOS-Uhr im XBIOS-Format.

Arbeitsweise

Ab TOS 1.2 und vorhandener MEGA-Uhr wird der Uhrenchip ausgelesen (siehe [2]). Die Sekunden werden auf die nächste gerade Zahl abgerundet.

Dem Tastaturprozessor wird ein ‘Interrogate time-of-day clock’-Kommando (Kommando $1c) übermittelt. Daraufhin wird ein Statuspaket zurückgesendet, das von einer Interrupt-Routine empfangen und anschließend ins XBIOS-Format konvertiert wird. Dabei werden auch hier die Sekunden auf die nächste gerade Zahl abgerundet.

Gettime’ wartet, bis diese Interrupt-Routine ihre Arbeit beendet hat, und liefert dann die schon konvertierte Zeit zurück. Da kein Abbruch durch Timeout vorgesehen ist, muß gewährleistet sein, daß ein Empfang des Statuspakets auch tatsächlich möglich ist. Insbesondere darf der MFP für IKBD-Interrupts nicht gesperrt werden, und der Interrupt-Level des 68000-Prozessors darf nicht 6 oder 7 sein.

Hilfsroutinen

Zu den Zeitfunktionen von GEMDOS und XBIOS sind einige Hilfsfunktionen in Listing 1 angegeben. Sie arbeiten hauptsächlich mit der GEMDOS-Uhr. Das verwendete ASCII-Format für Datum und Zeit ist übrigens besonders gut für edierbare Felder in GEM-Dialogboxen geeignet.

Hier noch ein kleiner Tip fürs Programmieren: Es empfiehlt sich, Datums- und Zeit-Variablen stets als 'unsigned int’ zu definieren, da so lästige Vorzeichenfehler vermieden werden. Dann können z.B. zwei Zeiten direkt miteinander verglichen und einzelne Felder problemlos mit und ‘>>’ isoliert werden.

Residente Uhr

Die IKBD-Uhr kann durch eine kleine Bastelei leicht batteriegepuffert werden, so daß auch bei den “kleinen” STs die Uhr nicht ständig neu gestellt werden muß (siehe [3]).

Es gibt aber auch eine Teillösung, die ohne Hardware-Zusatz realisiert werden kann. Da die IKBD-Uhr schon reset-resident ist, kann wenigstens erreicht werden, daß die Zeit nur beim Ausschalten des Rechners (bzw. beim Kaltstart) und nicht schon bei einem einfachen Reset (Warmstart) verlorengeht.

Das TOS bräuchte beim Reset eigentlich nur die IKBD-Uhr zu lesen und die GEMDOS-Uhr danach zu stellen, falls die IKBD-Zeit “vernünftig” ist. Diese Plausibilitätskontrolle würde dafür sorgen, daß eine nach dem Einschalten unsinnige IKBD-Uhr nicht berücksichtigt wird.

Damit bräuchte man bei STs ohne MEGA-Uhr nur nach jedem Einschalten des Rechners die Uhr neu zu stellen.

Eine Routine, die so etwas leistet, ist als Listing 2 abgedruckt. Sie kann leicht in ein Programm für den AUTO-Ordner integriert werden, das sogar ein Stellen der GEMDOS-Uhr ermöglichen könnte, wenn die Routine die GEMDOS-Uhr nicht sinnvoll setzen kann. Außer der XBIOS-Uhr wird auch noch die GEMDOS-Uhr auf eine sinnvolle Zeit überprüft, um Treiber für fremde Hardware-Uhren zu berücksichtigen, die die GEMDOS-Uhr schon gestellt haben könnten.

Diese Routine könnte man sicher auch ins TOS patchen, doch wäre dies nur ab TOS 1.2 sinnvoll, da im TOS 1.0 die GEMDOS-Zeitfunktionen die XBIOS-Uhr nicht setzen. Außerdem hatte ich, als ich erfuhr, daß die IKBD-Uhr reset-resident ist, schon einen anderen Patch, der ganz anders arbeitet (Abb. 7).

Beim Reset werden nämlich alle globalen Variablen des GEMDOS gelöscht. Der Trick besteht nun darin, vor diesem Löschen die GEMDOS-Variablen ‘dos_date’ und ‘dos_time’ zu retten und anschließend wieder zurückzusetzen.

Nach dem Einschalten, also beim Kaltstart, werden diese Variablen von einer noch eher arbeitenden Routine ebenfalls gelöscht. Damit kann die Patch-Routine an einem “gelöschten” Datum erkennen, ob ein Kaltstart vorliegt, und in diesem Fall das Default-Systemdatum des “system header blocks” zur Initialisierung der GEMDOS-Uhr verwenden.

Der Patch ist deswegen so lang geraten, weil es an den entscheidenden Stellen des TOS keine Möglichkeiten zum Einfügen kleiner Änderungen gab. Daher wurde die XBIOS-Routine ‘Random’ drastisch verkürzt (sie war selten umständlich programmiert), um etwas Platz zu schaffen.

Da die Initialisierung der GEMDOS-Uhr ab TOS 1.2 anders ist, funktioniert der Patch in dieser Form nur beim TOS 1.0. Und zum Umschreiben konnte ich mich auch noch nicht aufraffen (vermutlich, mein MEGA-ST auch so ganz gut mit der Uhrzeit klarkommt).

# Patch für ROM-TOS 1.0 vom 6.2.1986:

fc01ce 4d fa 00 06 60 00 11 82
fc03c4 60 0c      fc132c 20 38 29 b8 66 0c 20 38 04 ba 72 10 e3 a0 80 b8
       04 ba 2f 3c bb 40 e6 2d 2f 00 61 00 38 9c 50 8f
       52 80 21 cO 29 b8 e0 88 4e 75 32 38 60 9e 66 06
       32 39 00 fc 00 1e 34 38 4e Oe 70 00 30 c0 b3 c8
       66 fa 31 c2 4e 0e 31 c1 60 9e 4e d6

# Patch für RAM-TOS 1.0 vom 6.2.1986:

00615c 4d fa 00 06 60 00 10 82
006352 60 0c      0071ba 20 38 29 b8 66 0c 20 38 04 ba 72 10 e3 a0 80 b8
       04 ba 2f 3c bb 40 e6 2d 2f 00 61 00 38 9c 50 8f
       52 80 21 c0 29 b8 e0 88 4e 75 32 38 60 9e 66 06
       32 39 00 00 61 1e 34 38 4e 0e 70 00
       66 fa 31 c2 4e 0e 31 c1 60 9e 4e d6

Abb. 7: Patch für reset-residente Uhr

Das war’s...

...fürs erste. In 11 Ausgaben der ST Computer der vergangenen 15 Monate habe ich Ihnen nun einiges über die Programmierung und Arbeitsweise des GEMDOS erzählt.

Mir, und hoffentlich auch Ihnen, hat das viel Spaß gemacht, und ich hoffe. Sie haben dabei wirklich Neues gelernt und Ihre Neugier etwas befriedigen können, auch wenn vielleicht noch manche Frage offen und einige Rätsel ungelöst geblieben sind.

Hier möchte ich mich vor allem bei denen bedanken, die noch weitere TOS-Fehler gefunden und zum Teil analysiert haben.

Die meisten bekannten Fehler wurden ATARI inzwischen mitgeteilt, und die Zukunft wird zeigen, ob wir dafür mit einem funktionstüchtigeren TOS für so manchen Ärger versöhnt werden.

Da das neue TOS 1.4, von dem Sie sicherlich schon gehört haben, hoffentlich bald fertig ist, verzichte ich vorläufig darauf, der Serie noch einige Ergänzungen und Korrekturen anzuschließen, was hauptsächlich eine Diskussion weiterer “alter” GEMDOS-Fehler wäre.

Denn, soviel kann man schon vorwegnehmen, im neuen TOS hat sich auch viel im GEMDOS verändert; es gibt viele Fehlerkorrekturen und Verbesserungen, aber auch Negatives zu berichten.

Doch halten wir uns hier erst einmal zurück, bis die endgültige, im übrigen schon für November ’88 angekündigte, Version von TOS 1.4 erscheint, und möchten kein vorschnelles Urteil fällen.

Alex Esser

Literatur:

[1] A. Esser: Die Systemvariablen des TOS, ST Computer 11/88

[2] Jankowski/Rabich/Reschke: Das ATARI ST Profibuch, Sybex-Verlag GmbH 1988

[3] Low Cost Uhr für den ATARI ST, ST Computer 6/86

/*  Hilfsroutinen für die Systemzeit
    ----------------
    (c) 1987 A.Esser
    entwickelt mit MEGAMAX C,
    Application Systems Heidelberg

    Aufrufe:
    unsigned int date_to_a(unsigned int date, char *str) 
        wandelt Datum (GEMDOS-Format) nach ASCII-String der Form TTMMJJ 
        date = DOSCLK: System-Datum (GEMDOS-Uhr) nehmen 
    unsigned int a_to_date(char *str, int set) 
        wandelt ASCII-String der Form TTMMJJ nach Datum (GEMDOS-Format) 
        set = TRUE: Datum als System-Datum setzen und 'str' korrigieren 
    unsigned int time_to_a(unsigned int time, char *str) 
        wandelt Zeit (GEMDOS-Format) nach ASCII-String der Form SSMM 
        time = DOSCLK: System-Zeit (GEMDOS-Uhr) nehmen
    unsigned int a_to_time (char *str, int set)
        wandelt ASCII-String der Form SSMM nach Zeit (GEMDOS-Format)
        set = TRUE: Zeit als System-Zeit setzen und str korrigieren 
    void cva(int num, char *str) 
        wandelt 8-Bit-Integer in 2-Byte-ASCII-String (nullterminiert) 
    unsigned int cvi(char *str)
        wandelt 2-Byte-ASCII-String in 8-Bit-Integer 
    long bios_clock (unsigned date, unsigned int time)
        XBIOS-Uhr setzen, sofort lesen und im XBIOS-Format zurück
*/

#include <toslib.h>
#include <osbind.h>

#define TRUE 1 
#define FALSE 0 
#define DOSCLK 0xFFFF

/* 1-Byte-Integer -> 2-stelliges ASCII */ 
void cva(num,str) 
int num;
register char *str;
{   asm
    {   moveq   #0,D0
        move.w  num(A6),D0 
        divu    #10,D0
        or.l    #0x300030,D0 
        move.b  D0,(str)+ 
        swap    D0
        move.b  D0,(str)+ 
        clr.b   (str)
    }
}

/* 2-Byte-Dezimal-ASCII nach 1-Byte integer */ 
unsigned int cvi(str) 
register char *str;
{   asm
    {   move.b  (str)+,D0 
        and.w   #15,D0
        mulu    #10,D0
        move.b  (str)+,D1 
        and.w   #15,D1
        add.w   D1,D0
    }
}

/* System-Datum holen */ 
unsigned int date_to_a(date,str) 
register unsigned int date; 
register char *str;
{   int year;

    if (date == DOSCLK) 
        date = Tgetdate(); 
    cva(date & 31,str); 
    cva((date >> 5) & 15,str+2);
    /* bei Jahr 2 MSB ignorieren und auf 99 beschränken */ 
    year = ((date >> 9) & 31) + 80; 
    cva(min(year,99), str+4); 
    return date;
}


/* System-Zeit holen */ 
unsigned int time_to_a(time,str) 
register unsigned int time; 
register char *str;
{   if (time == DOSCLK)
        time = Tgettime(); 
    cva((time >> 11) & 31,str); 
    cva((time >> 5) & 63,str+2); 
    return time;
}


/* System-Datum setzen */ 
unsigned int a_to_date(str,set) 
register char *str; 
int set;
{   register unsigned int date;

    date = cvi(str) | cvi(str+2)<<5 | (cvi(str+4)-80)<<9;

    if (set)
    {   Tsetdate(date);
        return date_to_a(DOSCLK,str);
    }
    else
        return date;
}

/* Systemzeit setzen */
unsigned int a_to_time(str,set)
register char *str;
int set;
{   register unsigned int time;

    time = cvi(str+2)<<5 | cvi(str)<<11;
    if (set)
    {   Tsettime(time);
        return time_to_a(DOSCLK,str);
    }
    else
        return time;
}


/* XBIOS-Uhr setzen */

unsigned long bios_clock(date,time)
unsigned int date;  /* Datum */
unsigned int time;  /* Zeit */
{ 
    asm
    {
        move.w  date(A6),D0
        swap    D0
        move.w  time(A6),D0     ;Datum/Zeit im BIOS-Format
        move.l  D0,-(A7)
        move.w  #22,-(A7)       ;Settime
        jsr     xbios
        move.w  #23,(A7)        ;Gettime (Rückkontrolle)
        jsr     xbios
        addq.w  #6,A7           ;Stackkorrektur für beide TRAPS
        ;D0 enthält neues Datum der Uhr 
    }
}

Listing 1: Hilfsroutinen zur Systemzeit

sysbase  = $4f2 
TGETDATE = 42 
TSETDATE = 43 
TSETTIME = 45
GETTIME  = 23
SUPEXEC  = 38

ikbd_time:
    move.w  #TGETDATE,-(A7)     ;GEMDOS-Uhr (Datum) lesen
    trap    #1
    addq.w  #2,A7
    move.w  D0,D2               ;Datum merken

    pea     get_sysdate(PC)     ;Default-System-Datum nach D0 
    move.w  #SUPEXEC,-(A7)
    trap    #14
    addq.w  #6,A7
    cmp.w   D0,D2               ;GEMDOS-Uhr neuer?
    bhi.s   ret                 ;-> ja: Datum ist gültig

    move.w  #GETTIME,-(A7)      ;BIOS-Uhr lesen 
    trap    #14
    addq.w  #2,A7
    move.w  D0,D1               ;Zeit merken
    swap    D0                  ;Datum holen
    cmp.w   #$2800,D0           ;"vernünftiges" Datum?
    bcc.s   ret                 ;-> nein: Uhr bleibt unverändert 
    cmp.w   D2,D0               ;Vergleich mit GEMDOS-Datum
    bcs.s   ret                 ;-> echt kleiner: BIOS-Uhr stimmt nicht

    move.w  D0,-(A7)
    move.w  #TSETDATE,-(A7)     ;BIOS-Datum -> GEMDOS-Datum
    trap    #1
    addq.w  #4,A7
    move.w  D1,-(A7)
    move.w  #TSETTIME,-(A7)     ;BIOS-Zeit -> GEMDOS-Zeit 
    trap    #1
    addq.w  #4,A7
ret:
    rts 

get_sysdate:                    ;nur Supervisor-Mode!
    move.l  sysbase,A0
    move.w  30(A0),D0           ;System-Default-Datum holen
    rts

Listing 2: Residente Uhr


Alex Esser
Aus: ST-Computer 02 / 1989, Seite 64

Links

Copyright-Bestimmungen: siehe Über diese Seite