back : next : content =

 4.4 ** Jobs

Wie Interrupt-Aufrufe im QL bearbeitet werden, ist gerade beschrieben worden. Eine wichtige Anwendung neben den Device Handlern findet sich in der Verwaltung der zugleich ablaufenden programe, der Jobs.

Zunächst einmal sollen die Strukturen zum Aufbau der Jobs beschrieben werden. Solchermaßen mit den Dingen vertraut, wird der Leser dann gleich wieder durch neue Vorschriften, -stellungen und -schläge in die Zange genommen. Denn für gewisse Vorgaben ist der Gedanke einer einheitlichen Konfiguration aller Jobs in einer Art Standardpapier formuliert worden, das Gegenstand des letzten Absatzes hierzu sein wird.
 

4.4.1 * Verwaltung

Mehrere programe können in einem relativ einfachen Gerät, wie dem QL, nicht wirklich gleichzeitig ablaufen, da die CPU das nicht leistet. Wenn andererseits ein program einfach gestartet wird, ist es nur durch einen Fehler oder sein vorgesehenes Ende aufzuhalten. Sollen also mehrere programe quasi zugleich betrieben werden, braucht es eine übergeordnete Organisation. Sie hat bei möglichst geringem Zeitaufwand die Aufteilung der Einzelprograme in kleinste Abschnitte zu überwachen. Dabei sind verschiedene Aufgaben zu erledigen, wie die gerechte Verteilung der Rechnerzeit z.B. einer angegebenen Prioritätszahl entsprechend, oder Sichern bzw. Wiederherstellen des Zustandes vom Augenblick der Umschaltung.

So werden beispielsweise der Reihe nach einzelne Jobs aufgerufen, wenn zwei Bedingungen zutreffen:

Zuerst muß ein Prioritätszähler einen bestimmten Wert erreicht haben, etwa abwärts von der Prioritätszahl an bis Null gezählt, oder ab Null aufwärts bis zur Gleichheit der beiden Zahlen.

Dann muß der Rechner zum Job-Wechsel freigegeben sein. Das ist immer der Fall, wenn kein weiterer Job existiert, wenn der laufende Job seine Aufgabe beendet oder unterbrochen hat, oder er von außen unterbrochen wurde.

Es können stattdessen auch Haltepunkte oder nur Pausen festgelegt werden, an denen andere Jobs eine Chance erhalten. Solche Systeme sind auch unter dem Stichwort "Round Robin" bekannt. Ihre Überwachung und Steuerung ist leicht zu bewerkstelligen, im Gegensatz zu interruptmäßig mit absoluten Prioritätszahlen verwalteten Systemen. Dabei ist aber die zeitlich genaue Steuerung nicht möglich. Die Prioritätszahlen selbst geben nämlich nur Relationen für die Aufteilung der Rechnerzeit an, eventuell auch noch die einzuhaltende Reihenfolge. Eine zeitliche Unschärfe entsteht außerdem dadurch, daß die Umschaltung immer nur dann stattfindet, wenn es der Vorgänger erlaubt. In kleinen Systemen, oder dort, wo nicht an zeitkritischen Mess- oder Steuerungsaufgaben zu arbeiten ist, genügt das völlig. An geeigneten Stellen muß aber beim programieren immer die Freigabe zur Job-Weiterschaltung eingefügt werden.

Dagegen gibt der Timer-Interrupt die Möglichkeit direkter Kontrolle. Dies sollte jedoch kurzen und nur besonders zeitkritischen programen vorbehalten bleiben. Sie müssen äußerst genau überwacht werden. Sonst können die Aufrufe durch fortwährende Verschachtelung den Computer völlig durcheinanderbringen.

Der Scheduler des QL ist solch ein kurzes program. Wenn der Rechner keine vorrangigen Interrupt-Routinen bedienen muß, wird er wird durch eine Uhr, die hier als Zeitgeber "timer" heißt, 50 Mal in der Sekunde aktiviert, zusätzlich immer dann, wenn ein neuer Job in den Arbeitsablauf aufgenommen wird (MT.ACTIV), und bei allen QDOS-Traps, wenn sie im User-Modus aufgerufen werden.

Er seinerseits gibt die Jobs frei, die dann in der beschriebenen Weise weiterarbeiten.

So ist das Multitask-System des QL eine Verbindung aus regelmäßigem Aufruf durch Timerinterrupt in kurzen Zeitabschnitten, dem Aufruf immer dann, wenn der Vorgänger müßig geht, und Aufruf nach einer durch eine Prioritätenordnung festgelegten Reihenfolge für einen festgelegten Zeitanteil.

