Prospero-Fortran im Detail: Dem Compiler auf die Finger geschaut

Hilfeoption des Debuggers

In der Februar-Ausgabe wurde über die neueste Version von Pro-spero-Fortran berichtet, die einerseits über ihre Workbench besser in GEM eingebunden ist, andererseits aber auch durch eine umfangreiche Unterprogrammbibliothek dem Programmierer einen erweiterten Zugriff auf die leistungsfähigen GEM-Routinen ermöglicht.

In diesem Artikel sollen einige Aspekte dieses Compilers im Detail betrachtet werden. Dabei haben wir vor allem diejenigen Programmierer vor Augen, die zwar schon Erfahrung mit Fortran haben, sich bisher aber noch nicht weiter mit GEM beschäftigt haben und auch mit dem neuen Prospero-Fortran noch keinen intensiven Kontakt hatten.

Zunächst werden wir uns mit dem Debugger beschäftigen, anschließend wird ein wenig gefensterlt, wobei uns einige GEM-Routinen assistieren werden, danach kommen einige Vergleichsmessungen mit der alten Fortran-Version und mit Fortran-Versionen auf anderen Rechnern. Zum Schluß geben wir noch Hinweise auf weitere, bisher gefundene Fehler und Unstimmigkeiten.

Fehlersuche mit PROBE

Ein Debugger ist ein Hilfsmittel, im allgemeinen ein Zusatzprogramm, welches bei der Fehlersuche eingesetzt wird. Normalerweise werden allenfalls Faufzeitfehler mit Fehlerart und Programmzeile gemeldet. Dies ist oft aber nur die Wirkung von Fehlem, die Ursache liegt dann meist im Dunkeln. Logische Fehler findet man auf diese Weise ohnehin nicht. Es kommt daher darauf an, den Ablauf eines Programms genau zu beobachten und an manchen Stellen den Wert signifikanter Variablen zu prüfen. Hierzu quält man sich oft mit zusätzlich ins Programm aufgenommenen Druck-anwei-sungen: “Hier ist Unterprogramm ABC”, “Die Variable XYZ hat den Wert... “ und ähnliches. Diese Methode ist zwar jedem gestandenen Fortran-Programmierer wohlvertraut, nichtsdestotrotz aber sehr umständlich: Die zunächst als fehlerhaft verdächtigten Stellen sind natürlich völlig in Ordnung, man muß ganz woanders suchen, also neue Druckbefehle ‘rein, nochmal übersetzen, binden, wieder falsch, ... Wir kennen das!

Mit einem Debugger hingegen ist man viel besser dran: Das Originalprogramm wird unter der Kontrolle dieses Hilfsprogramms ausgeführt, man kann sich zur Faufzeit überlegen, was man ansehen möchte, wo man weitermachen will und was einem sonst noch so einfällt. Debugger sind natürlich nicht genormt, und so ist ihre Bedienung und Fei-stungsfähigkeit recht unterschiedlich, wenn man überhaupt einen hat. Selbst auf Großrechnern ist dieses wichtige Hilfsmittel nicht immer selbstverständlich.

Der Einstieg

Soll eine Programmeinheit mit PROBE untersucht werden, so ist sie mit der Option ‘N’ zu übersetzen, alle anderen Programmteile und Programme sind für den Debugger transparent. Beim Übersetzen mit ‘N’ werden die Hilfsdateien der Typen ‘.NAM’ und ‘.SYM’ erzeugt, die PROBE bei seiner Arbeit benötigt. Verwendet man zusätzlich noch die ‘F’-Option, dann kann PROBE die dadurch erzeugte Auflistung des Quellprogramms in der '.PRN’-Datei während einer Sitzung ebenfalls verwenden. PROBE kann direkt vom Desktop gestartet oder aus der Workbench über ‘Debug Program’ oder die Tastenkombination (Alternate+P) aufgerufen werden. Im folgenden werden wir den letzteren Fall betrachten.

