Programming the Microsoft Mouse ------------------------------- "A Mouse! What A Great Idea!!" -W. Disney Written for the PC-GPE by bri (accbpf@vaxc.hofstra.edu) and Mark Feldman (pcgpe@geocities.com) Disclaimer ---------- We 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 ------------ Programming the mouse is one of the easiest tasks you'll ever have to undertake. Mouse drivers hook themselves into interrupt 33h, so you can just call driver-functions the same way you would BIOS functions. Basics ------ The first step is to initialize the mouse driver. You do this by setting the ax register to 0 and calling interrupt 33h. This will return a non-zero number in ax if the driver is installed(which usually means if the mouse is installed. From here on in, anything I say about the mouse actually refers to the driver). To display the mouse cursor on the screen, set ax to 1 before calling int 33h. In text-mode you should get what appears to be a block-like text cursor, and in any graphics mode you should get the arrow by default(although we'll see how to change this later). The driver detects what mode you're in and draws the appropriate cursor. To hide the mouse cursor, you set the ax register to 2 and call the interrupt. Showing and hiding the mouse cursor is something you'll probably have to do often when you draw images to the screen. Believe you me, having a mouse move across something you're drawing can really wreck you're display. To get around this, hide the mouse cursor, draw what's necessary, and re-display the mouse cursor. Please note, mouse drivers often keep a 'display counter' that reads 0 if the cursor is displayed and less then 0 if its hidden. Consecutive calls to hide the mouse cursor will decrement the counter and make it more and more negative-in which case it will take more calls to display the mouse cursor to get the counter to 0. Once the counter is 0, calls to display the mouse cursor have no effect on the counter. To read the state of the counter, you can call function 2Ah, and the counter is returned in the ax register. I'll touch on function 2Ah a bit more later. Last but not least, we should be able to figure out if any buttons are pressed, if so which ones, and where the mouse is. This is easy, just set ax to 3 and call int 33h-the horizontal coordinate is returned in the cx register, the vertical coordinate in the dx register and bx has the button status. In bx, each bit reads 1 if a corresponding button is pressed-for example bit 0 is 1 if the left button is pressed, bit 1 is 1 if the right button is pressed, and bit 2 is 1 if the center button is pressed. As for the coordinates-be careful, as a lot of mouse drivers use a "virtual screen" of 640x200 pixels-which mean if you're screen resolution isn't this- you'll have to do some converting. SETTING THE CURSOR SHAPE ------------------------ When you use your mouse in a graphics mode, by default, you're stuck with the shape of the mouse cursor as an arrow. This is fine most of the time, but it can get boring after a while. Don't fret!!! You can alter the shape to accomodate your needs! The graphics mode cursor image is a 16-by-16 pixel area on the screen that is defined by a 64 byte buffer passed to int 33h, function 9. The first 32 bytes contain the cursor mask-the appearance of the cursor on the screen. The second 32 bytes is the screen mask-it defines the appearance of the screen image under the cursor. In this 64 byte buffer, each bit corresponds to 1 pixel, i.e. the first two bytes in both of the masks corresponds to the 16 pixels that make up the top row of the cursor. When you're designing the cursor mask, each bit is 1 if it is displayed, and 0 if it is not. On the screen mask, bits that are 1 are transparent. The mouse driver takes the screen mask, and the cursor mask and shoves them together, coming up with the following: Screen Mask Bit is Cursor Mask Bit is Resulting Screen Bit is ------------------ ------------------ ----------------------- 0 0 0 0 1 1 1 0 Bit is Not Changed 1 1 Bit is Inverted To set the shape you call Function 9h with ES holding the segment of your buffer containing the masks, and DX containing the offset of the buffer containing the cursor mask. One other important thing to note: your cursor has a hot spot=the point on your mouse cursor that is where the cursor is actually pointing. Usually this is 1, 1(that is 1 pixel from the top of the cursor and 1 from the right). But when you change the image of your mouse cursor you might need to change the hot spot. You can do this by setting CX and DX of Function 9 to the horizontal and vertical offset of the hot spot. ODDS AND ENDS ------------- Well, there are a few odds and ends here that you might find useful. You can set limits on where you want to allow the mouse cursor to roam. Function 7 & 8(AX= 7 & AX = 8) set the horizontal and vertical limits of the mouse cursor respectively. In both cases you input cx as the minimum coordinate in pixels, and dx as the maximum coordinate in pixels. You can take the mouse and move it to a certain position on the screen from inside your program. That's function 4(ax=4). You specify the horizontal coordinate in CX, and the vertical coordinate in DX. If you specify a coordinate outside a range you've set using functions 7 & 8, the mouse driver will most likely put the cursor at the very edge of that boundary. Lastly, you can set the amount of distance you're actual mouse moves to get the cursor on the screen to move. Mouse movement is measured in mickeys(I'm not joking here!) where each mickey is approximately 1/200 of an inch. To adjust this use function 0Fh. CX should contain the number of horizontal mickeys, and dx the number of vertical ones. The numbers in CX and DX actually refer to the amount of mickeys needed to move the mouse cursor a total of 8 pixels. By default, CX is 8, while DX is 16. You can set a range of 1(hyper-active energetic mouse) to 32,767(unbelievably sluggish and lazy). SLAM DUNKS AND LOW CEILINGS ---------------------------- (With special thanks to Feldman the Great for this section) Yes, like the subject header, something else that has never mixed very well together was mouse and SVGA programming. The reason, of course is that your mouse driver is what takes care of the updating of the image of the mouse cursor in graphics mode. You see, in the beginning, SVGA was created. This was widely regarded as a bad idea. Sure it looked cool, and there were more pretty colors than you could shake a kaleidoscope at, but there was no standard. (This was before VESA, and sadly even today many mouse drivers don't use VESA) This left all SVGA chip makers to deviously make chips however they wanted, depending on what kind of mood they were in, and what they had had for lunch. As most SVGA chip designers rarely ate the same thing at lunchtime, you wound up with a googleplex full of SVGA cards that all were 110% incompatible with each other. Remember what I said about your mouse driver handling your mouse cursor. Now mouse drivers had to know how to handle every single SVGA card, so they could draw the cursors correctly in SVGA mode. Mouse driver maufacturers got around this problem in a rather novel way: they ignored SVGA. Because of this, most mouse drivers throw up their hands in disgust when confronted with the ugly head of SVGA and simply provide you with no mouse cursor at all. Likewise, if you're programming for Mode X, you're likely to run into the same trouble. You CAN get around this, however. How? you ask with baited breath. Simply install your own mouse handler(I'm using the word 'simply' rather loosely here). What this will do, is cause the mouse driver to call one of your functions-and then you're responsible for updating the graphics cursor image on the screen. Basically, you call Function 0Ch. CX contains the event mask: on what conditions the mouse driver will call your function. The mask is listed below: Bit If set 0 Mouse Cursor Movement 1 Left Button Pressed 2 Left Button Released 3 Right Button Pressed 4 Right Button Released 5 Center Button Pressed 6 Center Button Released The ES register holds the segment of your mouse code that the driver should call, and DX holds the offset. (As an aside, I'd recommend doing the mouse handler itself in assembly, as getting it to work in C or Pascal is an uphill struggle at best). When the mouse driver calls your function, AX will contain the event flag that you set earlier. BX will contain the button status: 0 if the left button is pressed, 1 if the right button is pressed, and 2 if the center button is pressed. CX and DX contain the horizontal and vertical position of the mouse cursor respectively. To disable an installed handler, simply call function 0Ch with an event mask of 0, or call Function 0h. Well that about wraps it up...if you have any questions at all, please feel free to contact me (bri) at accbpf@vaxc.hofstra.edu and I'll do my best to answer them. Quick Reference Guide to Interrupt 33h -------------------------------------- FUNCTION: AX = 0h Description: Determines whether a mouse is available and if it is initializes t...
Teemu1.huusko