Universelle GDOS-Unit für MAXON-Pascal

In einem früheren Artikel [1] zeigte ich, wie man die Bildschirmzeichensätze des GDOS nutzt. Des weiteren bietet GDOS die komfortable Ausgabe auf verschiedene Medien wie Drucker und Datei an. Deshalb möchte ich heute eine Unit vorstellen, die die Verwaltung der Ausgabegeräte von GDOS übernimmt.

Zur Wiederholung möchte ich noch einmal kurz die Grundlagen des GDOS (Graphics Device Operating System) erklären: Das GDOS ist eigentlich der Teil des Betriebssystems, der die Verwaltung von verschiedenen Gerätetreibern (Bildschirm, Drucker und Metafile) übernimmt. Aus Platzmangel im ROM ist dieserTeil des Betriebssystems als eigenständiges Autoordnerprogramm realisiert. Es erlaubt dem Programmierer hardwareunabhängige Programmierung, da der Kontakt mit den Ausgabegeräten vollkommen vom GDOS übernommen wird. Der Vorteil der GDOS-Nutzung wird am Beispiel eines Druckertreibers schnell klar. Stellen Sie sich vor, alle Ihre Programme würden über GDOS ausgegeben, dann bräuchten Sie nur einen einzigen GDOS-Druckertreiber und könnten das Problem Druckeranpassung für alle Zeit vergessen. Leider sieht die Wirklichkeit ganz anders aus, da GDOS leider von den wenigsten Programmen unterstützt wird. Dies liegt zum Teil an der etwas komplizierten Installation sowie an der mangelnden Geschwindigkeit des Atari-GDOS, die aber mit Erscheinen des neuen GDOS ,Speedo‘ der Vergangenheit angehören sollte. Schon jetzt steht mit dem Programm NVDI ein sehr schnelles GDOS zur Verfügung.

Beim Start des GDOS aus dem Autoordner sucht das Programm zuerst nach der Datei ,ASSIGN.SYS' im Wurzelverzeichnis des Boot-Laufwerks. Diese Datei beinhaltet alle Informationen über die zu ladenden Gerätetreiber und ihre zugehörigen Zeichensätze (Information über den genauen Aufbau findet man in [2] und [3]). In dieser Datei wird jedem Gerätetreiber eine Nummer zugeordnet, über die man ihn später ansprechen kann. Dabei gelten die Zuordnungsregeln aus Tabelle 1. Sie werden sich nun fragen, warum zu jedem Typ mehrere Treiber zur Verfügung gestellt werden können? Nehmen wir z.B. den Drucker, so hat man die Möglichkeit, im ,ASSIGN.SYS' einen Laser- und einen Nadeldrucker zu installieren. Jetzt kann man in einem Programm, in dem es nicht auf die Optik ankommt, den Nadeldrucker und in der Textverarbeitung den Laserdrucker ansprechen, ohne zwischendurch ,ASSIGN.SYS' anpassen und einen Reset durchführen zu müssen. Dies funktioniert allerdings nur, wenn der Anwender mit Hilfe der Gerätenummer im Programm den Gerätetreiber auswählen kann. Dies sollte man in eigenen Programmen immer ermöglichen; im Demo wird aus Bequemlichkeit immer der Standardtreiber gewählt.

Nummer Typ des Gerätes
01-10 Bildschirm
11-20 Plotter
21-30 Drucker
31-40 Metafile
41-50 Kamera
51-60 Grafiktablett

Durch die Gerätenummern können verschiedene Ausgabegeräte angesprochen werden.

Nutzung des GDOS in eigenen Programmen

Kommen wir jetzt zur Nutzung des GDOS in unseren eigenen Programmen: Beim Aufruf aller VDI-Befehle wird immer das sogenannte Workstation-Handle benötigt. Gibt man auf den Bildschirm aus, wird hier das Handle einer virtuellen Workstation übergeben, das man mittels der Prozedur v_openvwk erhält (siehe Listing Routine Init_GEM). Der große Vorteil der Nutzung von GDOS besteht nun darin, daß man eigentlich nur der Ausgaberoutine statt des Bildschirm-Handles das Handle eines anderen Ausgabegerätes übergeben muß, um die Ausgabe auf dieses Gerät umzulenken. Um an das Handle des Ausgabegerätes zu gelangen, dient die Funktion v_opnwk, die praktisch das Laden der Gerätetreiber übernimmt. Ich habe diese Routine durch eine kompaktere ersetzt, der man nur noch die Nummer des Gerätetreibers (device) und das gewünschte Koordinatensystem (koord, siehe [2] und [3]) übergeben muß. Sie hat folgendes Aussehen:

