Day08

今天讲了如何接收、处理鼠标数据,之后讲了系统是怎么从16位到32位的。

一些感想

从鼠标接收数据

当鼠标产生一个中断的时候,会连着发送4B的信息。第一次是0xfa(个人感觉应该是说明数据是鼠标来的意思),第二次是点击信息以及是否有移动,第三次是左右移动幅度,第四次是上下移动幅度。我们对相应信息进行解码,就能移动鼠标了。

从16位到32位

大概有这么几个步骤,全程都要用汇编语言来完成

  1. 关中断
    初始化的时候,不能有中断信号进来,所以我们先关中断。
  2. 初始化键盘控制电路、扩展内存
    初始化键盘控制电路并不是为了初始化键盘,而是使用控制电路0xdf的端口,通过此端口可以使用各种功能。之后通过这个端口让A20GATE变为ON之后,就能使内存1MB以上的部分可用。
  3. 使用32位保护模式
    首先先要INSTREST,使用EAX等关键字。
    之后我们通过设置CR0这个寄存器(最高位为0禁止分页,最低位为1切换到保护模式),这样就进入了不用分页的保护模式。
    之后,CPU执行指令会使用管道模式(个人感觉和流水比较类似),所以要重新把段寄存器解释一遍。
  4. 载入bootpack
    调用memcpy函数,把启动区传送到对应的地方(从磁盘加载到内存)。计算传送数据大小要以柱面数来计算,还要减去启动区一部分的长度。这些数作者已经事先给我们算好了。。
  5. 启动bootpack
    把执行bootpack的必需内容——一部分头部内容进行解析,然后传送进来。之后的工作就可以交给bootpack了。
  6. 之前操作需要的函数
    • 等待数据接收完毕
    • memcpy
      原来是32位32位读的,并不是一块直接复制。。
    • 将地址补全为某个倍数
      这样做是方便MOV,8的整数倍更好操作
  7. 注意事项
    先初始化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位的了。就不再写什么了。。