Modula-2: Der Maus auf die Sprünge geholfen

Eine Maus hat es schwer

Manch einem ist der Auslauf der Maus zu groß, eben weil der Schreibtisch zu klein ist. Andere hätten gerne einen größeren Bereich um genauere Zeichnungen anzufertigen. Das folgende Listing zeigt, wie man das in Modula ändern kann.

Allgemeines

Nach einer Verdoppelung der Mausgeschwindigkeit benötigt man nur ein Viertel der sonst üblichen Tischfläche, um die Maus über den gesamten Bildschirm zu bewegen, so daß man alle Programme aus dem Handgelenk steuern kann, ohne den Handballen vom Tisch zu heben.

Nachdem Sie einige Zeit mit diesem Accessory gearbeitet haben, werden Sie sicher gerne 1.5 KByte Ihres Hauptspeichers spendieren, um wesentlich zügiger und komfortabler mit Ihren GEM-Programmen zu arbeiten.

Bedienung

Nach dem Booten des Systems bewegt sich der Mauscursor am Bildschirm doppelt so schnell wie üblich. Außerdem können Sie im Menü DESK unter dem Menüpunkt mouse speed jederzeit folgende Parameter ändern:

SPEED : 1 2 3 (normale, doppelte, dreifache Geschwindigkeit)

TRIGGER: 0 1 2 (kein Trigger, Trigger = 1 oder 2 Pixel)

Der Trigger ermöglicht es, die Beschleunigung des Mauscursors bei sehr kleinen Bewegungen automatisch zu unterdrücken, so daß selbst bei einer dreifachen Geschwindigkeit ein pixelgenaues Arbeiten am Bildschirm möglich ist.

Funktionsweise

Im XBIOS existiert eine Routine, die immer dann aufgerufen wird, wenn von dem Tastaturprozessor des ATARI ST gemeldet wird, daß der Benutzer die Maus bewegt hat. Dabei befindet sich in dem Prozessorregister A0 ein Zeiger auf einem Datenblock im Speicher, der neben dem Zustand der Maustasten (gedrückt / nicht gedrückt) die relative Bewegung der Maus in X und Y-Richtung seit der letzten Meldung enthält.

Um nun die Mausgeschwindigkeit zu verdoppeln, genügt es, diese Routine für jede Bewegung der Maus zweimal aufzurufen. Durch einen Vergleich der relativen Bewegung mit der Variablen Trigger wird ein mehrfacher Aufruf verhindert, falls die Maus nur ein kleines Stückchen bewegt wurde.

Interessant für Insider ist vielleicht noch die Realisierung einer Assemblerroutine (MouseMovement) in Modula-2: Zunächst wird mit der Compiler-Direktive $P- die Erzeugung des üblichen Prozedurprologs und -epilogs verhindert. Am Beginn und Ende der Prozedur werden die verwendeten Register mit Assemblerbefehlen auf dem Stack gerettet, von wo die Register gezielt (mit stackrelativer Adressierung) geladen werden können. Außerdem zeigt die Variable oldvec, wie der (indirekte) Aufruf einer parameterlosen Assemblerroutine von Modula-2 aus möglich ist.

Die Standardvorgaben für Geschwindigkeit und Trigger (2 und 0) können Sie in der 3. Zeile des Hauptprogrammes modifizieren.

Übrigens läßt sich der erzeugte Maschinencode mit dem Hilfsprogramm DECLNK.PRG disassemblieren, um z.B. die verwendeten Register in einer Prozedur zu bestimmen.

Übersetzen des Quelltextes

Nach dem Abtippen übersetzen Sie das Programm wie üblich. Zum Linken wählen Sie neben der Option OPT (für Optimieren) die Option QUERY, so daß Sie bei der Frage des Linkers nach dem File GEMX.LNK das File GEMACCX.LNK eingeben können. Dieses File enthält ein sehr kompaktes Modula-Laufzeitsystem für Accesories. Das entstehende Programm (z. B. SPEEDMOU.PRG) speichern Sie auf der Bootdiskette unter dem Namen SPEED.ACC.

MODULE SpeedMouse;
(*
Florian Matthes 12.5.1987
Beschleunigung der Maus als Accessory
Beim Linken GEMACCX.LNK statt GOK.UJK verwenden!
*)

(*$T-,$S- (Stacktest etc. ausschalten) *)

IMPORT XBIOS;
FROM SYSTEM 			IMPORT ADR, ADDRESS, REGISTER, SEHREG, CODE, BYTE;
FROM AESApplications	IMPORT Appllnitialise;
FROM GEMAESbase			IMPORT AccessoryOpen, MesageEvent;
FROM AESForms			IMPORT FormAlert;
FROM AESMenus			IMPORT MenuRegister;
FROM AESEvents			IMPORT EventMessage;

