系统内核的洪荒之力:挪动鼠标

来源:互联网 发布:腾讯大王卡扫号软件 编辑:程序博客网 时间:2024/04/27 22:11

欲观看本章代码效果,请参看视频:
Linux kernel Hacker, 从零构建自己的内核

当我们获得鼠标发送的数据后,我们就可以根据相应的信息绘制鼠标,从而实现鼠标的挪动效果,在此之前,我们得了解,鼠标发送的数据是怎么解读的。

鼠标发送的数据,必须三个字节连在一起解读,所以我们的内核要等待鼠标发送足够的数据后才可以采取行动。前面我们看到,当鼠标被激活后,它会立马给内核发送一个字节数据,数值为0xfa, 当内核收到这个数据后,就可以开始积攒数据,每接收三个字节后,根据数据绘制鼠标。

这三个字节数据是有一定特点的,第一个字节0xab, a的数值必须在0-3这个范围内,由于a对应的是八比特中的高四位,所以这意味着该字节的第7,8两个比特位必须为0,b对应着八比特位中的低四位,它的值必须在8-F之间,这意味着该字节数据对应的第4个比特位必须为1.把第一个字节转换成二进制,那么它必须满足下面格式(X,*代表0或1):
0 0 X X 1 * \ * *
三个*用来表示鼠标按键,当鼠标的左键,滚轮,右键被按下时,对应的比特位会设置为1.

第二个字节用来表示鼠标的左右移动,对该字节进行相应处理后,可以得到鼠标平移的坐标变换。

第三个字节的数据表示鼠标的上下移动,对该字节进行相应处理后,可以得到鼠标垂直移动时的坐标数变化。

我们专门设置一个结构体来处理鼠标发送的相关信息:

struct MOUSE_DEC {    unsigned char buf[3], phase;    int x, y, btn;};

buf[3]用来存储鼠标发送的三个数据,phase表示当前接收到第几个数据了,x表示水平移动的坐标变换,y表示上下移动时鼠标的坐标变换,btn用来表示鼠标哪个按键被按下了。

内核中增加一个函数来解析鼠标发送的数据:

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) {        if ((dat & 0xc8) == 0x08) {            mdec->buf[0] = dat;            mdec->phase = 2;        }        return 0;    }    if (mdec->phase == 2) {        mdec->buf[1] = dat;        mdec->phase = 3;        return 0;    }    if (mdec->phase == 3) {        mdec->buf[2] = dat;        mdec->phase = 1;        mdec->btn = mdec->buf[0] & 0x07;        mdec->x = mdec->buf[1];        mdec->y = mdec->buf[2];        if ((mdec->buf[0] & 0x10) != 0) {           mdec->x |= 0xffffff00;        }        if ((mdec->buf[0] & 0x20) != 0) {           mdec->y |= 0xffffff00;        }        mdec->y = -mdec->y;        return 1;    }    return -1;}

当收到字节0xfa的时候,系统进入数据收集阶段,当收到第一个数据时,判断字节是否符合前面所说的要求,符合的话,进入第二阶段,接收第二个字节,当进入第三阶段,接收完第三个字节后,开始对坐标数据进行处理,btn存储的是第一字节的低3位,它表示当前鼠标哪个按键被按下了,接着看第一字节的第5,第6个比特位,如果第5个比特位设置为1,那么第二字节需要做一些处理,也就是从第8位开始全部设置为1,至于为何要这么做,我们暂且不用关心,同理,如果第6个比特位设置为1,那么把第3个字节做一些处理才得到鼠标的垂直变换。

由于鼠标给的垂直变换跟我们屏幕坐标是相反的,所以y值要去反。通过这一步我们就解析了鼠标发送过来的数据。

在内核主函数中,我们要根据鼠标传输的数据重新绘制鼠标:

static int mx = 0, my = 0;void CMain() {   ....   mx = (xsize - 16) / 2;   my = (ysize - 28 - 16) / 2;     init_mouse_cursor(mcursor, COL8_008484);   putblock(vram, xsize, 16, 16, mx, my, mcursor, 16);  .....   else if (fifo8_status(&mouseinfo) != 0) {           show_mouse_info();       }}void computeMousePosition(struct MOUSE_DEC* mdec) {    mx += mdec->x;    my += mdec->y;    if (mx < 0) {       mx = 0;    }    if (my < 0) {       my = 0;    }    if (mx > xsize - 16) {       mx = xsize - 16;    }    if (my > ysize - 16) {       my = ysize - 16;    }}void eraseMouse(char* vram) {    boxfill8(vram, xsize, COL8_008484, mx, my, mx+15, my+15);}void drawMouse(char* vram) {    putblock(vram, xsize, 16, 16, mx, my, mcursor, 16);}void  show_mouse_info(void) {    char*vram = bootInfo.vgaRam;    unsigned char data = 0;    io_sti();    data = fifo8_get(&mouseinfo);    if (mouse_decode(&mdec, data) != 0) {         eraseMouse(vram);         computeMousePosition(&mdec);         drawMouse(vram);    }}

eraseMouse 作用是使用桌面的背景颜色将鼠标抹掉, drawMouse是根据鼠标新的坐标重新绘制鼠标图案,show_mouse_info里,先将通过mouse_decode获得鼠标数据,然后通过调用eraseMouse将鼠标在当前位置抹掉,computeMousePosition重新计算鼠标移动后的新位置,drawMouse根据新位置重新绘制鼠标。

将上面的C语言编译,然后反编译成汇编后,加入内核汇编模块,编译时会出现类似这样的错误:
error: short jump is out of range

这是因为在汇编语言中,跳转指令,例如jmp, jne等,跳转的距离不能超过127字节,但是由于我们现在的C语言使用了结构体等复杂数据结构,反汇编后,汇编代码语句会增多,从而造成jmp等指令所要跳转的目的地与当前指令间的距离超过127字节,解决办法是在指令出错的地方增添一个near关键字,例如语句:
jmp ?0_57
改成
jmp near ?0_57
那么上面错误就可以消除了,内核编译并加载如虚拟机后,鼠标便能灵动起来:
这里写图片描述

博客无法显示动态效果,请大家观看视频。

0 0
原创粉丝点击