handle := Open_Work(koord,device);
handle, koord, device : INTEGER;

Sie übernimmt nicht nur das Öffnen der Workstation, sondern lädt auch gleich die zugehörigen Zeichensätze. Die Zeichensätze werden wie in [1] beschrieben geladen. Die Routine v_openwk stürzt leider hemmungslos ab, wenn kein GDOS geladen ist. Deshalb habe ich mit Hilfe des Inline-Assemblers die Funktion vq_gdos implementiert, die bei installiertem GDOS einen Wert ungleich Null zurückliefert. Ich verwalte jeweils nur ein anderes Ausgabegerät neben dem Bildschirm, da die Gerätetreiber und die Zeichensätze den Speicher doch belasten. Der Speicherplatz wird vom Betriebssystem zur Verfügung gestellt; man sollte deshalb der Link-Option {$M, stack,hmin,hmax,dosfree} einen großen Wert (-200 KByte) für dosfree übergeben. Nachdem man die Workstation geöffnet hat, übergibt man nur der Ausgaberoutine das neue Handle und hat den Text oder die Grafik auf Drucker oder das Metafile ausgegeben. Danach löschen wir mit der Routine Close_Work (handle: Integer) den Gerätetreiber und seine Zeichensätze aus dem Speicher. So schön einfach können GEM & GDOS sein.

Was ist eigentlich ein Metafile?

Diese Frage haben Sie sicher auch schon mal gestellt. Das Metafile ist eine standardisierte Datei, in der die VDI-Aufrufe, die zur Ausgabe nötig sind, abgespeichert werden. Es handelt sich also um ein objektorientiertes Format, da die Zeichenvorschriften und keine BIT-Images (gesetzte und ungesetzte Pixel) abgespeichert werden. Man bietet dem Anwender mit der Ausgabe auf Metafile eine einfache Möglichkeit zur Nachbearbeitung mit anderen Programmen an. Dem Metafile kann man im Gegensatz zum Drucker einige zusätzliche Informationen übergeben, die ich hier nur kurz auflisten möchte:

  1. den Dateinamen: hierzu dient die Funktion vm_filename (handle:INTEGER; filename:STRING80). Falls kein Name angegeben wird, wird defaultmäßig in die Datei ,GEMFILE.GEM‘ ausgegeben.
  2. Angabe der maximalen Ausdehnung der Ausgabe: Mit der Funktion v_meta_extents (handle, min_x, min_y, max_x, max_y) kann man den maximal benötigten Bereich der Ausgabe angeben.
  3. Mit v_meta_write können benutzerdefinierbare Objekte in ein Metafile geschrieben werden.
  4. Man kann mit vm_pagesize (handle, pagew, pageh: INTEGER) die Seitengröße in mm angeben.
  5. Zum Schluß kann man noch mit der Funktion vm_coords (handle, Ilx, Ilr, urx, ury) ein Koordinatensystem für die Ausgabeseite angeben. Hierbei übergibt man die Koordinaten der linken unteren und der rechten oberen Ecke. Zusammen mit Funktion 4 kann man also das Verhältnis Punkt pro mm festlegen.

Die Funktionen 1 - 3 werden von der Unit GEMVdi zur Verfügung gestellt. Die Funktionen vm_coords und vm_pagesize müssen wir direkt beim Betriebssystem anfordern. Wie man Routinen des AES bzw. VDI anspricht, habe ich schon in [4] und [5] beschrieben.

Das Demo-Programm besteht aus zwei Teilen: 1. der GDOS-Unit, die man nun in eigenen Programmen fleißig verwenden sollte und 2. der eigentlichen Demo. Das Programm nutzt ein RSC-File, das aber nur eine Menüleiste beinhaltet. Das Nachbauen des Menüs dürfte mit Bild 1 und dem Listing von ,GDOSDEMO.I‘ kein Problem darstellen. Auf der Monats-Disk liegt natürlich das RSC-File bei.