Nach (Alternate+P) darf man zunächst anhand der üblichen Auswahlbox das gewünschte Programm aussuchen. Leider gibt es keine direkte Möglichkeit, das gerade entwickelte Programm einzustellen. Oder ist die nur nicht gut genug dokumentiert? Die beim Aufruf von PROBE aus der Workbench verwendeten Pfadnamen sind etwas verwirrend: PROBE.PRG wird im Pfad für “Compiler overlays” gesucht; für das zu testende Programm wird in der Auswahlbox der Pfad für “user files” voreingestellt; die Dateien vom Typ ‘.PRN’ und ‘.NAM’ werden im Pfad für "libraries” gesucht, wo auch die Protokolldatei PROBE.LOG abgelegt und die Datei PROBE.HLP mit den Hilfstexten vermutet wird. Wird in der laufenden Sitzung für eine nicht gefundene Datei einmal ein anderer Pfadname eingegeben, so gilt der dann aber auch für die nächste gesuchte Datei.

Ein Beispiel

Natürlich können, dürfen und wollen wir hier nicht die Lektüre des Handbuchs ersparen. Wir geben aber einige Tips und Hinweise, die den Einstieg erleichtern und einen kleinen Eindruck von der Leistungsfähigkeit dieses Debuggers geben. Ganz instruktiv ist hierbei der Ausdruck in Bild 1. Dies ist das Protokoll einer Sitzung, die mit ECHO in die Datei PROBE.LOG protokolliert und dann mit einem Editor nachbearbeitet wurde: Alle Benutzereingaben sind unterstrichen. Das Protokoll enthält stets nur die von PROBE erzeugte, nicht die vom getesteten Programm erzeugte Ausgabe und auch nicht die vom Benutzer eingegebenen Pfadnamen. Diese wurden in Bild 1 nachträglich noch eingesetzt. Die vom Benutzer eingegebenen PROBE-Be-fehle stehen dagegen auch wieder im Protokoll.

Bild 1 vermittelt zunächst einen treffenden Eindruck von dem Chaos mit den Pfadnamen. Positiv fällt dagegen der Umfang der HELP-Funktion auf, die hier nur andeutungsweise demonstriert wurde. Zu jedem Punkt kann noch ein ausführlicher Hilfstext abgerufen werden. So etwas ist vorbildlich! Mit DISPLAY wird der Inhalt von Variablen und Feldern angezeigt. Natürlich kann man genau angeben, was man sehen möchte; ohne Parameter wirkt der Befehl auf alle Größen der aktuellen Programmeinheit.

Goodies

Nach Eingabe von PROFILE wird gezählt, wie oft jede Programmzeile ausgeführt wird. Gerade bei komplexen Programmen gewinnt man so wichtige Hinweise für die Optimierung. Mit LIST wird diese Liste ausgegeben, zuvor sollte natürlich das Mitschreiben der Ausgabe durch ECHO in die Datei PROBE.LOG eingeschaltet werden. Fragen Sie jetzt aber bitte nicht, in welchem Ordner diese Datei anschließend gesucht werden muß! Ein weiteres ECHO schaltet die Ausgabe übrigens wieder ab. Die so erzeugte Liste für das Beispiel aus Bild 1 finden Sie in Listing 1.

Ganz wichtig ist auch der Befehl STEP. Ohne Parameter wird genau eine Anweisung ausgeführt und dann die nächste zur Ausführung anstehende Zeile angezeigt. Fehlt die ‘.PRN'-Datei, dann wird nur die Zeilennummer gemeldet. Und nun raten Sie mal, was passiert, wenn man eine Zahl als Parameter angibt!

