fryman@webb.psych.ufl.edu (Josh Fryman) (01/05/91)
Greetings. I find that I am in... a rather interesting situation; one which
is causing me premature hair loss, actually. Since I'm having this problem
which is so baffling, it was recommended that I post a message asking any
gurus to see if they might lend me a hand. I seem to be having a bit of a
nasty problem with _some_ interrupts but _not_ others.
I'm writing a psychology experiment program for the professors here at UF, and
I need to do several things. Most of the special features are, naturally,
based on interrupts, and this merely compounds my problem. I'm programming
for a plain vanilla PS/2 50 (w/ mouse). I'm also using TP 5.0.
I'm _trying_ to get the alarm features of the 286 to work, something they're
not very keen on. Also, I am trying to install my own mouse handler (via the
option in interrupt 33h, function 0Ch). The only thing I can notice it doing
is: nothing. It doesn't respond. I check the values I pass into the the alarm
"setter" procedure, and they check as fine. But, the program apparently would
rather ignore anything that does seem to happen. The alarm features are listed
below. I would MOST appreciate someone pointing out what glaring error I have
made. This not the real code (ok, quite far from it), but a real watered down
all-in-one program to just set the alarm and be done with it. I use the same
(similar) code in my program, and it's beyond me as to what the problem is.
program test;
uses dos, crt;
var hour, min, sec, sec100: word;
regs: registers;
beeper: pointer;
procedure alarm; interrupt;
begin
sound(250);
delay(250);
nosound;
end;
begin
clrscr;
getintvec($4a, beeper);
setintvec($4a, @alarm);
fillchar(regs,sizeof(regs),0);
gettime(hour,min,sec,sec100);
with regs do
begin
ah := $06;
ch := hour;
cl := min + 1;
dl := sec;
Intr($1a, regs);
end;
setintvec($4a, beeper);
repeat
until keypressed;
end.
And yes, I am aware that if I run this at the wrong time, it will not function
because I didn't program it (here) to do a minute roll-over. The real program
has these safety features.
As I mentioned before, the other problem I have is the mouse event handler.
The code I wrote works great on my XT (gag) at home (with a LogiTech mouse
that responds fine to Intr 33h functions). It goes something like this:
Force far calls on (since it's an event handler); declare the handler as an
interrupt so TP will save the necessary DS registers so I can access my
globals; read ALL CPU registers and STORE them in a local array; perform
functions; move CPU registers BACK onto the stack; exit.
The problem here is a little more straightforward. It wedges the system. As in
power off and power back on. But that's NOT all. If I do this, in conjunction
with my alarm interrupts, my keyboard interrupt (which works fine), and my
time interrupts, it's nice enough to reset the computer for me. As in RESET.
As in go back to square zero, do the tedious memory check, etc, etc. Neat!
I would list the code, but it's rather long. As I recall, some people have
been asking around for a mouse unit in TP. I guess I'm willing to resign
on this and use the same thing if someone can point out where I can grab one.
This big questions are: these things work fine on my machine (well, I can't
try the alarm features since it's an XT), so are the interrupts different on
a PS/2? If not, what IS the problem?
I use similar convention for polling the interrupts (specifically, the
time interrupts; replacing the system timer and monitoring the real time
clock) and these features work great. Why do these work, but not others?
My reference (one of the better ones I have) for the interrupts came from
"PC System Programming" by Abacus books.
thanks for any information that can be offered and for reading this far,
Joshrichardw@uunet.uu.net (01/05/91)
Well, a few comments. One: you shouldn't try to do that much in an
interrupt. The very nature of the interrupt is to process something as fast
as possible, then return control to foreground process. You are defining an
interrupt that will execute with interrupts masked off for over 0.25 seconds.
Your alarm function should just post a note somewhere that indicates that
time is up, and your keyboard polling routine should check this value and
perform the appropriate tasks.
I'm also including a mouse unit which I have sent out on occasion. You can
use the function that handles mouse events as a model for your code. It was
developed on a ps/2 model 50, so you should have no problems.
None of this is endorsed by Microsoft Corp.
-Richard Ward
uunet!microsoft!richardw
microsoft!richardw@uunet.uu.net
The above has no relation to Microsoft corporate policy.
--------------8<-------------------8<------------------8<-------------------
Unit Mouser;
{ This is a base-level unit, i.e. it needs no units other than default
TP units. This code is not owned by any company that I work for, or
have worked for in the past, and therefore no liability for this code
can be assumed by any company I have been associated with. This code
is not copyright, and can be freely distributed, or modified without
risk of penalty. No guarantees, express or implied, about this code
are provided, and the author takes no responsibilities for any use of
this code. Light fuse and get away.
Richard Ward
rbw@cs.williams.edu - old address, may or may not work
microsoft!richardw@uunet.uu.net - current address
}
interface
uses crt,dos;
const
Mouse_EventMove = 1; { Mouse moved }
Mouse_EventLeftDown = 2; { left button down }
Mouse_EventLeftUp = 4; { left button up }
Mouse_EventRightDown = 8; { right button down }
Mouse_EventRightUp = 16; { right button up }
Mouse_EventMiddleDown = 32; { middle button down }
Mouse_EventMiddleUp = 64; { middle button up }
MouseWaitNoBlock = 0; { Wait: return immediately }
MouseWaitBlock = 1; { Wait: wait until mouse event }
MouseDefaultMask = #$77#$ff; { Default cursor MASK }
MouseDefaultCursor = #$77#$0; { Default cursor CURSOR }
Type
Mouse_CursorType = string[2];
Var
MouseExist : boolean;
Function Mouse_Reset : boolean; { Resets the mouse and driver }
Function Mouse_Test : boolean; { soft reset of driver }
{ Controlling the mouse }
Procedure Mouse_SetCursor(mask,cursor : Mouse_CursorType);
Procedure Mouse_Cursor(b : boolean);
Procedure Mouse_GetXYB(var x, y, b : word);
Procedure Mouse_SetXY(x, y : word);
{ Event management }
procedure Mouse_EnableEvents(driver:pointer; eventreport:word);
procedure Mouse_DisableEvents;
Function Mouse_TestEvent(block:byte):boolean;
Procedure Mouse_GetEvent(var etype,bstatus,curx,cury,shifts:word);
Procedure Mouse_StartEvents(emask:word);
Function Mouse_CountEvents:word;
Procedure Mouse_ClearEventQ;
implementation
Const
Mouse_Intr = $33;
Type
IntrnEventRec = record
eventtype : word;
buttons : word;
shiftstate : word;
x,y : word;
end;
Var Mouse_EventQueue : array[0..63] of IntrnEventRec;
Mouse_EventHead,
Mouse_EventTail : word; { Event Queue pointers }
CallCount : longint; { Count of mouse events }
MouseExists : boolean; { Internal flag }
MouseButton : byte; { number of buttons on mouse }
Mouse_Exitsave : pointer; { pointer to exit procedure }
{ Mouse_Reset performs a hard reset on the mouse driver. This can take a
few seconds. This generally doesn't need to be called, but old mouse
drivers may not support function 21h
}
Function Mouse_Reset:boolean;
var rec:Registers;
begin
rec.ax := 0;
intr(Mouse_Intr, rec);
MouseExists := rec.ax <> 0; { Determine if mouse is present }
MouseButton := lo(rec.bx); { number of buttons returned in BL }
Mouse_Reset := MouseExists; { Return value }
MouseExist := MouseExists; { Set external mouse-present flag }
end;
{ Mouse_Test performs a software reset on the mouse driver. This is much
faster than Mouse_Reset.
}
Function Mouse_Test : boolean;
var rec:Registers;
begin
rec.ax := $21; { software reset }
intr(Mouse_Intr, rec);
MouseExists := rec.ax = $ffff; { Determine if mouse is present }
MouseButton := lo(rec.bx); { number of buttons in BL }
Mouse_Test := MouseExists; { Return value }
MouseExist := MouseExists; { Set external mouse-present flag }
end;
{ Mouse_SetCursor determines the format of the mouse cursor on the screen.
The driver will logically AND the current mouse position character with
the value defined in mask, then XOR the value in cursor. Mouse_CursorType
is a string of length 2, where character 1 is the attribute of the screen
character, and character 2 is the character itself. Generally speaking,
you should use the defaults provided, though you can certainly experiment.
}
procedure Mouse_SetCursor(mask, cursor: Mouse_CursorType);
var rec:registers;
begin
if not MouseExists then exit;
rec.ax := 10;
rec.bx := 0;
rec.cx := ord(mask[1]) shl 8 + ord(mask[2]);
rec.dx := ord(cursor[1]) shl 8 + ord(mask[2]);
intr(Mouse_Intr,rec);
end;
{ Mouse_Cursor controls whether the mouse cursor is displayed or not. The
calls are cumulative, meaning if you call Mouse_Cursor(false) four times,
you must call Mouse_Cursor(true) four times before the cursor will show.
The mouse always starts with the cursor off.
}
procedure Mouse_Cursor(b:boolean);
var rec:registers;
begin
if not MouseExists then exit;
if b then
rec.ax := 1
else
rec.ax := 2;
intr(Mouse_Intr,rec);
end;
{ Mouse_GetXYB gets the current x,y position and the state of the buttons.
}
procedure Mouse_GetXYB(var x, y, b : word);
var rec:registers;
begin
if not MouseExists then exit;
rec.ax := 3;
intr(Mouse_Intr,rec);
x := rec.cx div 8 + 1; { Mouse coordinates are in pixels }
y := rec.dx div 8 + 1; { this translates to TP cursor coordinates }
b := rec.bx;
end;
{ Mouse_SetXY sets the current x,y position, based on TP cursor coordinates.
}
Procedure Mouse_SetXY(x, y : word);
var rec:registers;
begin
rec.ax := 4;
rec.cx := (x - 1) shl 3; { convert into pixels, short hand for * 8 }
rec.dx := (y - 1) shl 3;
intr(Mouse_Intr, rec);
end;
{ Mouse_EnableEvents allows you to set up your own event handler for the
mouse driver. Before using this call, be sure you know what is required.
}
procedure Mouse_EnableEvents(driver:pointer; eventreport:word);
var rec:registers;
begin
if not MouseExists then exit;
rec.ax := 12;
rec.cx := eventreport;
rec.dx := ofs(driver^);
rec.es := seg(driver^);
intr(Mouse_Intr,rec);
end;
{ Mouse_DisableEvents stops the mouse driver from calling the driver you set
up with Mouse_EnableEvents.
}
procedure Mouse_DisableEvents;
var rec:registers;
begin
if not MouseExists then exit;
rec.ax := 12;
rec.cx := 0;
rec.dx := 0;
rec.es := 0;
intr(Mouse_Intr,rec);
end;
{$F+}
{ MouseDD is the event handler that I wrote for the mouse. It stores the
mouse event in the Mouse_EventQueue array, and returns to the driver.
It is set up as an interrupt handler, because the mouse driver passes
the state of the mouse in the registers.
}
procedure MouseDD(flags,cs,ip,ax,bx,cx,dx,si,di,ds,es,bp:word);
interrupt;
begin
Mouse_EventTail := (Mouse_EventTail + 1) mod 64;
with Mouse_EventQueue[Mouse_EventTail] do
begin
eventtype := ax;
buttons := bx;
shiftstate := memW[$40:$17]; { Gets the current state of the shift keys }
x := cx;
y := dx;
end;
inc(CallCount);
inline( $8B/$E5/ { Even though we were called as an interrupt, we can't }
$5D/ { exit as one, because TP inserts an IRET when we want }
$07/ { to do a RETF. So this mass of inline pops all the }
$1F/ { registers off the stack, then does a FAR return }
$5F/
$5E/
$5A/
$59/
$5B/
$58/$CB);
end;
{ Mouse_TestEvent allows you to query if there are any pending mouse events.
If you specify the NoBlock option (see constants), TestEvent will return
immediately. If you specify Block, then it will wait until something does
happen.
}
Function Mouse_TestEvent(block : byte) : boolean;
begin
if not MouseExists then exit;
if block = MouseWaitNoBlock then
begin
Mouse_TestEvent:=Mouse_EventHead <> Mouse_EventTail;
exit;
end;
while Mouse_EventHead = Mouse_EventTail do; { spin until an event }
Mouse_TestEvent := true;
end;
{ Mouse_GetEvent will copy the pending event into the variables specified.
If there is no pending event, etype is set to 0.
}
Procedure Mouse_GetEvent(var etype,bstatus,curx,cury,shifts:word);
begin
if not MouseExists then exit;
if Mouse_EventHead = Mouse_EventTail then
begin
etype := 0;
exit;
end;
Mouse_Eventhead:=(Mouse_Eventhead + 1) mod 64;
with Mouse_EventQueue[Mouse_EventHead] do
begin
etype := eventtype;
bstatus := buttons;
shifts := shiftstate;
curx := x div 8 + 1;
cury := y div 8 + 1;
end;
end;
{ Mouse_CountEvents returns the number of pending events
}
Function Mouse_CountEvents : word;
begin
if not MouseExists then
Mouse_CountEvents := 0
else
Mouse_CountEvents := (Mouse_EventTail - Mouse_EventHead + 64 ) mod 64;
end;
{ Mouse_ClearEventQ discards all pending events.
}
Procedure Mouse_ClearEventQ;
begin
Mouse_EventTail := Mouse_EventHead;
end;
{ Mouse_StartEvents starts the event queue stuff. emask is the sum of
all events that you want to watch for, so if you want to collect all
mouse clicks, emask := Mouse_EventRightDown + Mouse_EventLeftDown;
}
Procedure Mouse_StartEvents(emask : word);
begin
if not MouseExists then exit;
Mouse_EnableEvents(@MouseDD,emask and $1F);
end;
{ This is the exit procedure for the unit, which turns off any mouse
event drivers.
}
Procedure Mouse_ExitProc;
begin
ExitProc := Mouse_exitsave;
Mouse_DisableEvents;
end;
{ Unit startup code. ExitProc is installed, mouse is soft reset.
}
begin
Mouse_EventHead := 0;
Mouse_EventTail := 0;
CallCount := 0;
Mouse_exitsave := ExitProc;
ExitProc := @Mouse_ExitProc;
MouseExists := Mouse_Test;
end.fryman@webb.psych.ufl.edu (Josh Fryman) (01/08/91)
Richard wrote: >Well, a few comments. One: you shouldn't try to do that much in an >interrupt. The very nature of the interrupt is to process something as fast >as possible, then return control to foreground process. You are defining an >interrupt that will execute with interrupts masked off for over 0.25 seconds. >Your alarm function should just post a note somewhere that indicates that >time is up, and your keyboard polling routine should check this value and >perform the appropriate tasks. A good observation. I appreciate that. (Hadn't really thought about it that way...) I guess it was my fault for not being a little more clear in my first message. The problem isn't whether it's too long or not, the problem is: It just doesn't work. I can not (for the life of me) get the alarm interrupt (4Ah) to respond. It just doesn't seem to work with the PS/2, but a PS/2 specific book clearly states that there are NO differences between the AT and PS/2 interrupts here. Perhaps I am setting it wrong, but I don't see how that's possible. I use function 6 of interrupt 1Ah and set it accordingly. I've even reset the time to 0:00:00.00, and programmed the alarm time for 0:00:15.00 by using hex or decimal. Still, it just will not seem to "go off". Ideas? Opinions? P.S. Thanks for the mouse unit. Now I just need to incorporate it into the program. Josh (Confused...)