Literatur:

[1] ST Computer 6/91, S. 82, ,GDOS-Zeichensätze in MAXON-Pascal'
[2] Jankowski, Reschke, Rabisch, ,Atari ST Profibuch', Sybex-Verlag
[3] Geiß, D. und J., ,Vom Anfänger zum GEM-ProfT, Hüthig-Verlag
[4] ST Computer 2/92, S.83, ,Form_Keybd & Form_Button in MAXON-Pascal ‘
[5] ST Computer 7/8/92, S.1992, Quicktip

Mit der Unit GDOS kann man bequem die Ausgabe vom Bildschirm auf den Drucker oder in eine Metadatei umlenken.


Unit GDOS;

{ von Wolfgang Sattler 1992     }
{ (c) MAXON Computer 1993       }

Interface
USES GEMVdi,GEMdecl,GEMAes;

TYPE

{ Feld zum Verwalten von Zeichensätzen }
    SCH_NAME_TYP = STRING[32];
    SCHRIFTART = RECORD
            { Nummer die bei vst_font   }
            { wird:                     }
                index:INTEGER; 
            { Zeichensatzname:          }
                name: SCH_NAME_TYP;
                END;
    SCHRIFTFELD = ARRAY[1..20] OF SCHRIFTART; 
    SchPtr      = ^SCHRIFTFELD;

VAR
{ Schriftfelder }
    bild_schrift,device_schrift
                        : SCHRIFTFELD;
{ Anzahl der Schriften }
    bild_anz,device_anz : INTEGER;

{ GDOS installiert ? }
    gdos_flag           : BOOLEAN;

{ Felder zum Öffnen von VDI-Workstations }
    workin              : IntIn_Array;
    workout             : WorkOut_Array;

{ Handle, d.h. Kennung des virtuellen   }
{ Bildschirmes:                         }
    vdi_handle          : INTEGER;

{ Kennung des AES-Prozesses:            }
    aes_handle          : INTEGER;

PROCEDURE vm_coords(handle,llx,lly,urx,ury : INTEGER);

PROCEDURE vm_pagesize(handle,pagew,pageh   : INTEGER);

PROCEDURE Init_GDOS(ohne:BOOLEAN);

FUNCTION Open_Work(koord,device:INTEGER):INTEGER; 

PROCEDURE Close_Work(handle:INTEGER);

PROCEDURE Exit_Gdos;

Implementation

TYPE

( Parameterblock dient zur Konversation mit }
{ dem VDI:                                  }
    VDIParBlk = RECORD
        control : ^control_Array;
        intin   : ^intin_Array;
        ptsin   : ^ptsin_Array;
        intout  : ^intout_Array;
        ptsout  : ^ptsout_Array;
        END;

VAR     vdipb   : ^VDIParBlk; 
        point   : Pointer;
        GDOS_FEHLT :STRING; 
        junk    : INTEGER;


{ Überprüfung auf installiertes GDOS: }
FUNCTION Vq_Gdos:INTEGER;ASSEMBLER;


ASM
        move.w  #-2,D0 
        trap    #2
        cmp.w   #-2,d0 
        sne     d0
        ext.w   d0 
        move.w  d0,@result
END;

PROCEDURE vm_coords(handle,llx,lly,urx,ury : INTEGER);
BEGIN
    vdipb:=addr(vdi_pb);

    vdipbc.intin^[0]:=1; 
    vdipb^.intin^[1]:=llx; 
    vdipb^.intin^[2]:=lly; 
    vdipb^.intin^[3]:=urx; 
    vdipb^.intin^[4]:=ury;

    vdipb^.control^[0]:=5; 
    vdipb^.control^[1]:=0; 
    vdipb^.control^[2]:=0; 
    vdipb^.control^[3]:=5; 
    vdipb^.control^[4]:=0; 
    vdipb^.control^[5]:=99; 
    vdipb^.control^[6]:=handle;
    ASM
    { VDI-Parameterblock    } 
        move.l  vdipb,d1
    { Magic-Number für VDI  } 
        move.w  #115,d0 
    { GEM-Aufruf            }
        trap    #2
    END;

