Skip to content

PS/2 Mouse

The PS/2 mouse shares the exact same 8042 controller the PS/2 keyboard already covers, but through the controller’s second, auxiliary channel, and speaks a completely different packet-based protocol that has to be explicitly enabled before the device reports anything at all.

The 8042 controller multiplexes two device ports behind the same pair of I/O ports (0x60 for data, 0x64 for commands), and the second one, conventionally wired to the mouse, is commonly left disabled until software explicitly turns it on: command 0xA8 enables it, and command 0x20 (read configuration byte) followed by 0x60 (write it back) with the second port’s interrupt-enable bit set is what lets the controller actually raise an interrupt when mouse data arrives, mirroring the same controller-command sequence the keyboard article already describes for the first channel.

mov al, 0xA8 ; enable second PS/2 port
out 0x64, al
mov al, 0x20 ; read configuration byte
out 0x64, al
in al, 0x60
or al, 0x02 ; set bit 1: enable IRQ12 (second-port interrupt)
mov ah, al
mov al, 0x60 ; write configuration byte
out 0x64, al
mov al, ah
out 0x60, al

Writing a byte to the mouse itself, rather than to the controller, additionally requires prefixing the write with command 0xD4 on port 0x64, which tells the controller to route the next data-port byte to the second channel instead of the first; omitting this prefix sends the byte to the keyboard channel instead, a common source of a mouse that silently never responds to what looks like a correctly formatted command.

A mouse defaults to polling mode (reporting data only when explicitly asked) rather than reporting movement on its own, so a driver has to send the Enable Data Reporting command (0xF4), prefixed with 0xD4 as above, to switch it into streaming mode, where the device sends a packet automatically the moment it has new movement or button data rather than waiting to be polled.

mouse_write:
mov al, 0xD4
out 0x64, al ; next byte goes to the mouse, not the keyboard
mov al, 0xF4 ; Enable Data Reporting
out 0x60, al
; expect 0xFA (ACK) back on port 0x60

A well-behaved device acknowledges most commands with 0xFA before acting on them, which a driver should check for rather than assuming every command silently succeeds, since a mouse that never received the enable command at all simply stays quiet rather than reporting an error the driver could otherwise detect directly.

Once streaming, the mouse sends a fixed-size packet, three bytes for a standard mouse or four when a scroll wheel is present, every time it has new data to report, always beginning with a byte whose bit 3 is set, a property a driver can use to resynchronize if a byte gets dropped or corrupted mid-stream.

Byte 0: Y-overflow | X-overflow | Y-sign | X-sign | 1 | middle-btn | right-btn | left-btn
Byte 1: X movement (signed, relative, -256 to 255)
Byte 2: Y movement (signed, relative, -256 to 255)
Byte 3: scroll delta (signed), present only with a scroll-wheel mouse

X and Y movement are each a signed 8-bit relative delta, not an absolute position, with the corresponding sign bit in byte 0 extending that 8-bit value to a full signed integer before a driver adds it to whatever cursor position it’s tracking; the overflow bits in the same byte flag a movement large enough that the 8-bit field couldn’t represent it accurately, a rare but real condition a driver should either clamp or discard rather than silently accepting a wrapped, incorrect delta.

void handle_mouse_packet(uint8_t b0, uint8_t b1, uint8_t b2) {
int dx = b1 - ((b0 << 4) & 0x100); // sign-extend using bit 4 of byte 0
int dy = b2 - ((b0 << 3) & 0x100); // sign-extend using bit 5 of byte 0
cursor_x += dx;
cursor_y -= dy; // Y is typically inverted relative to screen coordinates
}

Because both PS/2 channels raise interrupts on the same controller but on two different IRQ lines (IRQ1 for the keyboard, IRQ12 for the mouse), a driver’s mouse handler has to read exactly the number of bytes one packet requires and no more before returning, tracking packet-boundary state across interrupts the same way a keyboard driver tracks multi-byte scancode sequences, since a handler that reads one byte per interrupt without remembering which byte of the current packet it’s on will misalign every packet after the first dropped or extra byte. A scroll-wheel mouse has to be explicitly negotiated into its four-byte mode through a specific, otherwise-meaningless sequence of sample-rate-set commands (0xF3 with values 200, 100, 80 in sequence, then an identify command confirming the mouse responded by switching modes) rather than simply being detected from the hardware alone, a quirk of how this extension was retrofitted onto a protocol with no dedicated capability-negotiation mechanism of its own.

  1. ^ IBM, Personal System/2 Hardware Interface Technical Reference: the same 8042 controller specification the keyboard article’s own citation covers, including the auxiliary (mouse) channel.
  • PS/2 Keyboard: the 8042 controller, its I/O ports, and the IRQ handling discussion this article’s second channel builds directly on.