Dabei übernimmt der Scheduler die Zuteilung der Rechenzeit und bei den einzelnen programen ist nur noch die Angabe einer Prioritätszahl erforderlich.

Diese wird jedem Job bei der Einrichtung mit JB.PRINC (priority increment) eingeprägt und bestimmt seinen Anteil an Rechnerzeit in Relation zu den sonst noch vorhandenen Jobs. D.h., ein Job mit der Prioritätszahl 1 erhält ein Zehntel der Zeit wie ein anderer mit der Prioritätszahl 10. Werte von 0 bis 127 sind erlaubt, bei 0 erhält der betreffende Job keinerlei Zeitzuweisung. Realisiert wird dieses Zuteilungssystem auf besonders einfache Weise durch einen Prioritätszähler JB.PRIOR. Diesen richtet das System in der jdt ein. Es wird dort ein für den Augenblick gültiger Rang (temporary priority {1}) dadurch ermittelt, daß er mit jedem Scheduleraufruf um die Prioritätszahl weitergezählt wird. Der Job, dessen Zähler den höchsten Stand erreicht hat, bekommt Rechnerzeit bis zum nächsten Aufruf des Schedulers, der Zähler selbst wird zuvor auf Null zurückgesetzt.

Nur für wenige besondere Ereignisse, die auf jeden Fall ohne Unterbrechung zu Ende geführt werden müssen, wird der Scheduler stillgelegt. Das geschieht mit dem Übergang in den Supervisor-Betrieb des Processors. programe dieser Art heißen "atomic" (unteilbar). Nahezu alle Systemaufrufe werden derart geschützt.

Etwa die zeitlich genaue Steuerung mechanischer Teile ist ein Ereignis, das nicht unterbrochen werden darf. Wegen absolut vorrangiger Datensicherheit gehört dazu besonders der Datenaustausch mit Microdrive oder Discetten-Laufwerk. Darum darf der Scheduler nicht zur Unzeit tätig werden. Er bleibt bis zum Ende einer derartigen Aktion abgeschaltet.

Einige Aufrufe erfordern besondere Beachtung, weil sie den Betriebszustand des Processors wechseln. Sie heißen hier "partially atomic". Wenn ein Codeteil "atomic" zu bleiben hat, wo also der Scheduler nicht aktiv werden darf, müssen sie strikt gemieden werden. Die anderen programe belassen den Processor in der zuvor eingestellten Betriebsart.

Schließlich soll ein Hinweis auf die generell mit einer Taktung verbundene zeitliche Unschärfe nicht fehlen. Zumal währenddessen oft noch zusätzlich irgendein Rechenprozess durchgeführt wird. Weiter ist die eigene Trägheit, die den gesteuerten Teilen innewohnen kann, nicht außer Acht zu lassen, so z.B. beim Abklingen der Löschspannung eines Magnetkopfes (Hardware der MDV).
 

4.4.2 * Jobs als programe

Neben der schon obligatorisch gewordenen Uhrenanzeige gibt {1} ein sehr anschauliches Beispiel mit dem "self cloning program", einer Demonstration sich selbst immer wieder neu aufrufender und bei Bedarf löschender Jobs, die eine strahlenförmige farbige Spur im Bild hinterlassen. Damit ist zugleich eine Besonderheit der Jobs im QL gezeigt. Es kann nämlich ein und dasselbe program als Job mehrfach und völlig selbständig betrieben werden, obwohl es nur einmal im memory vorhanden ist. Nur ein kleiner Block Systemdaten muß jeweils neu eingerichtet werden, dazu ggf. noch die eigenen Arbeitsbereiche. Bevor wir in solche Zonen vorstoßen, erst einmal zu den Grundlagen.