END;

PROCEDURE vm_pagesize(handle,pagew,pageh : INTEGER);
BEGIN
    vdipb:=addr(vdi_pb);

    vdipb^.intin^[0]:=0; 
    vdipb^.intin^[1]:=pagew; 
    vdipb^.intin^[2]:=pageh;

    vdipb^.control^[0]:=5; 
    vdipb^.control^[1]:=0; 
    vdipb^.control^[2]:=0; 
    vdipb^.control^[3]:=3; 
    vdipb^.control^[4]:=0; 
    vdipb^.control^[5]:=99; 
    vdipb^.control^[6]:=handle;
    ASM
    { VDI-Parameterblock    } 
        move.l  vdipb,d1 
    { Magic-Number für VDI  } 
        move.w  #115,d0 
    { GEM-Aufruf            }
        trap    #2
    END;

END;

{ Ermitteln der Länge eines C-Strings:  } 
FUNCTION Strlen(name:STRING): INTEGER; 
VAR la: INTEGER;
BEGIN
    la:=1;
    while name[la]<>chr(0) DO 
        Inc(la);
    Strlen:=la-1;
END;

{ Laden von GDOS-Zeichensätzen:         }

PROCEDURE Loadfonts(handle:INTEGER;schrift : SchPtr;VAR anzahl:INTEGER);

VAR la:INTEGER;
BEGIN

    IF handle>0 THEN 
        BEGIN
    { Zeichensätze laden }
        anzahl:=vst_load_fonts(handle,0); 
        anzahl:=anzahl+1;

    { Namen und Index werden erfragt    }
    { und in das Feld eingetragen       }
        FOR la := 1 TO anzahl DO 
            WITH schrift^[la] DO 
                BEGIN
                index:=vqt_name(handle,la,name);
            { Länge des C-Strings erfragen und in } 
            { name[0] eintragen:                  }
                name[0]:=chr(strlen(name));
                END;
        END;
END;

FUNCTION Open_Work(koord,device:INTEGER):INTEGER; 
VAR la :INTEGER;
BEGIN

{ Bei installiertem GDOS öffnen der     }
{ Workstation, sonst den Wert '0' als   }
{ Fehler zurückgeben:                    }
    IF gdos_flag THEN 
        BEGIN
        { GDOS ist installiert }

        { Parameter für den v_opnwk-Aufruf: } 
        workin[0] := device;

        FOR la := 1 TO 9 DO 
            workin[la] := 1;

        workin[10] := koord;

        { Öffnen der Workstation: } 
        v_opnwk(workin,device,workout);

        { Zeichensätze laden } 
        loadfonts(device,@device_schrift,device_anz);
        END

    ELSE
    { GDOS nicht installiert:   }
    { Fehler: Open_Work:=0      }
        device:=0;

    Open_Work:=device;

END;

PROCEDURE Init_GDOS(ohne:BOOLEAN);
BEGIN
    GDOS_FEHLT:='[3][ | GDOS ist nicht '+'installiert ! ][OK]'+#00 ;

    IF gdos_flag=FALSE THEN
        { Kein GDOS installiert ! }
        BEGIN
        junk:=form_alert(1,GDOS_FEHLT[1]);
        { ohne = FALSE -> Programmabbruch }
        IF NOT ohne THEN 
            BEGIN 
            Halt(0);
            END;
        END

END;

{ Löschen der Zeichensätze und      }
{ des Gerätetreibers                }
PROCEDURE Close_Work(handle:INTEGER);
BEGIN
    IF handle <> 0 THEN 
        BEGIN
        vst_unload_fonts(handle,0); 
        v_clswk(handle);
        END;
END;


PROCEDURE Exit_Gdos;
BEGIN
{ Löschen der Bildschirmzeichensätze: } 
    vst_unload_fonts(vdi_handle,0); 
    rsrc_free;
    v_clsvwk(vdi_handle); 
    appl_exit;
END;

PROCEDURE Init_GEM;
VAR la,dummy:INTEGER;
    feld:ARRAY_10;
