[Rx86OS-IX] 解读鼠标数据 移动鼠标
平台
处理器:Intel Celeron(R) Dual-Core CPU
操作系统:Windows7 专业版 x86
阅读书籍:《30天自制操作系统》—川合秀实[2015.03.23 ]
将《30天自制操作系统》简称为“书”。对于书中的工具,可以专门对其笔记学习。
工具:../toolset/
1 解读鼠标数据
1.1 鼠标3字节数据含义
每当鼠标中断发生时,鼠标会向鼠标控制电路回复3字节数据,CPU通过IN指令从鼠标电路设备端口中读取这些数据。这3字节的含义分别为:
- 第一字节的低3位表鼠标键状态,bit[0]为1表鼠标左击,bit[1]为1表鼠标右击,bit[2]为1表鼠标点击滑轮;(低4位只在点击鼠标(左中右)时才会发生变化,在8~ F范围内变化,鼠标第一字节的高4位在0~3范围内变化,具体含义见解读鼠标数据的程序);
- 鼠标的第二字节与鼠标左右移动有关系;
- 鼠标第三个字节与鼠标上下移动有关系。
判断第一字节高4位是否在0~ 3的范围内,同时判断第一字节低4位是否在8~ F的范围内,这样就可以避免鼠标接触不良产生数据丢失时对错位数据的解析。
1.2 解读鼠标数据
(1) 描述鼠标数据的结构体
struct MOUSE_DEC {unsigned char buf[3], phase; //鼠标3字节数据,用于读取鼠标3字节变量int x, y, btn; //(x, y)用于存解读到的移动信息,btn存点击信息
};
(2) 读取缓冲区3字节数据解析显示
/*获取鼠标3字节数据,读到3字节数据时解读它们
*mdec存储鼠标3字节数据和解读结果
*dat表示从缓冲区读来的3字节数据
*/
int mouse_decode(struct MOUSE_DEC *mdec, unsigned char dat)
{if (mdec->phase == 0) {//激活鼠标时鼠标回复的内容if (dat == 0xfa) {mdec->phase = 1;}return 0;}if (mdec->phase == 1) {//鼠标第1字节内容if ((dat & 0xc8) == 0x08) {//11001000,//判断第1个字节高4位是否在0~3范围内,//判断第1个字节低4位是否在8~F范围内mdec->buf[0] = dat;mdec->phase = 2;}return 0;}if (mdec->phase == 2) {//鼠标第2字节内容mdec->buf[1] = dat;mdec->phase = 3;return 0;}if (mdec->phase == 3) {//鼠标第3字节内容mdec->buf[2] = dat;mdec->phase = 1;mdec->btn = mdec->buf[0] & 0x07; //得到鼠标按键状态mdec->x = mdec->buf[1]; //鼠标x方向移动数据mdec->y = mdec->buf[2]; //鼠标y方向移动移动if ((mdec->buf[0] & 0x10) != 0) {mdec->x |= 0xffffff00; //第一字节bit[4]为1时,x方向坐标高24位为1}if ((mdec->buf[0] & 0x20) != 0) {mdec->y |= 0xffffff00; //第一字节bit[5]为1时,y方向坐标高24位为1}mdec->y = - mdec->y; //鼠标的y方向与画面符号相反return 1;}return -1;//错误时mdec->phase != 0时返回
}
修改激活鼠标的函数,使鼠标回应的0xfa数据在这里得到标记。如果不提前让mdec->phase= 0,mouse_decode()函数就会返回-1。
#define KEYCMD_SENDTO_MOUSE 0xd4
#define MOUSECMD_ENABLE 0xf4void enable_mouse(struct MOUSE_DEC *mdec)
{//鼠标有效wait_KBC_sendready();io_out8(PORT_KEYCMD, KEYCMD_SENDTO_MOUSE);wait_KBC_sendready();io_out8(PORT_KEYDAT, MOUSECMD_ENABLE);//成功后0xfa会被发送过来mdec->phase = 0; //舍掉0xfa的标记return;
}
将这些函数和结构体加入bootpack.c中,访问缓冲区部分的程序变为:
enable_mouse(&mdec);//不断查询缓冲区,如果有数据,将缓冲区内的数据显示完为止for (;;) {io_cli(); //查看缓冲区时,屏蔽中断if (fifo8_status(&mousefifo) == 0) {io_stihlt(); //如果缓冲区内无数据则开启中断并让CPU休眠,直到中断唤醒} else {data = fifo8_get(&mousefifo);io_sti(); //开启中断if (mouse_decode(&mdec, data) != 0) {//显示解读到的鼠标数据sprintf(str, "[lcr %4d %4d]", mdec.x, mdec.y);if ((mdec.btn & 0x01) != 0) {str[1] = 'L';}if ((mdec.btn & 0x02) != 0) {str[3] = 'R';}if ((mdec.btn & 0x04) != 0) {str[2] = 'C';}boxfill8(binfo->vram, binfo->scrnx, COL8_000000, 32, 16, 32 + 15 * 8 - 1, 31);putfonts8_asc(binfo->vram, binfo->scrnx, 32, 16, COL8_FFFFFF, str);}}}
编译运行此时的操作系统程序,得到如下结果:
Figure1. 解读到的鼠标操作的数据
请根据显示的数据跟鼠标移动结合理解解读出来的数据。它表示鼠标x和y方向上相对于前一刻坐标的变化值。
2 移动鼠标图标
2.1 鼠标坐标处理
int mx, my;mx = (binfo->scrnx - 16) / 2;
my = (binfo->scrny - 28 - 16) / 2;mx += mdec.x;
my += mdec.y;
if (mx < 0) {mx = 0;
}
if (my < 0) {my = 0;
}
if (mx > binfo->scrnx - 16) {mx = binfo->scrnx - 16;
}
if (my > binfo->scrny - 16) {my = binfo->scrny - 16;
}
如果鼠标的坐标超出了屏幕范围,则做特殊处理。
2.2 移动鼠标
//隐藏上次鼠标移动留下的痕迹
boxfill8(binfo->vram, binfo->scrnx, COL8_000000, mx, my, mx + 15, my + 15);
/*…鼠标坐标处理…*/sprintf(str, "(%3d, %3d)", mx, my);
putblock8_8(binfo->vram, binfo->scrnx, 16, 16, mx, my, mcursor, 16);
往bootpack.c中加入鼠标移动的代码,主函数的代码如下(主函数代码过长,要被整理了撒):
void HariMain(void)
{int data;int mx, my;char str[30];char mcursor[256];char mousebuf[128]; //接收鼠标数据缓冲区struct MOUSE_DEC mdec;struct BOOTINFO *binfo = (struct BOOTINFO *) ADR_BOOTINFO;//初始化GDT和IDTinit_gdtidt();//初始化PICinit_pic();//屏蔽所有中断io_sti();fifo8_init(&mousefifo, 32, mousebuf); io_out8(PIC0_IMR, 0xfb); io_out8(PIC1_IMR, 0xef); //初始化鼠标控制电路init_keyboard();//初始化调色板init_palette();//初始化屏幕颜色init_screen8(binfo->vram, binfo->scrnx, binfo->scrny);putfonts8_asc(binfo->vram, binfo->scrnx, 110, 60, COL8_FFFFFF, "oh Still Haribote OS.");mx = (binfo->scrnx - 16) / 2;my = (binfo->scrny - 28 - 16) / 2;//初始化鼠标显示init_mouse_cursor8(mcursor, COL8_000000);//激活鼠标enable_mouse(&mdec);//不断查询缓冲区,如果有数据,将缓冲区内的数据显示完为止for (;;) {io_cli(); //查看缓冲区时,屏蔽中断if (fifo8_status(&mousefifo) == 0) {io_stihlt(); //如果缓冲区内无数据则开启中断并让CPU休眠,直到中断唤醒} else {data = fifo8_get(&mousefifo);io_sti(); //开启中断if (mouse_decode(&mdec, data) != 0) {//获得解读到的鼠标坐标sprintf(str, "[lcr %4d %4d]", mdec.x, mdec.y);if ((mdec.btn & 0x01) != 0) {str[1] = 'L';}if ((mdec.btn & 0x02) != 0) {str[3] = 'R';}if ((mdec.btn & 0x04) != 0) {str[2] = 'C';}boxfill8(binfo->vram, binfo->scrnx, COL8_000000, 0, 0, 32 + 15 * 8 - 1, 31);putfonts8_asc(binfo->vram, binfo->scrnx, 0, 0, COL8_FFFFFF, str);boxfill8(binfo->vram, binfo->scrnx, COL8_000000, mx, my, mx + 15, my + 15);//隐藏上次鼠标移动留下的痕迹mx += mdec.x;my += mdec.y;if (mx < 0) {mx = 0;}if (my < 0) {my = 0;}if (mx > binfo->scrnx - 16) {mx = binfo->scrnx - 16;}if (my > binfo->scrny - 16) {my = binfo->scrny - 16;}sprintf(str, "(%3d, %3d)", mx, my);putblock8_8(binfo->vram, binfo->scrnx, 16, 16, mx, my, mcursor, 16);}}}}
打开“!cons_nt.bat”编译成功后,使用“makerun”命令在QEMU中运行得鼠标移动的结果:
Figure2. 鼠标移动1
Figure3. 鼠标移动2
3 总结
[1] 鼠标控制电路在键盘控制电路中,读取鼠标发送数据的端口号为0x0060。
[2] 通过初始化鼠标控制电路激活鼠标时,鼠标会给鼠标控制电路回复oxfa数据,会触发鼠标中断函数。鼠标中断发生时会连续向鼠标控制电路发送3个连续字节。所以,鼠标中断函数内不可包含复杂的处理,否则中断函数往缓冲区存储的数据就不对。
[3] 可以通过PIC指定鼠标中断的中断号。
[4] mouse_move。
[x86OS] Note Over.
[2015.04.17]
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
