Home Programmierung
und BIOS
Shareware-
FAQ
Kontakt u.
Infos

Pascal-Toolbox Nr. 1

Mausunterstützung (DOS)

Als erstes Projekt dieser Ausgabe habe ich mir eine Unit zur Mausunterstützung überlegt.

Zuerst legen wir eine Datenstruktur mit Maus-Informationen an:


   TYPE TMaus=RECORD  

     Func,Taste,X,Y:Word;

     TextModus:Boolean;

   END; 

   VAR Maus:Tmaus;

"Taste" gibt an, welche Taste zuletzt gedrückt wurde, "X" und "Y" sind die letzten Koordinaten des Mauszeigers und "Textmodus" gibt an, ob der Textmodus aktiv ist oder nicht (Wenn ja, müssen X und Y entsprechen verändert werden, da der Maustreiber die Koordinaten im Textmodus nur in 8er-Schritten ausgibt)

Als nächstes brauchen wir eine Routine, mit der die elementaren Mausfunktionen (Initialisierung, Zeiger an, Zeiger aus) schnell ausgeführt werden können:


   PROCEDURE MausDa(Nr:Word);

   INLINE($58/$cd/$33);

Wir verwenden hierzu die INLINE-Anweisung, die den Compiler dazu bringt, den Code der Funktion bei jedem Aufruf der Funktion direkt in den Programmcode einzufügen (statt eine Sprunganweisung zur Funktion zu schreiben). Dies hat zur Folge, das der Programmablauf schneller wird, sich andererseits aber auch der Programmcode immens aufblähen kann, da jeder Funktionsaufruf durch den kompletten Funktionscode ersetzt wird.

Die Folge $58/$cd/$33 steht für die Assembler-Anweisungen POP AX; INT $33; und hat zur Folge, daß der Funktionsparameter vom Stack geholt und in dem Register AX gesichert wird und dann der Mausinterrupt $33 aufgerufen wird. Zulässige Werte für "Nr" sind 0 (Initialisierung), 1 (Zeiger an),2 (Zeiger aus).

Um die Bedienung zu Vereinfachen, führen wir noch eine Funktion ein, die einfach nur zum An- bzw. Ausschalten des Mauszeigers dient:


   PROCEDURE ShowMouse(Show:Boolean);

   BEGIN

     IF Show=True THEN MausDa(1)

     ELSE MausDa(2);

   END;

Je nach Wert von "Show" wird der Mauszeiger an-(Show=True) oder ausgeschaltet(Show=False).

Im Prinzip würde "MausDa" schon reichen, um die Maus zu initialisieren, aber es fehlt noch eine Prüfung, ob der Maustreiber überhaupt installiert ist. Die folgende Funktion löst dieses Problem recht elegant:


   FUNCTION MausReset(TextModus:Boolean):Boolean;

   BEGIN

     IF Mem[MemW[0:$CC+2]:MemW[0:$CC]]<>$CF THEN BEGIN

       MausDa(0);    

       MausDa(1);

       MausReset:=True;

     END

     ELSE BEGIN

       MausReset:=False;

     END;

     Maus.TextModus:=TextModus;

   END;

Zuerst wird geprüft, ob an der Stelle, auf die der Interrupt-Vektor von $33 zeigt, wirklich ein Maustreiber installiert ist oder ob dort ein $CF (=nicht installiert) steht. Hierzu muß erst herausgefunden werden, auf welche Stelle der Interrupt-Vektor zeigt; dazu wird zuerst der Speicherplatz des Interuptvektors berechnet: 51(=$33)*4(Byte pro Int.-Vektor)=204. An 0:$CC und 0:$CC+1 steht das Offset der Speicherstelle, an 0:$CC+2 und 0:$CC+3 das Segment; um immer beide Bytes zu sichern, benutzt man MEMW. Nun muß nur noch per MEM auf die Adresse (Segment:Offset) zugegriffen werden.

Ist der Maustreiber installiert, wird die Maus initialisiert und dann der Mauszeiger sichtbar gemacht. Der Rückgabewert der Funktion wird auf TRUE gesetzt, was einen erfolgreichen Abschluß signalisieren soll. So kann der Anwender gleich prüfen, ob ihm überhaupt eine Maus zur Verfügung steht und gegebenenfalls das Programm beenden. Ist kein Treiber installiert, wird der Rückgabewert auf FALSE gesetzt.

Weiterhin wird der Funktion noch der Parameter "TextModus" übergeben, der angibt, ob das Programm im Textmodus ausgeführt wird oder nicht; entsprechend wird der Parameter "Maus.TextModus" gesetzt.

Für die weiteren Anwendungen benötigen wir eine erweiterte Version der Funktion MausDa, die etwas flexibler (aber auch langsamer) ist:


   PROCEDURE MausInt; ASSEMBLER;

   ASM

     MOV AX,Maus.Func; 

     MOV BX,Maus.Taste;

     MOV CX,Maus.X;

     MOV DX,Maus.Y;

     INT $33;

     MOV Maus.Func,AX;

     MOV Maus.Taste,BX;

     MOV Maus.X,CX;

     MOV Maus.Y,DX;

   END;