Die meisten Anwendungen werden als Jobs in Form von compilierten oder assemblierten Code-programen geschrieben und z.B. mit MT.CJOB aktiviert. Ein Job ist eine Einrichtung, die sich der Hilfsmittel bedient, die der Computer verfügbar hält:

  1. Er hat einen Prioritätenrang (setzbar bei Aktivierung oder mit MT.PRIOR), der ihm mit der Prioritätszahl eingeprägt wird. Sie ist beliebig änderbar, und er kann sie sich auch selbst geben. Damit fordert er Rechnerzeit für seine Aufgaben an.
  2. Er verfügt über einen eigenen memorybereich von vorgegebener Größe, in dem Daten und Code abgelegt werden. Für diesen läßt sich mit Hilfe von MT.FREE der verfügbare memoryplatz ermitteln. Gewöhnlich beginnt der Codeteil am Anfang und der Datenbereich am Ende dieses memorys. Dafür gibt es die "transient program area" (tpa), einen Bereich nicht begrenzter programe. Die programe können dort an beliebigem Ort liegen. Sie können entfernt und wieder neu und nicht unbedingt in genau demselben Bereich eingerichtet werden.
  3. Er kann I/O-Kanäle und auch andere Jobs "besitzen" und über sie verfügen, er kann sich aber auch einem schon vorhandenen Job unterordnen. Für die Kanäle gibt es die Aufrufe der Traps #2 & #3. Qdos errichtet zwischen den Jobs keine Barrieren. Darum können alle z.B. auf die Kanäle zugreifen, auch diese schließen, oder den Status anderer Jobs ändern (MT.SUSJB u.a.), sie auch löschen (MT.RELJB). Lediglich beim Auflösen des Besitzer-Jobs werden die ihm untergeordneten Jobs und Kanäle auch immer aufgegeben.
  4. Schließlich kann mit MT.TRAPV jeder Job seine eigenen Trap-Vectoren aktivieren und anderen Jobs vererben.
Der Befehls-Interpreter (im SuperBASIC enthalten) ist selbst so ein Job, hat jedoch als einziger die Möglich-keit, seinen Datenbereich zu verändern. Da er auch im Ganzen verschiebbar ist, liegt er am offenen Ende des RAM, "unterhalb" der sonstigen Jobs. An dieser Stelle sind weniger häufig Verschiebungen aufgrund anderer memoryreservierungen zu erwarten. Jedenfalls aber stören sie dort nicht allzusehr, da während der Errichtung eines Jobs ohnehin keine Aktivitäten des Basic möglich sind.

Jobs können einen dieser klar definierten Zustände haben:

  1. Der Job ist aktiv. Er teilt sie sich die Rechnerzeit mit anderen Jobs. Die Prioritätszahl hat dabei einen Wert ab 1 bis 127, nach welcher die Zeit aufgeteilt wird.
  2. Der Job ist suspendiert. Er wartet z.B. auf Ein-/Ausgabe, einen anderen Job oder auf den Ablauf einer Wartezeit. Solche Jobs wurden für eine angegebene Zeit (oder auch unbegrenzt) aus dem Scheduler herausgenommen, ihr Ablauf ist damit suspendiert. Gleichzeitig kann auch ein Job angegeben werden, der irgendeine Verrichtung erledigt, deren Ergebnis für die weitere Arbeit abzuwarten ist. Dann erfolgt Freigabe ggf. sobald der abzuwartende Vorgang durchgeführt ist. Suspendierte Jobs gelten weiterhin in der angegebenen Priorität als aktiv.
  3. Der Job ist inaktiv. Er verweilt lediglich im memory. Mit Prioritätszahl 0 erhalten Jobs keine Rechnerzeit.
Die Prioritätszahl JB.PRINC kann Null sein, der Job ist dann inaktiv, sonst gelten Zahlen von 1 bis 127, in manchen Systemerweiterungen auch noch 128 bis 255 (gleichbedeutend mit -128 bis -1). Mit der Variablen JB.STAT kann ihm eine Wartezeit in Vielfachen von 20 ms vorgegeben werden, ist diese -1, wird der Job nie aktiv, -2 signalisiert das Warten auf einen anderen Job. Null bewirkt Freigabe, und die Prioritätsbewertung kommt nun zum Tragen. Der Wert -2 soll der Benutzung nur durch das System vorbehalten bleiben, programiertes Eintragen wird ziemlich sicher zu Störungen führen.

Nach dem Einrichten eines Jobs gibt es in seinem memory zwei Bereiche mit besonderer Bedeutung:

Zu Anfang der Code und von hohen Adressen an abwärts der Stack. Vor dem memoryblock des Jobs und seines Codebereiches steht die jdt, die Job Definition Table, wo das System für jeden Job eigene Variable zur Steuerung des Multitasking anlegt. Die jdt wird auch Job Control Block (jcb) genannt. Sie ist zwar üblicherweise aber keineswegs zwingend an den Codememory gebunden. Beide könnten durchaus auch getrennt stehen. Ihre Adressen erhält man auf völlig verschiedenen Wegen.

