操作系统实验--30天自制操作系统第8天实验日志

一、实验主要内容
1、内容1:鼠标解读(1)
第七天已经实现了从鼠标中取得数据,因此这一天就是解读之前取得的数据,让鼠标能够动起来。所以我们需要HariMain函数进行修改,将读鼠标得到的最初的0xfa舍弃。将之后的鼠标传送的3个字节一组的数据,使其显示在屏幕上,代码修改如下:
在这里插入图片描述

其中变量mouse_phase是用来标记鼠标接收的数据是第几个了。接收到的数据会放在mouse_dbuf中。对应不同的mouse_phase值(1、2、3),相应的做不同的处理。
实验结果:
在这里插入图片描述

其中,08部分对应的是mouse_dbuf[0],当移动鼠标时,08的0会在0~3的范围内变化。如果只是移动鼠标,08部分的8不会有任何变化,只有点击鼠标的时候它才会发生变化。左击、右击和点击中间滚轮都会有反应,它的值会在8F之间变化。其变化意义如下
1001——9 左键,1010——A 右键,1100——C 滑轮按下
第一个1C部分对应的是mouse_dbuf[1],和鼠标的左右移动有关,第二个00部分对应的是mouse_dbuf[2],和鼠标的上下移动有关系。
2、内容2:稍事整理
整理HariMain函数,创建一个结构体MOUSE_DEC用于整理鼠标的属性信息。
在这里插入图片描述

具体处理函数如下:
在这里插入图片描述

这里先定义了一个mouse_decode(&mdec,i)函数,用来实现对不同数据进行接收处理。在3个字节凑齐之后,该函数峰会1;然后对这些数据进行处理(将其显示出来)。
对于enable_mouse函数的最后,将会对phase进行归零处理。
在这里插入图片描述

实验结果:
在这里插入图片描述

3、内容3:鼠标解读(2)
对mouse_decode函数进行修改,在结构体中增加几个用于存放解读结果的变量,x、y和btn,分别用来存放移动信息和鼠标按键状态。
在这里插入图片描述

在这里插入图片描述

