HomeProtokolleFont-ProtokollLTL-Protokoll

15.6 GDPS: Gerti's Driver Piping System

Stand: 23.08.90

15.6.1 Allgemeine Beschreibung

Dieses Treiberkonzept soll ermöglichen, speicherresidente Gerätetreiber der verschiedensten Art von beliebigen Programmen aus aufzurufen. Die Kommunikation zwischen den Programmen und den Treibern erfolgt über eine Datenstruktur, die der Treiber zur Verfügung stellt. Dabei ist ein fester Kern dieser Datenstruktur vorgegeben, und muß von allen Treibern, die diesen Standard erfüllen sollen, eingehalten werden. Darüber hinaus sind Erweiterungen möglich. Anhand von Versionsnummern kann dann das aufrufende Programm feststellen, inwiefern Erweiterungen der Datenstruktur vorhanden sind. Alle Datenstrukturen sind aufwärtskompatibel.

  1. Vorbemerkungen

    Alle Werte sind, sofern sie mehr als 1 Byte benötigen, immer in der Reihenfolge MSB...LSB definiert. Die Beispielcodes sind in 68000er Assembler formuliert. Es folgt die Beschreibung für ATARI ST.

15.6.2 Definition der Treiberverkettung

Basis des GDPS ist der vom Betriebssystem nicht genutzte Vektor 0x41C. Dieser Vektor enthält normalerweise OL. Im GDPS zeigt der Vektor auf den Header des ersten Devicetreibers in der Kette. Dieser Header hat folgenden Aufbau (rAdr=relative Adresse, S=Size):

     rAdr S  Bedeutung

      OH  L  Pointer auf nächsten Treiberheader oder 0 im letzten Treiber
             der Kette
      4H  L  'GDPS' = 0x47445053 Magic als Kennung für das GDPS

Daraus folgt die Methode des Ein- und Ausketten eines Treibers.

  1. Einketten eines Treibers

    Der Treiber kopiert den in 0x41C stehenden Vektor in seinen Header. Anschließend trägt er die Adresse seines Headers in 0x41C ein.

    Assemblerbeispiel (ist im Supervisormodus auszuführen):

        len     header(pe),a0  * Adresse des GDPS-Headers
        clr.l   (a0)           * NEXT auf 0 initialisieren
        cmp.l   0x41C,a0       * = alter Wert
        beq     unten          * ja keine Endlosverkettung!
        move.l  0x41C,(a0)     * sonst NEXT eintragen
        move.l  a0,0x41C       * und Header in die Kette hängen
    

  2. Ausketten eines Treibers

    Der Treiber sucht in der Kette den Pointer auf seinen Header. Diesen ersetzt er durch den in seinem Header befindlichen Pointer.

    Assemblerbeispiel (ist im Supervisormodus auszuführen):

                   move.l  #0x41C,a0
                   lea     header(pe),a1
       searchen:   cmp.l   (a0),a1
                   beq     found
                   move.l  (a0),a0
                   bra     searchen
       found:      move.l  (a1),(a0)
    

  3. Suchen eines Treibers

    Das Suchen nach einem Treiber sollte ebenfalls im Supervisormode erfolgen. Da das Betriebssystem die Variable 0x41C beim Warmstrat nicht auf 0 setzt, muß zu jedem gefunden Vektor MAGIC überprüft werden.

    Es folgt eine Beispielsroutine in GFA-Basic Notation, die die gefundenen und gültigen Vektoren in ein Integerarray schreibt:

       DIM vektor%(31)                   ! Dahin kommen die Adressen der Treiber
       index%=0                          ! Zähler auf 0 initialisieren
       adr%=SLPEEK($41C)                 ! Adresse des ersten Treibers
       WHILE adr%<>0 AND SLPEEK(adr%+4)=$47445053
                                         ! Test: Adresse gültig und Magic OK?
          vektor%(index%)=adr%           ! wenn ja Adresse merken
          inc index%                     ! Zähler um eins erhöhen
       WEND
       PRINT index%;" Treiber gefunden!"
    

15.6.3 Datenstruktur

 rAdr S  Bedeutung
  0H  L  Pointer auf nächsten Treiberheader oder 0 im letzen Treiber
         der Kette
  4H  L  'GDPS' = 0x47445053 Magic als Kennung für das GDPS
  8H  W  100 = Versionsnummer Datenstrukturdefinition * 100
  AH  W  Treibertyp (siehe 3.1)
  CH  L  Pointer auf Treiberinfo, 0-terminierter String,
         max. 32 Charakters
 10H  L  Pointer auf Programmierer/Copyright, 0-terminierter String,
         max. 32 Charakters
 ab 14H folgt die treiberspezifische Datenstruktur