In der Identitätszahl der Jobs ist in den niederwertigen 16 Bit die Job-Nummer enthalten. Sie ist zugleich die Tabellenposition in der Liste der Jobs. Ist die Zahl negativ, so existiert der betreffende Job nicht mehr.
Im Gegensatz zu anderslautenden Hinweisen vieler Literaturstellen - und welcher Fehlinformation auch der Autor dieser Schrift lange Zeit erlegen war - mag aber die "tag"-Nummer (s.u.) wie bei Kanälen auch bei Jobs durchaus negativ sein (MGG).

Die Adresse einer jdt läßt sich ermitteln:

jdt.adresse = 4 * job.nr + (SV.JBBAS(sb))
jdt.basis = (jdt.adresse)
Gültige Jobnummern liegen im Bereich Null bis (SV.JBMAX(sb)).
Eine absolute Obergrenze dafür ist nicht definiert.
Die jdt-Basisadresse ist außerdem nur gültig, wenn das erste Byte des betr. Tabelleneintrags nicht $FF ist.

Das Maximum von 119 Jobs im QL ist allein durch den verfügbaren memory und die vom System angelegte Tabellengröße begrenzt: Mit "mclone" entstanden z.B. im 4MB-Atari-QL mit SMSQ/E weit über 1000 Jobs.

Auf diese Adresse sind dann die Systemvariablen JB.. als Offsetangaben zu beziehen. Der Wert in JB.TAG (Ofs. 16) muß mit den höherwertigen 16 Bit der ID übereinstimmen, sonst existiert der gesuchte Job nicht (mehr).

Bei Aktivierung steht in A6 die Basisadresse des Jobs. Der Trap-Aufruf MT.JINF liefert diese für einen beliebigen Job nach A0. Hierzu muß die ganze ID bekannt sein, sie kann, wenn nur die Job-Nummer vorliegt, mittels JB.TAG aus der jdt zusammengestellt werden, wo dieser höherwertige Teil der Job-ID als Laufende Nummer verzeichnet ist.

Der memorybereich ist folgendermaßen aufzubauen:

Zu Anfang sind sechs Bytes Platz für einen langen Sprung frei, dort kann natürlich auch BRA.S oder BRA stehen, und davor irgendwelcher anderweitg sinnvoller (ausführbarer!) Code. Die sechs Bytes sind auf jeden Fall reserviert.

Es folgt die Konventionszahl $4AFB, die dem Betriebssystem einen Job signalisiert.

Danach ist die Länge des Namen in zwei Bytes und der Name selbst einzutragen.

Weiter auf gerader Adresse kann hier noch einmal die Konventionszahl $4AFB folgen. Ist der TK2 vorhanden, wird dann nach Aufruf mit EX das program wie eine Basic-Procedur behandelt und als besonderer Job mit den weiter unten erklärten Bedingungen eingerichtet.

Es schließt sich der Code-Teil an. Die Sprungadresse am Anfang wird gewöhnlich in diesen Bereich weisen. Dort beginnt die Ausführung des Jobs, falls nicht mit MT.CJOB eine besondere Startadresse vorgeschrieben wurde.

Wenn der Job erstmalig aktiviert wird, steht in seinem Register A6 die Basisadresse, in A4 der Offset zur Untergrenze, in A5 der zur Obergrenze des Datenbereichs. A7 ist der Stackpointer und zeigt auf eine mit dem Aufruf übergebene Kanal-Liste und den evtl. noch angegebenen Befehls-String.

Es obliegt dem programierer, die Zusatzinformationen zu nutzen. Die Basic-Befehle EX, EW und ET aus der TK2-Erweiterung, mit EXEC und EXEC_W des standardmäßigen SBasic können keine Parameter übergeben werden.

Aus dem Qdos ist die Reihenfolge bei Einrichten eines Jobs:

  1. memoryplatz für den Job reservieren.
  2. Dorthin den Code laden.
  3. Die Kanäle aufmachen.
  4. Den Job aktivieren.
Soll nicht ein besonderes Hilfsprogram zur Anpassung der Adressen aufgerufen werden, muß der Code positionsunabhängig sein, denn es steht nie fest, wo das System dem Job memory zuweisen wird.

Der memoryblock für einen Job:
 
0(A5,A6.L)
2 * 00 .w oder Parameter .w + .b Parameter-String data.len
.l letzte Kanal-ID
.. 
erste Kanal-ID
 
