让你自己的操作系统支持鼠标
——做一个支持图形界面的操作系统(下)
Version 0.01
( 对应 pyos 版本 2004_10_06_15_00 )
哈尔滨工业大学 谢煜波
( email:[email protected] 网址:http://purec.binghua.com )
( QQ:13916830 哈工大紫丁香 BBS ID:iamxiaohan )
前言
在上篇中,我们已经了解了怎样显示一个图形界面,包括怎样操作显卡,怎样显示图片,怎样显示中文汉字及英文字符。然而,作为一个完整的图形界面,不光需要显示信息,还需要接受用户输入,而在图形界面中,最重要的输入手段就是通过鼠标进行输入,在这一篇中我们将描述一下怎么样让你自己的操作系统支持鼠标。
由于鼠标存在很多的接口协议,这里我们只对最常用的PS/2协议进行描述。本文主旨在于引导对此感兴趣而又不知怎么下手的朋友,亲自动手进行实验,因此,不会详细描述协议的完整内容,如果你需要了解整个协议内容,你应当去寻找相关文献进行阅读,也可以参考一个本文的参考文献。
本文所描述的内容均基于我自己对此问题的理解,其中定然存在偏差及不当之处,因此,千万别将此文中所描述的内容作为“标准”,如果你一旦发现其中存在的问题,非常欢迎您及时与我联系,我也会在纯C论坛上(http://purec.binghua.com)及时对其中出现的问题进行反馈。
本次我同样将以pyos为基础进行实验,下面我们先来看看这次实验的最终成果:
这是pyos启动后,等待用户登陆的界面
这是用户登陆后的界面,相当于windows的桌面
用鼠标的右键可以拖动应用程序图标
单击图标可以打开应用程序,这个应用程序是一个推箱子游戏
用I,J,K,L四个键可以玩游戏,Esc键或者用鼠标单击窗口标题栏的关闭按钮可以关闭它
用鼠标右键拖动窗口的标题栏可以拖动整个窗口,就像windows中一样
单击左下角的“关机”按钮,可以退出pyos
这是关机后,pyos退出后的界面
上面就是本实验的最终实验结果,希望通过阅读此篇实验报告之后,你能完成一个比这更好的实验:)。
本次实验对也是对前几次实验的一个综合,其中用到了《操作系统引导探究》、《保护模式下的8259A芯片编程及中断处理探究(上下)》、《编写操作系统之键盘交互的实现》、《做一个支持图形界面的操作系统(上)》这几篇实验报告中所描述的内容。因此,如果你想更好的理解本实验的内容,可以在纯C论坛上找到上面几篇实验报告进行参考,本篇实验报告将不会对以前实验报告中已提到的内容重复论述。
本次实验,得到了不少朋友的帮助,其中,哈尔滨工业大学紫丁香网友SwordLea建议支持符合windows标准的bmp图片格式,pineapple帮助我找了到了windows下的图标,hotice指导使用PhotoShop, and so on~~:)
言归正传,下面开始进行我们的实验。
一、通过对i8042 键盘控制器编程控制鼠标
现在我们用的鼠标,大多是支持PS/2协议的鼠标,这样的鼠标也被称为PS/2 鼠标,PS/2协议其实支持两种设备,一种是键盘,一种是鼠标,它是由IBM公司制定的,协议的本身定义了键盘与鼠标同主机进行通迅的规则,其中包括了大量的物理及电器方面的信息,比如鼠标连接线的插头的管脚(针)数,每个管脚(针)的用途,电平是多少等,不过幸运的是,我们并不需要对这样的硬件细节有详细的了解,就可以完成我们的操作系统,我们需要了解的就是怎样初始化鼠标,以及怎样从鼠标中获得信息。
这里,我们首先来看看怎样初始化鼠标。根据PS/2协议,鼠标是由键盘的控制器(i8042)进行控制的,键盘控制器(i8042)总共有两个通道,一个通道由键盘使用,另一个通道由鼠标使用,我们对鼠标进行操作也是通过i8042芯片来完成的,因此,现在的重点就是了解并熟悉怎样对i8042进行编程,来完成对鼠标的控制。
i8042支持两种工作模式——AT模式及PS/2模式,这都是由IBM所定义的一些规范,i8042在计算机启动时会自动检测用户是否使用的支持PS/2协议的键盘及鼠标,以决定是否工作在PS/2模式下,现在我们假设我们使用的都是PS/2键盘及鼠标,因此,现在i8042工作在PS/2模式下(请记住这一点,即i8042可以工作在AT模式或者PS/2模式下,并且现在假设其工作在PS/2模式下,这在后面将会用到)。
与i8042有关的I/O端口共有两个,一个是0x60端口,一个是0x64端口,如果我们想获得i8042的状态,我们需要读0x64端口,这将返回i8042中状态寄存器的内容。如果我们想向i8042发送命令,我们需要先把命令发送到0x64端口,然后紧接着把这个命令的参数发送到0x60端口,然后我们可以通过读0x60端口,获得i8042返回给我们的数据。
下面我们就来看看,应当发送什么样的命令去控制鼠标,这涉及到下面几个需要发送给i8042的命令:
0xA8命令:许可i8042的鼠标通道,即允许鼠标操作。
0xD4命令:把发往0x60端口的参数数据发给鼠标。
0x60命令:把发往0x60端口的参数数据写入i8042的控制寄存器。
从上面的分析我们可以基本窥见怎样操作鼠标。首先,我们应向i8042的0x64端口发送0xA8命令,以许可i8042的鼠标通道,以便完成对鼠标的操作。其次我们应向i8042的0x64端口发送0xD4命令,以通知i8042我们需要控制鼠标,并把控制鼠标的命令发到i8042的0x60端口,再从i8042的0x60端口取回鼠标发给我们的数据,这一过程无疑是比较简单的,我们先来看看,我们应向鼠标发送什么样的控制命令,然后再看看实际的代码。
控制鼠标的命令非常之多,比如0xFF命令可以让鼠标复位;0xFE命令可以让鼠标重新发送上次的数据包;0xF3命令可以设置鼠标的采样率,也即鼠标滑动时的灵敏度;0xF4命令可以允许鼠标向主机发送数据包等。这里最重要的就是0xF4命令,而其它的设置鼠标的命令我们暂时可以不用理会,因为使用默认值已经能很好的完成本实验了。要理解0xF4命令有什么作用,我们需要先了解一下鼠标的四种工作模式:Reset模式,Stream模式,Remote模式及Wrap模式。
首先是Reset模式,当鼠标初次加电或收到0xFF命令之后,鼠标就处于此模式,然后鼠标将进行一系列的初始化及检测工作,包括设定默认的采样率等,完成初始化极检测之后,鼠标将进入Stream模式。
在Stream模式下,如果鼠标被移动,或者有键被按下,那么鼠标将向主机发送数据包,并提请一个中断请求,主机需要响应此中断,并在中断处理程序中取得鼠标发送的数据包。如果在Stream模式下,我们向鼠标发送0xF0命令,将使鼠标进入Remote模式。
Remote模式同Stream模式差不多,主要工作就是检测鼠标是否被移动及是否有键被按下,不过它与Stream模式的区别在于,它并不会主动的向主机提请中断请求,也即它不会主动的向主机发送数据包,而是被动的等待主机使用0xEB(读数据命令)后,再向主机提请中断请求,发送数据包。换句话说,如果在Remote模式下,你每次欲读数据时,均需要向鼠标发送0xEB命令,而如果是在Stream模式下,鼠标会自动向你发送数据。
Wrap模式主要用于检测鼠标与主机之间的连线是否正常,主机向它发送命令,它一般不会执行此命令,而只是简单的将此命令原封不同的回送给主机,主机可比较它发出的命令及接收到的命令是否一致,并以此来认定鼠标与主机之间的连线是否正常。
从上面的描述中我们可以看出,我们需要关心的只有Reset模式及Stream模式,但对于操作系统编写人员而非BIOS编写人员来说,真正需要关心的只有Stream模式,这是因为当计算机启动的时候,BIOS会自动检测鼠标,与鼠标进行通信,这个时候它会向鼠标发送0xFF(复位)命令,然后鼠标会自检,并通知主机自检是否正常,然后鼠标就将处于Stream模式,此时,鼠标已经开始检测鼠标是否移动及是否有键按下了,但是它不会立即就向主机发送数据,因为有可能主机还没有进入真正的操作系统,主机还正在启动中,因此,鼠标会等待主机的通知,直到主机给它发送0xF4命令后,它才开始向主机发送数据。
所以,在操作系统中初始化鼠标的动作就很简单了,请看下面pyos中初始化鼠标的相关代码:
// 许可 鼠标
void mouse_enable_mouse()
{
// 对 8042 键盘控制芯片进行编程
// 允许 鼠标 接口
io_write_to_io_port( 0x64 , 0xa8 ) ;
// 通知 8042 下个字节的发向 0x60 的数据将发给 鼠标
io_write_to_io_port( 0x64 , 0xd4 ) ;
// 允许 鼠标 发数据
io_write_to_io_port( 0x60 , 0xf4 ) ;
// 通知 8042,下个字节的发向 0x60 的数据应放向 8042 的命令寄存器
io_write_to_io_port( 0x64 , 0x60 ) ;
// 许可键盘及 鼠标 接口及中断
io_write_to_io_port( 0x60 , 0x47 ) ;
}
有了上面的描述,这段代码就相当简单了,首先它向i8042的0x64端口发送了一个0xA8命令,通知i8042,允许鼠标通道。然后,它向i8042的0x64端口发送了一个0xD4命令,这个命令表示,下面发给0x60的命令需要发给鼠标,所以,紧接着,它向i8042的0x60端口,也即向鼠标,发送了0xF4命令,这个命令将允许经过BIOS初始化后,现在已处于Stream模式下的鼠标给主机发送数据包。随后,它向i8042的0x64端口发送了0x60命令,表示,下面发向0x60端口的数据需要写入i8042的控制寄存器,最后它向i8042的0x60端口发送了值为0x47的数据,这个数据被写入了i8042的控制寄存器。下面,我们就来看看这个控制寄存器,以明白,这里为什么需要向它发送这样一个值为0x47的数据。
下面就是i8042的控制寄存器的格式,这个控制寄存器总共有8位,即1个字节。
位0:键盘中断标志位,如果置1,那么如果有键盘动作,则i8042将提请IRQ1中断。
位1:鼠标中断标志位,如果置1,那么如果有鼠标动作,则i8042将提请IRQ12中断(在AT模式下这位不使用,只在PS/2模式下有效。这里可以回忆一下前面我们提到的i8042可以工作在AT或者PS/2两种模式下的描述)。
位2:系统标志位,上电的时候是0,自检成功后是1。
位3:键盘锁标志位,如果是1,将忽略键盘锁,这主要是为了兼容一些老式的带锁的键盘,而且这位只在AT模式下使用,PS/2模式下将不使用此位。
位4:键盘接口标志位,如果置1,将禁止使用键盘接口。
位5:在AT模式下,这是AT键盘协议位。置0的时候,i8042将使用AT协议,如果置1,将使用XT协议。在PS/2模式下,这是鼠标接口(通道)标志位,如果置1将禁止鼠标接口(通道)。
位6:键盘扫描码转换标志位。如果置1将把真实的键盘扫描码转换为第一套键盘扫描码(有关此内容详见《编写操作系统之键盘交互的实现》一文)。
位7:保留,应置为0。
二、鼠标数据包简析
在上节中,我们知道了怎样对i8042进行编程,以完成对鼠标的初始化操作,那么当鼠标的初始化完成之后,如果有鼠标动作发生,比如用户移动了一下鼠标,或者按下了鼠标键,那么鼠标将把数据包发回给主机,现在我们就来看看鼠标发回给主机的数据包的结构,在了解这个结构之前,我们先要知道现存的总共有两类鼠标,一类就是所谓的2D(二维)鼠标,它就是我们平常用的那种没有滚轮的鼠标,由于这种鼠标在位移上只有X与Y两个方向,所以称之为2D(二维)鼠标;还有一类就是现在比较常见的3D(三维)鼠标,它们中间存在有一个滚轮,而这个滚轮会产生一个额外的Z位移量,因此,它在位移上有X、Y、Z三个方向,所以又称之为3D(三维)鼠标。下面,我们就来看看这两类鼠标发给主机的数据包有什么不同。下面,我们先来看看二维鼠标。
位0:左键按下标志位,为1表示左键被按下。
位1:右键按下标志位,为1表示右键被按下。
位2:中键按下标志位,为1表示中键被按下。
位3:保留位,总是为1。
位4:X符号标志位,为1表示X位移量为负。
位5:Y符号标志位,为1表示Y位移量为负。
位6:X溢出标志位,为1表示X位移量溢出了。
位7:Y溢出标下位,为1表示Y位移量溢出了。
下面,我们再来看看三维鼠标的数据包结构。
三维鼠标数据包中第一个数据包每位的含义与二维鼠标数据包中第一个数据包中每位含义完全相同,唯一不同的就在于它每次会多发送一个数据包,即第4个数据包,这个数据包包含了Z的位移量,同X、Y位移量相同的是,它们都是以补码表示的。不过与X及Y位移量不同的是,Z位移量是4位的,其中最高位(第四位)是符号位,因此,Z位移量的有效的范围为:-8~7。而X与Y的位移量是9位的,最高一位(第9位)是符号位,这个符号位在第一个数据包中表示,故,X与Y的位移量的有效范围为:-256~255。
看到这里,你或许有疑问了,系统是怎么来知道到我到底应当接收3个数据包还是接收4个数据包的呢?三维鼠标的标准是由微软制定的,最初,这种三维的鼠标只工作在标准的PS/2模式下,如果你想让它工作在三维模式下,你需要用0xF3这个设置鼠标采样率的命令,按如下的顺序进行操作:
1. 设置鼠标采样率为200
2. 设置鼠标采样率为100
3. 设置鼠标采样率为80
这之后,如果你的鼠标是个三维鼠标,那么,它将转到三维模式下进行工作,这个时候,主机向它发送0xF2(获得鼠标类型ID)命令,你的工作在三维模式下的鼠标将向主机返回它的类型ID,但如果你的鼠标不支持三维模式,即如果你的鼠标只是一个二维鼠标,它返回给主机的类型ID将是0,这样,主机就能够知道现在你用的鼠标是什么类型的鼠标,并由此知道应当接受3个还是4个数据包了。本实验将只操作标准的二维鼠标,如果你有兴趣,你可以对程序进行改动,以让它支持三维鼠标。
现在,我们来看看,这个数据包是怎么被主机获得的。在《保护模式下的8259A芯片编程及中断处理探究(上下)》这篇实验报告中我们知道了,鼠标中断是通过IRQ12提出的,因此,在操作系统中,我们需要为IRQ12这号中断编写一个中断处理程序,然后在这个中断处理程序中读取鼠标发来的数据包,下面,就让我们来看一看这段存在于pyos中的真实的代码:
// 鼠标 中断处理函数
void mouse_handle_for_mouse_interrupt()
{
static int x_position = 444 ; // 定义 鼠标 的初始 x 坐标
static int y_position = 300 ; // 定义 鼠标 的初始 y 坐标
static int count = 0 ; // 此变量用来记录这是第几个数据包了
// 因为 鼠标 发来的每个数据包
// 都会引起一个中断
static int x_sign = 0 ; // 用来表示 x 位移量的符号
static int y_sign = 0 ; // 用来表示 y 位移量的符号
static struct message_message_struct message ; // 定义一个消息
// 这个消息用来通知操作系统内核有鼠标动作
char ch = io_read_from_io_port( 0x60 ) ; // 通过 0x60 端口,获得 鼠标 发来的数据包
switch( ++count ){ // 检测是第几个数据包
case 1 :
// 收到的是第一字节
// 检测按键信息
message.dose_left_button_down = ch & 0x1 ;
message.dose_right_button_down = ch & 0x2 ;
// 获得 x 及 y 位移量的符号
x_sign = ch & 0x10 ? 0xffffff00 : 0 ;
y_sign = ch & 0x20 ? 0xffffff00 : 0 ;
break ;
case 2 :
// 收到的是第二字节,即 x 的位移量
x_position += ( x_sign | ch ) ; // 计算新的 x 坐标
break ;
case 3 :
// 收到的是第三字节,即 y 的位移量
y_position += -( y_sign | ch ) ; // 计算新的 y 坐标
// 把消息发送给内核
message.message_type = MESSAGE_MOUSE_MESSAGE ; // 设定消息的类型
message.x_position = x_position ; // 设定消息的参数(x 的坐标)
message.y_position = y_position ; // 设定消息的参数(y 的坐标)
message_put_message( &kernel_message_queue , message ) ; // 把消息放进内核的消息队列中
count = 0 ; // 三个数据包已接收完毕,故清零计数器
break ;
}
}
上面的代码注释已经很详细了,有关“消息”这部份,如果你已前看过《编写操作系统之键盘交互的实现》这篇实验报告,应当对它非常熟悉,如果你没有看过,那么也别着急,在后面分析整个pyos实验程序架构的时候,还会对它进行描述。
这次的pyos实验,支持了Windows标准的BMP图片格式,下面我们就来描述一下对这个标准BMP图片格式的处理问题,以便于更好的理解本实验。
三、Pyos所支持的Windows标准的BMP图片格式
BMP图片格式其实不是只单单的一种格式,而是一类格式的总称,这包括皆如24位真彩格式,16位真彩格式,16位真彩格式中又有555格式,565格式等,还有4位,8位调色板格式,压缩存储格式等,非常复杂,本次Pyos只支持了其中的一种,即非压缩的,16位真彩色555格式,这也是现在的32位windows默认的16位BMP格式。由于BMP格式的纷繁复杂,我想有关这方面最权威最全面的资料莫过于通过MSDN获得了,因此,本实验报告并不打算对此进行完整而详尽的描述,本实验报告将只描述本实验所用到的一些细节,以便于你能非常便利的阅读本实验的代码。
下面我们来简单理解一下BMP格式。BMP格式又称之为位图格式,它是把一幅图的色彩信息完整的记录了下来,每个点都用一个色彩于表示,这样在显示的时候,只需要读取此文件,然后获得每个点的色彩数据,然后把这个点用这个色彩显示到屏幕上就行了,由于每幅图都是由一个一个的点构成的,因此,把所有的点都显示完了之后,就完成了整幅图片的显示。
但是,一个文件光记录每个点的色彩信息还不行,还需要记录这个图片的大小,以及这幅图片每个点用几个数据位来记录色彩等信息,这部份信息被称之为BMP文件的头信息,而被记录在文件的开头位置。下面我们就来看看这个文件头:
struct bmp_bmp_head_struct{
short type ; // 类型
int size_file ; // 大小
short reserved_0 ;
short reserved_1 ;
int offset ; // 位图阵列的起始位置
struct bmp_bmp_info_head_struct info_head ;
} ;
这个结构比较简单,首先是2字节的类型数据,用来认定这是不是一个BMP文件,紧接着是一个4字节的数据,用来表示这个文件的大小,跟着的4字节保留给将来使用,随后的4字节是一个偏移量,这个偏移量指出了每个点的色彩数据在这个BMP文件中的什么位置,这对我们来说非常重要。随后,是一个所谓的BMP信息结构,下面我们就来看看这个所谓的BMP信息结构中到底都有些什么数据值得我们关心。
struct bmp_bmp_info_head_struct{
int the_struct_size ;
int width ;
int height ;
// 下面还有数据,但目前 pyos 只处理 16 位真彩位图,因此下面的数据不要了
} ;
这个结构也比较简章,首先是4字节的数据,用来表示这个结构的大小,随后的4字节数据给出了这个文件所描述的图片的宽度,紧接着的4个字节给出了这副图片的高度,随后还有一些数据,它们给出了这个图片是否使用了压缩格式来存放数据,每个点是用多少位来表示的,以及是否使用了调色版,如果使用了调色版,后面还有调色版的数据。由于本篇只处理 16 位的真彩位图,所以下面的数据本实验都不考虑了,但是,如果你想做一个更好更完善的系统,那么你需要好好的研究一下BMP完整的格式,然后根据其中所记录的不同信息,在程序中进行不同的处理,pyos只是一个原理性的实验系统,故而,并不打算在此方面多下功夫,非常希望能在以后见到改进版本:P。
有了上面的描述,我们可以对怎么样显示图片有个比较良好的认识了,我们现在来看看pyos中的实际的处理代码:
// 显示 pbmp 格式的图片
void vesa_show_bmp_picture( unsigned int x , unsigned int y , void *bmp_addr , unsigned short mask_color , int dose_use_mask_color )
{
// 这里只支持 windows 标准 16 位 bmp 格式图片,(1:5:5:5)
struct bmp_bmp_head_struct *bmp_head = ( struct bmp_bmp_head_struct * )bmp_addr ;
int width = bmp_head->info_head.width ; // 获得图片的宽度
int height = bmp_head->info_head.height ; // 获得图片的高度
// 下面记算存储每个点的色彩的信息所在的位置
unsigned short *color = ( unsigned short * )( ( unsigned int )bmp_addr + bmp_head->offset ) ;
// 由于一行的字节数比须是 4 的倍数,因此,这里先计算每行需要的填充数,除 2 是因为每个像素两个字节
int fill_length = width * 2 % 4 / 2 ;
// bmp 的存放顺序是从下到上,从左到右
for( int i = height - 1 ; i >= 0 ; --i ){
for( int j = 0 ; j < width ; ++j ){
// 取得每个点的色彩信息
// 由于 windows 默认的是 555 格式,而 pyos 用的是 565 格式,因此先进行一下转换
unsigned short temp_color = vesa_change_color_form_555_to_565( *color ) ;
if( !dose_use_mask_color || temp_color != mask_color ){
// 画出每个点
vesa_draw_point( x + j , y + i , temp_color ) ;
}
++color ;
}
// 填充
color += fill_length ;
}
}
上面的代码也是比较简单的,而且有较为详尽的注释,这里就不多描述了,需要提醒的只有两点:第一,BMP文件存放图片的默认顺序是从一幅图的最左下角开始,从左到右,从下到上,而不是按正常的从上到下的顺序。而且,每存一行数据,都要检测一下是否是4的倍数,如果不是,则填充0,以使它总是4字节的倍数,这也称之为4字节对齐。
第二个需要注意的地方就是,windows默认使用的16位真彩位图是555格式,也就是说红(R)、绿(G)、兰(B)都用5位数据来表示,而pyos现在用的是565格式(即红(R)用5位表示,绿(G)用6位表示,兰(B)用5位表示),因此,在显示之前,需要将其转换一下,具体的内容,可以参看源代码。
四、pyos系统架构
本实验所需的基础知识,前面已经全部描述完了,这里我们就来看看pyos的主程序架构。首先需要说明的是,由于目前pyos还没有完成磁盘驱动及文件系统,因此,pyos中所用到的所有资源,包括图片、字库等,都是在最初制作pyos的启动映象(pyos.img)的时候,被一个称之为“make_together.exe”的程序直接写到磁盘上的固定地址的,然后,再由setup.asm在系统引导时,读入内存中的固定位置,而操作系统内核在使用它们时,也是通过这个内存的固定地址使用的。了解这一点,对看源代码非常有帮助。
当setup.asm读入了所用的图片、字库等资源后,系统将跳到kernel.c处执行,这是系统的内核,我们现在来看看这个文件中的一些程序:
// 内核主函数
void kernel_main()
{
// 操作系统初始化
system_init() ;
old_picture = ( unsigned short * )0x100000 ; // 越过前面的只读内存区
// 画登陆界面
kernel_draw_login_form() ; // 0x903b3
// 清空键盘缓冲区
io_read_from_io_port( 0x60 ) ;
// 初始化消息队列
message_init_message_queue( &kernel_message_queue ) ;
// 开中断
interrupt_open_interrupt() ;
struct message_message_struct message ;
kernel_kernel_state = KERNEL_WAIT_USER_LOGIN ;
// 进入消息循环
for( ;; ){
if( !message_get_message( &kernel_message_queue , &message ) ){
continue ;
}
if( message.message_type == MESSAGE_SHUTDOWN_COMPUTER ){
break ;
}
switch( message.message_type ){
case MESSAGE_KEYBOARD_MESSAGE : // 处理键盘消息
……
case MESSAGE_MOUSE_MESSAGE : // 处理 鼠标 消息
……
}
}
// 停机
for( ;; ){
__asm__( "hlt" ) ;
}
}
这段程序也是非常简单的,首先调用system_init()来进行一些初始化操作,比如初始化中断向量表、初始化键盘、初始化鼠标等,然后,它调用kernel_draw_login_form()这个函数来显示等待用户登陆的界面,最后它初始化了一个消息队列,然后进入一个消息循环,不断的从这个消息队列中取出消息,并根据消息的类型进行不同的处理。每当有键盘动作或鼠标动作的时候,键盘与鼠标的中断处理程序都会构造一条消息,然后把这条消息放到内核的消息队列中,这样内核就能知道用户进行了什么操作,然后根据用户操作进行处理。比如,如果鼠标消息中指示出用户按下了鼠标左键,那么内核将检测用户的鼠标是否处在应用程序图标所在的范围内,如果是,则启动应用程序(这在实验中就是那个推箱子游戏)。如果你有在windows下编写windows应用程序的经验,对此应当是比较好理解的。具体的细节可以参看源程序。另外,有关消息队列,中断处理,这部份内容在《编写操作系统之键盘交互的实现》有很详尽的描述,这里就不多费时间了。
/* 系统初始化 */
void system_init()
{
// 由于以后可能向 gdt 表中增添项目,因此,此处应重新初始化 gdt 表,以保存 gdt 表位置
system_init_gdt() ;
// 初始化中断
interrupt_init() ;
// 初始化键盘
keyboard_init() ;
// 初始化 鼠标
mouse_init() ;
}
上面一段代码,就是系统初始化的程序,它在kernel.c的main()函数中被内核调用,以完成系统的一些初始化操作,具体的代码,可以参看源程序。
注:pyos源代码中,每个函数或变量的第一个单词都表明了这个函数存在于哪一个文件中,比如:system_init()中第一个单词system表明了这个函数存在于system.c这个文件中,希望这种命名方式能有助于你阅读源程序。
五、进一步实验的建议
由于pyos或许是更多的着眼于原理性或说基础性,因此它在很多方面做了一些假定及忽略。比如,在本实验中,它没有检测用户是否使用了三维鼠标,因此,没有对三维鼠标提供支持,它也没检测用户使用的到底是不是555格式的真彩BMP图片文件,而假定用户就是使用的这种图片,也没有对这种图片文件是否有错误进行任何的差错检验与校正。另外,它采用了一种最蜗牛的方式,它的每幅图,每条线,都是一个点一个点画上去的,图形拷贝也是一个点一个点的赋值,因此,pyos运行起来非常的慢,而实际的系统是绝不会像这样的。你可以针对它进行一系列的优化。比如在拷贝图片时用memcpy一块内存一块内存的拷贝或者在内存中先建一个缓冲区,在缓冲区中把图都画好了,再一次性的使用memcpy拷贝到显卡的内存中进行显示。知道了基本原理,怎样优化应当并不是一件高不可及的事。
另外,如果你想对本文中所泛泛描述的东东的细节有较深入的了解,可以阅读本文的参考文献,它们中的绝大部分都能在纯C论坛上找到。
后记
本文是《做一个支持图形界面的操作系统》的下篇,上篇在纯C论坛上贴出之后,收到了不收朋友的反馈意见,大家都对是否应当完成一个图形化的操作系统,或者pyos是否应当走图形化的道路,提出了自己非常宝贵的意见,在这里对各位朋友表示由衷的感谢,也想就此机会,谈谈自己对此问题的看法。
首先,从pyos的角度来说,pyos只是一个实验性的操作系统,目的是用来验证所学,因此,pyos追求的是从一种一无所有的原始状态开始建造,力图通过这样一个过程了解较为详细的实现细节,所以,pyos简单、慢,但力图完整。并不打算成为一个真正可以实用并被使用的os,在pyos中,只是验证可以做到,但并不是在实际的os中,一定要做到。pyos是一个起步,而不会是最终的结果。在本实验中,pyos刻意的去模拟了windows的图形界面,原因只在于,想通过这样的模拟,能些许的揭开长期以来披在windows或其它操作系统上神秘的面纱,让对此不了解的人,能认识到:只要一步一步的做下去,并不是只有微软,而我们也有可能做到。从而,让更多的人有信心也有这个兴趣来编写属于自己的操作系统。
我总是力度将一个完整的os,切分成若干细节,而每次实验,都用pyos去实现一个细节,而忽略掉其它的细节,因此,每次实验的pyos的架构都不一样,变化非常大,有时甚至没有多少共通之处,然而,我总认为只要把每个细节都了解清楚了,每个细节我们都可以自己实现了,那么,希望有一天,我们可以将这若干个细节组合起来,像这次的这个图形界面其实就是前面绝大部份实验综合的结果。
其次,从一般意义上讲,如果你打算开发一个os,是否开发图形界面,我的观点是:如果你是为一个特定的目标开发,那么你需要跟据你的目标进行选择,你是要开发一个高效的系统,还是一个界面更友好的系统。如果需求不同,我想这结论也应当是不同的,像pyos,定义为一个中文操作系统,要支持中文,图形模式就是必须的了。
如果你是为学习一种技能或知识,那么我建议你可以尝识一下图形界面的开发,它要考虑的问题会比字符界面更复杂,而且更难处理,这样的一种挑战显然是有益的。
其实,不论你决定是否选用图形界面,最关键的一点是你有兴趣去这样做。为完成这个实验,我三天总共只睡了不到15个小时,四顿没吃饭,逃了三天整整十节大课(幸好老师没点我名,thank good ness~~:P,当然,也并不是非要这样,不过一般说来,写上程序了,一般是很难中途停手的),这一切只有一个支撑,就是兴趣。所以,如果你要决定使用图形界面,或者不使用图形界面,请先确认你对它有足够的兴趣,that's your own os, just as you like!
Pyos是一个开放的系统,你可以在纯C论坛上找到它从创生以来的所有资料,包括所有的实验报告及所有的源代码,所有的相关资源。Pyos没有基于任何现有操作系统,它只是一些原理的实现,因此,Pyos不遵守GPL公约,它也不要求对Pyos进行修改的朋友遵守GPL公约,不过,我非常希望,如果你也在进行类似的开发或者实验,你也能在可能的情况下,公开你的心得,你的体会,让更多的朋友了解你的工作,也让更多的朋友能从中获得益处。学术需要的是交流,中国古代春秋战国时期的百家争鸣,导致了许多古代杰出思想家的出现,你有一个苹果,我有一个苹果,我们交换之后,每人还是只有一个苹果,但如果你有一种思想,我有一种思想,我们交换之后,每人就有两种思想。pyos最初也是看到国外一位朋友写的指南(tutorial)而起步的,在网上搜一下,国外有不少朋友在自己写操作系统,而且有很不错的成果,最重要的是,这些成果都是开放的,你可以随意的取得,而且还有大量的教程、指南(tutorial)告诉你应当怎么做,各种资料,相对来说都很丰富,还有不少新闻组、论坛,有不少热心人在上面讨论,我想这一切也正是Linux诞生的根基。
科学研究需要一种氛围,系统开发也需要一种氛围,然而相对来说,国内的这种自由交流的氛围却要淡很多,至少从我的感觉来说是这样,我处在一所全国前九所的大学当中,然而所能感受到的学术的氛围却不是想象中的那么浓厚,特别是在本科阶段,基本上大体处于老师讲什么,学生学什么的氛围中,老师不太指导学生怎么去更深层次的挖掘,怎么去自主的进行实验,而学生也似乎乐于单方面的接受,并着眼于考试考什么;课余时间,更喜欢追求新兴技术,而对于相对底层的支撑技术不太感兴趣。曾经一位德高望重的老师也对我说:做一些别人已有的东东,有什么意思?有什么价值?不过,我总认为,别人已有的,是别人的,不是我们自己的,虽然别人已有的东东,我们或许不需要重新再去做,但我们应当知道别人是怎么做的,去不去做与能不能做,是两个概念。
正是于此,诞生了哈工大纯C论坛,它追求一种纯粹的计算机科学技术,特别着眼于一些相对底层的支撑技术,现在,我们又打算在每月的28日出版《哈工大·纯C论坛·电子杂志》,很高兴,找到了不少志同道合者共同来完成此项工作,希望能由此营造一种向往已久的氛围。
参考文献
1. 《PS/2技术参考》(Adam Chapweske著,Roy Show译)(网文,纯C论坛可以下载)
2. 《i8042——控制命令、寄存器、端口、键盘和鼠标命令》(好像是中国Linux论坛(http://www.linuxforum.net/)的网友hyl著)(网文,纯C论坛有转载)
3. 《BMP文件格式分析》(网文,作者yz,纯C论坛有转载)
附录:《哈工大·纯C论坛·电子杂志》成立公告
《哈工大·纯C论坛·电子杂志》正式成立既征稿公告!
非常高兴而隆重的宣布,在大家顶力支持及热情支援下,《哈工大·纯C论坛·电子杂志》编辑部于今天正式成立了!本刊定位为相对底层而纯粹的计算机科学及技术研究,着眼于各专业方向基本理论,基本原理的研究,重视基础,兼顾应用技术,以此形成本刊独特的技术风格。
本刊将于每月28号通过网络发行,任何人均可在这一天从互联网上下载本刊,并将通过电子邮件,向纯C论坛的注册用户寄送。本刊以哈工大纯C论坛,哈工大BBS电脑技术区部份版块为基础,邀请了各大知名版主,大侠加盟本刊编辑部,目前本刊有十大骨干技术版块:
栏目名
技术顾问
投稿信箱
计算机组成原理及体系结构
kylix
编译原理
worldguy
算法理论与数据结构
xiong
C与C++语言
sun
汇编语言
ogg
数据库原理
pineapple
计算机网络
true
计算机病毒
swordlea
人工智能及信息处理
car
操作系统
iamxiaohan
从现在开始,本刊面向全校,全国,全网络公开征集各类稿件,你的投稿将由本刊各栏目的技术顾问进行审校,对于每一份稿件我们都会认真处理,并及时通知您是否选用,或由各位技术顾问对稿件进行点评。所有被本刊选用的稿件,或者暂不适合通过电子杂志发表的稿件,将会在纯C论坛网站上同期发表。所有稿件版权完全属于各作者自己所有,非常欢迎您积极向本刊投搞,让你的工作被更多的人知道,让自己同更多的人交流、探讨、学习、进步!
为了确保本刊质量,保证本刊的技术含量,本刊对稿件有如下一些基本要求:
1. 主要以原创(包括翻译外文文献)为主,可以不需要有很强的创新性,但要求有一定的技术含量,描述的问题可以很小但细致,可以很泛但全面,最好图文并貌,投稿以word格式发往各栏目的投稿信箱,或直接与各栏目技术顾问联系。本刊对稿件的风格或格式没有特殊要求,注重质量而非形式,版权归各作者所有,不限一稿多投但限制重复性投稿(如果稿件没有在本刊发表,则不算重复性投稿)。
2. 来稿中如有代码实现部份,在投稿时最好附带源代码及可执行文件,本刊每期发行时,除了一个pdf格式的杂志外,还会附带一个压缩文件,其中将包含本期所有的源代码及相应资源。当然,提不提供源代码,随各位作者自便。
3. 如果稿件是翻译稿件,请一定附带英文原文,以便校对。另外本刊在刊发翻译稿的同时,将尽可能的随稿刊发英文原文,因此请在投稿前确认好版权问题。
4. 来稿请明确注明姓名,电子邮箱,以便编辑及时与各位交流,发送改稿意见,选用通知及寄送样刊等,如果你愿意在发表你稿件的同时,提供一小段的作者简介,我们非常欢迎。
5. 所有来稿的版权归各作者所有,也由各作者负责,切勿抄袭,如果在文中有直接引用他人观点结论及成果的地方,请一定在参考文献中说明。每篇稿件最好提供一个简介及几个关键词,以方便读者阅读及查询。由于本刊有可能被一些海外朋友阅读,所以非常推荐您提供英文摘要。
6. 所有来稿,编辑部在处理后,会每稿必复,如果您长时间没有收到编辑部的消息,请您同本编辑部联系。
7. 由于本刊是纯公益性质,没有任何外来经济支持,所有编辑均是无偿劳动,因此,本刊暂时无法向您支付稿筹,但在适当的时候,本刊会向各位优秀的作者赠送《纯C论坛资料(光盘版)》,以感谢各位作者对本刊支持!
下面再一次诚恳邀请您加盟本刊,为本刊投稿!
本刊创刊号将于 10.28 日发行,希望能在上面见到您的身影!
最后,祝愿大家中秋节快乐!
《哈工大·纯C论坛·电子杂志》编辑部
http://purec.binghua.com
2004.09.28
本文地址:http://com.8s8s.com/it/it26867.htm