Mit ‘BREAK 8’ wird das Programm vor Ausführung der Anweisung in der Quellzeile Nummer 8 unterbrochen und PROBE meldet sich zurück. Warum hier gerade die Zeile Nummer 8 angegeben wird? Schauen Sie sich einmal die Auflistung des Quellprogramms in Listing 1 an! Irgendwann muß man ja, bevor sich alles in Wohlgefallen auflöst, die mit PROFILE erzeugte Statistik ausgeben. Der Befehl WATCH ist wesentlich leistungsfähiger, als man zunächst glaubt. Man kann beispielsweise, wie hier geschehen, die Veränderung von Variablen oder Feldelementen überwachen. Dies kann auch noch von Bedingungen abhängen, etwa ‘WATCH C<=5.0\ Stets wird jedoch nicht einfach nur auf die Verwendung des angegebenen Namens in der aktuellen Programmeinheit geachtet; vielmehr wird jeder Zugriff auf den zugehörigen Speicherplatz auch aus aufgerufenen Unterprogrammen gemeldet, wo die untersuchte Größe natürlich ganz anders heißen kann.

Bild 1: Beispiel einer Sitzung mit PROBE. Alle Benutzereingaben sind unterstrichen. Deutlich zu sehen ist das Problem mit den Pfaden.

Kür verpatzt

Während einer Sitzung mit PROBE kann durch den Befehl X ein beliebiges anderes Programm gestartet werden. Längere PROBEn könnte man dadurch wesentlich erleichtern: Aus dem Desktop wird zunächst PROBE aufgerufen und dann erst die Workbench mittels des Kommandos ‘X F_BENCH\ Während der gesamten Sitzung läuft dann jedes mit der N-Option übersetzte Programm automatisch unter PROBE, ohne daß der Debugger jedesmal erneut auf gerufen und geladen werden müßte. Dies klingt nun zwar alles sehr schön, zuvor sollten die Programmentwickler allerdings ihren Debugger selbst einmal debuggen. Nach dem Aufruf von PROBE und Eingabe des Befehls ‘X F_BENCH’ - der Cursor ist selbstverständlich nicht zu sehen -wird zunächst einmal der Quellcode eines mysteriösen Programms namens P03MG angemahnt. Dieses Ansinnen kann man nun mittels Return-Taste zurückweisen und die Chose anschließend endlich durch den Befehl GO zum Laufen bringen. Der daraufhin sichtbare Anfangsbildschirm der Workbench sieht allerdings schon recht merkwürdig aus; auch scheinen sich einige Zeiger zu verhaspeln, so daß bei mehrfachem Start eines Programms mit (Alternate+R) dieses an unterschiedlichen Stellen begonnen wird. Wenn aber in Zukunft einmal alles so läuft, wie im Handbuch beschrieben, dann wäre dies gewiß ein enormer Komfort.

Und nun zu den Menüs...

Die für GEM charakteristischen Betriebssystem-Routinen gliedern sich in zwei Hauptgruppen: AES (Application Environment Services) und VDI (Virtual Device Interface). Die VDI-Routinen dienen hauptsächlich der graphischen Ausgabe, während die AES-Routinen vor allem die WIMP-Benutzerschnittstelle verwalten. WIMP steht hier für “Windows, icons, mice, pull-down menus”. Am interessantesten ist sicher der Einstieg in AES, hier kann nun jeder Fortran-Programmierer seinen Programmen das GEM-typische Aussehen verleihen.

Ohne RSC-Datei

Bisher wurde hierzu immer eine besondere RSC-Datei benötigt, die alle Angaben zu Menues, deren Funktionen und sonstigen Ausgaben enthält und mit einem speziellen Editor erstellt werden muß. Dies kann man zwar mit den Fortran-Program-men auch tun, jetzt gibt es jedoch noch zusätzlich die Möglichkeit, eine Menüleiste durch die Funktion menu_create dynamisch innerhalb eines Programms selbst zu erstellen. Unser Beispielprogramm in Listing 2 macht davon Gebrauch.

Was sind Bindings?

Die AES-Routinen des Betriebssystems werden aus Maschinenprogrammen, die üblicherweise in Assembler geschrieben sind, über einen Software-Interrupt aufgerufen.

