287 lines
8.8 KiB
Plaintext
287 lines
8.8 KiB
Plaintext
ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
|
|
³ Programming the Microsoft Mouse ³
|
|
ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ
|
|
|
|
|
|
ÚÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
|
|
³ Disclaimer ³
|
|
ÀÄÄÄÄÄÄÄÄÄÄÄÄÙ
|
|
|
|
I assume no responsibility whatsoever for any effect that this file, the
|
|
information contained therein or the use thereof has on you, your sanity,
|
|
computer, spouse, children, pets or anything else related to you or your
|
|
existance. No warranty is provided nor implied with this information.
|
|
|
|
ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
|
|
³ Introduction ³
|
|
ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ
|
|
|
|
A complete list of mouse function calls can be found in the file GMOUSE.TXT,
|
|
the file contains calls for both Microsoft (2 button) and Genius (3 button)
|
|
modes.
|
|
|
|
Calling these functions from within a Pascal program is a fairly simple
|
|
matter. This procedure would get the mouse position and button states:
|
|
|
|
const MOUSEINTR = $33;
|
|
|
|
procedure GetMousePos(var x, y : word; var button1, button2 : boolean);
|
|
var regs : registers;
|
|
begin
|
|
regs.ax := 3;
|
|
Intr(MOUSEINTR, regs);
|
|
x := regs.cx;
|
|
y := regs.dx;
|
|
button1 := (regs.bx and 1) <> 0;
|
|
button2 := (regs.bx and 2) <> 0;
|
|
end;
|
|
|
|
|
|
The mouse position is returned in variables x and y, the button states are
|
|
returned in variable button1 and button2 (true = button is pressed).
|
|
|
|
|
|
ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
|
|
³ Writing Custom Handlers ³
|
|
ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ
|
|
|
|
Most mouse drivers do not support SVGA modes, so you must write custom
|
|
handlers if you want mouse support for these modes.
|
|
|
|
Rather than writing an entire mouse driver, you can write a simple handler
|
|
routine to take care of the graphics and tell the mouse driver to call it
|
|
whenever the mouse does anything. This function is descibed in the GMOUSE.DOC
|
|
file, but this demo Pascal program shows the general idea. It sets mode 13h,
|
|
resets the mouse and waits for a key to be pressed. Whenever you do anything
|
|
to the mouse (moving it or pressing a button) the handler will get called
|
|
and it will draw a pixel on the screen. The color of the pixel depends on
|
|
which buttons are being pressed.
|
|
|
|
Uses Crt, Dos;
|
|
|
|
{$F+}
|
|
{ called with bl = buttons, cx = x * 2, dx = y }
|
|
procedure Handler; far; assembler;
|
|
asm
|
|
|
|
{ This mouse "handler" just draws a pixel at the current mouse pos }
|
|
pusha
|
|
mov ax, $A000
|
|
mov es, ax
|
|
shr cx, 1
|
|
xchg dh, dl
|
|
mov di, dx
|
|
shr dx, 2
|
|
add di, dx
|
|
add di, cx
|
|
mov al, bl
|
|
inc al
|
|
stosb
|
|
popa
|
|
end;
|
|
{$F-}
|
|
|
|
begin
|
|
asm
|
|
|
|
{ Set graphics mode 13h }
|
|
mov ax, $13
|
|
int $10
|
|
|
|
{ Initialize mouse driver }
|
|
xor ax, ax
|
|
int $33
|
|
|
|
{ Install custom handler }
|
|
mov ax, SEG Handler
|
|
mov es, ax
|
|
mov dx, OFS Handler
|
|
mov ax, 12
|
|
mov cx, $1F
|
|
int $33
|
|
|
|
{ Wait for a key press }
|
|
xor ah, ah
|
|
int $16
|
|
|
|
{ Back to text mode }
|
|
mov ax, 3
|
|
int $10
|
|
end;
|
|
end.
|
|
|
|
|
|
|
|
|
|
Alternatively you may wish to write your own interrupt handler to process
|
|
mouse events as they happen. When a mouse event occurs, 3 interrupts are
|
|
generated and the bytes are availble via the COM port.
|
|
|
|
ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
|
|
³ Interrupt Port ³
|
|
ÃÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ´
|
|
³ COM1 $0C $3F8 ³
|
|
³ COM2 $0B $3F8 ³
|
|
ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ
|
|
|
|
The three bytes sent are formatted as follows:
|
|
|
|
|
|
1st byte 2nd byte 3rd byte
|
|
ÚÄÂÄÂÄÂÄÂÄÂÄÂÄÂÄ¿ÚÄÂÄÂÄÂÄÂÄÂÄÂÄÂÄ¿ÚÄÂÄÂÄÂÄÂÄÂÄÂÄÂÄ¿
|
|
³-³1³?³?³X³X³Y³Y³³-³0³X³X³X³X³X³X³³-³0³Y³Y³Y³Y³Y³Y³
|
|
ÀÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÙÀÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÙÀÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÙ
|
|
³ ³ ÀÂÙ ÀÂÙ ÀÄÄÄÄÂÄÄÄÄÙ ÀÄÄÄÄÂÄÄÄÄÙ
|
|
³ ³ ³ ³ ³ ³
|
|
³ ³ ³ ÀÄÄÄÄÄÄÄÄÄÄÄÄÅÄÄÄÄÄÄÄÄ¿ ³
|
|
³ ³ ÀÄÄÄÄÄÄÄÄ¿ ³ ³ ³
|
|
³ ³ ÚÁ¿ ÚÄÄÄÄÁÄÄÄÄ¿ ÚÁ¿ ÚÄÄÄÄÁÄÄÄÄ¿
|
|
³ ³ ÚÄÂÄÂÄÂÄÂÄÂÄÂÄÂÄ¿ÚÄÂÄÂÄÂÄÂÄÂÄÂÄÂÄ¿
|
|
³ ³ ³ ³ ³ ³ ³ ³ ³ ³ ³³ ³ ³ ³ ³ ³ ³ ³ ³
|
|
Left Button ÄÄÙ ³ ÀÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÙÀÄÁÄÁÄÁÄÁÄÁÄÁÄÁÄÙ
|
|
Right Button ÄÄÄÄÙ X increment Y increment
|
|
|
|
|
|
The X and Y increment values are in 2's compliment signed char format. (BTW
|
|
thanks go to Adam Seychell for posting this info to comp.os.msdos.programmer).
|
|
|
|
|
|
A simple Borland Pascal 7.0 mouse handler follows. First we declare a few
|
|
things we'll need:
|
|
|
|
|
|
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
|
|
Uses Crt, Dos;
|
|
|
|
{$F+}
|
|
|
|
const COM1INTR = $0C;
|
|
COM1PORT = $3F8;
|
|
|
|
var bytenum : word;
|
|
combytes : array[0..2] of byte;
|
|
x, y : longint;
|
|
button1, button2 : boolean;
|
|
MouseHandler : procedure;
|
|
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ
|
|
|
|
The bytenum variable is used to keep track of which byte is expected next
|
|
(ie 0, 1 or 2). The combytes variable is simply an array to keep track of
|
|
bytes received so far. The mouse position will be stored in the x and y
|
|
varaibles (note that this example will not perfrom any range checking).
|
|
Button1 and button2 will be used to store the states of each of the buttons.
|
|
MouseHandler will be used to store the normal mouse driver event handler.
|
|
We'll need it to reset everything once we are finished.
|
|
|
|
Here's the actual handler:
|
|
|
|
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
|
|
procedure MyMouseHandler; Interrupt;
|
|
var dx, dy : integer;
|
|
var inbyte : byte;
|
|
begin
|
|
|
|
{ Get the port byte }
|
|
inbyte := Port[COM1PORT];
|
|
|
|
{ Make sure we are properly "synched" }
|
|
if (inbyte and 64) = 64 then bytenum := 0;
|
|
|
|
{ Store the byte and adjust bytenum }
|
|
combytes[bytenum] := inbyte;
|
|
inc(bytenum);
|
|
|
|
{ Have we received all 3 bytes? }
|
|
if bytenum = 3 then
|
|
begin
|
|
{ Yes, so process them }
|
|
dx := (combytes[0] and 3) shl 6 + combytes[1];
|
|
dy := (combytes[0] and 12) shl 4 + combytes[2];
|
|
if dx >= 128 then dx := dx - 256;
|
|
if dy >= 128 then dy := dy - 256;
|
|
x := x + dx;
|
|
y := y + dy;
|
|
button1 := (combytes[0] And 32) <> 0;
|
|
button2 := (combytes[0] And 16) <> 0;
|
|
|
|
{ And start on first byte again }
|
|
bytenum := 0;
|
|
end;
|
|
|
|
{ Acknowledge the interrupt }
|
|
Port[$20] := $20;
|
|
end;
|
|
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ
|
|
|
|
Once again pretty simple stuff. We just read the byte from the com1 port and
|
|
figure out if it's time to do anything yet. If bit 6 is set to 1 then we
|
|
know that it's meant to be the first byte of the 3, so we reset our
|
|
bytenum variable to zero (in a perfect world bytes would always come in 3's
|
|
and we would never need to check, but it never hurts to be careful).
|
|
|
|
When 3 bytes have been received we simple decode them according to the
|
|
diagram above and update the appropriate variables accordingly.
|
|
|
|
The 'Port[$20] := $20;' command just lets the interrupt controller know we
|
|
have processed the interrupt so it can send us the next one when it wants to.
|
|
|
|
Note that the above "handler" does nothing more than keep track of the
|
|
current mouse position and button states. If we were writing a proper mouse
|
|
driver for an SVGA game we would also have to write custom cursor routines.
|
|
I'll leave that bit to you!
|
|
|
|
To actually install our mouse driver we'll have to set up all the variables,
|
|
save the address of the current mouse handler and install our own. We'll
|
|
also need call the existing mouse driver to set up the COM1 port to make
|
|
sure it sends us the mouse bytes as it receives them. We could do this
|
|
ourselves, but why make life harder than it already is?
|
|
|
|
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
|
|
procedure InitMyDriver;
|
|
begin
|
|
|
|
{ Initialize the normal mouse handler }
|
|
asm
|
|
mov ax, 0
|
|
int $33
|
|
end;
|
|
|
|
{ Initialize some of the variables we'll be using }
|
|
bytenum := 0;
|
|
x := 0;
|
|
y := 0;
|
|
button1 := false;
|
|
button2 := false;
|
|
|
|
{ Save the current mouse handler and set up our own }
|
|
GetIntVec(COM1INTR, @MouseHandler);
|
|
SetIntVec(COM1INTR, Addr(MyMouseHandler));
|
|
end;
|
|
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ
|
|
|
|
|
|
And finally when our program is finished it'll need to clean up after
|
|
itself and return control back to the normal mouse driver:
|
|
|
|
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
|
|
procedure CleanUpMyDriver;
|
|
begin
|
|
SetIntVec(COM1INTR, @MouseHandler);
|
|
end;
|
|
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ
|
|
|
|
|
|
This little bit of source will test the above code. It does nothing more
|
|
than repeatedly write the mouse position and button states to the screen
|
|
until a keyboard key is pressed:
|
|
|
|
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿
|
|
begin
|
|
ClrScr;
|
|
InitMyDriver;
|
|
while not keypressed do
|
|
WriteLn(x : 5, y : 5, button1 : 7, button2 : 7);
|
|
CleanUpMyDriver;
|
|
end.
|
|
ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ
|