In dem ersten Teil unserer Serie über das Betriebssystem des Atari-ST wurde über die allgemeine Ausgabe eines Betriebssystems berichtet. Ebenso wurde über die Unterteilung des TOS in einem Hardware-unabhängigen Teil (GEM-DOS genannt) und einem Hardwareabhängigen Teil (BIOS und XBIOS) gesprochen.
Wir haben einen Teil der wichtigen Routinen des GEMDOS in unserem ersten Teil schon behandelt Diesmal möchten wir mit der GEMDOS Routine fortfahren.
Wegen eines Fehlers bei der Zusammenstellung unserer Zeitung wurde die Tabelle aller 51 Routinen der GEMDOS nicht als Tabelle gedruckt, sondern als fortlaufender Text, der natürlicherweise Verwirrung stiftete. Deswegen sehen Sie noch einmal in Bild 1 alle 51 Routinen abgebildet.
SET DATA
MOVE.L # PUFFER, — (SP) ;Adresse 44-Byte Puffer
MOVE.W #$1A, — (SP) .-Funktionsnummer
TRAP #1 ;GEMDOS Aufrufen
ADDQ.L #6,SP ;Stack Korrektur
Durch Set Disk Transfer wird die Adresse eines 44 Byte langen Puffers festgelegt, die für verschiedene Disketten-Operationen nötig ist.
Diese Funktion steht in enger Beziehung zu einem wichtigen Merkmal des 68 000 Mikroprozessors. Der 68 000 CPU kann in zwei Ebenen arbeiten: der sogenannten USER-Ebene und die SUPERVISOR-EBENE. Es gibt bei dem Befehlsvorrat dieses CPU’s eine Anzahl an Befehlen (Privilegierte), die nur in SUPERVISOR-Modus zu erreichen sind. Durch das S-Bit im Status-Register wird bestimmt, ob der Mikroprozessor sich in dem einen oder anderen Modus befindet. Bei dem Atari-ST gelingt es durch die Funktion SUPER, den SUPERVISOR-Modus zu erreichen. Diese Funktion ist für Programmierer, die Peripheriebausteine oder die Systemvariablen manipulieren wollen, unerläßlich. Alle diese Adressen sind sozusagen „geschützt“ und nur in SUPERVISOR-Modus erreichbar. Wir werden im Anhang ein Beispiel für die Wirkung dieser Funktion demonstrieren. Es gibt bei den Systemvariablen, bei Adresse $4EE, einen Flag, der für die Bildschirm-Hardcopy verantwortlich ist. Wird ALT und HELP gedruckt, so wird der Inhalt von $4EE um 1 inkrementiert und eine Hardcopy aufgelöst. In unserem Beispiel tun wir nichts anderes. Nachdem wir in SUPERVISOR-Modus gelandet sind, verändern wir den Inhalt dieser Systemvariablen und verursachen damit eine Hardcopy des Bildschirms.
Die GEMDOS Funktionen $30 GEMDOS NUMBER
$00 TERM $31 KEEP PROCESS
$01 CONIN $36 GET DISK FREE SPACE
$02 CONOUT $39 MKDIR
$03 AUXIN
$04 AUXOUT $3A RMDIR
$03 PRINTOUT $3B CHDIR
$06 RAWCONIO $3C CREATE
$07 CONIN WITHOUT ECHO $3D OPEN
$08 GONIN WITHOUT ECHO $3E CLOSE
$09 PLINE $3E READ
$0A READLINE $40 WRITE
$0B CONSTAT $41 UNLINK
$0E SETDRV $42 LSEEK
$10 CONOUT STAT $43 CHANGE MODE
$11 PRTOUT STAT $45 DUP
$12 AUXINSTAT $46 FORCE
$13 AUXOUT STAT $47 GETDIR
$19 CURRENT DISK $49 MFREE
$1A SET DTA $4A SETBLOCK
$20 SUPER $4B EXEC
$2A GET DATE $4C TERM
$2B SET DATE $4E SFIRST
$2C GET TIME $4F SNEXT
$2D SET TIME $5b RENAME
$2F GET DTA $57 GSDTOF
SUPER CLR.L -(SP)
MOVE.W #$20,-(SP) ;Funktionsnummer
TRAP # 1 ;GEMDOS Aufrufen
ADDQ.L #6,SP ;Stack Korrektur
MOVE.L D0,A2 ;Stack merken
MOVE.L #$1,$4EE ;1 in $4EE
MOVE.L A2,-(SP) ;Alten Wert in SP
MOVE.W #$20,-(SP)
TRAP #1
ADDQ.L #6,SP
CLR -(SP)
TRAP #1 END
Durch diese Funktion läßt sich das eingestellte Datum auf dem Kontrollfeld ermitteln. Nach Aufruf von GET DATE steht das Datum in dem LOW-WORD des Registers DO. Allerdings ist das Datum in eine kodierte Form gepackt, so daß diese noch verarbeitet werden müssen.
LOW-WORD Register DO
Bit 0—4 = Tag
Bit 5-8 = Monat
Bit 9-15 = Jahr + 1980
GET DATE
MOVE.W #$2A, — (SP) ;Funktionsnummer
TRAP #1 ;GEMDOS aufrufen
ADDQ.L #2, SP ;Stack Korrektur
Inhalt von Register DO:
DO = 00000C78
SET DATE bildet das Gegenstück der oben beschriebenen Funktion. Dadurch läßt sich das Datum einstellen. Eine solche Funktion ist interessant für Anwenderprogramme, zu denen das Datum mit zum Protokoll gehört. Insofern kann man eine Routine einbauen, in der der Anwender gefordert wird, das aktuelle Datum einzugeben. Das Datum wird der Funktion SET DATE in ein Word übergeben in folgendem Format:
Bit 0—4 = Tag
Bit 5-8 = Monat
Bit 9-15 = Jahr - 1980
SET DATE
MOVE.W #%0001001010011000, - (SP)
MOVE.W #$2B, - (SP)
TRAP #1 ADDQ.L #6,SP
0001001010011000 entspricht 24.4.1989 #%0001001010011000 = $1298
Die Funktion GET TIME verhält sich analog zu GET DATE, aber es wird bei dem Aufruf die Uhrzeit geliefert. Nach Ausführung von GET TIME steht in LOW-Word des Registers DO die aktuelle Uhrzeit zur Verfügung. Das Uhrzeit-Format sieht folgendermaßen aus:
Bit 0—4 = Sekunden (muß mit 2 multipliziert werden)
Bit 5-10 = Minuten
Bit 11-15 = Stunden (24h Format)
GET TIME
MOVE.W #$2C, - (SP)
TRAP #1
ADDQ.L #2,SP
Inhalt von Register DO :
DO = 00001A16 entspricht 3,23
Dadurch läßt sich die Uhrzeit einstellen. Die gewünschte Uhrzeit wird als Wort-Länge auf dem Stack übergeben. Das Format sieht genauso aus wie bei GET TIME.
Bit 0-4 = Sekunden
Bit 5—10 = Minuten
Bit 11-15 = Stunden
SET TIME
MOVE.W #%0001101010101001, -(SP)
MOVE.W #$2D, - (SP)
TRAP #1
ADDQ.L #6,SP
00011010101010011 entspricht 3,22
Die Funktion GET DATA ist das Gegenteil der Funktion SET DATA. Nach Aufruf dieser Funktion wird die momentane Adresse eines Puffers für Disketten-Daten-Transfer im Register DO übergeben.
GET DATA
MOVE.W #$2F, - (SP)
TRAP #1
ADDQ.L #2,SP
Inhalt von Register DO :
DO = 000ADA52
Nach Ausführung von GETVNUMBER erhält man im LOW-WORD des Registers DO die derzeitige Versions-Nummer des GEMDOS.
GET VERSION-NUMBER
MOVE.W #$30, - (SP)
TRAP #1 ADDQ.L #2,SP
Inhalt von Register DO :
DO = 00001300
Diese Funktion verhält sich ähnlich wie der in unserer letzten Ausgabe beschriebenen Funktion TERM. Auch hier wird nach Ausführung von KEEP PROCESS das laufende Programm beendet und kehrt zurück an das Programm, von dem aus es gestartet wurde.
Zwischen KEEP PROCESS und TERM gibt es einige Unterschiede. Einer davon ist die sogenannte Abschlußbedingung. TERM liefert immer den Abschlußwert 0, der signalisiert, daß kein Fehler vorhanden ist. KEEP PROCESS Kann einen Wen zwischen 0 oder 1 annehmen, wobei 0 „kein Fehler“ bedeutet und 1 das Gegenteil darstellt.
Ein anderer, schwerwiegender Unterschied liegt bei der Speicherverwaltung. Wird ein Programm durch TERM beendet, so wird der gesamte Speicher freigegeben und mit Nullen gefüllt. Mit der Funktion KEEP PROCESS darf man einen bestimmten Speicherbereich schützen. Die Anzahl von Speicherplätzen wird in den Stack übergeben.
KEEP PROCESS
MOVE.W #0, - (SP)
MOVE.L #$200,-(SP)
MOVE.W #$31, -(SP)
TRAP #1
Hierdurch kann der Anwender die Größe der freien Plätze auf der Diskette er- I fahren. Für Programme, die ständig Daten auf Diskette ablegen, ist das von sehr großer Bedeutung, denn es wird ein möglicher Datenverlust vermieden. Diese Funktion benötigt mehrere Parameter, die alle nacheinander auf den Stack übergeben werden müssen. Der erste Parameter ist die Nummer des aktiven Laufwerkes. Der nächste Parameter ist die Adresse eines 16 Byte großen Puffers. Der letzte ist die Funktionsnummer selbst. Die gewonnene Information wird in dem oben genannten Puffer abgelegt. In dem ersten LONG-WORD dieses Puffers liegt die Anzahl der noch freien Allocation Unites. Das zweite Long Word beinhaltet die Menge der totalen Allocation Units. Das dritte enthält die Größe eines Sectors in Byte (512 Byte). In dem letzten Long-Word steht die Anzahl von Sectoren, die zu jedem Allocation Unit gehören.
GET DISK FREE SPACE
PUFFER ;
BLK.B 16
MOVE.W #01, - (SP)
MOVE.L # PUFFER, - (SP)
MOVE.W #$36, - (SP)
TRAP #1
ADDQ.L #8,SP
CLR - (SP)
END.
Mit Hilfe dieser Funktion lassen sich auf eine sehr einfache Art und Weise sogenannte Ordner anlegen. Um einen neuen Ordner anzulegen, muß man die Adresse, in dem sich der Name des Un-terdirectorys befindet, in den Stack übergeben. Der Name darf acht Zeichen lang sein und die Extension drei Zeichen lang. Der Ordnername muß mit einem Null-Byte abgeschlossen sein. Ob die Funktion erfolgreich druchgeführt wurde, wird im Register DO mitgeteilt. Eine Null bedeutet eine gelungene Ausführung und eine negative Zahl stellt eine fehlerhaft ausgeführte Funktion dar.
MKDIR
ORDNAME:
DC.L ”ST-COM.DAT”,0
MOVE.L # ORDNAME, - (SP)
MOVE.W #$39, - (SP)
TRAP #1 ADDQ.L #6,SP
Ein leeres Subdirectory läßt sich mit der Funktion RMDIR löschen. Wie zuvor bei der MKDIR-Funktion wird auch hier der sogenannte Pfadname in den Stack abgegeben und dann die Funktion aufgerufen. Der Erfolg oder Mißerfolg der Durchführung wird im Register DO signalisiert.
RMDIR
ORDNAME:
DC.L ”ST-COM.DAT”,0
MOVE.L # ORDNAME, - (SP)
MOVE.W #$3A,-SP
TRAP #1
ADDQ.L #6,SP
Um eine bessere Datenorganisation zu erhalten, kann man bei einem Ordner einen Unterordner anlegen. Das kann man noch weiter fortsetzen (bedeutet: daß bei dem zweiten Unterordner wieder ein Ordner angelegt werden kann). Möchte man einen bestimmten File von diesem Subdirectory einladen, muß man den ganzen Pfadnamen als Argument eingeben.
Beispiel:
Zeitung . dat / Fachz. dat /
C-Zeitung. dat / ST-Z. dat
Das läßt sich folgendermaßen erklären: Lade den File ST-Z aus dem Subdirectory C-Zeitung, der sich seinerseits in dem Subdirectory-Fach befindet und der zu dem Ordner „Zeitung“ gehört. Um die ganze Prozedur bei erneutem Laden von einem anderen File zu vermeiden, kann man durch die Funktion CHDIR einen Subdirectory zum aktuellen Directory machen. Dann sieht der Aufruf folgendermaßen aus:
CHDIR
NAME:
DC.L ”/ZEITUNG.DAT/ST-Z.DAT/”,0
MOVE.L #NAME, - (SP)
MOVE.W #$38, - (SP)
TRAP # 1
ADDQ.L #6,SP
Um überhaupt mit der Datei etwas anfangen zu können (lesen, schreiben usw.) muß diese Datei zuerst vorhanden sein. Mit der Funktion CREATE wird eine kreiert. CREATE verlangt zwei Parameter: Erstens ein sogenanntes Attribut und zweitens die Adresse, in der sich der File-Name befindet.
Attribut
0 = Datei kann beschrieben oder gelesen werden
1 = Datei kann nur gelesen werden
2 = Datei ist eine „Versteckte Datei“
4 = Datei ist eine „Versteckte System-Datei“
8 = erzeugt ein sogenanntes Volumen-Label
Nach der Ausführung von CREATE wird im Regisdter DO ein File-Deskriptor zurückgegeben. Dieser File-Deskriptor ist für weitere Zugriffe auf der Datei von Bedeutung. Wenn sich bei Aufruf dieser Funktion eine gleichnamige Datei auf der Diskette befindet, so wird diese auf Null-Länge reduziert, in einem Wort, gelöscht und von der neuen Datei ersetzt.
CREATE
NAME:
DC.L ”ST-COM.DAT”,0
FILED:
BLK.W 1
MOVE.W # 1, - (SP)
MOVE.L # NAME, - (SP)
MOVE.W #$3C, - (SP)
TRAP #1
ADDQ.L #8,SP
MOVE.W DO,# FILED
Um mit der Datei weiter arbeiten zu können, muß sie zuerst geöffnet werden. Auch diese Funktion benötigt zwei Parameter. Der erste Parameter signalisiert den arbeitenden Modus. Der zweite zeigt die Adresse, in dem sich der File-Name befindet. Dieser muß, wie schon bekannt, mit einem Null-Byte abgeschlossen sein.
Arbeits-Modus
0 = Datei kann nur gelesen werden
1 = Datei kann nur beschrieben werden
2 = Datei kann wahlweise gelesen oder beschrieben werden
Auch hier wird der schon bekannte File-Deskriptor ins Register DO übergeben.
OPEN
NAME:
DC.L "TEST.DAT”,0
F1LED:
BLK.W 1
MOVE.W #$2, - (SP)
MOVE.L #NAME, - (SP)
MOVE.W #$3D, - (SP)
TRAP #1
ADDQ.L #8,SP
MOVE.W DO,# FILED
Wenn aus einer Datei nicht mehr zugegriffen wird oder das Arbeiten mit dem Programm beendet ist, sollte eine geöffnete Datei wieder abgeschlossen werden. Vor allem bei Dateien, in denen es sich um „Schreiben“ handelt, muß, um Dateiverluste zu vermeiden, ordnungsgemäß abgeschlossen werden. Die Funktion CLOSE verlangt, daß die File-Deskriptor-Nummer als Parameter ins Stack übergeben werden sollte. Eine ordnungsgemäß geschlossene Datei erkennt man an dem Wert Null im Register DO.
CLOSE
FILED:
BLK.W 1
MOVE.W # FILED, - (SP)
MOVE.W #$3E, - (SP)
TRAP #1
ADDQ.L #4,SP
Um Daten auf eine vorhandene Datei zu lesen, bedient man sich der Funktion READ. READ wird mit drei Parametern versorgt: Erstens die Adresse eines Puffers, in dem die Daten abgelegt werden sollten, zweitens die Anzahl der zu lesenden Bytes. Der dritte und letzte Parameter beinhaltet die File-Deskriptor-Nummer, die durch die Funktion OPEN ermittelt wurde. Wenn die Operation fehlerfrei gelaufen ist, steht im Register die Anzahl der gelesenen Bytes.
READ
FILNAME:
DC.L ’’TEST.DAT”,0
FILED:
BLK.W 1
PUFFER:
BLK.B #256
MOVE.W #$02, - (SP)
MOVE.L # FILNAME, - (SP)
MOVE.W #$3D, - (SP)
TRAP #1
ADDQ.L #8,SP
MOVE.W DO,# FILED
MOVE.L # PUFFER, - (SP)
MOVE.L #256, - (SP)
MOVE.W # FILED, - (SP)
MOVE.W #$3F, - (SP)
TRAP #1
ADD.L # 12,SP
WRITE beschreibt das Gegenstück der oben beschriebenen Funktion. Auch hier werden drei Parameter benötigt. Die Parameterbeschreibung entspricht ungefähr der bei der READ-Funktion. Im Puffer befinden sich allerdings die Daten, die geschrieben werden müssen.
Die Ausgabe muß nicht unbedingt auf einem Laufwerk geschehen (Datei), sondern bei Änderungen der File-Deskriptor-Nummer kann auf den Bildschirm, Drucker usw. umgeleitet werden.
Andere File-Deskriptor-Nummern:
0 = Konsole — Eingabe
1 = Konsole - Ausgabe
2 = RS 232
3 = Drucker
WRITE
FILNAME:
DC.L ”TEST.DAT”,0
FILED:
BLK.W 1
MOVE.W #$02, - (SP)
MOVE.L # FILNAME, - (SP)
MOVE.W #$3D, - (SP)
TRAP #1
ADDQ.L #8,SP
MOVE.W DO,# FILED
MOVE.L #PUFFER, - (SP)
MOVE.L #256, - (SP)
MOVE.W # FILED, - (SP)
MOVE.W #$40, - (SP)
TRAP #1
ADD.L # 12,SP
Mit dieser Funktion können überflüssige Dateien gelöscht werden. Die Adresse, in der sich der File-Name befindet, wird in den Stack abgelegt. Bei erfolgreicher Löschung der Datei enthält das Register DO eine Null. Ein Wert ungleich Null signalisiert eine fehlerhafte Ausführung.
UNLINK
FILNAME:
DC.L ”TEST.DAT”,0
MOVE.L # FILNAME, - (SP)
MOVE.W #$41, - (SP)
TRAP #1
ADDQ.L #6,SP
Fortsetzung folgt.