Zuerst werden die Register mit den (zuvor belegten) Mausvariablen gefüttert, dann der Mausinterrupt ausgeführt und dann die Register wieder ausgelesen. Die Mausvariablen übernehmen also praktisch die Funktion von Parametern der Funktion.

Nun brauchen wir nur noch eine Funktion, die die Mausvariablen neu belegt:


   PROCEDURE MausData;

   BEGIN

     Maus.Func:=3;

     MausInt;

     IF Maus.TextModus THEN BEGIN 

       Maus.X:=Maus.X DIV 8;

       Maus.Y:=Maus.Y DIV 8;

     END;

     Maus.Y:=Maus.Y+1;

     Maus.X:=Maus.X+1;

   END;

Zuerst wird in "Maus.Func" die auszuführende Mausfunktion(3) geschrieben und dann MausInt aufgerufen. MausInt füllt dann die Mausvariablen mit den aktuellen Mausdaten. Danach wird geprüft, ob der Textmodus aktiv ist; ist dies der Fall, werden die Mauskoordinaten durch Acht dividiert, da der Maustreiber im Textmodus die Daten nur in 8er-Schritten liefert. Dann werden die Koordinaten noch jeweils um Eins erhöht, damit der Koordinatenursprung bei (1,1) beginnt (sonst (0,0)).

Die Prozedur muß ständig aufgerufen werden, um ständig aktuelle Daten zu haben! (Beispiel s.u.)

Da es manchmal auch nötig ist, ein Mausfenster zu setzen, also einen bestimmten Bereich des Bildschirms, in dem sich die Maus nur bewegen kann, führen wir noch folgende Funktion ein:


   PROCEDURE MausWindow(X1,Y1,X2,Y2:Integer);

   BEGIN

     Dec(X1); Dec(X2); Dec(Y1); Dec(Y2);

     IF Maus.Textmodus=True THEN BEGIN  

       X1:=X1*8; X2:=X2*8; Y1:=Y1*8; Y2:=Y2*8; 

     END;

     MausDa(2);

     Maus.Func:=7; 

     Maus.X:=X1; 

     Maus.Y:=X2;

     MausInt;   

     Maus.Func:=8;

     Maus.X:=Y1;

     Maus.Y:=Y2;

     MausInt;

     MausDa(1);

   END;

Zuerst werden die vier Variablen (X1,Y1=linke,obere Ecke; X2,Y2=rechte,untere Ecke) jeweils um Eins verkleinert, um die Koordinaten an das Maustreiberkoordinatensystem (Ursprung(0,0)) anzupassen. Dann werden, falls der Textmodus gesetzt ist, noch alle Parameter mit Acht multipliziert, da der Maustreiber auch hier in 8er-Schritten zählt.

Dann wird der Mauszeiger abgeschaltet. Im Folgenden werden nun der horizontale (Maus.Func=7) und der vertikale (Maus.Func=8) Bereich festgelegt, wobei jedesmal mittels der Variablen Maus.X und Maus.Y die Start- und Endposition übergeben werden. Nach der Belegung wird beide Male MausInt aufgerufen, um den Interrupt auszuführen. Zum Schluß wird der Mauszeiger wieder angeschaltet.

So, das wars schon!

Ein rudimentäres Beispielprogramm könnte etwa so aussehen:


   USES CRT;

   [...]

   VAR Flag:Boolean;

   [...]

   BEGIN

     IF MausReset(True)=False THEN HALT(1);

     REPEAT

       MausData {!!!}

       GotoXY (20,20);

       Write ('X:',Maus.X,' Y:',Maus.Y,'Taste:',Maus.Taste,'  ');

     UNTIL KEYPRESSED;

     ReadKey;  

     ShowMouse(False); {WICHTIG!!!}

   END.

Wichtig ist, daß, wenn Daten vom Maustreiber gebraucht werden, die Prozedur "MausData" ständig (z.B. mittels einer Schleife) aufgerufen wird, um die Variablen entsprechend zu belegen, und auch, daß der Mauszeiger vor dem Beenden des Programmes ausgeschaltet wird.

Die Mausfunktionen funktionieren sowohl im Text- als auch in den Standard-Grafikmodi von Pascal (16 Farben).

Wer weitere Informationen zum Thema sucht, dem empfehle ich das Buch "Turbo Pascal 7.0 - Das Kompendium", erschienen im Markt&Technik-Verlag.

Tabelle 19 bietet eine Übersicht über die Funktionen des Maus-Interrupts $33.

Die fertige Unit (ca. 1,5 KB) könnt Ihr hier downloaden.

Alle Angaben ohne Gewähr.

Achtung: bitte Hinweise zur Aktualitšt der Daten beachten! Copyright