Dies ist in Hochsprachen nicht ohne weiteres programmierbar, man benötigt hier einen Satz Unterprogramme, die die entsprechenden AES- (oder auch VDI-) Funktionen aufrufen und dabei auch, falls nötig, das Umsetzen von Parametern besorgen. Diese Unterprogramme sind die sogenannten “Bindings”, die man etwa bei jedem C-Compiler für den ATARI mitbekommt. In Prospero-Fortran sind, ab Version 2, solche Bindings nun ebenfalls, wohldokumentiert, enthalten.

Einarbeiten mit Handbuch

Für die AES- und VDI-Bindings von Prospero wird jeweils ein Handbuch mit etwa 250 Seiten Umfang mitgeliefert. Die Qualität dieser Handbücher ist so gut, daß man sich damit auch ohne zusätzliche Literatur in die GEM-Programmierung einarbeiten kann. Zumindest die ersten Gehversuche werden gut unterstützt, wobei allerdings auch die mitgelieferten Beispielprogramme wertvolle Hilfestellung leisten.

Zu den Bindings

Die originalen AES-Routinen geben meist einen Parameter zurück, der oft aber nicht benötigt wird. Viele AES-Funktionen werden daher in Fortran in SUBROUTINE-Aufrufe umgesetzt; wird der Ergebnisparameter doch einmal benötigt, so kann man ihn nachträglich noch mittels der Funktion AESret abholen. Datenaustausch mit den AES-Routinen des Betriebssystems geschieht über Speicherbereiche, deren Startadressen der jeweiligen Funktion mitgeteilt werden. In den Fortran-Bindings wird dies über benannte COMMON-Bereiche realisiert, deren Namen alle mit den vier Zeichen “AES_” beginnen. Ähnlich ist es bei den VDI-Bindings. In den Bindings wird bei Bedarf noch eine Umwandlung von INTEGER4-Konstanten (aus dem rufenden Programm) in INTEGER2-Werte (zum Betriebssystem) vorgenommen. Einige für den Aufruf der GEM-Bin-dings nützliche Konstanten werden durch PARAMETER- und Typ-Anweisungen in der mitgelieferten Datei GEMCONST.FOR vereinbart, die der Programmierer mittels IN-CLUDE-An Weisung ins Quellprogramm integrieren kann. In unserem Beispielprogramm in Listing 3 wird dies zur Erläuterung auch getan, obwohl lediglich die Konstante MN_SELECTED benötigt wird.

Die Beispielprogramme

Zur Demonstration der Anwendung einiger AES-Funktionen dienen die Programme PROG1, GEM1 und TOS1 aus den Listings 3, 4 und 5. In PROG1 wird ein Menü aufgebaut, mit dem im wesentlichen die Programme GEM1 und TOS1 hinzugeladen und ausgeführt werden können. Dies sind vollkommen selbständige Programme, keine Unterprogramme! In diesem Sinne ist PROG1 eine sogenannte “Shell'’. Das Programm erklärt sich durch die im Quellcode eingebrachten Kommentare weitgehend selbst. Grundsätzlich wird zu Beginn jedes GEM-Programms mit appl_init Arbeitsspeicher reserviert, der am Ende mit appl_exit wieder freigegeben werden muß. GEM1 zeigt einen kleinen Text unter GEM an und arbeitet mit PROG1 gut zusammen. TOS1 dagegen ist ein reines TOS-Programm und zerstört den Bildschirmaufbau des rufenden GEM-Programms. Um den Effekt vorzuführen, wird dies in PROG1 nicht verhindert. Die Anzahl der Formalparameter der Routine menu_create weicht in den Beispielprogrammen von Prospero vom Handbuch ab; da es mit der Verdoppelung des ersten Parameters (bisher!) keine Fehlfunktionen gab, haben wir uns hier an die Beispielprogramme gehalten.

Bleibt also noch, unseren Lesern viel Spaß beim Fensterin in Fortran zu wünschen. In einer der nächsten Ausgaben werden wir ein größeres Programm vorstellen, bei dem die Möglichkeiten des GEM ausgiebig verwendet und erläutert werden.