15.6.4 Treibertypen

Es dürfen beliebig viele Treiber im Speicher sein, auch Treiber gleichen Typs!

Die Treibertypen sind zunächst in Gruppen unterteilt:

Gruppe Treibertyp Bedeutung
0 000-0FF graphische Eingabegeräte
1 100-1FF graphische Ausgabegeräte
2 200-2FF diverse Eingangsschnittstellen
3 300-3FF diverse Ausgangsschnittstellen
4 400-4FF I/O-Schnittstellen
5 500-5FF Massenspeicher
6-F 600-FFF reserviert
10-FF 1000-FFFF 'private' Treiber, deren Datenstruktur nicht in den
    GDPS-Definitionen erfaßt sind

15.6.4.1 Treibergruppe Scanner

Festlegung: Bei Bi-Level-Daten entspricht ein gesetztes Bit einem 'vorhandenen` Pixel. Bei Multivalue-Daten entspricht 0 `nichts`, mit steigendem Wert nimmt die betreffende Farbintensität bzw. Helligkeit zu.

ACHTUNG: Beim Kommandos aus der 10xH-Gruppe werden Graustufenbilder invertiert!

  14H  W  Scannerbeschreibung
          Enthält dieses Word den Wert 0, so ist der Scanner/Treiber noch
          nicht initialisiert, Kommando 105H ausführen
          Bit 0: Bi-Level möglich
              1: Dithern möglich
              2: Multivalue möglich
              3: Multivalue und Dithern möglich
              4: Bi-Level Farbe möglich
              5: Dithern Farbe möglich
              6: Multivalue in Farbe möglich
              7: Multivalue in Farbe und Dithern möglich
              8: Komprimierung der Daten (siehe unten) möglich
              9: Blockweise Rückgabe möglich
             10: Einzelblatteinzug per separatel Kommando
             11: Einzelblatteinzug automatisch
             12: Prescan möglich
             13: virtuelle Speicherverwaltung möglich
                 (GDPS-Version >= 1.10)
             14: reserviert (0)
             15: reserviert (0)
               Multivalue und Dithern: Sondermodus, immer 2 Bit/Pixel
  16H  W  Zahl der Farben
  18H  W  Mögliche Multi-Value-Bittiefen
          Aufbau:  Bit 0 gesetzt: monochrom möglich
                   Bit 1 gesetzt: 2 Graustufen möglich
                   Bit 2 gesetzt: 4 Graustufen möglich
                   Bit 3 gesetzt: 8 Graustufen möglich
                   etc...
  1AH  W  Scanner reservieren
  1CH  W  Kommando an Scanner (0=Ready, 1-FFH reserviert!!!)
  1EH  L  Pointer auf Kommandostruktur

Es wird von einem Scannertreiber mit eigener Bedienoberfläche ausgegangen. Will ein Programm den Scannertreiber ansprechen, so geschieht dies folgendermaßen:

   a) Warten, bis Word 1AH = 0 (Scanner frei)
   b) Scanner reservieren durch Eintrag eines Wortes in 1AH
   c) Erstellen der Kommandostruktur
   d) Eintragen des pointers auf die Kommandostruktur nach 1EH
   e) Eintragen des Kommandos in 1CH
   f) in einer EVENT-Schleife (!!) (Timer- oder Multi-Event) warten, bis
      Kommando-Word vom Scannertreiber wieder auf 0 gesetzt ist.
   g) Schritte ab c) beliebig oft wiederholen
   h) Scanner freigeben (0 in 1AH eintragen)

Die vom aufrufenden Programm zur Verfügung zu stellende Kommandostruktur hat drei Aufgaben:

   a) Übergabe der kommandostruktur
   b) Rückgabe einer Erfolgs- bzw. Fehlermeldung
   c) Rückgabe der verwendeten Parameter