.l
.w Anzahl Kanäle
Stack A7, Laufpointer  
 
Datenbereich
 
0(A4,A6.L)
der Job... 

...beginnt hier irgendwo
    code.len
Code-Bereich
 
... JB.START ...

Job-Header .w + .b Job-Name
.w $4AFB
3 * .w Job-Einsprung-Code
A6
  104 * .b jdt QDOS
 

Für Jobs gibt es besonders diese Aufrufe der Trap #1:
 
MT.INF aktuelle Job-ID, Basis der Systemvariablen, etc.
MT.CJOB 1 richtet einen Job ein
MT.JINF 2 Status eines Job
MT.RJOB 4 entfernt inaktiven Job mit Nachfolgern
MT.FRJOB 5 entfernt einen Job-Zweig in jedem Falle
MT.FREE 6 längster freier Bereich der tpa
MT.TRAPV 7 richtet Tabelle der Trap-Vectoren ein
MT.SUSJB 8 suspendiert einen Job
MT.RELJB 9 gibt einen suspendierten Job frei
MT.ACTIV 10 aktiviert einen ruhenden Job
MT.PRIOR 11 setzt die Prioritätszahl fest

Nur mit MT.FRJOB kann ein Job sich selbst auflösen.
Der Job 0 (Basic) bleibt immer erhalten.
Hier, wie sonst auch, gilt -1 statt der ID für den aktiven Job selbst.

Über den anfangs Zugewiesenen hinaus kann ein Job weiteren memory im Bereich des "common heap" besetzen. Hierzu dient MT.ALCHP, freigeben läßt sich so ein memoryblock mit Hilfe von MT.RECHP. Dabei kann allerdings die Freigabe nicht ganz erfolgen. Es bleibt ein Pointerfeld von 8 Bytes zu "Haushaltungszwecken" erhalten, wo u. a. der ehedem besetzte memory als nun wieder Verfügbar markiert wird. Reservierungen mit MT.ALCHP tragen ein Feld von 16 Bytes ein. Zwar wird hier einerseits recht effizient, d.h. schnell und einfach, für die Wiederverwendbarkeit gewisser memorybereiche gesorgt. Andererseits, da neue Jobs kaum je dieselbe Größe haben, wie ein Vorgänger, wird der memory bei häufigem Wechsel der Jobs allmählich mehr und mehr zerstückelt.

Dieses scheußliche Ereignis ist die berüchtigte "heap fragmentation".

Umgehen läßt sich dergleichen oftmals durch einen "user heap", den man zu Beginn mit der maximal zu erwartenden Anforderung an die memorygröße dem Job zuweist, und dann von diesem selbst verwalten läßt. MT.ALLOC und MT.LNKFR sind hierzu vorgesehen. Weitere wichtige Hinweise stehen bei der Beschreibung der memoryverwaltung (3.1).

Die jdt eines jeden Jobs enthält in 104 Bytes folgende Angaben:
 
JB.LEN 00   X Gesamtgröße des Code- & Datenmemorys
JB.START 04     Startadresse bei aktivierung oder Null
JB.OWNER  08     ID des Besitzer-Jobs
JB.HOLD 12     Ptr auf ein Byte, das bei Entfernen des Jobs zu löschen ist.
JB.TAG 16 .w X Laufende Nummer, bei Initialisierung festgesetzt
JB.PRIOR 18 .b QL Prioritätszähler, bei aktivem Job mit jedem Scheduleraufruf um die Prioritäts-zahl weitergezählt. Wenn die Umschaltung freigegeben ist, wird der Job mit dem höchsten Wert Rechnerzeit erhalten. Sein Prioritätszähler beginnt dann wieder ab Null.
JB.PRINC  19 .b   QL Prioritäts-Increment (Prioritätszahl), Null legt einen Job still
JB.STAT 20 .b  Job-Status:
    0   freigegeben
    > 0   Wartezeit bis zur Freigabe
    -1   suspendiert (gesperrt, Job ruht)
    -2   wartet auf einen anderen Job
