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äßt
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 das 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 die Compileranweisung /*+T*/
benutzt wird.
Ein PEARL-Programm läuft auf eine Bus-Error
- oder eine Wrong-Opcode
-Exception
Auch hier kommen als mögliche Fehlerursachen
Feldüberschreitungsfehler oder die (falsche) 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
Numerierung.
Trotz scheinbarer Verwendung des Line-Markers wird immer nur die Zeilennummer 0
ausgegeben
Bei Verwendung der Compileroptionen muß 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*/
Dies gilt entsprechend auch für alle anderen Compileroptionen.
/* +M ist ein Kommentar*/
!+M ist auch ein Kommentar
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 sind nicht
initialisierte Variablen. Dieser Fehler kann auch auftreten, wenn einzelne
Module eines Programmpaketes für unterschiedliche Prozessoren übersetzt
wurden (s. auch Wrong-Operand-FPU
).
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
Compilerlaufes wird der Speicherplatzbedarf mit 8 KByte für
DATION
s, modulweite Variablen und Code vorbesetzt. Übersteuern
läßt 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 muß 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,
muß 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 auch "Zeilennummer existiert nicht"),
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 Abschlußmeldung 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, also gehen nicht verloren.
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 Fall 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, USB-Stick,
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 z.B.
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
veranlaßt 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 EINGABEFLOAT1 FROM TERMINAL BY E(15);
! Dieser Variablen wird die 67.0 zugewiesen, da
! die 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 vardat 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 als
GET var1, var2, ... , varn FROM vardation BY SKIP,
RST(varfixed1), format1, RST(varfixed2), format2, ... ,
RST(varfixedn), formatn;
programmiert werden.
Wie läßt sich eine durch das Betriebssystem veranlaßte Fehlermeldung beim Zugriff auf eine Datenstation unterdrücken?
Die Datenstation muß 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 DATION
s, 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
DATION
s zuweisen?
Task- und Prozedurweite DATION
s 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;
Die Datenstation vardation erhält eine lokale Kopie von
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; 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äßt 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äßt 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,
bzw.
CHAR(24)) GLOBAL; ! PEARL 80-Compiler: Version 14.x-y
SPC ASSIGN ENTRY (DATION ALPHIC CONTROL(ALL) IDENT, CHAR(24))
spezifiziert werden muß. Der Aufruf erfolgt mittels des Codes
GLOBAL; ! PEARL 90-Compiler: Version 15.x-y
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
Befehles ENVGET
(siehe PEARL-Handbuch, Kapitel "Environment") ist auch möglich:
varstring = ENVGET('$WORKDIR/DATEI');
Lautet das Working-Directory z.B.
OPEN vardation BY IDF(varstring);/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 muß. Reicht der Platz nicht aus, kann die
DATION nicht geöffnet werden. Der Platzhalterstring muß 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 DATION
s verwendet werden.
Wie läßt 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;
Ist das File nicht vorhanden, gibt es im ersten Fall eine
Fehlermeldung, ist es vorhanden, beim Zweiten. Der Status der
OPEN vardation BY IDF(varstring mit filenamen),NEW;DATION
läßt sich mit der Funktion ST
abfragen (siehe PEARL-Handbuch).
Leider ist die Benutzung von OLD
und NEW
nur in Verbindung mit dem Schlüsselwort IDF
zulässig, so daß im
Systemteil angegebene Pfade zwangsläufig überschrieben werden.
Wie kann man auf eine Datenstation mit mehreren im Systemteil definierten DATION
s zugreifen?
Dieses ist problemlos möglich. Bei Filesystemen ist allerdings zu
bedenken, daß nur ein Lese/Schreibzeiger für das File vorhanden
ist. Soll gleichzeitig mit mehreren DATION
s auf ein File
zugegriffen werden, muß sichergestellt sein, daß 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 darauffolgendes 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äßt oder die Zeichenkette mit
varkette=MID(varkette,2,256);
korrigierbar ist.
Taskkommunikation mit Pipes/Message Passing
Wie können Tasks über Pipes kommunizieren?
In RTOS-UH ist es noch nicht vorgesehen, daß 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 übrigbleiben, 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, daß 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 GETP DATION IN ALPHIC CREATED(NEVERUSED);
GET PNEU,INEU,DNEU FROM GETP 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,
daß 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
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äßt 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 muß so erfolgen, daß 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) ==
Der PEARL-90 Compiler berechnet den Index effizienter, aber
natürlich mit dem gleichen Ergebnis. Der Zeiger auf das erste
Element zeigt auf feld
feld(I+J*(Imax-Imin+1)+K*(Imax-Imin+1)*(Jmax-Jmin+1),0,0)(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äßt 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\'AB'); ! [ESC] und 'AB'
DCL TEXT_ESCAPE CHAR(5) INIT ('CD'\1B\''); ! 'CD' und [ESC]
DCL T_CR_LF_T CHAR(4) INIT ('T'\ODOA\'T'); ! zwischen den
! Backslashs sind auch mehrere Hexadezimalwerte zugelassen.
Wie läßt 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, daß 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 weiterzuverarbeiten:
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);
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.
DCL varstring CHAR(varfixed);
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
"Wie kann man bei Benutzung des Befehls GET
abfragen, ob das Einlesen fehlerfrei erfolgte?").
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_n);
Das Lesen erfolgt entsprechend. Die Ablage erfolgt unformatiert,
so daß 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
Characterselectoren 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 muß 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.
Dann 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äßt 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, daß 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äßt 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, daß 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
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
"$":
varstring = ENVGET('$WORKDIR');
! varstring nun: '/ED/TEST'
varbit = EXEC('CD /ED/'); ! Neues Working-Directory
varstring = ENVGET('$WORKDIR'); ! varstring nun: '/ED'
! und nicht '/ED/-'
ENVGET
varbit = EXEC('CD /ED/TEST'); ! Neues Working-Directory
varstring = ENVGET('$WORKDIR''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 "'
" eingefaßt 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 Einfluß
auf das Working-Directory eines mit "--
" folgenden Befehles:
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 Pfennigen als
FIXED(31)
-Variablen und nicht mit "€" als FLOAT
-Variablen.
Ist ein Betrag in "€" 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); ! Ausgabe 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 muß 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 muß im Problemteil mit der Anweisung
SPC ENTLNG ENTRY (FLOAT(55)) RETURNS (FIXED(31)) GLOBAL;
spezifiziert werden. Soll gerundet werden, muß dem Argument noch eine 0.5(55)
hinzuaddiert 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:
1. durch die Anweisung LENGTH FLOAT(55);
Es wird standardmäßig die genaue Darstellung (auch der Zahlenkonstanten) verwendet.
2. Jede Zahlenkonstante muß im Programm als konstante(55)
dargestellt werden.
3. Die Konstante wird als modulweite Variable mit der Anweisung
DCL konstantenname INV FLOAT(55) INIT(zahl(55));
deklariert.
Allgemeine Fragen
Wie kann man erreichen, daß 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 DORM
ant haben.
Benutzt das Programm die Datenstationen /VI
oder /VO
, müssen
die Pipes definitiv geleert sein (siehe Punkt "Wie können Pipes gelöscht werden?" 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 DURATION
s 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äßt sich nur indirekt zuweisen.
Zuerst muß der Zeiger mit dem Befehlscode
zeiger = NIL;
auf die Adresse Null gesetzt werden, anschließend kann mit der
Funktion REFADD
der gewünschte 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.