Aufbau der Kommandostruktur:

   00H  W  Rückgabewert, wird vom aufrufenden Programm auf 0 initialisiert.
           Der Scannertreiber trägt hier nach Abarbeitung des Kommandos
           0xFFFF (=OK) oder eine positive Fehlernummer ein.
           Arbeitet der Scanner im Blockmodus, wird nach den Blöcken ein
           0xFFFE eingetragen, nach dem letzten Block ein 0xFFFF. Arbeitet
           im Blockmodus UND in Farbe, so gibt es zwei Möglichkeiten:
           a) der Scanner scannt die Farbauszüge einzeln, dann erfolgt die
              übergabe wie oben, aber entsprechend der Zahl der Farben
              mehrfach.
           b) der Scanner scannt alle farben gleichzeitig, dann wird nach
              den Blöcken 0xFFFD eingetragen. Der zurückgegebene Block
              besteht dann aus 'Anzahl der Farben' gleichgroßen Bereichen.
   02H  W  Erlaubte Scanmodi. Der Aufbau dieses Wortes ist wie 14H, ein
           gesetztes Bit besagt, daß der Scannertreiber den entsprechenden
           Modus benutzen darf.
   04H  W  Erlaubte Bittiefen (Aufbau wie oben 18H).
   06H  L  Pointer auf Speicher
   0AH  L  Anzahl der Bytes in Speicher (Rückgabe: Anzahl der verwendeten
           Bytes)
   0EH  W  Bytes pro Scanline oder 0 (immer mod 2!)
   10H  W  Anzahl Scanlines oder 0
   12H  W  Breite des Image in 1/10 mm oder 0
   14H  W  Höhe des Image in 1/10 mm oder 0
   16H  W  Auflösung in Main-Scan-Richtung (x) in dpi oder 0
   18H  W  Auflösung in Sub-Scan-Richtung (y) in dpi oder 0
           (Bei Rückgabe: Auflösung = Auflösung * Zoomfaktor!)
   1AH  W  Modulo-Wert für Scanlines (in Bytes, z.B. 2 = 2*n Bytes pro
           Scanline)
   1CH  W  x-Position des Image in 1/10 mm
   1EH  W  y-Position des Image in 1/10 mm

Seit GDPS-Version >= 1.10:
   20H  L  Seriennummer des aufrufenden Programms
   24H  W  ADD-Bits: Zusätzlich vom Programm benötigter Speicher (in Bits
                     pro Pixel)
   26H  L  Pointer auf 'Dchange_pointer' (Funktion der vS)
   2AH  L  Pointer auf Dupdate (Funktion der vS)
   2EH  W  v_handle LESEN (vS)
   30H  W  v_handle SCHREIBEN (vS)
   32H  W  virt_flag (1 = vS wird benutzt)

vS = virtuelle Speicherverwaltung

Nach Abarbeitung eines Kommandos trägt der Scannertreiber die TATSÄCHLICH BENUTZTEN WERTE in diese Datenstruktur ein.

Soll die Bildgröße fest sein, so kann dies entweder mit den Worten 0EH, 10H oder 12H, 14H, 1CH, 1EH definiert werden. Sind beide Wertepaare vorbesetzt, wird die Vorgabe in Bytes benutzt. Falls der Scanner nicht in der Lage ist, dieses Format einzuscannen, wird gegebenenfalls auf passenden Werte verändert; Daher bei Rückgabe prüfen!!

Komprimierung:

Bi-level und Dither-Daten sind IMMER so gepackt, daß 8 Pixel in einem Byte vorliegen. Dabei wird das erste Pixel ins MSBit eingetragen!

Multivalue-Daten liegen entweder unkomprimiert vor, dann belegt ein Pixel genau ein Byte. Dabei sind die Daten (bei weniger als 256 Graustufen) immer zum MSBit hin formatiert, die LSBits sind ggf. aufgefüllt (je nach Scanner mit 0 oder 1). In komprimierter Form werden die Multivalue-Daten so gepackt, daß Byte-Grenzen nicht überschritten werden, ggf. auftretende Leerbits sind (je nach Scanner) mit 0 oder 1 gefüllt:

                 +--+--+--+--+--+--+--+--+
   2 Bit/ Pixel  |B1|B0|B1|B0|B1|B0|B1|B0|   -> 4 Pixel/Byte
                 +--+--+--+--+--+--+--+--+

                 +--+--+--+--+--+--+--+--+
   3 Bit/ Pixel  |B2|B1|B0|--|B2|B1|B0|--|   -> 2 Pixel/Byte
                 +--+--+--+--+--+--+--+--+

                 +--+--+--+--+--+--+--+--+
   4 Bit/ Pixel  |B3|B2|B1|B0|B3|B2|B1|B0|   -> 2 Pixel/Byte
                 +--+--+--+--+--+--+--+--+

                 +--+--+--+--+--+--+--+--+
   5 Bit/ Pixel  |B4|B3|B2|B1|B0|--|--|--|   -> 1 Pixel/Byte
                 +--+--+--+--+--+--+--+--+

                 +--+--+--+--+--+--+--+--+
   6 Bit/ Pixel  |B5|B4|B3|B2|B1|B0|--|--|   -> 1 Pixel/Byte
                 +--+--+--+--+--+--+--+--+
   etc.

