上一章介绍了键盘与鼠标的中断以及操作码的读入,这一章将用鼠标的操作码,结合之前绘制的鼠标箭头,实现鼠标的移动。另外,再说明一下前文略过的asmhead.nas的内容。
一 鼠标移动
鼠标在中断中的存储的操作码包含三位的信息,即[操作,X坐标,Y坐标],我们在主函数中读取的时候,可以三位三位地读取。
这边鼠标数据的解读分为三个阶段,首先要把最初读到的0xfa舍弃掉。之后,每次从鼠标那里送过来的数据都应该是3个字节一组的,所以每当数据累 积到3个字节,就把它显示在屏幕上
对鼠标数据的解读如下:
struct MOUSE_DEC {
unsigned char buf[3], phase;
};
void HariMain(void)
{
//(中略)
struct MOUSE_DEC mdec;
//(中略)
enable_mouse(&mdec);
for (;;) {
io_cli();
if (fifo8_status(&keyfifo) + fifo8_status(&mousefifo) == 0) {
io_stihlt();
} else {
if (fifo8_status(&keyfifo) != 0) {
// 键盘的操作
} else if (fifo8_status(&mousefifo) != 0) {
i = fifo8_get(&mousefifo);
io_sti();
if (mouse_decode(&mdec, i) != 0) {
/* 3字节都凑齐了,所以把它们显示出来*/
sprintf(s, "%02X %02X %02X", mdec.buf[0], mdec.buf[1], mdec.buf[2]);
boxfill8(binfo->vram, binfo-
>scrnx, COL8_008484, 32, 16, 32 + 8 * 8 - 1, 31);
putfonts8_asc(binfo->vram, binfo->scrnx, 32, 16, COL8_FFFFFF, s);
}
}
}
}
}
/**
* @brief 获取鼠标状态
*/
int mouse_decode(struct MOUSE_DEC *mdec, unsigned char dat)
{
if (mdec->phase == 0) {
/* 等待鼠标的0xfa的阶段 */
if (dat == 0xfa) {
mdec->phase = 1;
}
return 0;
}
if (mdec->phase == 1) {
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;
return 1;
}
// shit value
return -1;
}
再从上面接收到地模糊信息中提取到鼠标真正地操作信息(扩充 struct MOUSE_DEC 中的信息,加入 x, y, btn 表示移动信息和鼠标按下的状态):
struct MOUSE_DEC {
unsigned char buf[3], phase;
int x, y, btn;
};
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;
}
在主函数中,将鼠标的操作具象地打印出来,原本显示lcr,当左键按下,l变成L,当右键按下,r变成R,当中键按下,c变成C:
if (mouse_decode(&mdec, i) != 0) {
sprintf(s, "[lcr %4d %4d]", mdec.x, mdec.y);
if ((mdec.btn & 0x01) != 0) {
s[1] = 'L';
}
if ((mdec.btn & 0x02) != 0) {
s[3] = 'R';
}
if ((mdec.btn & 0x04) != 0) {
s[2] = 'C';
}
boxfill8(binfo->vram, binfo->scrnx, COL8_008484, 32, 16, 32 + 15 * 8 - 1, 31);
putfonts8_asc(binfo->vram, binfo->scrnx, 32, 16, COL8_FFFFFF, s);
}
鼠标的移动就更简单了,原理就是在中断中读取到鼠标的最新位置处,重绘鼠标的图形区域。
/* 显示鼠标图形 */
// 隐藏鼠标
boxfill8(binfo->vram, binfo->scrnx, COL8_008484, 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(s, "(%3d, %3d)", mx, my);
// 隐藏坐标
boxfill8(binfo->vram, binfo->scrnx, COL8_008484, 0, 0, 79, 15);
// 显示坐标
putfonts8_asc(binfo->vram, binfo->scrnx, 0, 0, COL8_FFFFFF, s);
// 插画鼠标
putblock8_8(binfo->vram, binfo->scrnx, 16, 16, mx, my, mcursor, 16);
实际运行效果如下:
由图中可以看出,界面中的鼠标能够很好地响应实际鼠标的操作,包括点按操作和位置移动。但是仍有不足,就是当鼠标移动到下面菜单栏时,重绘的操作会将下面菜单栏也一并重绘了,具体的解决办法在后面的重叠区域处理中会有介绍,这边就不展开讲了。
二 32位切换
历史遗留问题,阐述了asmhead.nas中新添加的100多行内容的具体含义(大意就是按照一定的步骤将某些寄存器(CR0等)设置为特定的值),这边比较难懂,而且感觉也没有必要全搞懂,这边就贴一张作者目前操作系统设计的内存分布图: