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")