Wird in der Kommandostruktur das Komprimierungsbit nicht gesetzt, so werden die ggf. vom Scannertreiber entkomprimiert. Ist das Bit jedoch gesetzt, muß es nach Abarbeitung des Kommandos getestet werden, ob die Daten wirklich komprimiert sind.

Festgelegte Kommandos:

   100H: Scan
           Der Benutzerdialog wird aufgerufen, ggf. wird ein Scanvorgang
           vorgenommen.
   101H: Continue
           Dieses Kommando setzt im Blockmodus das Scannen fort.
   102H: Scan without Dialog
           Es wird ein Scanvorgang ohne weitere Benutzerintervention
           veranlaßt. Es werden die gleichen vom Benutzer einstellbaren
           Parameter benutzt wie beim vorhergehenden Scanvorgang.
   103H: Next Sheet
           Der Einzelblatteinzug zieht das nächste Blatt ein.
   104H: Prescan
           Führt einen Prescan mit fest eingestellten Parametern durch.
   105H: Scanner/Treiber initialisieren
           Holt Gerätebeschreibung vom Scanner

Die Kommandos 102H und 103H sind speziell für eine automatisierte Schrifterkennung gedacht.

Seit GDPS-Version 1.10 existieren neue Kommandos (wie oben, aber statt 10xH wird 20xh benutzt). Werden diese Kommandos benutzt, kann virtuell gearbeitet werden, falls virt_flag=1. Weiterhin werden Graustufenbilder bei diesen Kommandos wie folgt geliefert:

      0=schwarz
    255=weiß

ACHTUNG: bei 20xH-Kommandos muß vom aufrufenden Programm die erweiterte Kommandostruktur nach GDPS 1.10 übergeben werden!!!

Fehlermeldungen im Rückgabe-Wert:

   FFFEH: Block bereit, weitere Blöcke folgen.
   FFFFH: Scanvorgang (ggf. für eine Farbe) abgeschlossen, letzter (bzw.
          einziger) Datenblock liegt vor.
      0H: Abwarten und Tee trinken (bzw. mittels EVENT-Schleife warten)
      1H: Unbekanntes Kommando
      2H: Scannerfehler
      3H: Abbruch durch Benutzer
      4H: Out of Paper (nur bei Einzelblatteinzug)
      5H: Out of Memory: Der Benutzer hat ein zu großes Papierformat
          benutzt.
      6H: Scanner noch nicht initialisiert.

Die Fehlermeldungen werden dem Benutzer vom Treiber angezeigt. Sie dienen dem aufrufenden Programm nur als Information.

15.6.4.2 GDPS, virtuelle Speicherverwaltung

#define VOR              1
#define ZURUECK         -1
#define MITTE            0

/********************************************************************/
/*                        Scannerstrukturen                         */
/********************************************************************/
typedef struct                   /* Übergabestruktur für Scanner    */
{
   void  *next;                  /* Zeiger auf den nächsten Treiber */
   char  kennung[4];             /* Magic GDPS als Treiberkennung   */
   int   version;                /* Versionsnummer, derzeit < 200   */
   int   typ;                    /* Treiberart, 0 für Scanner       */
   char  *info;                  /* Zeiger auf Treiberinfo und die  */
   char  *copyright;             /* Copyrightmeldung                */
   int   gerbesch;               /* Gerätebeschreibungsflags        */
   int   farben;                 /* Anzahl der Farben 0=SW          */
   int   deep;                   /* mögliche Bittiefen              */
   int   free;                   /* Flag ob Scanner frei ist        */
   int   befehl;                 /* Kommando an Scanner             */
   void  *command;               /* Zeiger auf die Kommandostruktur */
} SCANHEADER;

