zurück : weiter  inhalt =
 

3.3 ** Ein-/Ausgabe-Hilfssysteme

Man vergewärtige sich, daß auf dem QL bereits ohne jeden zusätzlichen Ausbau mehrere Jobs mit dem "Pointer Environment" zusammen zu betreiben sind und dabei ihre Fenster zerstörungsfrei verwaltet werden. Dagegen benötigen auf bekannten Personal Computern gewisse "Windows" für ganze zwei Jobs allein an Arbeitsspeicher schon das Vierundsechzigfache der gesamten Speicherkapazität des QL. Von der immens aufgeblasenen Programmgröße in jenen Systemen ganz abgesehen. Was verdeutlicht, auch wenn man dessen relativ geringe Bildauflösung in Betracht zieht, über welch leistungsfähiges System der QL verfügt, und wie außerordentlich vielseitig einsetzbar es dem Datenaustausch und der Manipulation beliebiger Geräte dient.

Vor allen weiteren Ausführungen sei auf den Abschnitt der "Concepts" des User-Guide zu den "Einheiten" hingewiesen. Die dort genannten "Einheiten" sind das, was hier "Geräte" heißt, "Dateikennsätze" werden wir als "File-Header" noch näher kennenlernen.
 

3.3.1 * Struktur

Der gesamte Datenverkehr innerhalb des QL und zur Umgebung hin folgt einem standardisierten Protokoll auf Pfaden, die vom Qdos verwaltet werden. Auch mit der umgebenden Hardware findet der Datenaustausch auf solchen Pfaden statt. Bedient man sich konsequent der vielfältigen Einrichtungen, die das Betriebssystem verfügbar hält, müssen nicht einmal mehr reine Speicheroperationen mit absolut bekannten Adressen durchgeführt werden.

Diese Pfade sind die IO-Kanäle.

Sie werden beim Einrichten vom System mit einer Kennzahl versehen und sind mittels dieser stets eindeutig identifiziert und auffindbar. Alle Besonderheiten der jeweiligen Quellen und Ziele verbergen sich hinter diesen Zahlen und brauchen im Normalfalle nicht bekannt zu sein. Bei einigen Operationen genügt die Angabe einzig der Kennzahl, anderen sind ggf. noch Daten oder Hilfsadressen etwa für Pufferspeicher mitzugeben.

Die Operation selbst wird durch einen Schlüsselcode (key) bestimmt, der bei den zugehörigen Trap-Aufrufen stets in D0 anzugeben ist. Der Datenaustausch mit Hilfe der Teilprogramme einer jeden Operation ist genau geregelt, zugleich damit auch die Steuerung peripherer Einheiten. Zum Programm hin treten die Besonderheiten der zuständigen Geräteverwaltung nicht in Erscheinung.

Alle Aktionen, die der Verallgemeinerung zugänglich sind, werden in einheitlicher Form aus den genannten Trap-Aufrufen heraus geschaltet. Infolgedessen können allein durch Tausch der Identitätszahlen (ID) Ziel und Quelle weitgehend nach Belieben gewechselt werden.

Das gibt dem Verfahren das Attribut "redirectable" (umleitbar). Die Geräte, wie sie physisch vorhanden sind, zeigen sich einem Programm in Form der zugehörigen Device Handler, die sie jeweils direkt bedienen. Sie brauchen weder in ihrer Arbeitsweise noch im Aufbau dem Programm bekannt zu sein.

Die Handler haben ggf. eine Vielzahl gleichartiger Kanäle auch im Multitasking zu verwalten. Sie sind darum strikt reentrant aufgebaut. Insbesondere heißt das hier, daß für alle ihre Kanäle etwaige Datenbereiche in derselben Weise gültig sind. Die besondere Ausstattung der Kanäle wird in eigenen Tabellen mit Parameterblöcken jeweils einzeln beschrieben und der Situation angepaßt. Auch diese Daten müssen im allgemeinen nicht bekannt sein, sie sind aber in weiten Bereichen dokumentiert und leicht zugänglich.

Durch weitgehende Dokumentation stehen die Device Handler offen für vielfältige Erweiterungen. Darüber hinaus können beliebige neue Einheiten definiert und dem System zugefügt werden.

Bestehende Handler können auch teilweise mit neuen Definitionen überschrieben werden. Günstigstenfalls ist dazu lediglich ein Pointer mit der dann gültigen Adresse zu besetzen.
Etwa bei dem bereits erwähnten "Pointer Interface" wird durch Umdefinieren der Bildschirm-Handler Gebrauch davon gemacht.