Dr. V. Kurz

>>LIST 1..200

1   *   1   A « 10.
2   *   1   B = 5.
3   *   1   CALL ADD    (A, B,  C)
4   *   1   PRINT *,    C
5   *   1   B = 6.5
6   *   1   CALL ADD    (A, B,  C)
7   *   1   PRINT *,    C
8 * STOP
9 * END 
10 + *
11  SUBROUTINE  ADD (XI,    X2, X3)
12  *   2   X3 = XI + X2
13  *   2   RETURN
14  *   2   END

Listing 1: Ausgabe der mit dem Beispiel aus Bild 1 erzeugten Statistik. Das Hauptprogramm wurde einmal, das Unterprogramm zweimal ausgeführt. Alle ausführbaren Anweisungen werden mit ' *' gekennzeichnet.

* PROG1.FOR 02-02-88 VK 
* Demonstration einiger GEM-Funktionen...
*   - Menueleiste einrichten und anzeigen
*   - Tochterprogramme starten
*   - Textbox anzeigen
*
PROGRAM PROG1
*
*   Verwendete Funktionen des Bindings...
    INTEGER*4
    - menu_create,
        !erzeugt Menueleiste ohne RSC-Datei 
    - menu_title,   !Menuepunkt anfügen
    - menu_item,    !Funktion an Menuepunkt anfügen 
    - AESret,       !Parameter holen
    - form_alert    !zeichnet Alarm-(alert-)Box

