30天自制操作系统
上QQ阅读APP看书,第一时间看更新

6 总算讲到鼠标了(harib04f)

现在到了让大家期待已久的讲解鼠标的时间了。首先说一说,为什么虽然我们的电脑连着有鼠标,却一直不能用的原因。

从计算机不算短暂的历史来看,鼠标这种装置属于新兴一族。早期的计算机一般都不配置鼠标。一个很明显的证据就是,现在我们要讲的分配给鼠标的中断号码,是IRQ12,这已经是一个很大的数字了。与键盘的IRQ1比起来,那可差了好多代了。

所以,当鼠标刚刚作为计算机的一个外部设备开始使用的时候,几乎所有的操作系统都不支持它。在这种情况下,如果只是稍微动一动鼠标就产生中断的话,那在使用那些操作系统的时候,就只好先把鼠标拔掉了。IBM的大叔们认为,这对于使用计算机的人来说是很不方便的。所以,虽然在主板上做了鼠标用的电路,但只要不执行激活鼠标的指令,就不产生鼠标的中断信号(1)。

所谓不产生中断信号,也就是说,即使从鼠标传来了数据,CPU也不会接收。这样的话,鼠标也就没必要送数据了,否则倒会引起电路的混乱。所以,处于初期状态的鼠标,不管是滑动操作也好,点击操作也好,都没有反应(2)。

总而言之,我们必须发行指令,让下面两个装置有效,一个是鼠标控制电路,一个是鼠标本身。通过上面的说明,大家应该已经明白了,要先让鼠标控制电路有效。如果先让鼠标有效了,那时控制电路还没准备好数据就来了,可就麻烦了,因为控制电路还处理不了。

■■■■■

现在来说说控制电路的设定。事实上,鼠标控制电路包含在键盘控制电路里,如果键盘控制电路的初始化正常完成,鼠标电路控制器的激活也就完成了。

bootpack.c节选

#define PORT_KEYDAT               0x0060
#define PORT_KEYSTA               0x0064
#define PORT_KEYCMD               0x0064
#define KEYSTA_SEND_NOTREADY     0x02
#define KEYCMD_WRITE_MODE        0x60
#define KBC_MODE                  0x47

void wait_KBC_sendready(void)
{
    /* 等待键盘控制电路准备完毕 */
    for (; ; ) {
        if ((io_in8(PORT_KEYSTA) & KEYSTA_SEND_NOTREADY) == 0) {
            break;
        }
    }
    return;
}

void init_keyboard(void)
{
    /* 初始化键盘控制电路 */
    wait_KBC_sendready();
    io_out8(PORT_KEYCMD, KEYCMD_WRITE_MODE);
    wait_KBC_sendready();
    io_out8(PORT_KEYDAT, KBC_MODE);
    return;
}

首先我们来看函数wait_KBC_sendready。它的作用是,让键盘控制电路(keyboard controller, KBC)做好准备动作,等待控制指令的到来。为什么要做这个工作呢?是因为虽然CPU的电路很快,但键盘控制电路却没有那么快。如果CPU不顾设备接收数据的能力,只是一个劲儿地发指令的话,有些指令会得不到执行,从而导致错误的结果。如果键盘控制电路可以接受CPU指令了,CPU从设备号码0x0064处所读取的数据的倒数第二位(从低位开始数的第二位)应该是0。在确认到这一位是0之前,程序一直通过for语句循环查询。

break语句是从for循环中强制退出的语句。退出以后,只有return语句在那里等待执行,所以,把这里的break语句换写成return语句,结果一样。

下面看函数init_keyboard。它所要完成的工作很简单,也就是一边确认可否往键盘控制电路传送信息,一边发送模式设定指令,指令中包含着要设定为何种模式。模式设定的指令是0x60,利用鼠标模式的模式号码是0x47,当然这些数值必须通过调查才能知道。我们可以从老地方http://community.osdev.info/? ifno(AT)keyboard得到这些数据。

这样,如果在HariMain函数调用init_keyboard函数,鼠标控制电路的准备就完成了。

■■■■■

现在,我们开始发送激活鼠标的指令。所谓发送鼠标激活指令,归根到底还是要向键盘控制器发送指令。

bootpack.c节选

#define KEYCMD_SENDTO_MOUSE      0xd4
#define MOUSECMD_ENABLE          0xf4
void enable_mouse(void)
{
    /* 激活鼠标 */
    wait_KBC_sendready();
    io_out8(PORT_KEYCMD, KEYCMD_SENDTO_MOUSE);
    wait_KBC_sendready();
    io_out8(PORT_KEYDAT, MOUSECMD_ENABLE);
    return; /* 顺利的话,键盘控制其会返送回ACK(0xfa)*/
}

这个函数与init_keyboard函数非常相似。不同点仅在于写入的数据不同。如果往键盘控制电路发送指令0xd4,下一个数据就会自动发送给鼠标。我们根据这一特性来发送激活鼠标的指令。

另一方面,一直等着机会露脸的鼠标先生,收到激活指令以后,马上就给CPU发送答复信息:“OK,从现在开始就要不停地发送鼠标信息了,拜托了。”这个答复信息就是0xfa。

因为这个数据马上就跟着来了,即使我们保持鼠标完全不动,也一定会产生一个鼠标中断。

■■■■■

所以,我们将enable_mouse也做成了从HariMain中调用的形式。好,我们马上测试一下。运行“make run”。

鼠标中断终于出来了。这可是很了不起的进步哟。