programming | html tutorial | articles

up ]

Contents

The source codes in this page was generated by Borland Turbo C version (2.0) compiler. It may be use as well with other compiler released by Borland (Dos Version) or any compiler similar to Turbo C. Well, if you still want your codes to work with your compiler, try looking for similar routine/function in your compiler for substitution. Anyway, the purpose of this page is to give you the concept of mouse programming not just the codes. If you understand the concept, you can modify the source to suit your compiler.

Introduction to Mouse Programming
There are many ways to access the mouse. Either using tool that are already equipped with mouse routine or communicating with mouse driver or programming directly with the mouse. What this article is about is using a method, which deals with mouse with the existence of the mouse driver. Mouse driver is a device driver that assists the OS in determining what type of mouse is available to the OS.

Note: through out this article. The numbers which is prefixes by 0x is a base 16 number (hexadecimal).

What is Interrupts and How to Use Them?

WHAT ARE INTERRUPTS?

Interrupts are special functions, which is built in the BIOS (basic input output system). There are nearly hundreds of interrupt in the BIOS and we are dealing with Interrupt 0x33 (mouse functions). Whenever an interrupt is called, any task, which is in progress, will be interrupted to execute the requested task. This interrupt is called by setting the appropriate register value, call the interrupt and output value will be set to the same register. Either the programmer can look at the output value in the specified register to determine the called interrupt is performed perfectly or error has occurred.

WHAT ARE REGISTERS?

Register is a special memory placed in the microprocessor chip, i.e. the 8086, 80186, 80286, 80386, 80486 or Pentium® Chip for faster calculation. This memory location is used during calculation and other stuff. There are several kind of register in the chip and what I am listing here is just the information enough for us to get to mouse programming.

The Intel® 80x86 family and Pentium® has the following registers in their chips.

  • General-purpose register: AX, BX, CX, DX which will be used widely in our program.
  • AX (16 bits) is a combination of AH and AL (H-higher 8 bits, L-lower 8 bits). This combination is also applicable to BX, CX, DX register.
8 bits 8 bits Names
AH AL Accumulator
BH BL Base
CH CL Counter
DH DL Data
  • Segment Register which in our consideration
16 bits Names
ES Extra Segment
CS Code Segment
SS Stack Segment
DS Data Segment
  • Pointer Registers
16 bits Names
SI Source Index
DI Destination Index

CALLING INTERRUPT

To access the interrupts, we need special function or routine, which can access the microprocessor register and interrupts as well. For example, In Borland Turbo C (TC), there are special routines that deals with interrupts, the header file for this library routine is <dos.h>. i.e you have to #include<dos.h>

int86(int intrptnum, struct REGS *regs, struct REGS *regs);
int86x(int intrptnum, struct REGS *inregs, struct REGS *outregs, struct SSREGS *sregs);

Accessing General Purpose Register and Segment Register in C (Unions & Structures)

General Purpose Register

	union REGS {
	  struct  WORDREGS  x;
	  struct  BYTEREGS  h;
	};

	Structures: BYTEREGS & WORDREGS Structures for storing 
	byte and word registers.

	struct  BYTEREGS  {
	  unsigned char  al, ah, bl, bh;
	  unsigned char  cl, ch, dl, dh;
	};

	struct  WORDREGS  {
	  unsigned int  ax, bx, cx, dx;
	  unsigned int  si, di, cflag, flags;
	};

Segment Register

	struct SREGS {
	  unsigned int  es;
	  unsigned int  cs;
	  unsigned int  ss;
	  unsigned int  ds;
	};

For example if we want to to use interrupt 0x33, 0x00, which will check the existance of mouse driver. The code is


/*****************************************************
*** this code will check the existance of mouse driver
*****************************************************/
int check_mouse(void)
{
	union REGS inregs={0}
	union REGS outregs={0};	         /* declaring regs unions */
	regs.x.ax=0x00;	                     /* set the value in reg.x.ax :input*/
	int86(0x33,&inregs,&outregs);	/* call the interrupt 0x33 */
	return outregs.x.ax;	             /* returning 0x0000 or 0xFFFF :output*/
}

 After the interrupt is called, the returned value will be in outregs. For example, If the mouse is NOT installed, the AX value in regs will now be 0xFFFF. We can also use the same variables for input and output.

  • int86(0x33,&regs,&regs);

The value of output will be send to regs.

In above code, we are using int86 function because the register, which is under consideration, is from AX to DX. There are certain cases when we need to access other register like ES, DI, and SI. Therefore the second function, int86x, is desireable.

If you're working with other C compiler, you can search the help file and look for a function, which does the similar function, i.e. calling interrupt.

FAR & NEAR, SEGMENT:OFFSET thing.

  • Each memory location in the computer RAM is represented by SEGMENT:OFFSET.
  • For example if we declared char buffer[10]. The address of "buffer" can be referred as FP_SEG(buffer):FP_OFF(buffer).
  • FP_SEG is a macro that returns the segment address, while FP_OFF is a macro that return the offset address.
  • This representation is widely use when we want to refer a location of memory location to an interrupt. Look at the example below.

	/*******************************
	*** set graphic cursor.
	*** cursor is 64 byte array.
	*** 0-31 screen mask.
	*** 32-63 cursor mask.
	*** >> using int 33,0x09
	********************************/
	void mouse_set_grphcur(void *cursor)	/* custom cursor*/
	{	
	union REGS regs={0};		/* using AX,BX,CX,DX */
	struct SREGS sregs={0};	/* using ES */
	
		regs.x.ax=0x09;
		regs.x.bx=0;		/* active horiz pixel	 	*/
		regs.x.cx=0;		/* active vert pixel 		*/
		regs.x.dx=FP_OFF(cursor);	/* offset address of cursor 	*/
		sregs.es=FP_SEG(cursor);	/* segment address of cursor 	*/
		int86x(0x33,&regs,&regs,&sregs);
	}

