IntelliMouse 第 4 字节的 Z 符号与“向上 = 正值”的事件约定相反, 在驱动层统一翻符号;下游按正值表示滚轮向上处理,Terminal 的 scroll_offset -= w * 3 无需改动。
189 lines
5.7 KiB
Plaintext
189 lines
5.7 KiB
Plaintext
import asm
|
||
import intr.idt as idt
|
||
import platform.pch.pic as pic
|
||
|
||
|
||
define MOUSE_DATA = 0x60
|
||
define MOUSE_STATUS = 0x64
|
||
define MOUSE_COMMAND = 0x64
|
||
|
||
define MOUSE_BUF_SIZE = 1024
|
||
|
||
define MOUSE_LEFT = 0x01
|
||
define MOUSE_RIGHT = 0x02
|
||
define MOUSE_MIDDLE = 0x04
|
||
|
||
class mouse_packet:
|
||
flags: uint8
|
||
dx: uint8 # 原始幅值字节(符号在 flags.bit4)
|
||
dy: uint8 # 原始幅值字节(符号在 flags.bit5)
|
||
buttons: uint8
|
||
wheel: int8 # IntelliMouse 第 4 字节低 4 位的 Z 增量(3 字节模式恒 0)
|
||
|
||
packets: [MOUSE_BUF_SIZE]mouse_packet
|
||
static pack_head: uint32 = 0
|
||
static pack_tail: uint32 = 0
|
||
static pack_count: uint32 = 0
|
||
static mse_irq_cnt: uint64 = 0
|
||
|
||
static cycle: uint8 = 0
|
||
static byte1: uint8 = 0
|
||
static byte2: uint8 = 0
|
||
static byte3: uint8 = 0
|
||
static byte4: uint8 = 0
|
||
# 0 = 标准 3 字节 PS/2 包;1 = IntelliMouse 4 字节包(第 4 字节带滚轮)
|
||
static wheel_mode: int = 0
|
||
|
||
static mouse_x: int32 = 0
|
||
static mouse_y: int32 = 0
|
||
static mouse_buttons: uint8 = 0
|
||
|
||
def _wait_read():
|
||
timeout: uint32 = 100000
|
||
for _ in range(timeout, 0, -1):
|
||
if (asm.inb(MOUSE_STATUS) & 0x01) != 0: return
|
||
|
||
def _wait_write():
|
||
timeout: uint32 = 100000
|
||
for _ in range(timeout, 0, -1):
|
||
if (asm.inb(MOUSE_STATUS) & 0x02) == 0: return
|
||
|
||
def _write_cmd(cmd: uint8):
|
||
_wait_write()
|
||
asm.outb(MOUSE_COMMAND, 0xD4)
|
||
_wait_write()
|
||
asm.outb(MOUSE_DATA, cmd)
|
||
_wait_read()
|
||
asm.inb(MOUSE_DATA)
|
||
|
||
def _read_data() -> uint8:
|
||
_wait_read()
|
||
return asm.inb(MOUSE_DATA)
|
||
|
||
def irq_handler() -> int:
|
||
global cycle, byte1, byte2, byte3, byte4, pack_tail, pack_count, mse_irq_cnt
|
||
mse_irq_cnt += 1
|
||
status: uint8 = asm.inb(MOUSE_STATUS)
|
||
if (status & 0x20) == 0: return 0
|
||
data: uint8 = asm.inb(MOUSE_DATA)
|
||
if cycle == 0:
|
||
if (data & 0x08) != 0:
|
||
byte1 = data
|
||
cycle = 1
|
||
return 0
|
||
if cycle == 1:
|
||
byte2 = data
|
||
cycle = 2
|
||
return 0
|
||
if cycle == 2:
|
||
byte3 = data
|
||
if wheel_mode != 0:
|
||
cycle = 3
|
||
return 0
|
||
cycle = 0
|
||
else:
|
||
byte4 = data
|
||
cycle = 0
|
||
# 丢弃带溢出位的包(bit6/bit7 = X/Y overflow):此时设备给的是饱和值,
|
||
# 直接采用会产生 ±127 级跳变(表现为光标“飞”)。
|
||
if (byte1 & 0xC0) != 0:
|
||
return 0
|
||
# IntelliMouse 第 4 字节低 4 位是有符号 Z 增量(4-bit two's complement)。
|
||
# 实测该 Z 轴符号与“向上 = 正值”的事件约定相反,这里统一翻符号:
|
||
# 下游(Explorer/应用)按正值表示滚轮向上处理。
|
||
w: int = 0
|
||
if wheel_mode != 0:
|
||
w = int(byte4 & uint8(0x0F))
|
||
if w >= 8: w = w - 16
|
||
w = 0 - w
|
||
if pack_count < MOUSE_BUF_SIZE:
|
||
packets[pack_tail].flags = byte1
|
||
packets[pack_tail].dx = byte2
|
||
packets[pack_tail].dy = byte3
|
||
packets[pack_tail].buttons = byte1 & uint8(0x07)
|
||
packets[pack_tail].wheel = int8(w)
|
||
pack_tail = pack_tail + 1
|
||
if pack_tail >= MOUSE_BUF_SIZE:
|
||
pack_tail = 0
|
||
pack_count += 1
|
||
return 0
|
||
|
||
def read_packet(out: mouse_packet*) -> int:
|
||
global pack_head, pack_count
|
||
asm.cli()
|
||
if pack_count == 0:
|
||
asm.sti()
|
||
out.flags = 0
|
||
out.dx = 0
|
||
out.dy = 0
|
||
out.buttons = 0
|
||
out.wheel = 0
|
||
return -1
|
||
out.flags = packets[pack_head].flags
|
||
out.dx = packets[pack_head].dx
|
||
out.dy = packets[pack_head].dy
|
||
out.buttons = packets[pack_head].buttons
|
||
out.wheel = packets[pack_head].wheel
|
||
pack_head = pack_head + 1
|
||
if pack_head >= MOUSE_BUF_SIZE:
|
||
pack_head = 0
|
||
pack_count -= 1
|
||
asm.sti()
|
||
return 0
|
||
|
||
def has_data() -> int:
|
||
return pack_count
|
||
|
||
def get_x() -> int32:
|
||
return mouse_x
|
||
|
||
def get_y() -> int32:
|
||
return mouse_y
|
||
|
||
def get_buttons() -> uint8:
|
||
return mouse_buttons
|
||
|
||
def init():
|
||
global wheel_mode
|
||
wheel_mode = 0
|
||
_wait_write()
|
||
asm.outb(MOUSE_COMMAND, 0xA8)
|
||
# 读-改-写 CCB 期间屏蔽 IRQ1:与 keyboard.init 同理 —— 0x20 读 CCB 会把 CCB 值放进
|
||
# 0x60 输出缓冲并拉高 IRQ1,键盘 ISR 一旦运行就会把它当扫描码抢走(现场:sc=0x47
|
||
# 正是这里清 bit5 后的 CCB),于是下面的 inb(0x60) 只能拿到残留值 ⇒ 可能把错误 CCB 写回。
|
||
pic.setMask(1)
|
||
_wait_write()
|
||
asm.outb(MOUSE_COMMAND, 0x20)
|
||
_wait_read()
|
||
status: uint8 = asm.inb(MOUSE_DATA)
|
||
status = status | 0x02
|
||
status = status & 0xDF
|
||
_wait_write()
|
||
asm.outb(MOUSE_COMMAND, 0x60)
|
||
_wait_write()
|
||
asm.outb(MOUSE_DATA, status)
|
||
pic.clearMask(1)
|
||
_write_cmd(0xF6)
|
||
# IntelliMouse 魔数:采样率 200 → 100 → 80。成功后设备 ID 变成 3(滚轮)
|
||
# 或 4(滚轮 + 侧键),之后每个包变成 4 字节(第 4 字节低 4 位是 Z 增量)。
|
||
_write_cmd(0xF3)
|
||
_write_cmd(200)
|
||
_write_cmd(0xF3)
|
||
_write_cmd(100)
|
||
_write_cmd(0xF3)
|
||
_write_cmd(80)
|
||
_write_cmd(0xF2)
|
||
dev_id: uint8 = _read_data()
|
||
if dev_id == uint8(3) or dev_id == uint8(4):
|
||
wheel_mode = 1
|
||
# 魔数序列会把采样率停在 80;恢复正常采样率 100 后再设分辨率/使能。
|
||
_write_cmd(0xF3)
|
||
_write_cmd(100)
|
||
# 分辨率设为 8 counts/mm:0xF6(Set Defaults) 会把它重置为最粗的 4 counts/mm,
|
||
# 于是每个计数对应的位移偏大。设细一档后光标更稳、不易“飞”。
|
||
_write_cmd(0xE8)
|
||
_write_cmd(0x03)
|
||
_write_cmd(0xF4)
|
||
pic.clearMask(12)
|
||
idt.irqInstallHandler(12, irq_handler, "mouse")
|