typedef struct                 /* Kommandostruktur für GDPI-Scanner */
{
   int   result;                 /* Ergebnis das Treiber meldet     */
   int   modi;                   /* Erlaubte Scanmodi               */
   int   tiefe;                  /* Tiefe des Bildes in Bit/Pixel   */
   void  *vspeicher;             /* Dahin soll das Bild gehen       */
   long  vmaxlen;                /* verfügbarer Speicher            */
   int   bytebreite;             /* Breite einer Zeile in Bytes     */
   int   hoehe;                  /* Hoehe des Bildes in Zeilen      */
   int   mmbreite;               /* Breite und Höhe in 1/10 Milli-  */
   int   mmhoehe;                /* metern                          */
   int   xdpi;                   /* Auflösung in x und y-Richtung   */
   int   ydpi;
   int   modulo;                 /* 2=>Bild wird Wordbündig         */
   int   start_x;                /* linke obere Ecke x in 1/10mm    */
   int   start_y;                /* linke obere Ecke y in 1/10mm    */
   long  ser_nr;                 /* Seriennummer                    */

/****
der nun folgende Teil ist nur beim Aufruf des Scanners mit
den Befehlen der 0x2XX Serie definiert
******/
   int     add_bits;              /* Angabe, wieviele Bits zusätzlich*/
                                  /* z.B. besitzt CRANACH Studio     */
                                  /* zu jedem Bild 2 Masken. Wird    */
                                  /* eine Bitmap gescant, so benötigt*/
                                  /* das Programm nicht nur 1 BIT pro*/
                                  /* Pixel, sonder drei Bit          */
                                  /* Analog dazu müßten bei einem    */
                                  /* Grautonbild 8 + 2 = 10 Pixel    */
                                  /* berechnet werden. Sollte dieser */
                                  /* Wert nicht beachtet werden, so  */
                                  /* kann es nach dem Scannen passieren*/
                                  /* daß CRANACH Studio das Fenster  */
                                  /* nicht öffnen kann, da zwar für  */
                                  /* das gescannte Bild Speicher vor-*/
                                  /* handen ist, nicht aber für die  */
                                  /* notwendigen Masken.             */
                                  /* gebraucht werden, z.B. zwei     */
                                  /* zusätzliche Bits für die Maske  */
   void *Dchange_pointer;         /* Zeiger auf diese Funktion       */
   void *Dupdate;                 /* Zeiger auf jene Funktion;       */
   int  lesen;                    /* Lesebufferindex virtuell        */
   int  schreiben;                /* Schreibbufferindex virtuell         */
   int  virt_flag;                /* flag, ob virtuell gearbeitet wird   */
} SCANCOM;
SCANCOM scancom;

void *Dchange_pointer(
                        void *pointer,
                         int v_handle,
                         int richtung,
                         long *max_vor,
                         long *max_zurueck
                       );


void Dupdate(int v_handle);


Alle tms Produkte arbeiten mit einer virtuellen Speicherverwaltung.
Dies bedeutet, es muß auf nicht im RAM befindliche Speicherbereiche
zugegriffen werden. Die in tms- Produkte implementierte virtuelle
Speicherverwaltung wurde für die Belange von Bildern optimiert.

Allg. vorgehensweise: Der Treiber übergibt in scancom.schreiben eine
virtuelle Handle. Mit dieser Handle kann nun auf die virtuellen Daten
zugegriffen werden. Die Daten werden automatisch von der Platte,
wenn nötig, nachgeladen und gespeichert.

Eine mögliche Anwendung wäre:

UCHAR *real;
long max_vor, max_zurueck;

scancom->vspeicher zeigt auf den freien virtuellen Speicher.
Dieser ist wie ein normaler Speicher zu betrachten,
also z.B. freier Speicher von Adrr 16MB bis Adrr 50MB
mit scancom->vspeicher = Adrr 30MB
Da diese Adresse nicht wirklich existiert, muss der Pointer
auf einen realen Speicher abgebildet werden, und die Daten auf der
Platte geladen werden. das erledigt die Funktion Dchange_pointer.

 real = (UCHAR*)Dchange_pointer(
                                   scancom->vspeicher,
                                   scancom->schreiben,
                                   VOR,
                                   &max_vor,
                                   &max_zurueck
                                 );

real zeigt jetzt also auf einen RAM- Bereich.

scancom->schreiben ist die vom Programm übergebene Speicherhandle

VOR sagt der Speicherverwaltung, daß wir uns vor allem nach vorne
im Speicher bewegen werden. Somit können die Plattenzugriffe optimiert
werden.

max_vor liefert zurück, wieviele Bytes ab real im Speicher nach vorne
gelaufen werden darf.

max_zurück liefert zurück, wieviele Bytes ab real im Speicher nach
hinten gelaufen werden darf.