JB.RELA6 22 .b QL Bit 7 Flag für A6-relative Adressierung im nächsten Aufruf Trap #2 oder #3
JB.WFLAG 23 .b QL  =/= 0 wenn ein Job auf diesen wartet
JB.WJOB 24     Ggf. ID des wartenden Jobs
JB.TRAPV 28     Ptr auf Tabelle eigener Trap-Vectoren
JB.D0 32     Ablage der Register bei Job-Umschaltung. ... D0 bis A7
JB.A7 92      
JB.SR  96 .w   Zugehöriger Processor-Status
JB.PC 98     programzähler
jcb.reln 102   SM SM ersetzt dort JB.RELA6
SMSQ legt an derselben Stelle seine 16 "event"-Bits an

Alle Angaben .L, wo nicht anders bezeichnet.
'X' markierte Daten dürfen nicht verändert werden.
 
 

4.4.3 * Job als Procedur (TK2)

Die Beschreibungen zum TK2 sind gewohnt undurchsichtig und lassen wesentliche Fragen unbeantwortet. Weil diese besonderen Jobs aber nicht ganz uninteressant scheinen, sei das Wenige hier dennoch angegeben:

Die Spezial-Jobs des TK2 sind mit EX oder EW aktivierbar.

EX jobfilename {programspezifikation}
´programspezifikation' dabei
{Jobname} der mit EX aufgerufenen Procedur
und weiter den zusätzlich möglichen Angaben
,{Filename}    oder
,#Kanal         wahlweise gefolgt von
;{Parameterstring}
Sie unterscheiden sich von gewöhnlichen Jobs dadurch, daß sie nicht im eigentlichen Sinne ausführbare Jobs sind. Der aufgerufene Code bildet den Teil einer Basic-Procedur.

Nach Laden solch eines Jobs steht der ggf. auch übergebene String im Stack, dazu die ID einer erforderlichenfalls eröffneten Eingabe-PIPE.

Der Befehl EX veranlaßt den Sprung auf die Adresse nach der zweiten Kennzahl ($4AfB) am Job-Anfang.

Mit dem Einsprung enthalten die Register folgende Werte:
 
D4.l 1, wenn eine Ausgabe-PIPE existiert,, sonst 0
D5.l 1, wenn eine Ausgabe-PIPE existiert, die ID dazu steht im Stack
D6.l eigene Job-ID
D7.l gesamte Anzahl PIPEs und File-Namen der program-Spezifikation
A0 Adresse einer Tabelle vorgegebener Hilfs-Aufrufe 
{deren Sinn mangels Dokumentation weitgehend rätselhaft bleibt}
A1 Ptr auf den übergebenen Befehlsstring
A3 Ptr rel A6 auf den ersten Filenamen in der Name-Table
A4 eigener Stack-Ptr
A5 Ptr rel A6 hinter den letzten Filenamen der Name-Table
A6 Basis-Ptr des Basic 
A7 Stack-Ptr des Basic

A3 und A5 sind die Standardregister der Parameterübergabe aus dem Basic mit den Grenzen der Parametertabelle. Wichtig: der eigene Stack ist A4 und muß als solcher erhalten bleiben.

A6 und A7 dürfen nicht verändert werden.

Zunächst beginnt die Procedur durch Aufruf der auf (A0) zu diesem Zweck angegebenen Routine mit der Auswertung der Filenamen.

A3 rel a6 wird als Ptr auf den Namen dazu herangezogen.
D5 ist um die Zahl übergebener Kanal-IDs weiterzuzählen.
D1 - D7, A0 - A3 und A5 sind ansonsten frei verwendbar.
Danach kommen die Register zurück mit
D0 als Fehlercode,
A1 rel A6 bei fehlerfreiem Verlauf als Ptr auf den Namen,
A0 mit der Basic-Kanal-ID, falls eine Zahl mit Hash-Zeichen '#' übergeben wurde. D1-D3 werden smashed.
Die sonstigen Register bleiben erhalten.

Wurde ein Name übergeben, ist dann der zugehörige Kanal zu eröffnen. Dafür steht in 2(A0) eine Routine.  Dorthin ist zu übergeben:

D3 der Zugriffscode entsprechend IO.OPEN,
D6 die eigene Job-ID, wie sie bei Aufruf dort angekommen ist,
A1 rel A6 Ptr auf den Namen, der nicht im Basic-buffer stehen darf.

D0 kommt als Fehlercode zurück,
D1, D2 werden zerstört,
A0 erhält die Kanal-ID
A1 zeigt auf den benutzten Filnamen, ggf. mit einer Directory-Vorgabe.

Falls ein Kanal nicht geöffnet werden konnte, zeigt A1 auf den übergebenen Filenamen, ggf. um eine Directory-Vorgabe erweitert.