BEGIN
    aes_handle := appl_init;

    IF aes_handle >= 0 THEN 
        BEGIN
            vdi_handle := graf_handle(dummy,dummy,dummy,dummy);

            FOR la := 0 TO 9 DO workin[la] := 1; 
            workin[10] := 2;
            v_opnvwk (workin, vdi_handle, workout);

            END
    ELSE
        BEGIN
        WRITELN(' Applikation konnte nicht '+ 'geöffnet werden ! '); 
        repeat until Keypressed; 
        halt(0);
        END;
END;



BEGIN
{ Anmelden beim GEM: )
    Init_GEM;
    IF vq_gdos = 0 THEN 
        gdos_flag:=FALSE
    ELSE
        BEGIN
            gdos_flag:=TRUE;
    { Bildschirmzeichensätze laden: }
            loadfonts(vdi_handle,@bild.schrift, bild_anz);
        END;

END.

PROGRAM GDOS_Demo (INPUT,OUTPUT);

{ von Wolfgang Sattler 1992 }
{(c) MAXON Computer 1993    }

USES GEMDecl,GEMVdi,GEMAes,GDOS;

{$I GDOSDEMO.I)


VAR men : Pointer;
    RSC_FEHLT,INFO  : STRING;
    x0,y0,w0,
    h0,junk,
    win_handle      : INTEGER;
    handle          : INTEGER;
    fenst_titel     : STRING;
    ch:CHAR;

{ Laden des Resource-Files: }

PROCEDURE Init_Resource;
VAR ResourceName : STRING;
    fehler       : INTEGER;

BEGIN

    If RunFromMemory THEN
        ResourceName := 'GDOS\GDOSDEMO.RSC' + #0
    ELSE
        ResourceName := 'GDOSDEMO.RSC' + #0;

    rsrc_load(ResourceName[1]); 
    fehler:=GemError;

    IF fehler = 0 THEN 
        BEGIN
        junk:=form_alert(1,RSC_FEHLT[1]); 
        halt(0);
        END
    ELSE
        rsrc_gaddr(R_TREE,MENU, men);

END;


{ Die 'Ausgabe'-Routine wird von allen Geräten } 
{ benutzt. Übergeben wird das Gerätehandle     }

PROCEDURE Ausgabe(handle,x0,y0:INTEGER);
VAR p:ARRAY_4;
    la,dummy : INTEGER;

BEGIN

{ Textfarbe setzen: }
    vst_color(handle,BLACK);

{ Texthöhe mit vst_point setzen } 
    vst_point(handle,12,dummy,dummy,dummy,dummy);

    FOR la:=1 TO bild_anz DO 
        BEGIN

{ Auswahl der Schrift mittels Schriftindex }

        vst_font(handle, bild_schrift[la].index);

{ Ausgabe des Textes }
        v_gtext(handle,x0+40,y0+la*40,'Das ist der GDOS-Zeichensatz: '+ bild_schrift[la].name);

        END;

END ;

{ Zeichnen des Fensterhintergrundes }

PROCEDURE Do_Redraw(window,x0,y0,w0,h0:INTEGER); 
VAR p:ARRAY_4;

BEGIN


{ Bearbeitung beginnen } 
    graf_mouse(M_OFF, NIL); 
    wind_update(BEG_MCTRL); 
    wind_update(BEG_UPDATE);

    p[0] := x0;
    p[1] := y0;
    p[2] := x0 + w0 - 1;
    p[3] := y0 + h0 - 1;
{ Zeichenbereich festlegen } 
    vs_clip(vdi_handle, 1, p);

{ Parameter festlegen und Fensterhintergrund }
{ malen                                      }

    vsf_color(vdi_handle, WHITE);
    vsf_interior(vdi_handle, SOLID); 
    vr_recfl(vdi_handle, p);

{ Jetzt geht es zur Ausgabe des Textes       }
    Ausgabe(vdi_handle,x0,y0);

    wind_update(END_UPDATE); 
    wind_update(END_MCTRL); 
    graf_mouse(M_ON,NIL);

END ;

{ Event_Verwaltung: Es werden nur die Menü- }
{ aktionen überwacht.                       }

