Frequently Asked Questions
Typische Fragen bei der Programmierung in PEARL unter RTOS-UH
Scheinbare Compiler- und Betriebssystemfehler
Der Lader findet eine der PEARL-Einbaufunktionen BEG, INSER, INSTR, KON, LEN, MID, READ, WRITE nicht
In PEARL-90 sind die Prozeduren compilerintern angeschlossen. Sie dürfen daher nicht global spezifiziert werden.
Nach Drücken der [ABORT]-Taste erfolgt ein RESET des Systems
Dieses kann nur geschehen, wenn die Verzeigerung der Speicherkette zerstört ist. (Die Kette ist sowohl vorwärts als auch rückwärts verzeigert). Durch ein PEARL-Programm kann dies nur dadurch geschehen, wenn über die Feldgrenzen einer Zeichenkette oder eines Feldes hinaus geschrieben wurde oder mit REF-Variablen gearbeitet wird. Ein Feldüberschreitungsfehler lässt sich durch Neuübersetzen mit der Compileroption /*+T*/ (Feldindex- und Prozedurparametertester) und erneutes Starten am schnellsten finden.
Ein PEARL-Programm läuft auf eine Wrong-Operand-FPU Exception
Hier hat eine Task versucht, eine Float-Variable in die Floating-Point-Unit zu laden. Die Variable war entweder nicht initialisiert oder Ergebnis einer Ausnahmebehandlung (z.B. der Division durch 0.). In solchen Fällen gibt die FPU eine sogenannte Not-a-Number (NaN) zurück. Dieser Fehler kann auch auftreten, wenn einzelne Module eines Programmpaketes für unterschiedliche Prozessoren übersetzt wurden. Wenn dann gemeinsame FLOAT-Variablen benutzt werden, können bestimmte Zahlenwerte eine Exception auslösen. Bei PEARL-90 Prozeduren mit FLOAT-Variablen als Parameter kann der Fehler nicht auftreten, da der Compiler beim Testen der Prozedurparameter die unterschiedlichen Formate berücksichtigt, sofern der Anwender die Option /*+T*/ benutzt.
Ein PEARL-Programm läuft auf eine Bus-Error- oder eine Wrong-Opcode-Exception
Auch hier kommen als mögliche Fehlerursachen Feldüberschreitungsfehler oder Benutzung von REF-Variablen in Frage. In beiden Fällen ist dann allerdings Opcode unzulässigerweise überschrieben worden. Bei Verwendung von Feldern liegt die Überschreitung eines modulweiten Feldes am nächsten.
Eine mit DL taskname angezeigte Zeilennummer existiert in dem Modul, in dem die Task steht, überhaupt nicht
Wird aus einer Task eine externe Prozedur exekutiert, wird natürlich die Zeilennummer der Prozedur in dem zugehörigen Modul angezeigt. Auch mit #INCLUDE importierte Dateien haben ihre eigene Nummerierung.
Trotz scheinbarer Verwendung des Line-Markers wird immer nur die Zeilennummer 0 ausgegeben
Bei Verwendung der Compileroptionen muss die Option direkt hinter
dem Kommentarzeichen stehen, in diesem Fall also /*+M*/. Befindet
sich auch nur ein weiteres Zeichen, z.B. ein "blank", dazwischen,
wird die Option als Kommentar interpretiert:
/*+M schaltet den Linemarker ein*/
/* +M ist ein Kommentar*/
!+M ist auch ein Kommentar
Dies gilt entsprechend auch für alle anderen Compileroptionen.
Ein PEARL-Programm läuft auf eine wrong op-code FPU Exception
Hier kommen zwei Ursachen in Frage. Entweder wollte eine Task illegalen FPU-Code ausführen oder eine Float-Variable in die Floating-Point-Unit laden, deren Bitmuster keine Float-Zahl darstellt. Ein typischer Fehler bei nicht initialisierten Variablen. Dieser Fehler kann auch auftreten, wenn einzelne Module eines Programmpaketes für unterschiedliche Prozessoren übersetzt wurden (s. auch (>)).
PEARL-Tasks lassen sich trotz der /*+M*/ Option nicht tracen
Der Quelltext wurde mit der Compileroption MODE=NOLSTOP; übersetzt, was auf moderneren Prozessoren zu einer deutlichen Erhöhung der Arbeitsgeschwindigkeit führt. Dafür ist in Kauf zu nehmen, dass die Task nicht mehr zu tracen ist. Bei Fehlern oder dem Absetzen des Kommandos DL zeigt RTOS-UH die richtige Zeilennummer.
Beim Compilieren tritt der SIZE_LIMIT_ERROR auf
Der RTOS-UH-PEARL-Compiler ist ein einphasiger Compiler. Der
Quelltext wird also nur einmal abgearbeitet. Bei Beginn eines
Compilerlaufs wird der Speicherplatzbedarf mit 8 KByte für
DATIONs, modulweite Variablen und Code defaultiert. Übersteuern
lässt sich die Defaultierung durch die Anweisung
S = Speicherbedarf_in_hex-Darstellung;
in der ersten Zeile des Quelltextes.
Beispiel: S=8000; ermöglicht 32 KByte Opcode.
Ist die Anweisung schon vorhanden, ist die angegebene Zahl mindestens auf
die in der Compilerschlußbilanz ausgegebene zu vergrößern.
Bei der Verwendung von globalen Zahlenvariablen werden vermeintlich falsche Werte übergeben
Weder Lader noch Compiler können überprüfen, ob die im Problemteil spezifizierten globalen Variablentypen mit den in anderen Modulen deklarierten globalen übereinstimmen. Dieses muss der Anwender sicherstellen.
Der Feldindextester gibt eine Fehlermeldung aus, obwohl die Indizes innerhalb der gültigen Grenzen liegen (nur PEARL 80)
Der vom Compiler generierte Code kann Feldelemente des Feldes
DCL arrayname (Imax, Jmax, Kmax) vartyp;
nicht mehr mit der "kurzen" Zugriffsart erreichen. Gilt
I + J * Imax + K * Jmax * Imax >
32768 - (1 + Imax + Imax * Jmax)
bzw.
I + J * Imax >
32768 - (1 + Imax) bei zweidimensionalen Feldern,
muss die Variable des ersten Indizes eine Fixed(31)-Variable sein.
Beispiel:
arrayname (130,70,70) = 2.71828; ! falscher Schreibzugriff
arrayname (130(31),70,70) = 2.71828; ! korrekter Schreibzugriff
(solange Imax > 129, Jmax > 69, Kmax > 69 gilt).
Der Feldindextester gibt bei einem Zugriff auf ein nicht vorhandenes Feldelement eine Zeilennummer aus, in der kein Feldzugriff steht
Ist sichergestellt, dass sich die Zeilennummer tatsächlich auf
das Modul bezieht, in dem nach dem Fehler gesucht wird (siehe Punkt (>)),
gibt es noch Konstellationen, in denen eine andere Zeilennummer angezeigt wird.
Wird zum Beispiel die TASK
ABC: TASK; /*+M*//*+T*/
DCL MUELL FIXED;
DCL ARRAY(14) FIXED;
MUELL=0;
IF MUELL NE 0 THEN MUELL=17;
ELSE ARRAY(MUELL)=8;
FIN;
END;
exekutiert, wird immer der ELSE-Zweig durchlaufen, jedoch immer
die Zeilennummer des THEN-Zweiges angezeigt. Der PEARL-Compiler
generiert am Anfang einer jeden Zeile den Code zur Ablage der
Zeilennummer. In der Zeile mit dem Feldzugriff gehört die
Zeilennummerablage noch zum THEN-Zweig. Da dieser nicht durchlaufen
wird, ist die falsche Anzeige vorprogrammiert.
Der PEARL-Compiler meldet am Ende der Compilation "Premature end of input-file", obwohl am Ende des Quelltextes ein MODEND steht
Nach einer großen Anzahl von Fehlermeldungen kann diese Abschlussmeldung auftreten,
falls der Compiler die Task- oder Prozedurebene nicht verlassen hat. In diesem
Fall sollte man mit dem Beheben des ersten Fehlers beginnen. Interessanter ist diese
Meldung, falls vorher keinerlei Fehler angezeigt wurde.
Möglich ist hier ein mit /*
begonnener Kommentar,
der nie beendet wurde. Auch eine Zeichenkette, die nicht mit '
beendet
wurde, kann zu diesem Fehler führen.
Fragen zur Konsole (Terminal)
Eine Task soll genau ein Zeichen von der Tastatur lesen
Bei RTOS-UH gehören zu jeder seriellen Schnittstelle die
vier Datenstationen /Ax, /Bx, /Cx und /Dx,
wobei x die Nummer der seriellen Schnittstelle bezeichnet.
Soll ein Tastenanschlag erst nach Ausführung des Kommandos GET berücksichtigt werden,
ist /Ax zu verwenden, sonst /Bx.
/Ax bzw. /Bx sind dann wie folgt im Systemteil zu definieren:
vardation : /Ax(TFU=1); ! Löscht vor "GET" Eingabepuffer
vardation : /Bx(TFU=1); ! Tastenanschlag vor Abarbeitung des
! GET wird akzeptiert.
Wie kann abgefragt werden, ob die Tastatur gedrückt wurde?
Hierfür ist die Datenstation Cx zu verwenden. Sie ist im Systemteil
wie folgt zu spezifizieren:
vardation : /Cx(TFU=1);
Auf die Datenstation kann dann wie folgt zugegriffen werden:
GET varchar FROM vardation by A(1);
Anschließend muß der Variablenwert untersucht werden:
IF varchar EQ TOCHAR(0) THEN
/* Es wurde keine Taste gedrückt */
ELSE
/* Angeschlagene Taste steht in varchar */
FIN;
Wie kann unter PEARL eine Ausgabe auf dem Bildschirm erfolgen, wenn bereits mit GET auf die Tastatur zugegriffen wird?
Falls auf /Ax oder /Bx mit GET zugegriffen wird, kann eine Ausgabe mit PUT über /Dx erfolgen.
Warum sollte man statt der Datenstation /Ax die Datenstation /TYA verwenden, wenn eine Kommunikation mit der eigenen Konsole erfolgen soll?
In den meisten Fällen wird sich im Programmablauf nichts ändern, weil der Anwender über die serielle Schnittstelle 1 mit dem System kommuniziert. Ist er jedoch einmal über eine andere Schnittstelle verbunden, erfolgen trotzdem alle Ein- und Ausgaben der PEARL-Programme über die Datenstation /A1. Bei Verwendung der Datenstationen /TYA, /TYB und /TYC anstatt von /Ax, /Bx und /Cx überprüft der Lader beim Befriedigen der Referenzen /TYA.../TYC, von welchem User der Befehl zum Laden kam und setzt das Device von STDIN mit den Eingabeeigenschaften von /Ax ... /Cx ein.
Wodurch unterscheiden sich die Datenstationen /Ax, /Bx, /Cx und /Dx ?
Bei seriellen Schnittstellen gibt es prinzipiell die drei
Datenstationen /Ax, /Bx und /Cx, in den meisten Fällen
auch /Dx.
Bezüglich der Ausgabe sind die Datenstationen /Ax, /Bx und
/Cx identisch.
Bei der Eingabe gibt es jedoch einen Unterschied: Bei
der Verwendung von /Ax beim Einlesen mit GET wird
der Eingabepuffer gelöscht, d.h. alle vor Absendung des Eingabe-CEs
angeschlagenen und noch nicht abgearbeiteten Zeichen gehen
verloren. Bei Verwendung von /Bx werden diese jedoch als Eingabe
bzgl. dieses CEs interpretiert.
Bei der Verwendung der Datenstation /Cx füllt die Betreuungstask
der seriellen Schnittstelle das Eingabe-CE solange mit Zeichen
auf, bis das CE gefüllt ist. Sind alle angeschlagenen Zeichen
bereits vor Füllung des CEs abgearbeitet, verwendet die Task den
ASCII-Wert 0, um das CE zu füllen. Dadurch kann das CE sofort dem
Absender zurückgeschickt werden. Für diesen entstehen dadurch
keine Wartezeiten.
/Dx kann für die Ausgabe benutzt werden, auch wenn über /Ax oder
/Bx eine Eingabeaufforderung abgeschickt wurde. Mit /Dx sind
jedoch nur Ausgaben möglich.
Fragen zu Datenstationen
Anmerkung: In PEARL ist der Begriff alphanumerische Datenstation (DATION ALPHIC) ein Sammelbegriff für Terminal, Floppy, Festplatte, Netzwerk, Pipes und die Datenstation /ED. Das Verhalten aller Datenstationen ist - von Feinheiten abgesehen - identisch, solange keine physikalischen Gründe dagegensprechen. Alle in diesem Kapitel erörterten Fragen sind daher auch für das Arbeiten mit Pipes und dem Terminal gültig.
Wie funktioniert das Einlesen mit GET bei RTOS-UH?
Da RTOS-UH bei der Ein- und Ausgabe von ALPHIC-Dations mit Datenpaketen (CEs) arbeitet, die an E/A-Dämonen geschickt werden, funktioniert das Einlesen anders als bei anderen Betriebssystemen. Ein Datenpaket hat das folgende Format: Lies maximal 128 Zeichen, wenn jedoch vorher ein Cr, Lf oder Eot, vorkommt, beende dieses Eingabepaket. Das hat zur Folge, dass ein GET nicht immer mit dem Senden eines Datenpaketes verbunden ist: Ist ein Datenpaket noch nicht abgearbeitet, liest RTOS-UH aus diesem CE weiter. Wird bei einem GET zeichenweise eingelesen, kann das zur Folge haben, dass kein neues Eingabe-CE zu senden ist. Ist die durch die Formatliste vorgegebene Länge größer als 128, kann mit einem GET auch das Absenden mehrerer Eingabe-CEs verbunden sein.
Welche Bedeutung hat ein SKIP in der Formatliste beim Einlesen mit GET?
Ist beim letzten Zugriff mit GET ein CE nicht vollständig abgearbeitet worden, so veranlasst das SKIP das Betriebssystem, dieses CE zu verschrotten und auf jeden Fall ein neues Eingabe-CE anzufordern. Das folgende Beispiel demonstriert den Unterschied.
SPC TERMINAL DATION INOUT ALPHIC GLOBAL; ! Bedieninterface AAT: TASK; DCL EINGABEFLOAT FLOAT; DCL EINGABEFLOAT1 FLOAT; GET EINGABEFLOAT FROM TERMINAL BY E(15); ! Schlaegt Nutzer jetzt "4.345578901234567" + CR an, steht der ! Lesezeiger nach dem GET auf dem 16. Zeichen, also der 6. GET EINGABEFLOAT2 FROM TERMINAL BY E(15); ! Dieser Variablen wird ! die 67.0 zugewiesen, da Eingabe nicht mit SKIP,E(15) erfolgte!! END;
Wie kann man bei Benutzung des Befehls GET abfragen, ob das Einlesen fehlerfrei erfolgte?
Durch Einfügen des Codes RST(varfixed), wobei varfixed
eine Variable des Types FIXED ist.
Die Syntax lautet dann:
GET varlist FROM vardation BY RST(varfixed), SKIP, formatlist;
Ist das Lesen fehlerfrei erfolgt, wird
varfixed der Wert 0 zugewiesen, im anderen Fall erfolgt die
Belegung entsprechend der Funktion ST (siehe PEARL-Handbuch).
Soll sogar noch überprüft werden, bis zu welcher Variablen das
fehlerfreie Lesen erfolgte, kann die Zeile
GET var1, var2, ... , varn FROM vardation BY SKIP,
RST(varfixed1), format1, RST(varfixed2), format2, ... ,
RST(varfixedn), formatn;
programmiert werden.
Wie lässt sich eine durch das Betriebssystem veranlasste Fehlermeldung beim Zugriff auf eine Datenstation unterdrücken?
Die Datenstation muss wie folgt im Systemteil definiert werden:
vardation: device[/pfad][/dateiname](NE);
Wie wird das [CR] bei der Eingabe behandelt?
Das Laufzeitsystem unterscheidet, ob mit dem Format A oder dem Format A(varfixed) eingelesen wird. Im ersten Fall wird das [CR] in ein Blank verwandelt, im zweiten wird es an der entsprechenden Stelle im String als [CR] abgelegt.
Zur Laufzeit eines Programmes soll [CTRL]- A, B ,C unwirksam bleiben
Dazu sind alle DATIONs, die auf das entsprechende Terminal zugreifen,
im Systemteil mit AI=$0400 zu parametrieren:
TASTATUR : /A1(AI=$0400); ! [CTRL]- A,B,C unterdrücken
Die Blockierung gilt solange, bis die erste Ein- oder Ausgabe mit
nicht gesetztem "suppress command"-Bit erfolgt.
Daher sollte am Ende eines Programmes eine entsprechende Ausgabe
erzeugt werden, um anschließend mit [CTRL]-A weiterarbeiten zu
können. Soll keine weitere Datenstation deklariert werden,
könnte eine Ausgabe mit EXEC (siehe Punkt (>)) erzeugt werden:
varbit = EXEC('ECHO Diese Ausgabe gibt [CTRL]- A,B,C frei');
Lassen sich die DATION-Attribute MB, AI und NE auch task- und prozedurweiten DATIONs zuweisen?
Task- und Prozedurweite DATIONs sollen bei Ihrer Deklarierung
betriebssystemunabhängig sein. Daher ist die direkte Zuweisung
nicht möglich. Aber mit Hilfe des Schlüsselwortes CREATED lassen
sich einer Dation die Attribute einer anderen zuweisen:
SYSTEM;
NEVERUSED: /NIL(AI=$0400,NE); ! <CTRL>A,B,C unterdrücken
! Keine Fehlermeldung.
PROBLEM;
SPC NEVERUSED DATION INOUT ALPHIC;
T1: TASK;
DCL vardation DATION INOUT ALPHIC CREATED(NEVERUSED);
END;
Die Datenstation vardation erhält eine lokale Kopie von NEVERUSED.
Für eine Ein- und Ausgabe ist sie mit OPEN ... BY IDF(); zu öffnen,
da sonst alle Communication-Elemente zur Datenstation /NIL gesendet werden.
Wie lässt sich nach Zugriff auf eine Dation der Fehlerstatus der Dation abfragen?
Durch Benutzung der Funktion ST:
varfixed = ST(vardation);
Die Benutzung der Funktion ist im PEARL-Handbuch beschrieben.
Wie lässt sich zur Programmlaufzeit eine Datenstation ändern?
Bei vielen Anwendungen kommt es vor, dass das Device geändert
werden soll, beispielsweise um auf eine andere Partition einer Festplatte
zuzugreifen.
RTOS-UH kann den Befehl OPEN ... BY IDF(); auch mit dem
kompletten String aus Device und Pfad ausführen.
Beispiel:
OPEN DATEI BY IDF('/H0/TEST'); ! öffnet das File /H0/TEST.
Mit diesem Befehl ist es allerdings nicht möglich, nur das
Device, nicht aber den Pfad zu ändern. Dafür gibt es die
Funktion ASSIGN, die im Problemteil mit
SPC ASSIGN ENTRY (DATION INOUT ALPHIC CONTROL(ALL) IDENT,
CHAR(24)) GLOBAL; ! PEARL 80-Compiler: Version 14.x-y
bzw.
SPC ASSIGN ENTRY (DATION ALPHIC CONTROL(ALL) IDENT, CHAR(24))
GLOBAL; ! PEARL 90-Compiler: Version 15.x-y
spezifiziert werden muss. Der Aufruf erfolgt mittels des Codes
CALL ASSIGN(dationname, varstring mit Devicename);
Soll zum Beispiel über die Dation mit dem logischen Namen
SCHNITTSTELLE eine Ausgabe auf die Parallelschnittstelle erfolgen,
könnte die Umlenkung mittels der Zeile
CALL ASSIGN(SCHNITTSTELLE,'PP');
erfolgen.
Wie kann bei dem Befehl OPEN ... BY IDF() das Working-Directory mitbenutzt werden?
Beginnt der übergebene String mit "./", setzt das
Betriebssystem das Working-Directory vor den String. Eine Verwendung des
Befehls ENVGET (siehe PEARL-Handbuch, Kapitel "Environment") ist auch möglich:
varstring = ENVGET('$WORKDIR/DATEI');
OPEN vardation BY IDF(varstring);
Lautet das Working-Directory z.B. /H0/XD , wird die Datei
/H0/XD/DATEI geöffnet.
Einer DATION soll im Systemteil das Device zugeordnet werden, aus dem der S-Record geladen wurde
Im Systemteil der DATION ist als Device /ALDV (Actual Load Device) zu verwenden. Der Lader trägt dann das korrekte Device ein.
Wie kann auf ein beliebiges File einer Dation zugegriffen werden, wenn der Name des Files erst zur Laufzeit bekannt ist?
Für diesen Fall ist in PEARL das Schlüsselwort IDF vorgesehen.
Im Systemteil sollte die Dation wie folgt deklariert werden:
vardation : device/DIES_IST_NUR_EIN_PLATZHALTER;
Beim Öffnen der Dation kann dann der richtige Name verwendet werden:
OPEN vardation BY IDF(varstring mit Filenamen);
Statt des Files DIES_IST_NUR_EIN_PLATZHALTER wird nun der in dem
String angegebene Name verwendet. Der Platzhalter im Systemteil
ist notwendig, weil zur Laufzeit der neue Name im Systemteil
eingetragen werden muss. Reicht der Platz nicht aus, kann die
DATION nicht geöffnet werden. Der Platzhalterstring muss also
mindestens so viele Bytes lang sein wie die Subdirectories und
Filename.
Kann man auf mehrere Files eines Devices mit einer im Systemteil deklarierten Datenstation zugreifen?
Dieses ist prinzipiell möglich. Beim Durchlesen der vorherigen
Frage müssen jetzt jedoch alle Alarmglocken klingeln. Es ist
nämlich nur dann möglich, wenn zwischen dem Öffnen und
Schließen eines Files kein anderes geöffnet oder geschlossen
wird, da die Anweisung OPEN ... BY IDF() den Pfad der
DATION verändert und den Lese-/Schreibzeiger auf den Dateianfang
setzt.
Durch entsprechende PEARL-Zeilen lassen sich die dabei
auftretenden Probleme lösen, sind zwei Files gleichzeitig zu
bearbeiten, sollten lieber zwei DATIONs verwendet werden.
Wie lässt sich überprüfen, ob ein File auf einem Device vorhanden ist?
Durch Öffnen des Files mit einer der beiden folgenden Zeilen:
OPEN vardation BY IDF(varstring mit filenamen),OLD;
OPEN vardation BY IDF(varstring mit filenamen),NEW;
Ist das File nicht vorhanden, gibt es im ersten Fall eine
Fehlermeldung, ist es vorhanden, beim zweiten. Der Status der
DATION lässt sich mit der Funktion ST abfragen
(siehe PEARL-Handbuch und Punkt (>)).
Leider ist die Benutzung von OLD und NEW nur in Verbindung
mit dem Schlüsselwort IDF zulässig, so dass im
Systemteil angegebene Pfade zwangsläufig überschrieben werden.
Wie kann man auf eine Datenstation mit mehreren im Systemteil definierten DATIONs zugreifen?
Dieses ist problemlos möglich. Bei Filesystemen ist allerdings zu bedenken, dass nur ein Lese/Schreibzeiger für das File vorhanden ist. Soll gleichzeitig mit mehreren DATIONs auf ein File zugegriffen werden, muss sichergestellt sein, dass der richtige Zeiger verwendet wird.
Wie kann ein [CR] zu einer DATION geschickt werden?
Durch die Befehlszeile PUT TO vardation BY SKIP;.
Wird das Dateiende bereits beim Einlesen des letzten Zeichens erkannt?
Das hängt davon ab, ob z.B. beim Einlesen von Strings das Eingabefomat mit fester Länge erfolgt: GET varstring FROM vardation BY SKIP,A; liefert ein letztes fehlerfreies Lesen, wenn dieses GET das Dateiende trifft. Ein darauf folgendes Einlesen von varstring mit GET verändert varstring übrigens nicht!
Erfolgt das Einlesen mit fester Länge, ist das letzte Einlesen bereits
fehlerbehaftet:
Trifft
GET varstring FROM vardation BY RST(varfixed), A(255);
das Dateiende, weist RTOS-UH varstring ordnungsgemäß die letzten noch nicht
gelesenen Zeichen zu, varfixed steht jedoch auf 1, was ein Dateiende
andeutet.
Fehlt beim Einlesen einer FLOAT-Variablen der Dezimalpunkt, wird der eingelesene Wert um einige 10er-Potenzen verschoben
Hier wurde das falsche Einleseformat verwendet. Bei der Eingabe ist
eigentlich nur E(Zahl1) bzw. F(Zahl1) sinnvoll.
Dann wird die Eingabe von 1E3
auch als 1000 interpretiert.
E(Zahl1, Zahl2) bzw. F(Zahl1, Zahl2)
interpretiert den eingegebenen Wert anders als meistens erwartet.
Wie wird das [LF] beim Einlesen behandelt?
MS-DOS kennzeichnet das Ende einer Zeile mittels der Zeichenkombination
[CR], gefolgt von einem [LF]. Anhand der Beispieldatei
12345678.90[CR][LF]
2345678.901[CR][LF]
345678.9012[CR][LF]
45678.90123[CR][LF]
5678.901234[CR][LF]
soll das Verhalten von RTOS-UH bei der Eingabe demonstriert werden.
Werden die FLOAT Variablen mit den Formaten "SKIP, E(30)
",
"SKIP, F(30)
", "E(30)
" oder "F(30)
" eingelesen,
wird hinter jeder Zeile eine 0.0 eingefügt, da das [LF] als
Eingabeende interpretiert wird und zwischen [CR] und [LF] nichts steht.
Bei den Formaten A und SKIP,A wird ab dem zweiten Einlesen das [LF] an die erste Stelle der Zeichenkette gesetzt. (Vorausgesetzt, die Zeichenkette ist lang genug, um eine ganze Zeile aufzunehmen.) A(5) liest jeweils 5 Zeichen, wobei [CR] und [LF] je als ein Zeichen interpretiert werden.
Das Format SKIP,A(5) liest aus der ersten Zeile die 12345, alle weiteren Zugriffe ergeben am Anfang einer Zeichenkette ein [LF], gefolgt von den ersten 4 Zeichen der jeweiligen Zeile.
Ist die Länge einer Dateizeile (hier 14) bekannt, ergibt sich mit A(14) und SKIP,A(14) das beste Verhalten. Die Zahlen werden so wie gewünscht in der Zeichenkette abgelegt und können mit CONVERT weiterverarbeitet werden. Bei nicht bekannter Zeilenlänge ist SKIP,A zu empfehlen, wobei sich ein eventuelles beginnendes [LF] durch ein Leerzeichen ersetzen lässt oder die Zeichenkette mit varkette=MID(varkette,2,256); korrigierbar ist.
Taskkommunikation über Pipes und Message Passing
Wie können Tasks über Pipes kommunizieren?
In RTOS-UH ist es noch nicht vorgesehen, dass Tasks direkt über
Pipes miteinander kommunizieren. Daher gibt es die Datenstationen
/VI (Virtual Input) und /VO (Virtual Output), über die
indirekt kommuniziert werden kann.
Hier ein kleines Beispielprogramm:
MODULE;
SYSTEM;
AUS: /VO/TEST ->;
EIN: /VI/TEST <-;
PROBLEM;
SPC AUS DATION OUT ALPHIC;
SPC EIN DATION IN ALPHIC;
RAUSTASK: TASK PRIO 99;
PUT 1.5 TO AUS BY E(15,7),SKIP;
END;
REINTASK: TASK PRIO 99;
DCL INPUT FLOAT;
GET INPUT FROM EIN BY SKIP,E(15);
END;
START: TASK PRIO 98;
ACTIVATE REINTASK;
ACTIVATE RAUSTASK;
END;
MODEND;
Wie können Pipes gelöscht werden?
Werden /VI und /VO zur Intertaskkommunikation benutzt,
so können bei nicht ordnungsgemäßem Ablauf CEs übrig bleiben, die ein
erfolgreiches Neustarten des Programmes verhindern. Von der
Bedienoberfläche kann sich der Programmierer die eventuell
vorhandenen CEs mit dem Bedienbefehl S-C anzeigen lassen.
Entfernen lassen sich die Ausgabe-CEs mit dem Bedienbefehl RM /VO/name,
Eingabe-CEs mit RM /VI/name. Beide lassen sich
entweder von der Oberfläche aus nach [CTRL]-A oder vom Programm
über EXEC absetzen. Eleganter geht es in einem PEARL-Programm mit
der Anweisung
CLOSE vardation BY CAN;
Diese hat darüber hinaus den Vorteil, dass er unabhängig von der
Angabe des Pfades im Systemteil ist. Ein explizites OPEN zum
Zugriff auf eine Pipe ist danach nicht notwendig.
Eine Task möchte Daten direkt an eine andere senden
Die neueren Varianten von RTOS-UH (Nukleus 7.8-A oder höher) bieten einen direkten Datentransfer an andere Tasks ohne den Umweg über Pipes (Message-Passing) an. Lautet die Zieltask beispielsweise Regler, könnte eine Task folgendermaßen Daten senden.
AN_REGLER: TASK; DCL SENDDIRECT DATION OUT ALPHIC; OPEN SENDDIRECT BY IDF('/REGLER'); PUT 1.0,3.5,0.78 TO SENDDIRECT BY (3)E(15,7),SKIP; END;
Wie zu sehen ist, ändert sich an den PUT-Statements gar nichts.
Eine Task möchte über das Message-Passing Daten einlesen
Das Einlesen der an eine Task gesendeten Daten erfolgt über zwei Datenstationen. Soll eine Task immer weiterlaufen, ist dabei die Datenstation CRIM (Conditioned Report Input Message) zu verwenden. Soll sie warten, bis alle Daten eingetroffen sind - während des Wartens steht diese auf SCHD -, gibt es RIM (Report Input Message). Das folgende Beispiel zeigt eine Anwendungsmöglichkeit von CRIM. Die sendende Task könnte die aus (>) sein.
SYSTEM; NEVERUSED: /CRIM; PROBLEM; SPC NEVERUSED DATION IN ALPHIC; REGLER: TASK; DCL (P,I,D) FLOAT; DCL (PNEU,INEU,DNEU) FLOAT; DCL ERROR FIXED; DCL GETPARAS DATION IN ALPHIC CREATED(NEVERUSED); GET PNEU,INEU,DNEU FROM GETPARAS BY RST(ERROR),SKIP,(3)E(15); CASE ERROR ALT(0) !Neue Reglerparameter P=PNEU; I=INEU, D=DNEU; ALT(2) !Kein CE dagewesen OUT !Fehlerbehaftete Daten / Datentyp.... FIN; ... Regeln .... END;
Funktioniert Message-Passing auch mit binärem I/O?
Dem Betriebssystem ist die Datenart egal. Es ist nur zu bedenken, dass WRITE im Gegensatz zu PUT die sendende Task immer solange blockiert, bis das CE von der anderen Task komplett ausgewertet wurde.
Wie kann ich beim Message-Passing ein CE zurückgeben, in dem noch nicht ausgewertete Daten stehen?
Hier ist das PEARL-Statement CLOSE zu verwenden.
Arbeiten mit Feldern und Zeichenketten (ohne Bitvariablen)
Wie kann man ein eindimensionales Feld anlegen, welches eine Speichergröße von mehr als 32767 Bytes hat? (Nur PEARL-80)
In RTOS-PEARL ist eine Dimensionsbeschränkung vorgesehen. Der
Speicherplatz für eine Dimension ist auf
32768 - Länge_eines_Feldelementes_in_Byte begrenzt. Soll ein
größeres eindimensionales Feld angelegt werden, lässt sich dieses durch
Definition eines zwei- oder dreidimensionalen Feldes erreichen.
Ist ein Feld mit der Zeile
DCL vararray(Imax, Jmax, Kmax) typ_von_vararray;
deklariert, so gilt die Gleichheitsbeziehung
vararray(I,J,K) == vararray(I+J*Imax+K*Jmax*Imax,0,0).
Der Schreibzugriff auf ein Feldelement muss so erfolgen, dass
I, J und K mindestens den Wert 1 haben, sonst zerstört der Zugriff die
vor dem Array liegenden Speicherzellen.
Wie erfolgt die Feldelementberechnung in PEARL 90?
Die Feldelementberechnung soll an Hand einer Gleichheitsbeziehung
eines 3-dimensionalen Feldes demonstriert werden:
feld(I,J,K) ==
feld(I+J*(Imax-Imin+1)+K*(Imax-Imin+1)*(Jmax-Jmin+1),0,0)
Der PEARL-90 Compiler berechnet den Index effizienter, aber
natürlich mit dem gleichen Ergebnis. Der Zeiger auf das erste
Element zeigt auf feld(Imin,Jmin,Kmin).
Wie kann man auf Elemente einer Zeichenkette zugreifen?
Um das varfixed-te Zeichen einer Zeichenkette der Variablen
varstring zuzuweisen, lautet die Anweisung
varchar = varstring.CHAR(varfixed);
Beispiel:
DCL BUCHSTA CHAR;
DCL DREIBUCHSTA CHAR(3);
DCL STRING CHAR (12) INIT ('ABCDEFGHIJKL');
BUCHSTA = STRING.CHAR(6); ! BUCHSTA erhaelt den Wert 'F'
Wie lassen sich in einem String Sonderzeichen zuweisen?
In PEARL-90 ist dieses mit dem Zeichen "\" möglich. Am
leichtesten lässt sich die Verwendung an Beispielen zeigen:
(Hinweis: Der Taste [ESC] ist der Wert 27=$1B, [CR] der Wert $0D
und [LF] der Wert $0A zugeordnet.)
DCL ESCAPE INV CHAR INIT (''\1B\''); ! Ein einzelnes [ESC]
DCL ESCAPE_TEXT CHAR(5) INIT (''\1B\'TEXT'); ! [ESC] und 'TEXT'
DCL TEXT_ESCAPE CHAR(5) INIT ('TEXT'\1B\''); ! 'TEXT' und [ESC]
DCL T_CR_LF_T CHAR(4) INIT ('T'\ODOA\'T'); ! zwischen den Back-
! slashs sind auch mehrere Hexadezimalwerte zugelassen.
Wie lässt sich ein Apostroph einem String zuweisen?
Das Apostroph dient im Quelltext als Anfangs- bzw. Endemarkierung
von Strings. Als Inhalt einer Stringvariablen läßt es sich mit
varstring = TOCHAR(39);
oder mit
varstring = ''\27\'';
erzeugen. Elegant ist die Lösung
varstring = 'gggg''hhhh';
Innerhalb einer Zeichenkette werden zwei aufeinander folgende "'"
als ein "'" innerhalb der Zeichenkette interpretiert. Der Inhalt
von varstring ist also "gggg'hhhh".
Konsequenterweise wird ein einzelnes Apostroph mit der Programmzeile
varstring = '''';
zugewiesen.
Wie kann die Stringausgabe so formatiert werden, dass die den String beendenden Blanks nicht ausgegeben werden?
In den Formatangaben sind auch Ausdrücke und Funktionen erlaubt.
In Verbindung mit der Funktion LEN, die die Position des letzten
"nicht-Blanks" zurückgibt, könnte eine Ausgabe wie folgt lauten:
PUT varstring TO vardation BY A(LEN(varstring));
Ein PEARL-Programm soll die deklarierte Länge eines Strings auswerten
In PEARL-90 gibt es zwei Möglichkeiten, die deklarierte Länge
eines Strings weiter zu verarbeiten:
Der SIZEOF Operator gibt die deklarierte Länge zurück:
varfixed15 = SIZEOF(varstring) FIT varfixed15;
oder
varfixed31 = SIZEOF(varstring);
Ist die Länge eines Strings mit einer benannten Konstanten
deklariert, kann über die benannte Konstante zugegriffen werden:
DCL varfixed INV FIXED INIT(zahl);
DCL varstring CHAR(varfixed);
Ist die Stringlänge zu verändern, braucht der Programmierer bei
beiden Lösungen nur an einer Stelle sein Programm zu ändern. Das
Programm verhält sich bei richtiger Programmierung weiterhin korrekt.
Wie können FLOAT-Variablen als Zeichenketten dargestellt werden und umgekehrt?
Dazu dient der Befehl CONVERT.
Eine CHAR->FLOAT Umwandlung könnte so aussehen:
CONVERT varfloat FROM varstring BY E(x);
Umgekehrt, wenn man also eine FLOAT-Zahl in einen STRING umwandeln möchte, könnte die Befehlszeile
CONVERT varfloat TO varstring BY E(x,y);
diese Gestalt haben.
Die Verwendung von CONVERT erfolgt analog zu der von PUT und GET,
auch hier ist die Option RST zur Kontrolle der Konvertierung zulässig
(siehe Punkt (>)).
Die Verwendung von CONVERT ... TO ... führt manchmal am Ende eines Strings zu seltsamen Zeichen
Ist die Anzahl der in den String zu schreibenden Zeichen, die durch die Formatangabe festgelegt ist, kleiner als die definierte Länge des Strings, schreibt zwar CONVERT die Zeichen korrekt, füllt den Rest des Strings jedoch nicht, wie bei anderen String-Operationen, mit Leerzeichen. Abhilfe schafft die Formatangabe X(255), die an die alte Formatangabe angehängt wird.
Wie werden externe Felder spezifiziert?
Soll einer Prozedur ein Feld übergeben werden oder ist ein Feld
in einem anderen Modul deklariert, so darf im Prozedurkopf bzw.
bei der Spezifikation nur die Dimensionsanzahl, nicht die Größe
der einzelnen Dimension übergeben werden. Da der
Feldbeschreibungsblock immer mit übergeben wird, ist über diesen
die Größe der einzelnen Dimensionen festgelegt. Ist ein
3-dimensionales Array zu spezifizieren, lautet die PEARL-Zeile
SPC vararray(,,) GLOBAL;
Im Prozedurkopf ist die Sequenz vararray(,,) zu verwenden.
Die Spezifikation von Arrays mit weniger Dimensionen erfolgt durch das
Weglassen der entsprechenden Anzahl von Kommata.
Wie lassen sich Felder mit einem Befehl komplett in eine Datei schreiben?
Felder lassen sich unformatiert sehr schnell mit den Funktionen READ und WRITE lesen und schreiben (z.B. um Daten aus Feldern zwischenzuspeichern). Die beiden Funktionen sind im PEARL-90 Programm nicht im Problemteil zu spezifizieren.
Um mehrere Felder in eine Datei zu schreiben, ist die PEARL-Zeile
wie folgt zu programmieren:
CALL WRITE (vardation, vararray_1, vararray_2, ... , vararray_n);
Das Lesen erfolgt entsprechend. Die Ablage erfolgt unformatiert,
so dass die Daten mit einem Editor nicht lesbar sind.
Unter PEARL-90 kann auch die folgende Syntax verwendet werden:
WRITE vararray1, vararray2, ... ,vararrayn TO vardation;
Weitere Informationen zu den beiden Funktionen sind im PEARL-Handbuch zu finden.
Eine Stringvariable soll direkt hinter das letzte Zeichen einer anderen, das kein Leerzeichen ist, gehängt werden
PEARL-90 bietet für solche Fälle nur eine rudimentäre Behandlung.
Beispielsweise weist die Sequenz
DCL varchar1(20) CHAR INIT('ST1');
DCL varchar2(40) CHAR INIT('ST2');
varchar2= varchar1 CAT varchar2;
der Variablen varchar2 die Folge "ST1 ST2
"
zu. Mit Hilfe der Funktion KON
, die nicht global zu spezifizieren ist,
ist die gewünschte Zuweisung mittels
varchar2 = KON(varchar1, 1, LEN(varchar1), ! string1,von,bis
varchar2, 1, LEN(varchar2)); ! string2,von,bis
möglich. Sobald im PEARL-90-Compiler realisiert, ist auch mit Hilfe der
Characterselektoren variabler Länge die PEARL-Zeile
varchar2 = varchar1.CHAR(1:LEN(varchar1))
CAT varchar2.CHAR(1:LEN(varchar2));
codierbar. In beiden Fällen lautet das Ergebnis "ST1ST2
".
Ist die definierte Länge zweier Stringvariablen zusammen länger als 255, lassen sie sich nicht mit CAT aneinanderhängen
In diesem Fall muss auch die Funktion KON
verwendet werden, da das
compilerinterne Zwischenergebnis bei CAT
auch nur die Maximallänge
255 haben darf.
Wie lautet die Typvereinbarung für ein Feld?
Übergibt man einer Prozedur ein über eine Typvereinbarung definiertes Feld,
kann der Compiler schnelleren Code generieren. Ein Beispielprogramm verdeutlicht
Deklaration und Verwendung des neuen Typs:
DCL BOUND INV FIXED INIT(32767);
! Feld mit BOUND+1 FIXED Zahlen
TYPE FELDTYP(0:BOUND) FIXED;
INIT: PROC(FELDP FELDTYP IDENT);
FOR I FROM 0 TO BOUND REPEAT FELDP(I)=0; END;
RETURN;
END;
AAT: TASK;
DCL FELDT FELDTYP;
INIT(FELDT);
END;
Der Vergleich zweier unterschiedlich langer Zeichenketten liefert ein "wahr", falls die Zeichenketten bis zum Ende der kürzeren gleich sind
Ohne besondere Compileroption generiert der Compiler Code, bei dem nur bis zum Ende der kürzeren Zeichenkette verglichen wird. Soll der Vergleich bis zum Ende der längeren Zeichenkette durchgeführt werden, ist die Compileroption MODE=FULLCC; zu verwenden. Bei diesem Beispiel betrachtet eine Task die Zeichenketten als gleich, falls bei der längeren nur noch Leerzeichen folgen.
Beginnt eine Zeichenkette mit einem Leerzeichen, liefert der Vergleich mit dem Leerzeichen ein "wahr"
Auch hier wird nur bis zum Ende der kürzeren Zeichenkette verglichen. Abhilfe schafft auch hier die Compileroption MODE=FULLCC;
Ein CONVERT ... TO liefert seltsame Ergebnisse, wenn der Zielstring auch als Quelle benutzt wird
Die Beispielsequenz
DCL ZA FIXED INIT(1);
DCL CH CHAR(20);
CH=' ist die Loesung';
CONVERT ZA,CH TO CH BY F(1),A(10);
zeigt deutlich, wie der generierte Code abläuft.
Beim CONVERT arbeitet eine Task direkt auf dem Zielstring.
Es wird keine Hilfszeichenkette angelegt. Die Task beginnt mit dem Schreiben
der 1 an die erste Stelle von CH. Anschließend wird
das erste Zeichen von CH, inzwischen die '1', an
die zweite Stelle von CH kopiert usw. Das Ergebnis der Konvertierung
lautet konsequenterweise '11111111111111111111'.
Dieses Verhalten lässt sich durch Verwendung einer Hilfsvariablen umgehen.
Welche Typdeklarationen sind bei Feldern möglich?
Neben der in der Norm vorgesehenen Möglichkeit, Felder in Strukturen
zu definieren, ist unter UH-PEARL auch eine direkte Typdeklaration
möglich. Ein kleines Programm mit FIXED
-Feldern und -Zeigern
verdeutlicht dieses.
MODULE AA; PROBLEM; TYPE NEU_A () FIXED; ! Eindim. Feld mit variablen Grenzen TYPE NEU_B (4) FIXED; ! Eindim. Feld mit festen Grenzen TYPE NEU_C REF () FIXED; ! Zeiger auf eindim.Feld mit ! variablen Grenzen TYPE NEU_D REF (4) FIXED; ! Zeiger auf eindim.Feld mit ! festen Grenzen TYPE NEU_E () REF FIXED; ! Eindim. Feld variabler Groesse mit ! Zeigern auf FIXED-Variablen TYPE NEU_F (4) REF FIXED; ! Eindim. Feld fester Groesse mit ! Zeigern auf FIXED-Variablen TYPE NEU_G () REF () FIXED; ! Eindim. Feld variabler Groesse mit ! Zeigern auf Felder variabler Groesse TYPE NEU_H (8) REF () FIXED; ! Eindim. Feld fester Groesse mit ! Zeigern auf Felder variabler Groesse TYPE NEU_I () REF (4)FIXED; ! Eindim. Feld variabler Groesse mit ! Zeigern auf Felder fester Groesse TYPE NEU_K (8) REF (4)FIXED; ! Eindim. fester Groesse mit ! Zeigern auf Felder fester Groesse TYPE NEU_L () NEU_C ! Identisch mit NEU_G TYPE NEU_M (8) NEU_C ! Identisch mit NEU_H MODEND;
Natürlich sind auch andere Dimensionen und Datentypen erlaubt. Die
Typen NEU_L
und NEU_M
zeigen, dass auch
Zeigervariablentypen weiterverwendet werden können. Andere
Kombinationen sind durchaus möglich.
Bitfelder und Bitvariablen
Wie lassen sich Hexadezimalzahlen einer FIXED-Variablen zuweisen?
Unter Beachtung einiger Besonderheiten ist dieses in der Form
varfixed = TOFIXED('4_oder_8_hexaziffern'B4);
möglich. In dem Bitstring sollten nach Möglichkeit vier oder
acht Nibbles stehen, da der Bitstring in varfixed linksbündig
abgelegt wird. 4 Nibbles werden als FIXED(15)-Variable
interpretiert, auch wenn varfixed den Typ FIXED(31) hat.
Die Zeile
varfixed31 = TOFIXED('FFFF'B4);
weist varfixed31 den Wert -1 zu.
Eine BIT-Variable mit gesetztem Bit ergibt in FIXED-Darstellung den Wert -32768
Auch hier schlägt die linksbündige Ablage von Bitvariablen zu.
Die Programmsequenz
DCL varbit BIT INIT('1'B1);
DCL varfixed FIXED;
varfixed = TOFIXED(varbit); ! varbit ist $8000, varfixed ist -32768
Wo liegt der Vorteil bei der Verwendung von Bitvariablen für eine Programmablaufsteuerung?
Bei WHILE-REPEAT-END und IF-THEN-ELSE-FIN Strukturen kann direkt ein Bit
ohne Vergleich abgefragt werden.
An Hand der folgenden Programmzeilen lässt sich dieses sehr einfach demonstrieren.
DCL varbit BIT INIT('1'B1);
IF varbit THEN ! Ist wahr
FIN;
Warum ist direkt hinter dem SHIFT-Operator weder ein + noch ein - noch ein anderer Operator zugelassen?
SHIFT
ist ein dyadischer Operator. Deshalb muß, falls anschließend
ein weiterer Operator folgt, eine Klammer gesetzt werden (Man denke
z.B. an *-
). Daher lautet eine korrekte Zeile beispielsweise
varbit = varbit SHIFT (-2);
Shellmodule und das Environmnent
Wie kann man Bedienbefehle aus einem PEARL-Programm heraus ausführen?
Hierfür ist die Funktion EXEC vorgesehen. Sie ist in der Form
SPC EXEC PROC(CHAR(255)) RETURNS(BIT) GLOBAL;
zu spezifizieren.
Der Rückgabestatus von EXEC kann in einer FIXED-Variablen mit dem Code
varfixed = TOFIXED(EXEC(varstring mit befehlszeile));
abgelegt werden.
Falls er einer Bitvariablen zugewiesen werden soll, erfolgt der
Aufruf in der Form
varbit = EXEC(varstring mit befehlszeile);
Bei erfolgreicher Ausführung wird in varfixed eine 0 bzw. in
varbit ein '0'B zurückgegeben, sonst ein Wert ungleich 0 bzw. '1'B.
EXEC ist so programmiert, dass das Verhalten mit dem der Shell
identisch ist. Dieses hat bei der Ausführung von Sohn-Prozessen Folgen:
Bei der Ausführung der Programmzeile
varbit = EXEC('WAIT; WE /ED/TEST');
wird die eigene Task erst fortgesetzt, wenn der Bediener den Editor
beendet hat.
Bei Ausführung des Befehls
varbit = EXEC('WE /ED/TEST');
wird die eigene Task nicht unterbrochen. Editor und Task laufen
quasiparallel.
Wie kann ein PEARL-Programm mit ENVSET gesetzte Environment-Variablen, das Working-Directory und die Execution-Directories sowie STDIN, STDOUT und STDERR auswerten?
Um das Environment auswerten zu können, gibt es die Funktion
ENVGET. Sie ist wie folgt zu spezifizieren:
SPC ENVGET PROC(CHAR(255)) RETURNS (CHAR(255)) GLOBAL;
Im Übergabestring werden die Zeichenfolgen $WORKDIR, $EXEDIR1 und
$EXEDIR2 (Für Working- und Executiondirectories), sowie
$STDIN, $STDOUT und $STDERR, durch die entsprechenden
Pfade ersetzt.
Beispiel:
varbit = EXEC('CD /ED/TEST'); ! Neues Working-Directory
varstring = ENVGET('$WORKDIR'); ! varstring nun: '/ED/TEST'
varbit = EXEC('CD /ED/'); ! Neues Working-Directory
varstring = ENVGET('$WORKDIR'); ! varstring nun: '/ED' und nicht
! '/ED/-'
ENVGET kann in einem String auch mehrere Environment-Variablen
expandieren. Falls eine Variable nicht existiert, erhält der
Zielstring die erste nicht gefundene Variable mit vorangestelltem
"$":
varbit = EXEC('CD /ED/TEST'); ! Neues Working-Directory
varstring = ENVGET('$WORKDIR''Dies ist ein Test''$workdir');
! varstring nun: '$workdir'
! (falls workdir nicht mit ENVSET gesetzt wurde)
Um das Ende der Environmentvariablen zu kennzeichnen, kann normaler Text mit dem Zeichen "'" eingefasst werden. Mit der Einführung von EXEC und ENVGET ist die Datenstation /XC sowie die Einbaufunktionen CMD_EXW, GET_WORKDIR, GET_EXECDIR und SET_DATION nur noch aus Kompatibilitätsgründen im System.
Ein CD, mit der Einbaufunktion EXEC ausgeführt, wirkt bei Tasks und PEARL-codierten Befehlen unterschiedlich
Ein PEARL-codierter Bedienbefehl ist ein Shell-Sohn. Deshalb hat er sein eigenes Environment, zu dem u.a. Working- und Execution-Directories, STDIN, STDOUT und STDERR gehören. Daher wirkt ein mit EXEC aufgerufener Befehl CD (Change Working-Directory) unterschiedlich. Beim PEARL-Bedienbefehl wird nur die lokale Kopie geändert, bei der Task das Directory des zu der Task gehörenden Users (Was sich auf alle anderen Tasks auswirkt!).
Ein CD, von einem PEARL-codierten Bedienbefehl ausgeführt,
wirkt nur bis zum Ende des Shell-Sohnes, hat also keinen Einfluss
auf das Working-Directory eines mit "-" folgenden Befehls:
Führt der Bediener die Eingabezeile
CD /ED/TEST-PEARL-codierter Bedienbefehl-PWD
aus, zeigt PWD u.a. immer WD=/ED/TEST an, egal wie oft
mit EXEC CD ausgeführt wurde.
FIXED- und FLOAT-Variablen
Das Ergebnis von 10**5 wird einer FLOAT-Variablen falsch zugewiesen
Soll die PEARL-Zeile varfloat=10**5; exekutiert werden, erhält varfloat
den mysteriösen Wert -31072.0. An diesem Beispiel soll gezeigt werden, wie der Compiler
arbeitet und wie sich ähnliche Probleme umgehen lassen.
Die Potenzierung von 10 mit 5 ist eine Operation mit zwei FIXED(15)-Variablen.
Nach Norm ergibt die Potenz zweier FIXED(15)-Variablen ein FIXED(15)-Ergebnis.
Hier tritt dann der Rechenfehler auf, denn das
Zwischenergebnis ist größer als 32767. Die Bits 16 bis 31 werden
ignoriert. Der daraus entstandene Zahlenwert wird dann korrekt
varfloat zugewiesen. Beheben lassen sich solche Fehler durch
rechtzeitiges Umsteigen auf FLOAT-Variablen, in diesem Beispiel
also durch die PEARL-Zeile varfloat = 10.**5;.
Eine FIXED-Variable soll, um Zehnerpotenzen verschoben, ausgegeben werden
Ein Beispiel soll das Problem verdeutlichen.
Um Rechenzeit zu sparen und um die Genauigkeit zu erhöhen,
rechnet ein Programm Geldbeträge in Cent als
FIXED(31)-Variablen und nicht mit "EURO" als FLOAT-Variablen.
Ist ein Betrag in "EURO" an eine DATION auszugeben, geschieht
dieses am elegantesten mit dem Format F(varfixed,2,-2):
PUT 1234(31) TO vardation BY F(9,2,-2); ! Erzeugt ' 12.34'
Wie lassen sich Zahlen runden, die größer als 32767 sind?
In PEARL sind für die Rundung bzw. die Bildung der "größten
ganzen Zahl kleiner gleich dem Argument" die Funktionen ROUND bzw.
ENTIER vorgesehen. In RTOS-PEARL jedoch muss das Ergebnis dieser
Funktionen als FIXED(15) darstellbar sein. Um dieses Manko umgehen
zu können, kann die Funktion ENTLNG (ENTIER-long) verwendet
werden. Sie muss im Problemteil mit der Anweisung
SPC ENTLNG ENTRY (FLOAT(55)) RETURNS (FIXED(31)) GLOBAL;
spezifiziert werden. Soll gerundet werden, muss dem Argument noch eine 0.5(55) hinzu addiert werden.
Was ist beim Umgang mit Zahlenkonstanten zu beachten, wenn zum größten Teil FLOAT(55)-Variablen verwendet werden?
Standardmäßig wird als Genauigkeit bei Zahlenkonstanten die FLOAT(23)-Darstellung verwendet. Werden jedoch FLOAT(55)-Variablen mit Zahlenkonstanten verknüpft, so geht die Genauigkeit der Rechnung durch die relativ zur FLOAT-Variablen ungenaue Darstellung der Konstanten verloren. Um diesem Problem Abhilfe zu schaffen, gibt es im wesentlichen drei Möglichkeiten:
- durch die Anweisung LENGTH FLOAT(55); wird standardmäßig die genaue Darstellung (auch der Zahlenkonstanten) verwendet.
- Jede Zahlenkonstante muss im Programm als konstante(55) dargestellt werden.
- Die Konstante wird als modulweite Variable mit der Anweisung
DCL konstantenname INV FLOAT(55) INIT(zahlenwert(55));
deklariert. Diese Schreibweise hat gegenüber (>). den Vorteil, dass in einem arithmetischen Ausdruck weniger Klammern auftreten, was zu einer besseren Lesbarkeit des Programmes führt.
Allgemeine Fragen
Wie kann man erreichen, dass ein Programm(paket) immer wieder gestartet werden kann, ohne es neu in den Speicher zu laden?
Das Hauptproblem stellen über INIT bzw. PRESET vorbelegte
Modulvariablen und Semaphore sowie die implizit mit FREE vorbelegten BOLT-Variablen dar.
Werden alle veränderlichen Modulvariablen und Semaphore von der
aufzurufenden Task durch Zuweisung bzw. SEMASET und Boltvariablen
mit FREE initialisiert, ist das Hauptproblem gelöst.
Weiterhin müssen alle Tasks des Programmpakets den Zustand
DORMant haben.
Benutzt das Programm die Datenstationen /VI oder /VO, müssen
die Pipes definitiv geleert sein (siehe Punkt (>) und PEARL-Handbuch).
Bei allen Datenstationen, bei denen das Device und der Pfad sowie
die Datenstationseigenschaften (AI, MB, NE) nicht
nur beim Start des Programmes verändert wurden, ist zu überprüfen, wie
sich ein erneuter Start auswirkt.
Manchmal kann der Erhalt von Device/Pfad erwünscht sein, weil
dann das Programm mit der zuletzt eingestellten Datei
weiterarbeiten kann. Sind Datenstationseigenschaften zwischen
PUT/GET-Befehlen mit IDF_DATION verändert worden
(eher ungewöhnlich), müssen sie mit der gleichen Funktion auf die
Defaultwerte des Compilers beim Neustart gesetzt werden.
Wie können Zeitdauern auf die Millisekunde genau ausgegeben werden?
Mit PUT auf Datenstationen geschriebene DURATIONs sind auf die
Millisekunde genau angegeben, wenn das dazugehörige Datenformat
D(25,3) lautet. Soll die Ausgabe etwas kompakter erfolgen, kann
eine DURATION-Variable durch die PEARL Zeile
varfloat = varduration / 0.001 SEC;
einer FLOAT-Variablen mit der gedanklichen Einheit Millisekunde
zugewiesen werden. Die Unterteilung in Stunden, Minuten und Sekunden geht
dabei verloren.
Wie lassen sich Zeiger mit einer bestimmten Adresse initialisieren?
Die Initialisierung mit Variablen-Adressen eines PEARL
Programmpaketes erfolgt einfach über
zeiger_eines_typs = variable_gleichen_typs;
Eine bestimmte Systemadresse lässt sich nur indirekt zuweisen.
Zuerst muss der Zeiger mit dem Befehlscode
zeiger = NIL;
genullt werden, anschließend kann mit der
Funktion REFADD der Wert addiert werden:
REFADD(zeiger_eines_typs,adresse // typlänge_in_byte);
Falls die Adresse nicht durch die Typlänge teilbar ist, kann das
BY TYPE Konstrukt verwendet werden:
charzeiger = NIL; ! Einen Zeiger auf CHAR nullen
REFADD(charzeiger,adresse); ! Adresse zuweisen
zeiger_eines_typs = charzeiger BY TYPE typ; ! Zuweisung mit Typumwandlung
Wie kann bei einer laufenden Task eine gepufferte Aktivierung gelöscht werden?
In RTOS-UH besteht die Möglichkeit, für jede TASK bis zu drei Aktivierungen zu puffern. Mit dem Befehl PREVENT task_name; werden nicht nur zyklische Einplanungen gestrichen, sondern auch die gepufferten Aktivierungen gelöscht.
Basierend auf den ehemaligen Webseiten des IRT Hannover unter Prof. Gerth
Autoren: Thomas Probol, Thorsten Lilge
Inhaltsverzeichnis
- Scheinbare Compiler- und Betriebssystemfehler
- Fragen zur Konsole (Terminal)
- Fragen zu Datenstationen
- Taskkommunikation über Pipes und Message Passing
- Arbeiten mit Feldern und Zeichenketten (ohne Bitvariablen)
- Bitfelder und Bitvariablen
- Shellmodule und das Environmnent
- FIXED- und FLOAT-Variablen
- Allgemeine Fragen