问题
在做这个实验的时候,发现作者在文中介绍的鼠标数据信息不太详细,有一部分代码似懂非懂,但又没有找到具体的教程,经查询资料,终于得到解决,现在放上来方便大家。
背景介绍
在自制操作系统中使用的是PS/2鼠标,而一开始我在网上查询到的是USB的4字节信息,虽然读着好像差不多,还是有点不放心,后来发现PS/2鼠标数据包刚好是3字节。
PS/2鼠标介绍
实际上,PS/2鼠标有很多模式,包括Reset,Stream,Remote,Wrap模式,除此之外微软还提供了Intellimouse扩展,但是我们实际只需要关心Stream模式,也就是鼠标正常处于的模式。
下面是一个PS/2鼠标(图中Mouse)插上之后和主机(图中Host)的通信过程
前面初始化部分可以忽略,主要是初始化完成之后,最末尾有个FA,也就是为什么作者在
p
h
r
a
s
e
=
0
phrase=0
phrase=0的时候除去0xfa的原因
if (mdec->phase == 0) {
/* マウスの0xfaを待っている段階 */
if (dat == 0xfa) {
mdec->phase = 1;
}
return 0;
}
在初始化完成之后,要是点击鼠标按键或者移动一下鼠标就能读取数据了,而鼠标的数据格式是什么呢,由于鼠标处于Stream模式,而读取到的三个字节数据包的格式如下。
PS/2鼠标按照一个固定周期来读取鼠标的按键状态和鼠标的上下左右移动的位移,而按键状态储存在第一个数据包(字节)的0到2位中,即鼠标左键按下,第0位Left Btn(左键)为1,同理鼠标右键按下,第2位Right Btn为1。
当
p
h
r
a
s
e
=
3
phrase=3
phrase=3读取鼠标按键信息后
mdec->btn = mdec->buf[0] & 0x07;
HariMain中以下片端就能理解了
if ((mdec.btn & 0x01) != 0) {
s[1] = 'L';
}
if ((mdec.btn & 0x02) != 0) {
s[3] = 'R';
}
if ((mdec.btn & 0x04) != 0) {
s[2] = 'C';
}
而对于移动的位移,PS/2内置有两个位移寄存器,包括X方向和Y方向的位移寄存器(看见一些文档,可能部分鼠标还有Z方向,即滑轮方向,但这里我们不用关心Z方向),而这些寄存器存储的是9位带符号位整数(即9位补码整数,范围[-256,255])。
然而你的每个数据包都是8位的要怎么传输符号位呢?
当然9位不行就把他分成8更小的,这里分成了8位和1位的组合,那么这两个数据放在哪里呢?
对于8位数据,它就可以单独占有一个数据包了,即放在三个数据包中的任意一个就行,我们放在第二个数据包和第三个数据包中,分别对应X方向位移和Y方向位移的低8位。
而对于它们剩下的那1位,即符号位,我们放在第一个数据包的第4位和第5位,分别对应X方向的符号位和Y方向的符号位.
这样的话下面的片段就容易理解了,就是判断是不是位移是不是负的(即符号位为1),如果是负的就在前面24位加上1(因为是int,而int是32位的,而数据只有8位),即加上0xffffff,相当于或一个0xffffff00
if ((mdec->buf[0] & 0x10) != 0) {
mdec->x |= 0xffffff00;
}
if ((mdec->buf[0] & 0x20) != 0) {
mdec->y |= 0xffffff00;
}
还有三位,其中包括X和Y的溢出位,即当一次移动距离过大时,超过寄存器的范围时,就会发生溢出该位设置为1,而对于第三位默认设计为1.
而对于以下片端是因为,计算机是以屏幕左上角为原点的(左手坐标系,笑),而鼠标实际的坐标系是左下角为原点,当为了保持鼠标方向和屏幕移动一致,将Y方向位移反向即可
mdec->y = - mdec->y; /* マウスではy方向の符号が画面と反対 */
参考资料
链接:https://pan.baidu.com/s/1PVMOVm-u_cS3VLpouRtENQ
提取码:u0x5