冲区.0 表示输出缓冲区空,1 表示输出缓冲区已满.所以在向$60 端口写入数据之前要检查 该标志位是否已被置 0.另外在向$64 端口发送命令之前也要检查该标志位,已确保上次的操作已经完成. 向指定端口写入数据的程序如下,其中使用了内嵌汇编对端口进行操作.注意:鼠标键盘是慢速设备,每 次操作时要有一定的延时: procedure SetByte(Por,Cod : Byte); begin Sleep(1); asm PUSH EAX PUSH EDX //等待状态寄存器标志位 OBF 置 0 @Loop: IN AL,$64 AND AL,01b JNZ @Loop //写入数据 MOV AL,Cod MOV DL,Por MOV DH,0 OUT DX,AL POP EDX POP EAX end; end; 发送命令给 i8042 就是写$64 端口.在命令发送后,命令参数写到$60 端口.命令中用来模拟鼠标键盘 操作的有两条(其它命令请参考 PS/2 协议):
$D2:写键盘缓冲区,把参数写到输入缓冲区就像从键盘接收到的一样. $D3:写鼠标缓冲区,把参数写到输入缓冲区就像从鼠标接收到的一样. 例如:按下"A"键,利用上面的程序可以写成"SetByte($64,$D2); SetByte($60,$1E);".注意: 如果向$60 端口发送的数据不只 1 个字节,那么发送的每个字节前都要发送一条命令,例如:按下"Inser t"键的程序为"SetByte($64,$D2); SetByte($60,$E0); SetByte($64,$D2); SetByte($60,$52);",要 调用 SetByte 四次.知道了如何向 i8042 发送命令,下面就可以具体的模拟鼠标键盘. 三,键盘模拟 键盘的处理器会扫描或监视按键矩阵.如果它发现有键被按下,释放或按住,键盘将发送"扫描码" 的数据包到 i8042.扫描码有两种不同的类型:"通码"和"断码",当一个键被按下或按住就发送通码; 当一个键被释放就发送断码.每个按键被分配了唯一的通码和断码
,这样 i8042 通过查找唯一的扫描码就 可以测定是哪个按键.每个键一整套的通断码组成了"扫描码集".有三套标准的扫描码集分别是第一套, 第二套和第三套.i8042 缺省支持第一套扫描码. 一部份键的断码是将通码的最高位置 1,但并不是所有的键都这样,而且很多键的扫描码不只有 1 个 字节.所以没有一个简单的公式可以计算扫描码.如果你要知道某特定按键的通码和断码,你将不得不查 表获得.例如:"A"键的通码为$1E,断码为$9E,"Insert"键的通码为$E0,$52,断码为$E0,$D2,模 拟按键的程序如下: //按下并放开"A"键 SetByte($64,$D2); SetByte($60,$1E); SetByte($64,$D2); SetByte($60,$9E); //按下并放开"Insert"键 SetByte($64,$D2); SetByte($60,$E0); SetByte($64,$D2); SetByte($60,$52); SetByte($64,$D2); SetByte($60,$E0); SetByte($64,$D2); SetByte($60,$D2); 特别的在 PS/2 协议中说在第一第二套扫描码里没有"Pause/Break"键的断码.当这个键按下时发送 它的通码,当它释放时,什么都没有被发送.在第一套扫描码里 Pause 键的通码长达 6 个字节:$E1,$1D, $45,$E1,$9D,$C5.但是我在实际测试中发现 Pause 键的通码其实是前 3 个字节:$E1,$1D,$45,后 3 个字节$E1,$9D,$C5 是 Pause 键的断码.至少在我的键盘上是这样. 在 PS/2 协议中已经把所有三套扫描码集中所有的通码和断码做成了表格, 具体的内容可以查阅相关的 部份.在单元文件 MouseKeyboard.pas 中我已经将第一套键盘扫描码定义成常量数组,其中还包括了键对 应的字符.单元中有两个函数 MKFindKeyCode 和 MKFindKeyChar,可以用来对常量数组进行查找. 四,鼠标模拟 标准的 PS/2 鼠标支持下面的输入:X(左右)位移,Y(上下)位移,左键,中键和右键.鼠标以一个 固定的频率读取这些输入并更新不同的计数器然后标记出反映的移动和按键状态. 有很多 PS/2 鼠标具有额 外的输入比如微软的 Intellimouse,它既支持标准输入也支持滚轮和两个附加的按键.
标准的鼠标有两个计数器保持位移的跟踪:X 位移计数器和 Y 位移计数器.可存放 9 位的 2 进制补码 并且每个计数器都有相关的溢出标志.它们的内容连同三