Werden diese Grenzen erreicht, so muß Dchange_pointer erneut aufgerufen
werden. Die Mindestlänge für max_vor und max_zurück:
                    VOR           ZURÜCK                    MITTE
max_vor:            32k             0k                       16k
max_zurück:         0k              32k                      16k

Der Ram Speicher ist in 6 Blöcke unterteilt, von denen jeder einen
unterschiedlichen oder aber auch einen Überlappenden Teil des virtuellen
Speichers abbilden kann. Um beim Überlappen von Blöcken zu gewährleisten,
das nach einer Speicheränderung alle Blöcke den aktuellen Speicherinhalt
wiedergeben, wird die Funktion Dupdate(scancom->schreiben) aufgerufen.
Diese aktualisiert die anderen Blöcke. Dupdate ist aber nur nötig bevor
auf einen anderen Block zugegriffen wird. Für einen Scannertreiber der nur
den Block scancom->schreiben benutzt reicht es deshalb am Schluss des
Scannens Dupdate(scancom->schreiben) aufzurufen.

Beispiel löschen von 10Mb ab Adresse 32MB

v_pointer=32Mb
size=10Mb

while(size>0)
{
        real=Dchange_pointer(
                              v_pointer,
                              scancom->schreiben,
                              VOR,
                              &max_vor,
                              &max_zurueck);
        if(max_vor<=size)
        {
                memset(real,0,max_vor);
                size-=max_vor;
                v_pointer+=max_vor;
        }
        else
        {
                memset(real,0,size);
                size=0;
                v_pointer+=size;
        }
}
Dupdate(scancom->schreiben);  !!!!!!!!!!!!!



****************************************************************************
Alte Programme rufen den Scanner mit dem Befehl 0x100 (und nicht 0x200)
auf. Diese Programme (z.B. tms CRANACH) nutzen noch keine virtuelle
Speicherverwaltung. Daher ist die Structure SCANCOM ab der gekennzeichneten
Stelle nicht definiert. Die Funktionen Dchange_pointer bzw. Dupdate
sollten dann durch dummy_Funktionen im Treiber ersetzt werden.

/********************************************************************/

mögliche Anwendung als ACC.

/********************************************************************/
/*    main()                                                        */
/*                                                                  */
/*    Kernstück des Programms.                                      */
/********************************************************************/

int main( void )
{
        int work_in[12],work_out[58],dummy;
        int buffer[20];

   appl_id = appl_init();

        /* öffnen eine eigenen Workstation */
        handle=graf_handle(&dummy,&dummy,&dummy,&dummy);
   for(dummy=0;dummy<10;work_in[dummy++]=1);
        work_in[10]=2;
        v_opnvwk(work_in,&handle,work_out);

   if(!rsrc_load("SCANNER.RSC"))
   {
      form_alert(0,NO_RSC_FILE);
      goto FOREVER;
   }

   if( appl_id != -1 )
   {
      if( !_app )
      {
         scanner_moeglichkeiten();
         menu_id = menu_register( appl_id, "  SCANNER" );
         event_loop();   /* hier wird auf den Aufruf des Treibers */
                         /* durch das Programm gewartet, der      */
                         /* Dialog wird geführt, das Bild gescannt*/
                         /* und die Werte in der
      }

   }
FOREVER: /* Initialisierung hat nicht geklappt */
   while(1)
        evnt_mesag((int*)buffer);
}

/********************************************************************/
/* Scanner initialisieren                                         */
/********************************************************************/

void scanner_moeglichkeiten()
{
        long
                **zeiger;
        long
                stack;

/********** Zeiger auf GDPS verbiegen *******************************/

        stack=Super(0);

        zeiger = (long **)0x41c;

        if(*(*zeiger+1)== 0x47445053l)
                header.next = *zeiger;
        else
                header.next = NULL;

        *zeiger = (void *)&header;

        Super((void *)stack);

/********************************************************************/

        sprintf(header.kennung,"GDPS");
        header.version          =100;
        header.typ              =0;           /* scanner */
        header.info             =info;
        header.copyright        =copyright;
        header.gerbesch         =1 | 2 | 4;   /* Gerätebeschreibungsflags */
        header.farben           =1;           /* Anzahl der Farben 0=SW   */
        header.deep             =1 | 16;      /* mögliche Bittiefen       */
        header.free             =0;
        header.befehl           =0;           /* Kommando an Scanner      */
        header.command          =&command;

}

HomeProtokolleFont-ProtokollLTL-Protokoll