In beiden Fällen ist das Statusregister der CPU dem Inhalt von D0 entsprechend gesetzt.
 
 

4.4.4 * Konfiguration

Dieser - inzwischen (1997) um eine zweite Stufe erweiterte - Vorschlag {TT} für einheitliche Gestaltung eines besonderen Blocks an Daten soll zusammen mit dem Pointer Environment unter Kontrolle durch den Window Manager dem Nutzer die Anpassung an seine eigenen Wünsche und Gegebenheiten erlauben oder zumindest erleichtern.
Auch wenn o.g. Erweiterungen nicht vorliegen, wird es oft sinnvoll sein, Vorkehrungen dafür zu treffen. In der Tat ist nämlich die hier vorgestellte Datenstruktur in keiner Weise von irgendwelchen vorgegebenen programen oder Systemerweiterungen abhängig. Sie kann in beliebige programe eingebaut und von jedem geeigneten program be- rsp. verarbeitet werden.
Lediglich bei den vorbereitenden und abschließenden Routinen (s.u.) muß u.U.. auf die WMAN-Erweiterung (window manager) Rücksicht genommen werden, um die Compatibilität mit anderen Bearbeitungsprogramen zu wahren.
 

4.4.4.1 Die "level 01"-Konfiguration

Aufbau

Folgende Angaben werden verankert

Hier werden mit der Zeit sicherlich weitere Positionen angefügt werden, die dann von einem ggf. erweiterten Konfigurationsprogram zu betreuen sind (s.u.). Unabhängig von der Möglichkeit neuerer programe, die Liste bis zum Ende zu konfigurieren, wird es dann für ältere programe zunächst nicht möglich sein, einen neuen Block zu bearbeiten. Weil die Schlußmarke dann nicht erreicht wird, ist für störungsfreien Ablauf gesondert Sorge zu tragen.

Dem Vorschlag gemäß besteht die Identifizierung aus den Zeichen "<<QCFX>>" am Beginn des Konfigurationsblocks. Dieser Block kann sich an beliebiger Stelle des programs befinden.

Es folgen zwei ASCII-Ziffern für die Konfigurationsstufe, z.B. (mindestens) "01".

Der programname wird als String mit vorangestelltem Wort für die Länge eingetragen, desgleichen die Version.

Darauf folgt die Liste der einzelnen Posten.
Alle Eintragungen beginnen auf gerader Adresse.

Posten-Typen

Die Konfiguration der Stufe Eins erlaubt sieben Typen:

  0 String
  2 Schriftzeichen
  6 Auswahlcode
  4 Code
  8 Byte
10 Wort
12 Langwort
0 String

Ein Standardstring, ggf. mit angehängtem Schlußzeichen wird eingetragen, und danach genügend Platz für die längstmögliche Eingabe besetzt. Wieder auf gerader Adresse schließt sich ein Attribut-Wort an, dessen Bit 0 bindend definiert ist. Ungesetzt bedeutet es, daß führende und abschließende Leerzeichen einer Angabe zu streichen sind.

2 Schriftzeichen - character

Ein einzelnes Byte, Steuerzeichen werden in zwei Bytes als String ausgegeben, etwa ^J für $0A. Ein einzelnes Wort Attribute folgt und gibt die Art des Zeichens in zugeordneter Bitposition an:

0 druckuntaugliches Zeichen, Steuerzeichen
1 Ziffer
2 kleingeschriebener Buchstabe
3 großgeschriebener Buchstabe
4 sonst drucktaugliches Zeichen
6 Cursorzeichen
8 ein um $40 höherer Bytewert  ist eingetragen.
Bit 8 gerät u.U. in Widerspruch zu den anderen Bits. Es ist für richtigen Aufbau dieser Tabelle zu sorgen. Zu Bits 5 und 7 ist nichts angegeben.

4 Code

Ein einzelnes Byte mit einer Zahl für die Auswahlmöglichkeiten. Attribute sind eine Liste aus

Codebyte mit einem Wert
Auswahlbyte für den zugehörigen Tastencode
Standardstring
Ein Wort -1 schließt die Liste ab.

Die Tastenauswahl 00 steht für die Abarbeitung einer sinnvollerweise nur kurzen Liste Aktionen. Ansonsten ist zwischen Bearbeitung jedes Wertes einzeln nach Angabe oder der Wahl aus einem Menu zu entscheiden.

6 Auswahl - select

