OS操作系统功能扩充-鼠标驱动和图形化界面
update:
2022/06/27 - 1: 初始化仓库
2022/06/27 - 2: 添加鼠标驱动(看的不是太懂,但是似乎能用)
2022/06/29 - 3: 初次实现显示,测试显示成功
2022/06/30 - 4: 尝试实现消息系统,但是 G 了
2022/07/01 - 5: 参考并实现消息系统,并再次测试鼠标驱动
2022/07/01 - 6: 实现了游戏 floppy bird 但是需要优化
2022/07/03 - 7: 优化失败
2022/07/04 - 8: 通过修改内核优化成功驱动
2022/07/04 - 9: 增加实验报告
本实验参考《操作系统原理、实现与实践》中大型实验三,实现了对Bochs下的linux-0.11版本内核的鼠标驱动支持即图形化界面参考。实验成果主要为通过扩充linux-0.11内核系统调用等功能,实现基于linux-0.11的可流畅使用的鼠标驱动的 flappy bird 游戏。
实验代码仓库:https://github.com/yizimi-yuanxin/OS-Mouse-GUI(master & dev)
我们参考键盘驱动的配置方法,来主要考虑鼠标驱动。与键盘类似,添加鼠标中断程序(asm)并在内核编写可以分析鼠标数据的函数。配置鼠标中断信号处理,即配置中断控制器 8259A 以及键盘控制器 i8042(也是鼠标控制器)。对于键盘控制器 i8042,设置其允许鼠标操作,并设置鼠标自动给主机发送数据,并且连接鼠标数据给主机的接口。在内核中,在接受到 0x2c(x86中)的中断号后在操作系统中进行中断,并调用鼠标中断程序。对于中断控制器 8259A 中,要打开中断请求位的屏蔽(初始是屏蔽的),并在中断后向 8259A 发送结束中断的命令。
对于鼠标数据,我们使用二维鼠标,即每次操作会发送三个字节的数据,分析其各个字节的代表位数,判定其点击、移动和滚轮(此处没有)的操作,对于读到的数据,判断向内核发送消息,代表鼠标的相对应的操作。
对显示器的配置大体分为三步:启动图形模式,设置屏幕分辨率并完成像素点阵与显存的映射,在系统中添加修改显示的系统调用
启动图形模式:了解bochs所模拟的屏幕显示的基本配置,设置其为 shift256 模式与 Chain4 模式,使其可以通过线性地址来访问显存。
设置屏幕分辨率并完成像素点阵与显存的映射:设置CRT控制器,设置扫描线,使bochs中按照 320 x 200 的分辨率显示,并了解显存起始位置,将相应像素位置映射到显存的相应位置上。前两部分通过 sys_init_graphics 系统调用来初始化。
在系统中添加修改显示的系统调用:根据像素位置的映射,添加系统调用 sys_repaint,实现对于屏幕的某一矩形的像素统一修改。
对于鼠标的相应操作,我们通过相应的消息驱动,告诉内核以及用户,得到鼠标的消息,并处理鼠标操作对于内核或用户应用的影响。我们考虑用队列实现消息的存储与接收,每次接收调用一个系统调用 sys_get_message。用户每次可从队列最前端来接收消息,如果没有消息则返回-1,存在消息则从队列中取出,并处理消息。在接收的过程中,可以接收鼠标的中断产生的消息,用于影响用户的程序状态。
我们简单的设计 flappy bird,设计为鼠标点击则小鸟向上飞,每过一定的时间则地图整体向左移动,小鸟下落一段距离。
我们通过调用 get_message 系统调用,来确定我们更新游戏画面的时机,我们的消息中还需要时间片的更新消息。我们这时可以通过建立user_timer,在每次调用do_timer函数中计时,来实现时间片的更新,并在时间片为0时发送计时的消息。
然后我们每次画面变化,我们可以简单的实现为重新刷新,重新绘制画面。
当没有图形化界面的时候,可以通过 printk 在内核态直接输出鼠标的现实位置,实时更新。
对于鼠标的点击,我们编写一个测试程序,在每次 get_message 时输出消息除了-1,然后查看鼠标点击时是否有消息输出。
如果出现如下情况:
说明我们并没有更新bochs虚拟硬盘中的unistd.h,且里面的 unistd.h 没有我们自定义的系统调用号。我们可以直接将我们扩充的 unistd.h 直接复制到虚拟机中 /usr/include/unistd.h 的。
鉴于我们之前更新的方法是重新刷新整个画面。我们可以通过实现更新画面位置时,记录上次该物体的显示位置,再次更新时仅将之前的位置用背景色覆盖,然后更新物体位置,并且显示新位置。
而且我们之前的系统调用实现仅可传递三个参数,故仅能传其横坐标,纵坐标和颜色号,即一个像素点颜色的改变。但是这样会导致系统调用过多,拖慢整体程序。我们考虑实现 _syscall5 宏,但是对于 x86 结构的寄存器,并不能很好很规范的支持如此多的参数传递,所以此方法不能很好很简洁的满足要求。我们再考虑传递结构体,但是系统调用并不能支持参数,可以考虑传址,但是这又需要考虑内核态和用户态的内存关系,访存的不方便可能也会有障碍。
我们可以通过压缩位数的方式来传 5 个整型参数。我们将 xpos1 与 xpos2 压成一个整数型,因为 xpos1/2 的范围为[0, 320),ypos1/2 的范围为 [0, 200),0 ~ 8 位做 xpos2,剩下的位数为 xpos1,ypos1/2 同理。
这样就可以同时用一个系统调用更新一个矩形位置绘图。
实验环境:Window11 中的 VMware16.1 中的 Ubuntu Kylin 14.10 中的 Bochs 2.6.9 中的 Linux-0.11内核,VScode
调试:
- 打开 bochs,并打开鼠标端口
- 将 /usr/include/unistd.h 更换为自定义的 unistd.h
- 将 bird.c 与 mouse.c 通过 mcopy 复制入 linux 0.11
- 编译两 C 代码
- 运行 ./mouse,并测试鼠标效果
- 运行 ./bird,并测试鼠标与画面,进行游戏测试
效果:(游戏过程中)