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.

up

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.

up

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');

up

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.

up

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.

up

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.

up

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.

up

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;

up

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.

up

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);

up

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.

up

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:

  1. durch die Anweisung LENGTH FLOAT(55); wird standardmäßig die genaue Darstellung (auch der Zahlenkonstanten) verwendet.
  2. Jede Zahlenkonstante muss im Programm als konstante(55) dargestellt werden.
  3. 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.

up

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.

up

Basierend auf den ehemaligen Webseiten des IRT Hannover unter Prof. Gerth
Autoren: Thomas Probol, Thorsten Lilge