Path: utzoo!utgpu!news-server.csri.toronto.edu!cs.utexas.edu!usc!elroy.jpl.nasa.gov!ncar!gatech!mcnc!uvaarpa!haven!adm!news From: microsoft!richardw@uunet.uu.net Newsgroups: comp.lang.pascal Subject: Re: Interrupts on PS/2 Model 50 Message-ID: <25401@adm.brl.mil> Date: 4 Jan 91 21:30:23 GMT Sender: news@adm.brl.mil Lines: 334 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.