Grundsätzlich dasselbe, wie der Code-Typ. Nur wird hier das Byte als Index in eine Liste von Statusbytes bewertet. Jenes erhält, wurde es ausgewählt, den Wert $80 (WSI.SCLT), die vorangegengene Wahl wird mit 00 (WSI.AVABL) wieder verfügbar markiert. Jedes nicht verfügbare Byte erhält die Marke $10 (WSI.UNAV).
Dabei darf die erste Listenposition nie als nicht verfügbar markiert wrden.

8,10,12 Werte - values

Ihrem Typ entsprechende Eintragungen. Die Attribute geben die Grenzwerte an. Alle gelten vorzeichenlos.

Tastencode

Der Tastencode zur Posten-Auswahl wird als Großbuchstabe angegeben, mittels dessen der betreffende Posten im Hauptmenu auszuwählen ist. Der Auswahlvorgang selbst hängt vom betreffenden Typen ab.
Ist der Posten vom Typen "Code" oder "Auswahlcode", könnte man etwa ein Menu-window öffnen, das dem Nutzer die Wahl ermöglicht. Ist er vom Typ eines Schriftzeichens, wird ein einzelner Tastendruck auszuwerten sein. Ansonsten ist der Posten selbst Vorgabe für einen Editiervorgang.

Posten-Ptr

Ptr auf solche Posten sind zu ihrem Eintragungsort relative Adressen in Wortlänge.
 

Ptr auf vorbereitende Routine

Wird ein vorbereitender Prozess nicht erforderlich, so ist hier Null einzutragen. Sonst aber kann von hieraus etwa eine Bereichsvorgabe ermittelt werden, oder auch die gesamte Konfiguration steuern. Bei Aufruf erhält sie in den Registern

D7 Null oder Vector des Window Manager
A0 Ptr auf den Posten
A1 Ptr auf dessen Beschreibung
A2 Ptr auf die Attribute
A3 Ptr auf 4K Bytes memoryplatz
A7 Stack mit 512 nutzbaren Bytes
D2 - D7, A0, A3 - A6 sind frei verwendbar.
Zurück gehen
D0 Fehlercode
    <0 für Fehlerabbruch,
    >0 wenn der Posten schon besetzt wurde, 
         wobei dann keine Aktion erfolgen darf.
A1 (neuer) Ptr auf Beschreibung
A2 (neuer) Ptr auf Attribute
 
Ptr auf abschlieszende Routine

Dieses Hilfsprogram wird vor der eigentlichen Konfiguration vor jedem Einzelposten aufgerufen, außerdem für sämtliche Posten, wenn ein Einzelner verändert wurde.

Hier können etwa Grenzwerte oder andere Bedingungen überprüft werden, auch lassen sich die vorgegebenen Beschreibungen und Attribute ändern.

Bei Aufruf werden Übergeben

D1 .b gesetzt bei gerade geändertem Posten
D7 Null oder Vector des Window Manager
A0 Ptr auf den Posten
A1 Ptr auf dessen Beschreibung
A2 Ptr auf die Attribute
A3 Ptr auf 4K Bytes memoryplatz
A7 Stack mit 512 nutzbaren Bytes Register
D2 - D7, A0, A3 - A6 sind frei verwendbar.
Zurück gehen
D0 Fehlercode,
    <0 für Fehlerabbruch, sonst okay und
        Bit 0 Posten zurückgesetzt
        Bit 1 Beschreibung dto.
        Bit 2 Attribut dto.
D1 .b Status für den Posten (nicht / verfügbar)
A1 (neuer) Ptr auf Beschreibung
A2 (neuer) Ptr auf Attribute
Die Zeilenzahl der Posten muß auch bei Änderungen erhalten bleiben.
Bei Fehlern wird eine Meldung ausgegeben und das Verfahren unverändert neu durchlaufen.

Beschreibung

Die Beschreibung ist ein String und kann aus einer Anzahl Zeilen mit je bis zu 64 Zeichen bestehen, getrennt durch <NL>-Zeichen (10).
Die letzte Zeile muß wenigstens so lang sein, wie die Längste unter ihren Vorgängern.
Diese Liste ist auch aus dem program heraus verlängerbar.

Ptr auf Attribute

Diese sind von den einzelnen Typen abhängig und wurden dort beschrieben.

 

4.4.4.2 Die "level 02"-Konfiguration

Quelle: QL-Today, ..(hab ich verbummelt)..
 
 
 
 
 
 
 
 
 

top : back : next : content

= (count)