PROCEDURE Event_Loop;
VAR     msg     : ARRAY[0..15] OF INTEGER ;
        schluss : BOOLEAN;

BEGIN

    schluss:=FALSE;
    REPEAT
        Evnt_mesag(msg);

    { Programmende:     }
        IF msg[4] = ENDE THEN 
            schluss:=TRUE;

    { Ausgabe auf Drucker: }
        IF msg[4] = DRUC THEN 
            BEGIN
            graf_mouse(BUSYBEE,NIL);

        { Laden des Gerätetreibers und der }
        { Zeichensätze:                    }
            handle:=Open_Work(2,21);

        { Ausgabe-Routine aufrufen:        }
            Ausgabe(handle,10,40);

        { Um den Ausdruck zu starten muß man    } 
        { den internen Grafikpuffer vom         }
        { GDOS ausgeben:                        }
            v_updwk(handle);

        { Gerätetreiber und Zeichensätze    }
        { löschen:                          }
            Close_Work(handle);

            graf_mouse(ARROW,NIL);
            END;

    { Ausgabe auf Metafile:                 }
        IF msg[4] = META THEN 
            BEGIN
            graf_mouse(BUSYBEE,NIL);

        { Metafiletreiber hat Gerätenummer 31 } 
            handle:=Open_Work(2,31);

        { Dateiname auf 'GDOSDEMO.GEM' setzen } 
            vm_filename(handle,'GDOSDEMO.GEM');

            Ausgabe(handle,10,40);

        { Ausdehnung der Ausgabe:             } 
            v_meta_extents(handle,0,0,450,280);

        { Koordinatensystem und Ausmaße der   }
        { Ausgabenseite festlegen:            }
            vm_coords(handle,0,200,480,0); 
            vm_pagesize(handle,1905,2540);


            Close_Work(handle); 
            graf_mouse(ARROW,NIL);
            END;

    { Infomeldung:      }
        IF msg[4] = ABOUT THEN
                junk:=form_alert(1,INFO[1]);

    { Menütitel normal darstellen:  }
        menu_tnormal(men,msg[3],1);

    UNTIL schluss;

END;



BEGIN {HAUPTPROGRAM}
    RSC_FEHLT:='[3][ | GDOSDEMO.RSC nicht '+ 'gefunden ! ][Ende]'+#00 ;
    INFO:='[0][ | GDOS-Demo by| Wolfgang'+' Sattler ! | (c) 1992 ][OK]'+#00;

{ Resourcen laden   }
    Init_Resource;

{ Überprüfen ob GDOS geladen ist und gegeben- } 
{ falls Programm beenden                      }
    Init_Gdos(FALSE);

    menu_bar(men,1); 
    x0:=10;
    y0:=100; 
    w0:=480; 
    h0:=200;
    fenst_titel:=' Ausgabe ins Fenster '; 
    fenst_titel:=fenst_titel +#00 +#00 ; 
    win_handle:=wind_create(NAME,x0,y0,w0,h0); 
    wind_set(win_handle,WF_NAME,HiPtr(fenst_titel[1]),LoPtr(fenst_titel[1]),0,0); 
    graf_mouse(M_OFF, NIL); 
    wind_open(win_handle,x0,y0,w0,h0); 
    graf_mouse(M_ON, NIL); 
    wind_get(win_handle,WF_WORKXYWH,x0,y0,w0,h0);
    Do_Redraw(win_handle,x0,y0,w0,h0);

    graf_mouse(ARROW, NIL);

    Event_Loop;

    wind_close(win_handle); 
    wind_delete(win_handle); 
    menu_bar(men,0);

{ Bildschirmzeichensätze löschen und }
{ beim VDI und AES abmelden:         }
    Exit_Gdos;

END.

CONST

    MENU        =   0;
(* Menuebaum *)
    ABOUT       =   7;
(* Infomeldung im Menu GDOS_DEMO *)

    ENDE        =  16;
(* Programmende *)
    DRUC        =  18;
(* Ausgabe auf Drucker *)
    META        =  19;
(* Ausgabe in Metafile *)


Wolfgang Sattler
Aus: ST-Computer 02 / 1993, Seite 68

Links

Copyright-Bestimmungen: siehe Über diese Seite