*
*   Eigene Variable und Felder

    INTEGER*4
    - MENUE_BAUM,   !Adresse des Menue-Baums
    - MENUE1, MENUE2, !Titel der Menues 1 und 2 
    - FKT1, (Funktion von Menue 1
    - FKT2, FKT3, FKT4, (Funktionen von Menue 2
    - GEW_MENUE, GEW_FKT,   !gewa"hltes Menue und Funktion
    - DUMMY !Hilfsvariable
    INTEGER*2 PUFFER(0:7) !Ergebnisse der Menuewahl 
    CHARACTER*4 TEXT    !Ausgabe im Alarmfenster
*
    INCLUDE 'gemconst'
*
*   Hier werden die Menues aufgebaut...
*
*   {Immer zuerst GEM-Anwendung initialisieren}
    CALL appl_init
    IF (AESret() .lt. 0) GOTO 999 !Das ging schief!
*
*   {Menuebaum mit erstem Menue erstellen}
    MENUE_BAUM = menu_create ( 15, 15,
        ' Wie immer '//char(0) )
*
*   {Weitere Menues anfu"gen)
    MENUE1 = menu_title ( MENUE__BAUM,
        ' Ausgang '//char(0)    )
    MENUE2 = menu_title ( MENUE_BAUM,
        ' Programme '//char(0) )
*
*   {Funktionen fu"r Menues 1 und 2}
    FKT1 = menu_item ( MENUE_BAUM, MENUEI,
        ' Ende'//char(0) )
    FKT2 = menu_item( MENUE_BAUM, MENUE2,
        ' GEM-Meldung '//char(0)    )
    FKT3 = menu_item ( MENUE_BAUM, MENUE2,
        '-----' / /char (0))
    FKT4 = menu_item( MENUE_BAUM, MENUE2,
        ' TOS-Meldung '//char(0)    )
*
*   {Striche schattieren, damit Auswahl verhindern}
    CALL menu_ienable( MENUE_BAUM, FKT3, .FALSE. )
*
*   {Menueleiste anzeigen}
    CALL menu_bar( MENUE_BAUM, .TRUE.   )
*
*   Jetzt wird gearbeitet...
*
10  CALL evnt_mesag( PUFFER )
    !Menue anbieten, auf Auswahl warten 
    GEW_MENUE = PUFFER (3) !Gewa"hltes Menue und...
    GEW_FKT = PUFFER(4) !...daraus gewa"hlte Funktion 
    IF (PUFFER(0)   .EQ. MN_SELECTED)   THEN
        IF (GEW_MENUE .EQ. 3) THEN
            !Menue 3 ist immer das Desk-Menue 
            DUMMY = form_alert ( 1, '   [1] [ '//
    -       (C) Copyright:  |'//
    -       ' Gibt es fu"r ein so \'//
    -       'einfaches Programm nicht!]'//
    -       '[Na klar!]'//char (0) )
        ELSEIF (GEW_FKT .EQ. FKT1) THEN GOTO 999 
        ELSEIF (GEW_FKT .EQ. FKT2) THEN 
            DUMMY = form_alert ( 1, '[1]    ['//
        -   'Gleich wird sich ein|'//
        -   'GEM-Programm|'//
        -   'bei Ihnen melden.]'//
        -   '[Na los!)'//char(0) )
            CALL EXECPG( '\GEM1.PRG', DUMMY )
                !Tochterprogramm nachladen 
            IF (DUMMY .NE. 0) THEN
                WRITE (TEXT,' (I4) ' ) DUMMY 
                DUMMY = form_alert ( 1, '[3]['//
        -           'Das GEM-Programm konnte\'//
        -           'nicht gestartet werden.|'//
        -           'Fehlercode:    '//TEXT//']'//
        -           '[Ich habe gepfuscht]'//char(0) )
            ENDIF
            ELSEIF (GEW_FKT .EQ. FKT4) THEN 
            DUMMY = form_alert ( 1, '(1)    t'//
        -           'Gleich wird ein|'//
        -           'TOS-Programm|' / /
        -           'eine Ausgabe abliefern]'//
        -           '[Her damit!]'//char(0) )
            CALL EXECPG( '\TOS1.PRG', DUMMY )
                    !Tochterprogramm nachladen 
            IF (DUMMY .NE. 0) THEN
                WRITE (TEXT,'(I4)') DUMMY 
                DUMMY = form_alert ( 1, '[3]['//
        -           'Das TOS-Programm konnte\'//
        -           'nicht gestartet werden.\'//
        -           'Fehlercode:    '//TEXT//']'//
        -           '[Ich habe gepfuscht]'//char(0) )
            ELSE
                CALL menu_bar( MENUE_BAUM, .TRUE. )
                    !Bildschirm aufra"umen
            ENDIF
        ENDIF

*
*   {'Reverse Video' fu"r 'gewa"hltes Menue aufheben }
        CALL menu_tnormal ( MENUE_BAUM, GEW_MENUE,
            .TRUE.  )
        ENDIF 
        GOTO 10 
    999 CALL appl_exit 
    END

Listing 2: Das Oberprogramm PROG1. Die GEM-Funktionen sind in den Kommentaren erläutert, tu" = U, "ü" ist unter dem von Prospero mitgelieferten Editor nicht verfügbar.)

PROGRAM GEM1 
    CALL appl_init
    IF (AESret() .lt. 0) GOTO 999 
        !Das ging schief!
    DUMMY = form_alert ( 1, '   [1] ['//
        -           'Hier ist das|'// 'GEM-Programm|’//
        -           '[Weiss ich!)'//char(0) ) 
    999 CALL appl_exit 
        END

Listing 3: GEM1, ein ordentlicher GEM-Benutzer

PROGRAM TOS1

CHARACTER   DUMMY
PRINT*,
'Hallo, hier ist TOS1 !
PRINT*,
    'Leider ist jetzt das Desktop nicht mehr scho"n.' 
PRINT*,
'Weiter geht es mit <RETURN>    '
READ    (*, ' (A) ')    DUMMY
STOP 
END

Listing 4: TOS1, ohne alle GEM-Zutaten



Links

Copyright-Bestimmungen: siehe Über diese Seite
Classic Computer Magazines
[ Join Now | Ring Hub | Random | << Prev | Next >> ]