Day08
今天讲了如何接收、处理鼠标数据,之后讲了系统是怎么从16位到32位的。
一些感想
从鼠标接收数据
当鼠标产生一个中断的时候,会连着发送4B的信息。第一次是0xfa(个人感觉应该是说明数据是鼠标来的意思),第二次是点击信息以及是否有移动,第三次是左右移动幅度,第四次是上下移动幅度。我们对相应信息进行解码,就能移动鼠标了。
从16位到32位
大概有这么几个步骤,全程都要用汇编语言来完成
- 关中断
初始化的时候,不能有中断信号进来,所以我们先关中断。 - 初始化键盘控制电路、扩展内存
初始化键盘控制电路并不是为了初始化键盘,而是使用控制电路0xdf的端口,通过此端口可以使用各种功能。之后通过这个端口让A20GATE变为ON之后,就能使内存1MB以上的部分可用。 - 使用32位保护模式
首先先要INSTREST,使用EAX等关键字。
之后我们通过设置CR0这个寄存器(最高位为0禁止分页,最低位为1切换到保护模式),这样就进入了不用分页的保护模式。
之后,CPU执行指令会使用管道模式(个人感觉和流水比较类似),所以要重新把段寄存器解释一遍。 - 载入bootpack
调用memcpy函数,把启动区传送到对应的地方(从磁盘加载到内存)。计算传送数据大小要以柱面数来计算,还要减去启动区一部分的长度。这些数作者已经事先给我们算好了。。 - 启动bootpack
把执行bootpack的必需内容——一部分头部内容进行解析,然后传送进来。之后的工作就可以交给bootpack了。 - 之前操作需要的函数
- 等待数据接收完毕
- memcpy
原来是32位32位读的,并不是一块直接复制。。 - 将地址补全为某个倍数
这样做是方便MOV,8的整数倍更好操作
- 注意事项
先初始化GDT与IDT后,此时就可以开放中断了。然后初始化各种中断并开放,最后再进行一些图形的绘制。
进入剩下不多的正题。。
鼠标解读(1)
harib05a
每3(4)个字节,进行对应的操作。
unsigned char mouse_dbuf[3], mouse_phase = 0;
enable_mouse();
unsigned char i;
int j;
for(;;)
{
io_cli();
if((fifo8_status(&keyfifo) + fifo8_status(&mousefifo)) == 0)
{
io_stihlt();
}
else
{
if(fifo8_status(&keyfifo) != 0)
{
i = fifo8_get(&keyfifo);
io_sti();
sprintf(s, "%02X", i);
boxfill8(binfo->vram, binfo->scrnx, COL8_008484, 0, 16, 15, 31);
putfont8_asc(binfo->vram, binfo->scrnx, 0, 16, COL8_FFFFFF, s);
}
else if(fifo8_status(&mousefifo) != 0)
{
i = fifo8_get(&mousefifo);
io_sti();
if(mouse_phase == 0)
{ // 等待鼠标的0xfa状态
if(i == 0xfa)
mouse_phase = 1;
}
else if(mouse_phase == 1)
{ // 等待鼠标的第一字节
mouse_dbuf[0] = i;
mouse_phase = 2;
}
else if(mouse_phase == 2)
{ // 等待鼠标的第二字节
mouse_dbuf[1] = i;
mouse_phase = 3;
}
else if(mouse_phase == 3)
{ // 等待鼠标的第三字节
mouse_dbuf[2] = i;
mouse_phase = 1;
// 鼠标的三个字节都齐了,显示出来
sprintf(s, "%02X %02X %02X", mouse_dbuf[0], mouse_dbuf[1], mouse_dbuf[2]);
boxfill8(binfo->vram, binfo->scrnx, COL8_008484, 32, 16, 32 + 8 * 8 - 1, 31);
putfont8_asc(binfo->vram, binfo->scrnx, 32, 16, COL8_FFFFFF, s);
}
}
}
}
稍事整理
harib05b
把写在main里长长的一大段封装称函数。
struct MOUSE_DEC
{
unsigned char buf[3], phase;
};
void enable_mouse(struct MOUSE_DEC *mdec);
int mouse_decode(struct MOUSE_DEC *mdec, unsigned char dat);
void HariMain(void)
{
// PASS
struct MOUSE_DEC mdec;
enable_mouse(&mdec);
unsigned char i;
int j;
for(;;)
{
io_cli();
if((fifo8_status(&keyfifo) + fifo8_status(&mousefifo)) == 0)
{
io_stihlt();
}
else
{
if(fifo8_status(&keyfifo) != 0)
{
i = fifo8_get(&keyfifo);
io_sti();
sprintf(s, "%02X", i);
boxfill8(binfo->vram, binfo->scrnx, COL8_008484, 0, 16, 15, 31);
putfont8_asc(binfo->vram, binfo->scrnx, 0, 16, COL8_FFFFFF, s);
}
else if(fifo8_status(&mousefifo) != 0)
{
i = fifo8_get(&mousefifo);
io_sti();
if(mouse_decode(&mdec, i) != 0)
{
// 鼠标的三个字节都齐了,显示出来
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);
putfont8_asc(binfo->vram, binfo->scrnx, 32, 16, COL8_FFFFFF, s);
}
}
}
}
}
void 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);
//顺利的话,键盘控制其会返送回ACK(0xfa)
mdec->phase = 0; // 等待0xfa的阶段
return;
}
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;
}
return -1; // 应该不可能到这里来
}
鼠标解读(2)
harib05c
我们把鼠标传过来的信息解读成能懂的坐标变化。
struct MOUSE_DEC
{
unsigned char buf[3], phase;
int x, y, btn;
};
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)
{ // 等待鼠标第一字节的阶段
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; //鼠标的y方向与画面符号相反
return 1;
}
return -1; // 应该不可能到这里来
}
对于按键状态,只会和buf[0]的低三位有关,因此拿出来低三位就好了。
x,y坐标还需要检测buf[0]是否存在对应移动,存在的话就进行相应设定,要把高位全部置0或者置1才能正确解读坐标。
然后是到这一步的显示方法:
else if(fifo8_status(&mousefifo) != 0)
{
i = fifo8_get(&mousefifo);
io_sti();
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);
putfont8_asc(binfo->vram, binfo->scrnx, 32, 16, COL8_FFFFFF, s);
}
}
移动鼠标指针
harib05d
移动的意思,就是先在原来地方画成背景,然后把鼠标画到新的地方。
else if(fifo8_status(&mousefifo) != 0)
{
i = fifo8_get(&mousefifo);
io_sti();
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);
putfont8_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); // 隐藏坐标
putfont8_asc(binfo->vram, binfo->scrnx, 0, 0, COL8_FFFFFF, s); // 显示坐标
putblock8_8(binfo->vram, binfo->scrnx, 16, 16, mx, my, mcursor, 16); // 描画鼠标
}
}
之后就是讲解如何转换到32位的了。就不再写什么了。。