CONST
	Title = " Mouse Speed";

TYPE
	MousePackagePtr = POINTER TO 
						RECORD
							ButtonState,DeltaX,DeltaY : BYTE;
						END;

VAR
	applID : INTEGER; 				(* GEM application ID *)
	menuID : INTEGER;				(* menu ID *)
	Msg ; ARRAY [0..16] OF INTEGER; (* message buffer *)
	x : CARDINAL;

	(* globale Variablen für den neuen Interrupt-Handler; *)
	Trigger, Speed	: CARDINAL;
	KBVec 			: XBIOS.KBVectorPtr;
	PackagePtr		: MousePackagePtr;
	oldvec			: PROC;
	Count			: CARDINAL;

(*$P- Verhindere Erzeugung des Modula Prozedur-Eintritts und Ende-Codes, 
	da die folgende Prozedur direkt aus Maschinensprache aufgerufen werden soll
*)

PROCEDURE MouseMovement;
(* Diese Prozedur wird von XBIOS auf gerufen, falls dieses ein 'mouse 
package’ von dem 'intelligent keyboard processor' erhält, das anzeigt, 
daß die Maus bewegt wurde. Das CPU-Register A0.L zeigt auf ein package 
im folgenden Format:
	0: Zustand der Maustasten (Bit 0 und 1)
	1: X-Offset relativ zur letzten Position (-128..127)
	2: Y-Offset relativ zur letzten Position (-128..127)
*)
BEGIN
	(* rette benötigte Register auf den Stack: *)
	CODE(048E7H, 08C88H); (* MOVEM.L D0/D4/D5/A0/A4, -(A7) *)
	CODE(0202FH, 00O0CH); (* MOVE.L 12(A7),D0 (= A0 ) *)

	PackagePtr:= REGISTER(0); (* hole Inhalt von D0, zerstört auch D4 *)
	IF (CARDINAL(PackagePtr^.DeltaX)>Trigger)
		AND (CARDINAL(PackagePtr^.DeltaX)<256-Trigger)
	OR
		(CARDINAL(PackagePtr^.DeltaY)>Trigger)
		AND (CARDINAL(PackagePtr^.DeltaY)<256-Trigger) THEN
		Count:= Speed; (* rufe die alte Routinen Speed-mal auf: *)
		REPEAT
			oldvec; (* zerstört das Register A4 *)
			DEC(Count);
		UNTIL Count=0;
	ELSE
		oldvec;
	END;

	(* restauriere alle Register van Stack: *)
	CODE(04CDFH, 01131H); (* MOVEM.L (A7)+, D0/D4/D5/A0/A4 *)
	CODE(04E75H); (* RTS *)
END MouseMovement;

(*$P+ Rückkehr zum 'normalen' Modus der Codeerzeugung *)

BEGIN
	applID := ApplInitialise(); 
	menuID := MenuRegister(applID,Title);

	(* Voreinstellung: Kein Trigger, doppelte Geschwindigkeit: *)
	Trigger:= 0; Speed:= 2;
	(* hole Zeiger auf Tabelle der XBIOS-Routinen *)
	KBVec:= XBIOS.KeyboardVectors();
	WITH KBVec^ DO
		(* merke ursprüngliche Adresse des Interrupthandlers: *)
		oldvec:= PROC(mousevec);
		(* leite Interrupts zur Routine MouseMovement um: *)
		mousevec:= ADDRESS (MouseMovement);
	END;

	(* Erlaube Änderung der Parameter durch den Benutzer: *)
	LOOP
		EventMessage(ADR(Msg)); (* warte, bis Benutzer Acessory wählt *)
		IF Msg[0] = AccessoryOpen THEN 
			x := FormAlert(2,
				"[2][Which Parameter do you want to modify?][ Trigger | Speed ]"); 
			IF x = 1 THEN Trigger:= FormAlert(Trigger+1,
				"[2][Trigger for speedup (in pixel)?][ 0 | 1 | 2 ]") - 1;
			ELSE
				Speed := FormAlert(Speed,"[2](Mouse speed factor?][ 1 | 2 | 3 ]”); 
			END;
		END;
	END;
END SpeedMouse.

Listing 1: Mausspeed in Modula-2


Florian Matthes
Aus: ST-Computer 10 / 1987, Seite 48

Links

Copyright-Bestimmungen: siehe Über diese Seite