First of all, a few words on interrupts.
Interrupts are small programs set up by the BIOS or some other stay - resident programs. They are somewhere in the memory, so somewhere it is needed to save their position so that they can be called. The first 1 Kb of the conventional memory is for serving this: it stores 4 byte segment:offset values where these programs start. This is called interrupt vector table.
When a program is called it usually needs some input values to work with, and some output values what the caller can process later. This is served through the CPU's built - in memory: the registers.
Calling an interrupt has two steps: first setting the registers to the values needed by the program, then calling the interrupt (what only means jumping to the start offset of it in the memory). After the program finishes it's work, it usually return values in certain registers what can be processed by ours.
QBasic can do all of this safely with it's basic library, it may be only needed to include the "qb.bi" file to have the type declarations of registers and the declaration of the interrupt calling subs.
So here is that little mouse program what can use all of the features included in the Microsoft mouse driver 1.0:
Code:
DEFINT A-Z
'$INCLUDE: 'qb.bi'
DECLARE FUNCTION initmouse () 'Returns 1 if mouse is connected
DECLARE SUB showcursor () 'Shows built in cursor
DECLARE SUB hidecursor () 'Hides built in cursor
DECLARE FUNCTION getbutton1 () 'Returns 1 if left button is down
DECLARE FUNCTION getbutton2 () 'Returns 1 if right button is down
DECLARE FUNCTION getx () 'Gets mouse X position
DECLARE FUNCTION gety () 'Gets mouse Y position
DECLARE SUB poscursor (x AS INTEGER, y AS INTEGER) 'Positions cursor
DECLARE SUB limitcursor (xmin AS INTEGER, xmax AS INTEGER, ymin AS INTEGER, ymax AS INTEGER)
'Limits cursor movement
SCREEN 9
PRINT initmouse
IF initmouse = 0 THEN SYSTEM
showcursor
a$ = ""
DO
LOCATE 1, 1
PRINT getbutton1
PRINT getbutton2
PRINT getx
PRINT gety
a$ = INKEY$
LOOP UNTIL a$ = CHR$(27)
hidecursor
FUNCTION getbutton1
DIM regs AS regtype
regs.ax = &H3
CALL INTERRUPT(&H33, regs, regs)
IF (regs.bx AND 1) = 1 THEN getbutton1 = 1 ELSE getbutton1 = 0
END FUNCTION
FUNCTION getbutton2
DIM regs AS regtype
regs.ax = &H3
CALL INTERRUPT(&H33, regs, regs)
IF (regs.bx AND 2) = 2 THEN getbutton2 = 1 ELSE getbutton2 = 0
END FUNCTION
FUNCTION getx
DIM regs AS regtype
regs.ax = &H3
CALL INTERRUPT(&H33, regs, regs)
getx = regs.cx
END FUNCTION
FUNCTION gety
DIM regs AS regtype
regs.ax = &H3
CALL INTERRUPT(&H33, regs, regs)
gety = regs.dx
END FUNCTION
SUB hidecursor
DIM regs AS regtype
regs.ax = &H2
CALL INTERRUPT(&H33, regs, regs)
END SUB
FUNCTION initmouse
DIM regs AS regtype
DEF SEG = &H0
a = 0
FOR i = 0 TO 3
a = a + PEEK(&H33 * 4 + i)
NEXT i
DEF SEG
IF a > 0 THEN
regs.ax = &H0
CALL INTERRUPT(&H33, regs, regs)
IF regs.ax = &HFFFF THEN initmouse = 1 ELSE initmouse = 0
ELSE
initmouse = 0
END IF
END FUNCTION
SUB limitcursor (xmin AS INTEGER, xmax AS INTEGER, ymin AS INTEGER, ymax AS INTEGER)
DIM regs AS regtype
regs.ax = &H7
regs.cx = xmin
regs.dx = xmax
CALL INTERRUPT(&H33, regs, regs)
regs.ax = &H8
regs.cx = ymin
regs.dx = ymax
CALL INTERRUPT(&H33, regs, regs)
END SUB
SUB poscursor (x AS INTEGER, y AS INTEGER)
DIM regs AS regtype
regs.ax = &H4
regs.cx = x
regs.dx = y
CALL INTERRUPT(&H33, regs, regs)
END SUB
SUB showcursor
DIM regs AS regtype
regs.ax = &H1
CALL INTERRUPT(&H33, regs, regs)
END SUB
You can see here that all functions start with setting certain registers to the needed values (to select which function of the driver want we to use), then calling the interrupt, and finally processing the returned values.
In Initmouse the program first checks if there is something at the memory location of interrupt 33 because if not, calling it will jump to the start of the memory (0000:0000) what results freezing.