The DX register needs to be the offset of the cursor address and ES register needs to the segment of the cursor address in order for the function to work.

Interrupt 0x33 Listings
Click here

Mouse Control & User Interrupt
To gain control of the mouse, we can access the mouse using 2 ways. This can be accessed using loops or user defined software interrupt.
  • Looping

    Using loop is the simplest way and easiest to understand method. The method is to create a loop that will continue seeking cursor location, mouse button status. The cursor location can be read through interrupt (0x33,0x03) (get mouse location), interrupt (0x33,0x05) (mouse pressed ) and interrupt (0x33,0x06)(mouse release). When certain location or certain button operation is found (using if statement of switch statement), defined operation can be called. This method has the disadvantages, we cannot do anything while in the loop. It means that when you are at mouse control, no other control can be accessed for example, the keyboard. You will end up in a loop just controlling the mouse.

    There are certain ways to overcome this problem. By using kbhit, we can detect when a key is pressed and therefore quit the loop. But then we have to return control to the mouse. Kinda messy. That's why second method is preferable.

  • User defined software interrupt

    Using user-defined software interrupt is a bit complicated. A user defined software interrupt is a routine/function, which will be called ONLY if certain condition is satisfied. For example, if the user-defined software interrupt is set (by using interrupt 0x33, 0x0C) to be called when the mouse is moved. Therefore, whenever the mouse is moved, the routine will be called i.e. any other operation is paused for a while. In my example, the routine/user-defined software interrupt is called mouse_handler.

    user interrupt mask:

    
          ¦ F-5 ¦ 4 ¦ 3 ¦ 2 ¦ 1 ¦ 0 ¦ user interrupt mask in CX
              ¦     ¦    ¦    ¦   ¦    +--- cursor position changed
              ¦     ¦    ¦    ¦  +---- left button pressed
              ¦     ¦    ¦   +----- left button released
              ¦     ¦    +------ right button pressed
              ¦    +------- right button released
             +--------- unused
  • Create the mouse_handler. (the routine which will be called whenever events in the user interrupt mask occurs)
  • Set user defined software interrupt using Interrupt (0x33, 0x0C).
    • Set masking event which will set the mouse_handler to be called.
    • Passing the mouse_handler segment and offset.

When a user interrupt routine is called, number of parameter is passed through the user-defined software interrupt. The parameters which contain horiz position, vertical position, button status, event occurred, mickey position, are stored in the REGISTER every time the user-defined software interrupt is called.


	AX = condition mask causing call
       	CX = horizontal cursor position
       	DX = vertical cursor position
       	DI = horizontal counts
       	SI = vertical counts
       	DS = mouse driver data segment
       	BX = button state:

          ¦F-2¦1¦0¦
            ¦  ¦ +--- left button (1 = pressed)
            ¦  +---- right button (1 = pressed)
            +------ unused

To access the current register status, pseduo_register can be used. If you are using other C compiler, search the help file for register reading function/routine. For example.


/*************************************
*** mouse handler interrupt routine . 
*** does not pass or return any value
**************************************/
void far interrupt mouse_handler(void)
{
/* save the current register state */
unsigned int x=_CX, y=_DX, dispx=_DI,dispy=_SI; 
unsigned int bn_state=_BX, event=_AX;

	gotoxy(1,1);
	printf("X axis %u \n",x);
	printf("Y axis %u \n",y);
	printf("Hcount %u \n",dispx);
	printf("Vcount %u \n",dispy);
	printf("State %d \n",bn_state);
	printf("event %x ",event);
}

Note that the interrupt keyword was used because every time the handler was called, the compiler will save the current register value so that it would not be modified by the interrupt routine. For example, the printf statement would modify the current REGISTER value.

If the method of using user-defined software interrupt  is not available (see section below). We can archive multitasking by setting up a multitasking environment.

Problems Might Arise
For the mouse to be use with graphic driver, I discovered that the program should be compiled with LARGE memory model may be because of the graphics library is compiled on LARGE memory model. If the program doesn't work with graphics capabilities, MEDIUM memory model is satisfied.

When you work with software interrupt and if there were bugs in your routine, It would be more likely your computer will stop working. So, prepare yourself with these situations. Save your file before executing the codes.

The user must remember that the program communicates with software driver. The software driver is the one who sets up interrupt 0x33. There are certain cases where the interrupt 0x33, 0C is not supplied by the mouse driver. I have tested these codes with Win95 mouse driver and Microsoft mouse driver, it works perfectly.

However, there are certain cases where the mouse driver does not support int 0x33, function 0C.

There are certain condition when you want to set the bit in a word or byte. Therefore, I have included few routines, which deals with bits. Take a look in the C library.

SAMPLE MOUSE ROUTINE
Click here to get the sample mouse library routine
 

c programming | html tutorial | articles | home
about me | links | search | guestbook | photo album
Hosted by www.Geocities.ws

1