Das System wurde von Anfang an ganz auf das Multitasking ausgerichtet. Darum war klar, daß die einzelnen Kanäle irgendwie verriegelbar werden müßten. Es ist nämlich auf jeden Fall zu vermeiden, daß verschiedene Jobs zur selben Zeit auf denselben Kanal zugreifen. Dort könnten sie sonst womöglich fremde Daten zerstören.
Das wurde so gelöst, daß ein Kanal markiert wird, wenn er gerade für einen Job aktiv ist. Jobs erhalten eine gleichartige Marke, wenn aus andern Jobs irgendeine Art Vollzugsmeldung abzuwarten ist.
Jede weitere Zugriffs-Anforderung wird zurückgestellt.

Damit nun aber kein Stau entstehen kann, muß irgendein Abbruchkriterium vorgesehen werden. Dazu den Ablauf einer Zeitspanne heranzuziehen, liegt nahe. Die Dauer dieser Zeitspanne ist bei den Operationen zur Ein- und Ausgabe (Trap#3) auf den Ablauf eines Taktzählers einstellbar:

Die Zeit, die bis zum Ende der angeforderten Operation vergehen darf, ist D3 in Vielfachen von 20 ms als positive 16-Bit-Zahl mitzugeben. Die längstmögliche Wartezeit wird damit auf knapp 11 Minuten begrenzt.

Die Befristung läßt sich auch außer Kraft setzen:

Einmal durch unbegrenzte Wartezeit, was mit der Zeitangabe -1 codiert wird. Dies dient Operationen, die in jedem Fall augeführt werden sollen. Vorzeitig ist die Rückkehr dann nur beim Fehler-Abbruch möglich, wenn etwa die Kanalnummer nicht gültig war.

Zum andern kann die Wartezeit ganz außer Kraft gesetzt werden. Die Operation wird dann nur ausgeführt, wenn das sofort möglich ist. Dazu dient die Zeitangabe Null.
Ein Aufruf der I/O-Traps im Supervisor-Mode gehört zu den Dingen, bei denen das erforderlich ist. Nur dann wird der Scheduler mit Sicherheit nicht aufgerufen, worin sonst der Processor die Betriebsart wechseln könnte {1,2}.

Der "timeout" durch Ablaufen einer "Uhr" ist mehr, als nur eine höchst vielseitige Schutzfunktion. Wenn etwa eine erforderliche Tastatureingabe allzu lange auf sich warten läßt, könnten entsprechenden Hinweise im Bild ausgegeben werden.
 
 

3.3.2 ** Device Handler, Device Driver - Geräte-Verwaltung

3.3.2.1 * Aufbau

Die Verwaltung durch den Handler steht der sinnfälligen Verwendung der Daten in einem Programm gegenüber. Auf der anderen Seite besorgt er die Verbindung mit dem Geschehen außerhalb des eigentlichen Rechnerbereiches. "Außerhalb" ist hierbei in weitestem Sinne jede irgendwie zugängliche Einrichtung, nur nicht die selbst gerade aktiven Teile. Insbesondere sind das die Standard-Einheiten des QL:
Die Geräteverwaltung verbindet also zwei Ebenen (Schichten - layer). Die Zugangsebene, "access layer", auf Seiten der Software und für die Hardware die körperlich vorhandene, physische Ebene, "physical layer".

Der Device Handler verwaltet die Hardware, indem er ihr Daten in der benötigten Form zuführt oder sie ihr in entsprechender Weise entnimmt. Zugleich ist darin auch der Transport von oder zur Softwareseite organisiert. Dank dieser Aufteilung kann einerseits die Software weitestgehend standardisierten Protokollen folgen, die Hardware andererseits davon völlig unabhängig ihren meist hoch spezialisierten Anforderungen gemäß gesteuert werden.

Der große Gewinn liegt in eben dieser Trennung.

Es ist weitgehend gleichgültig, wohin die Daten gehen oder woher sie geholt werden. Die Programmierung wird vereinfacht und ermöglicht besonders vielseitige Anwendungen. Zum anderen ist bei der Hardwarebetreuung keinerlei Rücksicht auf die Programme erforderlich. Sie kann völlig auf die jeweiligen Anforderungen ausgerichtet werden.

In sich geschlossene Datenaufzeichnungen heißen Files. Das entspricht einem Begriff der Buchhaltung, wo sie Notizen eines gewissen Zeitraumes zu einem bestimmten Thema sind. Die Verwendung wird auch hier damit recht gut dargestellt. Der Handlichkeit halber werden größere Sammlungen an Files auf einem Datenträger zusammengefaßt. Zugleich ist mit der Datenspeicherung dann immer auch die Aufzeichnung eines Inhaltsverzeichnisses (directory) erforderlich. Von einer gewissen Anzahl Files an werden schließlich noch weitere Unterverzeichnisse sinnvoll, die "subdirectories".
Dementsprechend unterscheidet man im Betriebssystem des QL Geräte mit und ohne Directory.

Es gibt einfache Devices und Directory Devices. Diese werden in zwei verketteten Listen notiert. Die "device handler" in der Liste von SV.DRLIST auf $44(sb) verwalten Geräte ohne Directory, die Geräte mit Directory werden von einem "directory device handler" aus der Liste SV.DDLIST auf $48(sb) betreut. Alle Funktionen der einfachen Device Handler sind auch den Directory Device Handlern zueigen. Letztere dagegegen haben mit der Betreuung der Directories noch umfangreiche zusätzliche Aufgaben zu erledigen.
 

3.3.2.2 * Physical Layer

Ausführlich kann hier darauf nicht näher eingegangen werden, denn die Hardware wird nur wenig von den Gegebenheiten des Computers bestimmt. Der Aufbau der Physical Layer kann erst erfolgen, wenn diese genau bekannt ist.

Hierzu werden später noch einige Hilfsmittel beschrieben, die im Betriebssystem bereitstehen.

Besonders die Interruptverwaltung des QL ist bei der Hardware-Bedienung von großem Nutzen. Zugleich kann damit auch eine besondere Kathegorie Programme betreut werden. Darum finden sich die näheren Angaben in einem gesonderten Kapitel und bei den verschiedenen Programmarten des QL.

Unter dem Begriff des "slaving" schließlich ist von hier aus dem Access Layer ein Hilfsdienst zu leisten, der seine Aufgabenstellung im Grenzbereich der beiden Layer findet. Zwischen Speicherung und Verarbeitung werden die Daten in einem Pufferspeicher aufgefangen. Damit können sie kompakt und zu einem Zeitpunkt weitergeleitet werden, zu dem der Transport das System möglichst wenig belastet.

Beim Schließen eines Kanals ist dafür zu sorgen, daß nicht noch irgendwo gepufferte Daten verlorengehen. Das regelt eine Scheduler-Task, die die Puffer bereinigt und nach der ordungsmäßigen Abwicklung dies der Physical Layer in Form einer Flag mitteilt. Dann wird der nun freie Speicherblock endgültig aufgegeben und die Überwachung beendet.

Für das Öffnen von Kanälen ist eine Kontrolle vorzusehen. Vor allem bei dem Versuch, auf einen bereits offenen Kanal zuzugreifen, müssen zwei Zustände erkannt werden: Falls ein regulär noch arbeitender Kanal eröffnet werden soll, ist der Aufruf mit dem Fehlercode ERR.IU abzuweisen. Befindet sich der Kanal im gerade beschriebenen Endstadium seiner Auflösung, wird wieder eine Scheduler-Task aktiv werden, um erst nach Freigabe der Sicherungsblöcke das Öffnen zu veranlassen.
 

3.3.2.3 * Acces Layer

Seitens der "access layer" ordnen wir drei Zugriffsarten:
  1. Öffnen eines Kanals mit Einrichtung eines zugehörigen Datenblocks, dem "channel definition block", wo die besonderen Daten eines jeden Kanals liegen, und die von seinem Handler verwaltet werden.
  2. Schließen eines Kanals, Auflösen der Speicherblöcke.
  3. Datentransport zur und von der Hardwareseite.
Im allgemeinen sind einige Angaben nötig, die die Art der Datenübertragung zur Physical Layer definieren. Für den Weg aus der Access Layer in die Programme entsprechend.

Das ist der "statische Arbeitsbereich" der Device Handler. Er enthält organisatorische Angaben. Etwa die Anzahl der offenen Kanäle und deren Adressen oder den Pointer auf eine verkettete Liste davon, wenn ihre Anzahl unbegrenzt sein soll. Der aktuelle Zustand der betreuten Hardware ist dort zu verzeichnen, oder auch gewisse Grundeinstellungen, wie z.B. die Spurenanzahl einer Floppy Disc. Darum wird ein Device Handler bei der eigenen Installation stets auch einen kleinen Bereich im RAM reklamieren. Dort werden Daten eingetragen, die das Verfahren bestimmen. Für jeden einzelnen der im System bereits vordefinierten Handler wird so ein "device handler definition block" in der Umgebung der Systemvariablen gleich nach dem Einschalten des QL angelegt. Er kann ansonsten aber auch an irgendeiner anderen Stelle eingerichtet werden. Es gibt Beschränkungen hierbei nur bezüglich der Sicherheit gegen Überschreiben oder versehentliches Auflösen der Bereiche.

Solche Speicherblöcke können mit den Vectoren MM.ALCHP ($C0) im Common Heap eingerichtet, mit MM.RECHP ($C2) wieder freigegeben werden. Beide dürfen nur im Supervisor Mode aufgerufen werden. Besitzer ist der Device Handler, er wiederum ist Teil des Betriebssystems.

Dagegen dürfen die Physical Layer eigene Codeteile nur einfügen oder entfernen, wenn das aus einem Scheduler-Aufruf heraus geschieht. Besonders bei der Freigabe sind unbedingt zuvor die zugehörigen Listen zu korrigieren, falls zugleich damit auch der Handler selbst aufgegeben wird.

Unabhängig voneinander und zur selben Zeit können nun mehrere Kanäle auf ein Gerät offen und tätig sein. Darum muß ein Device Handler unbedingt reentrant aufgebaut sein. Aus diesem Grunde werden den Kanälen beim Öffnen eigene Tabellen zugeordnet, die zugleich auch helfen, das ganze Verfahren übersichtlich zu halten. Dorthin werden die Daten gebracht, die die Besonderheiten einzelner Kanäle aufzeichnen. Das sind z.B. stets die vom System zugeteilte laufende Nummer und die Adresse des Handlers. Dann auch Angaben, wie die gültige Zeichenposition für das nächste PRINT-Kommando, die Farben eines Fensters, die Position in einem Datenfile, etc.

Die Speicherbereiche dieser Tabellen werden vom Handler mit Schließen der Kanäle freigemacht.

Ein einfacher Device Handler mit unterstützenden Befehlen, die als Basic-Erweiterungen aufgebaut sind, ist mit dem Namen STAK in den Beispielprogrammen angegeben.

Für das Einfügen der Device Handler in das Betriebssystem und ihren Aufbau selbst gibt es die Manager-Traps (1):
 
MT.LXINT 26 $1A externe Interruptbearbeitung eintragen
MT.RXINT 27 $1B diese aus der Liste herausnehmen
MT.LPOLL 28 $1C Abfrage-Interrupt (polled) eintragen
MT.RPOLL 29 $1D diesen aus der Liste herausnehmen
MT.LSCHD 30 $1E Scheduler-Programm-Aufruf eintragen
MT.RSCHD 31 $1F diesen aus der Liste herausnehmen
MT.LIOD 32 $20 einfachen Device Handler eintragen
MT.RIOD 33 $21 diesen aus der Liste herausnehmen
MT.LDD 34 $22 Directory Device Handler eintragen
MT.RDD 35 $23 diesen aus der Liste herausnehmen
 
Mit der Thing erweiterung noch
 
MT.LTHG 38 $26 Thing in die Liste SV.THGL eintragen
MT.RTHG 39 $27 unbenutztes Thing aus der Liste nehmen
 

Weiter die Vectoren:

Für den Aufbau von Handler, Tabellen und Kanälen
 
MM.ALCHP $00C0 Speicher im Heap reservieren
MM.RECHP $00C2 diesen wieder freigeben
IO.NAME $0122 einen Devicenamen analysieren
 
Für die Handhabung der Daten
 
IO.QSET $00DC eine Queue einrichten
IO.QTEST $00DE Status einer Queue prüfen
O.QIN $00E0 ein Byte dorthin bringen
O.QOUT $00E2 ein Byte abholen
IO.QEOF $00E4 EOF markieren
 
Für die allgemeine Handhabung der Queues besonders
 
IO.SERQ $00E8 direkte Queue-Behandlung
IO.SERIO $00EA erweiterte Queue-Behandlung
 

3.3.2.3.1 - Aufbau

Das Verfahren ist wie folgt:

Mit Aufruf des Handlers durch eine Trap der Kanalzuweisung (2) oder der I/O-Behandlung (3), die auch in einem Vectoraufruf verborgen sein kann - UT.CON zum Beispiel - befindet sich der Processor im Supervisor Mode. Der Handler ist dementsprechend zu programmieren.

Das Qdos stellt in einigen Registern folgende Angaben zur Verfügung:

Innerhalb des Handlers sind nur D2 und D3 immer beliebig verwendbar, bei den i/o-Traps kommt D3 beim ersten Versuch mit 0, ansonsten mit -1 an.

D0 gibt mit unten näher beschriebenen Besonderheiten einen Fehlercode zurück.
 

Programme sind bereitzustellen für

1. CH.AIO Datentransport Trap #3
Die verschiedensten Bedingungen können hier geprüft und eingestellt werden. Es werden die vielfältigen Aufgaben der I/O-Traps bearbeitet. Einzelheiten entnehme man den betreffenden Beschreibungen. Insbesondere werden hier mit Unterstützung durch das IOSS die gelesenen Bytes an das aufrufende Programm weitergeleitet und daraus übergebene Bytes in den Kanal geschrieben.

2. CH.AOPEN Öffnen von Kanälen Trap #2
Darin sind alle Kanaltabellen, Puffer und Datenpfade einzurichten. Die zugehörigen Aufzeichnungen über Ort und Größe sowie Art der Ausstattung werden in die entsprechend erweiterten Kanaltabellen eingetragen.

3. CH.ACLOS Schließen von Kanälen Trap #2
Erst wird kontrolliert, ob der betreffende Kanal überhaupt vorhanden ist; dann, ob er nicht noch einem vorrangigen Gebrauch unterliegt. Ist dies nicht der Fall, werden die zugehörigen Eintragungen der Qdos-Tabellen markiert und die Speicherblöcke des Kanals aufgegeben.

Hier die Liste der besonderen Aufrufe, die man im Handler zweckmäßig zu Beginn des Codebereichs einträgt. Der Ort ist nicht vorgeschrieben. Die Tabelle kann völlig getrennt vom zugehörigen Code auch in einem sicheren Bereich des Ram stehen {MINERVA}.

Zunächst optional, also nur erforderlich, wenn wirklich benutzt:
 
CH.LXINT -24  00 Link auf nächsten Externen Interrupt
CH.AXINT -20  04 hier gültiger Aufruf
CH.LPOLL -16  08 Link auf nächsten Poll-Int.
CH.APOLL -12  12 hier gültiger Aufruf
CH.LSCHD -08  16 Link auf nächstes Scheduler-Programm
CH.ASCHD -04  20 hier gültiger Aufruf
 
Weiter die Pointer, die mit Null besetzt werden müssen, wenn das Betriebssystem keine entsprechende Bearbeitung zulassen soll:
 
CH.LNEXT  00  24 Pointer auf nächsten Device Handler
CH.AIO  04  28 Ptr auf die I/O-Bearbeitung
CH.AOPEN  08  32 dto., zum Öffnen eines Kanals
CH.ACLOS  12  36 dto., zum Schließen eines Kanals
 
Die Offsetangaben sind in {1,2} unterschiedlich gezählt. Davon unabhängig ist immer CH.LXINT die Basis, die mit A3 im Trap-Aufruf ankommt, vom System ermittelt aus CH.LNEXT-$18.

CH.L... sind Namen, die auf Posten deuten, die das Betriebssystem verwaltet. Deren Orte sind als Adressen den Traps beim Einfügen in die zugehörige Liste mitzugeben.
CH.A... sind die Eintragungen der im Handler aufgerufenen Adressen.

Beim Einfügen einer neuen Handlerdefinition ist dessen Link-Adresse die Position CH.LNEXT, welche dem System mit MT.LIOD oder MT.LDD in A0 zu übergeben. ist
 

3.3.2.3.2 - Fehlermeldungen

Die Rückkehr aus einem Trap-Aufruf erfolgt grundsätzlich stets mit einem Fehlercode in D0. Als Fehler gilt jede Zahl ungleich Null.
Vom System auswertbar ist nur die negative Standardcodierung. Positive Zahlen sollten mit Vorsicht und nur in genau dokumentierten Sonderfällen zurückgegeben werden. Deren Auswertung kann in Unkenntnis dieser Möglichkeit leicht zu Mißverständnissen führen, etwa weil dann durch das Basic-OPEN kein Fehler gemeldet wird.
Auch sonstwie nach QDOS-Konvention nicht wirklich zutreffende Rückgabewerte können, z.B. in Gestalt endloser OPEN-Versuche mit immer wieder neu vorangestellter TK2-Devicevorgabe, lästige Fehlererscheinugen auslösen.

Das System oder direkt ein Aufruf veranlaßt mittels UT.ERRØ die Ausgabe eines Textes auf den Bildschirm. Neben den vom Basic her bekannten Meldungen mit den Zahlen bis -22, MGG bis -27, sind Möglichkeiten der freien Definition vorgesehen.

Zunächst einmal lassen sich für die genannten Codes neue Texte in Form einer Tabelle definieren. Das Verfahren dazu ist ausführlich im Abschnitt 3.8.2 beschrieben.

Ursprünglich vielleicht vorgesehen, jedoch nicht erkennbar umgesetzt, wurde die in {1,2} beschriebene Form freier Systemmeldungen, welche hier nur zitiert ist, um den aus ihrer Verwendung resultierenden Fehlern vorzubeugen:
Eine positive Zahl soll das System als Offset zu $8000 bewerten und an der resultierenden Adresse einen String für die Ausgabe als Fehlermeldung erwarten.
Überprüfung mit MGG und GC ergab, daß zumindest dort diese Auswertung NICHT stattfindet, solche Texte in Gegenteil sogar zu üblen Fehlern führen können.

Folgende außerdem noch angegebene Konvention ist zuverlässig anwendbar:
Fehlernummern mit ungesetztem Bit 30 bewertet das System direkt als Adresse eines als Meldung auszugebenden String.

Die Sequenz
lea textadresse(pc),a1
move.l a1,d0
bset #31,d0
 
kann an beliebiger Stelle im Speicher stehen, und liefert die richtige Codierung. Der Text muß mit führendem Längenwort auf gerader Adresse beginnen.
 

3.3.2.3.3 - Programmierung

Zu 1. CH.AIO Trap #3

Dort wird die eigentliche Arbeit erledigt. Der Anforderung aus D0 ist nun in entsprechender Weise nachzukommen. Zunächst vielleicht:

D0=9   führt etwa mit JMP(A2) in den SD.EXTOP-Aufruf,
D0=66 setzt eine Position entsprechend FS.POSAB.
D0=67 FS.POSRE, Position relativ zum aktuellen Wert.
Was übrigbleibt, könnte etwa dem Vector IO.SERIO übergeben werden, wo I/O-Trap-Aufrufe (3 / 0, 1, 2, 3, 5, 7, 70, 71, 72, 73) behandelt werden. In diesem Falle müssen wir eine Tabelle bereitstellen, die dem Aufruf des Vectors als Subroutine unmittelbar folgt. Das erspart die Übergabe der Adresse, da sie mit dem Stackpointer schon bekannt ist.
move.w 234,a2    Vector IO.SERIO
jsr (a2)         > io_test
* absolute(!) adressen *
dc.l kanal.prüfen
dc.l byte.holen
dc.l byte.senden
rts
Dafür müssen noch die zugehörigen Programmteile für den Test auf Vorhandensein sowie Abholen und Senden einzelner Bytes bereitgestellt werden.
Die Übergabe der Basis des Kanal-Definitionsblocks im Register A0 ist besonders hier von großem Nutzen. Mit Hilfe der in die Trap hineingereichten Register lassen sich im allgemeinen alle nötigen Informationen ermitteln und die betreffenden Aufgaben leicht durchführen.

D3 hat beim ersten Aufruf den Wert Null. Wenn die geforderte Operation nicht erfolgreich beendet werden konnte und solange eine womöglich angegebene Wartezeit noch nicht abgelaufen ist, kommt danach jedesmal -1 herein.

Nach der Arbeit gibt man ggf. A1 (Ort) und D1 (Länge) von Strings und zugehörigen Puffern weitergezählt zurück. Dies ist Voraussetzung für einwandfreie Funktion der Wartezeibearbeitung durch das System, oder auch dafür, daß z.B. bei langsamen Peripheriegeräten alle Daten successive in den Puffer geholt werden können.

Denn um das Multitasking nicht durch ständiges Abschalten bei längerwährenden i/o-Operationen ad absurdum zu führen, stellt das Betriebssystem die Wartezeit durch Simulation einer Programmschleife auf dem betr. Trap-Code dar, ausgelöst durch die Fehlermeldung ERR.NC (D0=-1). Haben dann A1 und D1 nicht die korrekten Werte, wird es zu Fehlern kommen.
Ggf. muß zwischen erstmaligem oder wiederholtem Einsprung unterschieden werden, wozu der Eingangswert von D3 (anfangs 0, sonst -1) herangezogen werden kann, um D1 und A1 so einzustellen, die sie sich ohne Datenverlust für einen erneuten Versuch eignen. Das bedeutet z.B. beim Holen eines String, daß der hereingereichte Wert D1 vor Addition der empfangen Anzahl Zeichen nur dann gelöscht wird. wenn nicht unmittelbar davor dieselbe Trap mit ERR.NC abgebrochen wurde.
Ohne weitere Abfrage läßt sich das ganz einfach durch eine AND-Operation D3 mit D1 erledigen.

Durch Rückgabe von ERR.NC kann also im Handler selbst dafür gesorgt werden, daß die Trap solange wiederholt wird, bis entweder die Wartezeit abgelaufen ist, oder die Operation erfolgreich war. Damit läßt sich erheblicher Programmaufwand einsparen, bei zugleich optimaler Ausnutzung des QDOS-eigenen Multitasking. - Vorausgesetzt, daß nicht irgendeine idiotische "dynamische" Pufferung auf Systemebene jedwede Wartezeitangabe ad absurdum führt (z.B. SER im SMSQ/E!).

Wenn irgendwo eine Zwischenspeicherung organisiert wurde, ist nötigenfalls gesondert dafür zu sorgen, daß die Operation auch im Phsical Layer richtig abgeschlossen wird. Vorgesehen ist die Trap FS.MDINF (3/69) für die Angabe, ob der Datentransport beendet wurde, oder die Traps FS.CHECK (3/64, insbes. zur Überprüfung bei MDV-Aktionen) und FS.FLUSH (3/65), welche mit dem TK2 noch zusätzliche Operationen zum sicheren Abschluß auch offener Files auslöst.

Die Definition selbst muß für Fehlersituationen die passende Bearbeitung bereitstellen und unbedingt auch den Regeln folgend die entsprechende Fehlernummer zurückgeben. Bei fehlerfreiem Ablauf geht Null in D0 zurück, in den restlichen ungültigen Fällen der Fehlercode ERR.BP (-15).

Zu 2. CH.AOPEN Trap #2

CH.AOPEN hält die Adresse des Teilprogrammes zum Öffnen von Kanälen.

Namen von zu öffnenden Kanälen haben einer genau einzuhaltenden Konvention zu folgen, ebenso die Definition des Erlaubten im Device Handler. Ihr Aufbau zusammen mit den Möglichkeiten zur Parameterangabe ist allerdings längst nicht so schwer durchschaubar gestaltet, wie es zuerst den Anschein haben mag und in der Literatur gelegentlich dargestellt wird. Ganz im Gegenteil sind sie trotz ihrer Vielseitigkeit eher plausibel zusammengesetzt.

Mit dem Aufruf stehen ergänzend zu eingangs Erwähntem in

D3 ein Schlüsselcode für die Art, wie der Kanal zu öffen ist.
A0 der Pointer auf den Namen
A3 die Basis des Device Handlers.
D0 - D7, A1 - A6 sind frei verwendbar.
Folgende Aufgaben müssen erfolgreich bendet worden sein, bevor ein Kanal vom Qdos anerkannt wird:
Der übergebene Name wurde fehlerlos decodiert.
Es ist Speicher für Kanal-Definitionsblock und ggf. weitere Puffer reserviert worden.
Außer den vom System belegten ersten sechs Langworten wurde der Definitionsblock gültig besetzt.
Die Adresse des Blocks wurde in A0 zurückgegeben.
Vom IOSS her sind alle Kanäle in beiden Richtungen gangbar. Unerwünschte Operationen müssen ggf. in der Handlerdefinition selbst abgefangen und mit einem Fehlercode quittiert werden.

Eine sehr wirksame Hilfsfunktionen stellt der Qdos-Vector IO.NAME bereit:

Neue Kanäle werden durch Angabe einer namentlichen Bezeichnung definiert. Der Vector IO.NAME hilft bei "einfachen" Devices, diese zu decodieren; bei Directory-Devices erledigt dies das IOSS für den Gerätenamen, und nur der Filename selbst, samt "Pfad", ist im Handler zu bearbeiten.

Eingangs ist soviel Platz an Zwischenspeicher zu reservieren, wie maximal für die Übergabeparameter erforderlich werden kann. Dessen Basis wird in A3 notiert. Da es nicht allzuviele Daten sind, die so gespeichert werden, bietet sich als leicht zu verwaltender Datenpuffer der Stack an.

Der Vectoraufruf analysiert nun den Namen gemäß dessen Definition im Handler. Die entsprechend ausgewerteten Parameter legt er in Reihenfolge der Definitionsliste im Pufferspeicher ab. A3 dient als Ptr dorthinein, die Zahlen haben Wort-Länge. Je nach Ablauf dieser Analyse erfolgt die Rückkehr an eine von drei Adressen in Wort-Abstand nach dem Vectoraufruf:

Nach dem Block dieser drei Wortadressen steht unmittelbar die Namensdefinition nach folgendem Muster:
count.w+name
anzahl.w definierter parameter
parameter
Am Anfang der Devicename in der üblichen Form mit zwei Längenbytes und den Zeichen auf gerader Adresse:
DC.B 0,2,'SCR',0
Dann ist die Anzahl möglicher Parameter angegeben:
DC.W 4
In einer von drei Formen folgen nun die Parameter:

1. Leerzeichen, (Trenn-)Zeichen, Vorgabezahl

DC.W ' _',448
DC.W ' X',180
DC.W ' A',32
DC.W ' X',16 ...
wie das etwa für das SCR-Device gelten kann.

2. eine negative Zahl, eine Vorgabezahl

DC.W -1,128
3. ein Zähler, ebensoviele Codezeichen
DC.W 3
DC.B 'POI',0
Alle Parameter beginnen auf gerader Adresse. Buchstaben müssen groß geschrieben werden. Zahlen sind vorzeichenbehaftete 16-Bit-Zahlen. Einschränkungen gibt es sonst nicht.

Der Vectoraufruf liefert nun den übergebenen Wert oder den der Vorgabe. Bei Codezeichen kommt deren Position in ihrer besonderen Liste zurück. Ist keines angegeben, wird das mit Null signalisiert. Bei dieser Einteilung können Parameter auch teilweise fortgelassen werden, egal, an welcher Stelle sie stehen. Bis hier kann der Code dann etwa so aussehen:
 

chopen  subq.l #8,sp    Zwischenspeicher
        move.l sp,a3    angeben
        move.w 290,a2   IO.NAME, A0 zeigt
        jsr (a2)        auf den Namen
        rts             bei falschem Namen
        rts             bei Parameterfehler
        bra.s aufmachen alles richtig
namtab  dc.w 4,'STAK'   der Name
        dc.w 4          vier Parameter
        dc.w -1,1       etwa laufende Nummer, Vorgabe 1
        dc.w ' _',32766 ein Datenpuffer, Vorgabewert 32766
        dc.w 3          drei Codezeichen
        dc.b 'FIS',0    Typ float, integer, string
        dc.w 1          ein Codezeichen
        dc.b 'P',0      für permanenten Puffer
 
Erst jetzt ist die Speicherverwendung wieder frei.
Die Fehlerbehandlung darf nur die vorgesehene Fehlercodierung zurückgeben, weil sonst die Routinen des Systems in die Irre geführt werden und Fehler produzieren, die nicht mehr abgefangen werden können. Das gilt verschärft, wenn die TK2-Devicevorgabe aktiv ist, da diese die Vorgabenamen auf Basis der Fehlercodes verwaltet.

Wie zu sehen war, können dabei verschiedene Kathegorien auftreten. Über diese Angaben muß irgendwo Buch geführt werden.

Vom Handler selbst sind auf jeden Fall Speicherbereiche für die Kanal-Definitionsblöcke einzurichten.

Mittels des Vectors MM.ALCHP wird Platz für mindestens 24 Bytes einer Tabelle fixiert. Diesen Bereich belegt und verwaltet das IOSS. Eine Begrenzung ist nicht vorgesehen, sodaß für weitere Angaben die Reservierung entsprechend vergrößert werden kann. Alle Informationen, die das Erscheinungsbild eines Kanals bestimmen, haben hier ihren Platz.

Standardmäßig belegt das IOSS:
 
CH.LEN
 00 
Länge des Definitionsblockes
CH.DRIVR
 04 
Adresse des Handlers
CH.OWNER
 08 
ID des Besitzer-Jobs 
CH.RFLAG
$0C 
bei Bereichsfreigabe zu markierende Adresse eines Byte
CH.TAG
$10 
Laufend Nummer des Kanals
CH.STAT
$12 
A1 gilt mit -1 absolut, mit $80 rel A6, 0 wenn die Bearbeitung erledigt ist, sonst negativ bei Wartezustand
CH.ACTN
$13 
Verrichtung des wartenden Jobs (Trap#3 Op-Code)
CH.JOBWT
$14 
ID des Jobs, der auf I/O-Bedienung wartet
 
Hier können nun frei definierte Daten folgen.

Zu 3. CH.ACLOS Trap #2

Vor dem Schließen eines Kanals ist zu prüfen, ob er noch irgendwo in Gebrauch ist. In solchem Fall geht der Fehlercode -9 für ERR.IU zurück. Sonst muß man ggf. noch darauf achten, daß alle Operationen auf Seiten des Physical Layer auch wirklich schon durchgeführt sind.

Mit Schließen des Kanals werden auch die ihm zugeeigneten Speicherbereiche freigegeben. Dabei könnten Daten verloren gehen. Diese Kontrolle kann aber z.B. auch der besonderen Anforderung, etwa durch FS.FLUSH (3/65), überlassen bleiben. Besonders dieser Aufruf erledigt alles Nötige selbst. Mit TK2 werden die Daten im Directoryfile des zugehörigen Datenträgers zusätzlich aktualisiert, sodaß es dann sogar durch Bedienungsfehler kaum noch Schäden geben kann.

Nun sind alle Tabellen im zugehörigen Device Handler zu bereinigen, damit nicht irgendein Irrläufer den inzwischen ungültigen Datenbereich benutzen kann.

Außer D0 und A7 sind alle Register frei verwendbar.

Der Vector MM.RECHP auf $0C2 (192) löst den reservierten Speicherbereich wieder auf. Dessen Basisadresse ist in A0 zu übergeben. Das Register kommt dafür durch diesen Aufruf der Trap #2 schon richtig besetzt an.

Als letzte Aktion kann die Freigabe erfolgen. Z.B. in Gestalt dieses Zweizeilers

move.w    194,a2
jmp         (a2)

Eine Fehlermeldung aus dem Vector gibt es nicht. Da auch die CLOSE-Trap keine zurückzugeben braucht, ist hier das Ende erreicht. Wie schon beim Öffnen nimmt auch hier Qdos die Anpassung der Systemtabellen vor, ohne daß das extra programmiert werden müßte.
 
 


oben : zurück : weiter  inhalt 

(count)