这里将之前if(mdec->phase==1)修改为
if (mdec->phase == 1) {
if ((dat & 0xc8) == 0x08) {
用于判断第一字节对移动有反应的部分是否在03的范围内,同时判断第一字节对点击有反应的部分是否在8F的范围内,即必须是00XX1XXX,如果不在该范围内的话,说明数据出错,就舍去,以保证在鼠标的连线接触不良,断线丢失数据后可以自己恢复。
在鼠标读数据第三字节的处理中,鼠标键的状态,存放在buf[0]的低3位中,只需取出这3位。十六进制的0x07相当于二进制的0000 0111,因此通过与运算,便可以很顺利的取出低3位的值,即左键右键滑轮键。
X和y是直接使用buf[1]和buf[2],需要使用第一字节中对鼠标移动有反应的几位信息,将x和y的第8位及第8位以后全部都设为1,或全部保留为0。
第四位如果是1,表示鼠标左移;如果为0,右移
第五位如果是1,表示鼠标下移;如果为0,上移
1,2字节为移动的大小。
在这里插入图片描述

在解读处理的最后,对y的符号进行了取反的操作,因为鼠标与屏幕的y 正好相反,为了配合画面方向(鼠标与屏幕的移动方向相反,坐标左上角),便对y符号进行取反。
之后再修改显示部分:
在这里插入图片描述

如果mdec.btn的最低位为1,表示左键按下,就把s的第2个字符换成L,其余类似,本质上就是将小写符置换成大写字符。

实验结果:
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

4、内容4:移动鼠标指针
修改图形显示部分的程序,使得鼠标指针能够在屏幕上动起来。
在这里插入图片描述

要使鼠标指针能够移动,只需要不断的改变其的坐标然后每次先覆盖掉原来位置的鼠标然后画出新位置的鼠标即可。两个显示部分,就是先隐藏掉鼠标指针,然后在鼠标指针的坐标上,加上解读得到的位移量。最后对鼠标坐标进行调整,使其不跑到屏幕外面。
实验结果:
在这里插入图片描述

5、内容5:通往32位之路
在这里插入图片描述

因为如果当CPU进行模式转换时进来了中断信号,便会导致不必要的错误。并且之后还要进行PIC的初始化,在初始化的时候也不允许有中断发生,因此通过CLI指令将所有的中断全部屏蔽掉。
该段程序相当于以下部分的C程序(禁止所有的中断):
在这里插入图片描述

在这里插入图片描述

上面里的waitbdout等同于wait_KBC_sendready,该段程序的功能是向键盘控制电路发送指令,这里发送的指令是指指令键盘控制电路的附属端口输出0xdf,这个附属端口,连接着主板上的很多地方,通过这个端口发送不同的指令,就可以实现不同的控制功能。
这里输出的0xdf是让A20GATE信号线变成ON的状态。该信号线能使内存的1MB以上的部分变成可使用状态。
该段程序需相当于以下内容的C程序:
在这里插入图片描述

这里的wait_KBC_sendready函数是为了等待A20GATE的处理切实完成。
在这里插入图片描述
在这里插入图片描述

INSTRSET指令,是为了能够使用386以后的LGDT,EAX,CR0等关键字。
LGDT指令,是先临时定义的,之后还需要重新设置。然后将CR0这一特殊的32位寄存器的值代入到EAX,并将最高位置为0,最低位置为1,再将整个值返回给CR0寄存器,这样便能够完成模式的转换,进入到保护模式。
所谓的保护模式就是段寄存器的解释不是16倍的,而是能够使用GDT。在这种模式下,应用程序既不能随便改变段的设定,又不能使用操作系统专用的段。这样操作系统就可以受到CPU的保护。最后通过代入CR0而切换到保护模式时,要马上执行JMP指令。
因为变成保护模式后,机器语言的解释要发生变化。CPU为了加快指令的执行速度而使用了管道机制,也就是前一条指令还在执行的时候,就开始解释下一条,甚至是再下一条的指令。因为模式变了,因此就需要重新解释一遍,而JMP指令会让CPU舍去预先解释的指令重新开始解释。
在程序中,进入保护模式以后,段寄存器的意思也变了(不再是之前乘以16再甲酸的意思了),除了CS以外的所有段寄存器的值都从0x0000变成了0x0008。CS保持原状是因为如果CS也变了,会造成混乱,因此只有将CS放到后面再处理。0x0008相当于gdt+1的段。
在这里插入图片描述

这一部分的程序只是在调用memcy函数。
在这里插入图片描述

函数memcpy是复制内存的函数,其参数为memcpy(转送源地址,转送目的地址,转送数据的大小);
转送数据大小是以双字为单位的,所以转移数据的单位数量需要用字节数除以4来指定。第二个memcpy语句的意思是从0x7c00复制512字节到0x00100000。这正好是将启动扇区复制到1MB以后的内存中。第三个memcpy语句的意思是将始于0x00008200的磁盘内容复制到0x00100200的位置。cyls是柱面的个数(10),18个柱头,每个柱面上下两面,最后减一个512是为了减去启动区的大小。
第一个memcpy语句的意思是将bootpack.hrb复制到0x00280000号地址处理。
在这里插入图片描述

这里是对bootpack.hrb的头部内容进行解析,将执行所必须的数据传送过去。EBX里代入的是BOTPAK,所以值如下:
在这里插入图片描述

在这里插入图片描述

这些值是通过二进制编辑器,打开harib05d的bootpack.hrb确认的,这些值因harib的版本不同而有所变化。
SHR指令是向右移位指令,相当于ECX>>2,与除以4有着相同的效果。
JZ是条件跳转指令,根据前一个计算结果是否为0来决定是否跳转。在harib05d里,ECX没有变成0,因此不跳转。
最后将0x310000代入到ESP中,通过一个特别的JMP指令,将2*8代入到CS里,同时移动到0x1b号地址。这里的0x1b号地址是指第2段的0x1b号地址。第2个段的基地址是0x280000,因此,实际上是从0x28001b开始执行的。
我们所要实现的系统的内存分布图如下所示:
在这里插入图片描述

在这里插入图片描述

该段程序是waithbdout所完成的处理,其与wait_KBC_sendready相同,也添加了从0x60号设备进行IN的处理,也就是如果控制器里有键盘代码或者是已经累计了鼠标的数据,就顺便将它们读出来。
Memcy程序具体内容(复制内存):
在这里插入图片描述

在这里插入图片描述

ALIGNB指令的意思是一直添加DBD,直到ALIGNB 16的情况下,地址能够被16整除的时候停止,如果最初的地址能被16整除,那么ALIGNB指令不作任何处理。
GDT0是一种特定的GDT,0号时空区域,不能够在那里定义段,1号和2号分别由下式设定。
在这里插入图片描述

GATR0是LGDT指令,通知GDT0已经有GDT了,在GDT0里,写入了16位的段上限和32位的段起始地址。
总得来说,最初状态时,GDT在asmhead.nas里,不在0x00270000~0x0027ffff的范围里。IDT仍处于中断禁止的状态。因此,在bootpack.c的HariMain里,应该在进行调色板的初始化以及画面的准备之前,先赶紧重新创建GDT和IDT,初始化PIC,并执行io_sti()。
二、遇到的问题及解决方法
问题1:在对bootpack.hrb的头部内容进行解析时,为什么ECX需要预先加3
解决方法: 因为后续我们需要将ECX的值除以4用于作为memcpy转移数据大小的参数,这里如果不加3,那么就相当于ECX进行了一次向下取整的除4,如果原ECX不能被4整除就会有数据遗漏没有转移,这里加3后只要不能被4整除(最后两位为0)就会(二进制中)向第3位进位,达到向上取整的目的。
问题2:在鼠标信息当中为什么buf[0]中的数据与0x10和0x20相与可以判断左右上下
在这里插入图片描述

解决方法:这里前面提到过这里的数据高一个字节在鼠标移动时会在0~3中变化,这就表示鼠标的移动方向(00,01,10,11),但在这里的4个方向并不是正上下左右,而斜方向的,具体表示的方向如图(这里为了减少数据变化开销采用的顺序是格雷码):
所以这里很明显,当第一位为1时(01,11)表示鼠标正在向左移动所以此时X要取反,当第2位为1时(11,10)表示鼠标正在向下移动,所以此时Y要取反。
在这里插入图片描述

问题3:鼠标移动到任务栏处时会“吃掉”任务栏
解决方法:导致这个现象的原因是鼠标的背景颜色处理不完善,可以先记录鼠标当前位置的背景信息,当鼠标移动后,将背景还原即可,具体代码见创新点1。
三、程序设计创新点

创新点1:改善鼠标图像吃掉任务栏的情况
在这里插入图片描述

想要解决这个问题我们有两个部分需要解决:
首先我们需要改良原先隐藏鼠标的策略,原代码中隐藏鼠标的方法是用背景色覆盖原来的鼠标,但我们的字符和任务栏并不是背景色,当鼠标移动到这些位置再移开时就会发生,鼠标
吃掉其他图标的情况。
这里我们改变隐藏鼠标的策略,先将原来鼠标位置的像素颜色信息记录下来,然后在鼠标移动后,将像素颜色还原
save函数用于将鼠标位置16*16的像素信息进行记录在数组中,restore函数用于在鼠标移动后将像素还原。
在解决鼠标隐藏问题后,鼠标的绘制函数中在鼠标图像周围会绘制背景颜色这样同样会有显示问题,就像如图:
在这里插入图片描述

所以第2步我们需要修改鼠标显示函数,使之只画出鼠标的图像而不画鼠标周围的背景色。
根据鼠标图像进行针对性的赋上颜色:
比如对于如图鼠标图像的第一行,我们就不需要显示最后两个
在这里插入图片描述

像素点(就是所有”.”的位置不用画),下面的每一行同理,因为不需要画的像素点每一行规律并不完全一样,所以根据不同行的范围进行不同的绘制策略
总代码如下:
在这里插入图片描述

最终效果见下:

创新点2:简单做一个鼠标左键的交互程序
做一个鼠标左键的交互程序:在屏幕显示一个按钮,当鼠标在按钮处按下时,一个圆形的颜色改变
我们的策略是,当鼠标的位置在我们按钮的范围内同时鼠标按下了左键,那么我们运行左图的程序,改变图像的颜色,这里我将处理代码放在了
中断信息接收的外面,因为我不想中断信息收集部分花费太多的时间
在这里插入图片描述

判断的代码如右图所示,其中160208和020就是我们按钮的范围。
最终效果如下:
在这里插入图片描述

四、实验心得体会
这次实验中我们主要学的是鼠标的信息接收和处理,和讲解如何进入32位模式,在实验中我们的鼠标现在可以移动了,不过还有点瑕疵(不过我们这次把它改好了),这次还做了一个简单的鼠标交互的小程序,所以现在鼠标可以通过按钮控制图形颜色那么后面我们是不是可以通过鼠标来完成更加复杂的操作呢,好了,现在我们的操作系统越来越像一个操作系统了,相信我们离我们的目标越来越近了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值