INT 33h AX=000Ch replaces polling with callbacks. The program gives the driver the far address of a routine and a mask of events; from then on, each time one of those events happens, the driver calls the routine from inside its own interrupt handler, while the main program is interrupted wherever it is.
| Mask bit | Event |
|---|---|
| 0 | the pointer moved |
| 1 / 2 | left button pressed / released |
| 3 / 4 | right button pressed / released |
| 5 / 6 | middle button pressed / released |
The handler is called with a far CALL and must return with RETF, not IRET. On entry AX holds the bits of the events that happened (same layout as the mask), BX the button state, CX and DX the position, and SI and DI the raw mickey counts. DS is whatever the driver had, so the handler reaches its own data through CS (in a .COM program CS = DS of the program) or loads DS itself and restores it. It runs at interrupt time: keep it short, do not call DOS, and usually just record the event for the main loop to pick up.
XOR AX, AX ; reset (and detect) firstINT 33hPUSH CSPOP ES ; ES:DX = the handlerMOV DX, OFFSET EVENT1MOV CX, 0006h ; call on: left press (bit 1), left release (bit 2)MOV AX, 000Ch ; mouse: install the event handlerINT 33hMOV DX, OFFSET ON1MOV AH, 09h ; DOS: print the $-string at DS:DXINT 21h; ... the program runs; the driver calls EVENT1 on each left click ...MOV AX, 000Ch ; remove it again: an empty maskXOR CX, CXINT 33hMOV DX, OFFSET OFF1MOV AH, 09h ; DOS: print the $-string at DS:DXINT 21hEVENT1 PROC FAR ; called by the driver: AX = events, BX = buttons, CX/DX = positionINC CS:CLICKS1 ; DS is not ours here: go through CSRET ; a far return (RETF), not IRETEVENT1 ENDPCLICKS1 DW 0ON1 DB "handler installed", 13, 10, "$"OFF1 DB "handler removed", 13, 10, "$"
Pitfalls
The driver stays resident after the program exits; a handler left installed is called in memory that now belongs to someone else, and the next mouse movement crashes the machine. Remove it (CX = 0) or reset the driver (INT 33h AX=0000h clears the handler) before exiting, including on error paths and Ctrl+C. AX=0014h swaps in a new handler and returns the old one, so a program can chain to a previous handler and restore it at the end. The handler interrupts the main program at any instruction, so data shared between them must be updated in a way the main loop cannot see half-done: a single word, or with CLI around the read.