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.
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