30天自制操作系统(day8)

时间:2024-04-12 08:16:35

第8天:鼠标控制与32位模式切换

1、内容1:鼠标解读(1)

前一天已经实现了从鼠标中取得数据,因此这一天就是解读之前取得的数据,让鼠标能够动起来。
首先对HariMain函数进行修改,将读鼠标得到的最初的0xfa舍弃。将之后的鼠标传送的3个字节一组的数据,使其显示在屏幕上。
为什么要舍弃0xfa?
因为鼠标已经**,不需要这个显示的数据了。
30天自制操作系统(day8)
其中,变量mouse_phase是用来记住接收鼠标数据的工作进展到了什么阶段。接收到的数据放在mouse_dbuf[0~2]中。对于不同的mouse_phase值(1、2、3),相应的做不同的处理。
实验结果:
30天自制操作系统(day8)
其中,08部分对应的是mouse_dbuf[0],当移动鼠标时,08的0会在03的范围内移动。如果只是移动鼠标,08部分的8不会有任何变化,只有点击鼠标的时候它才会发生变化。左击、右击和点击中间滚轮都会有反应,它的值会在8F之间变化。
1000——8 无意义
1001——9 左键
1010——A 右键
1100——C 滑轮
第一个00部分对应的是mouse_dbuf[1],和鼠标的左右移动有关,第二个00部分对应的是mouse_dbuf[2],和鼠标的上下移动有关系。

2、内容2:稍事整理

整理HariMain函数,创建一个结构体MOUSE_DEC,将鼠标所需要的变量都归总到这个结构体中。
30天自制操作系统(day8)
具体整理的函数如下:
30天自制操作系统(day8)
定义了一个mouse_decode(&mdec,i)函数,用来实现对不同数据进行处理。在3个字节凑齐之后,该函数执行return 1;将这些数据显示出来。
30天自制操作系统(day8)
在enable_mouse函数的最后,对phase进行归零处理。
实验结果:
30天自制操作系统(day8)
与第一部分的结果没有不同。

3、内容3:鼠标解读(2)

对mouse_decode函数进行修改,在结构体中增加几个用于存放解读结果的变量,x、y和btn,分别用来存放移动信息和鼠标按键状态。
30天自制操作系统(day8)
30天自制操作系统(day8)
将之前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。
30天自制操作系统(day8)
第四位如果是1,鼠标左移;如果为0,右移
第五位如果是1,鼠标下移;如果为0,上移
1,2字节为移动的大小
在解读处理的最后,对y的符号进行了取反的操作,因为鼠标与屏幕的y 正好相反,为了配合画面方向(鼠标与屏幕的移动方向相反,坐标左上角),便对y符号进行取反。
之后再修改显示部分:
30天自制操作系统(day8)
如果mdec.btn的最低位为1,就把s的第2个字符换成L,其余类似,也就是将小写符置换成大写字符。
实验结果:
30天自制操作系统(day8)
30天自制操作系统(day8)
30天自制操作系统(day8)

4、内容4:移动鼠标指针

修改图形显示部分的程序,使得鼠标指针能够在屏幕上动起来。
30天自制操作系统(day8)
要使鼠标指针能够移动,只需要不断的改变其的坐标即可。至于后面的两个显示部分,是先隐藏掉鼠标指针,然后在鼠标指针的坐标上,加上解读得到的位移量。
对鼠标坐标进行调整,使其不跑到屏幕外面。
实验结果:
30天自制操作系统(day8)

5、内容5:通往32位之路

30天自制操作系统(day8)
如果当CPU进行模式转换时进来了中断信号,便会导致不必要的错误。并且之后还要进行PIC的初始化,在初始化的时候也不允许有中断发生,因此通过CLI指令将所有的中断全部屏蔽掉。
NOP指令的意思是让CPU休息一个时钟周期长的时间,什么都不做。
该段程序相当于以下部分的C程序:
30天自制操作系统(day8)
30天自制操作系统(day8)
这里的waitbdout等同于wait_KBC_sendready,该段程序的功能是向键盘控制电路发送指令,这里发送的指令是指指令键盘控制电路的附属端口输出0xdf,这个附属端口,连接着主板上的很多地方,通过这个端口发送不同的指令,就可以实现不同的控制功能。
这里输出的0xdf是让A20GATE信号线变成ON的状态。该信号线能使内存的1MB以上的部分变成可使用状态。
该段程序需相当于以下内容的C程序:
30天自制操作系统(day8)
这里的wait_KBC_sendready函数是为了等待A20GATE的处理切实完成。
30天自制操作系统(day8)
30天自制操作系统(day8)
INSTRSET指令,是为了能够使用386以后的LGDT,EAX,CR0等关键字。
LGDT指令,是先将随意准备的GDT给读进来,之后还需要重新设置。然后将CR0这一特殊的32位寄存器的值代入到EAX,并将最高位置为0,最低位置为1,再将整个值返回给CR0寄存器,这样便能够完成模式的转换,进入到保护模式。
什么是保护模式?
保护模式与之前的16位模式不同,段寄存器的解释不是16倍的,而是能够使用GDT。在这种模式下,应用程序既不能随便改变段的设定,又不能使用操作系统专用的段。操作系统受到CPU的保护。
当通过代入CR0而切换到保护模式时,要马上执行JMP指令。
为什么要这样做呢?
因为变成保护模式后,机器语言的解释要发生变化。CPU为了加快指令的执行速度而使用了管道机制,也就是前一条指令还在执行的时候,就开始解释下一条,甚至是再吓一条的指令。因为模式变了,因此就需要重新解释一遍。
在程序中,进入保护模式以后,段寄存器的意思也变了(不再是之前乘以16再甲酸的意思了),除了CS以外的所有段寄存器的值都从0x0000变成了0x0008。CS保持原状是因为如果CS也变了,会造成混乱,因此只有将CS放到后面再处理。0x0008相当于gdt+1的段。
30天自制操作系统(day8)
这一部分的程序只是在调用memcy函数。
30天自制操作系统(day8)
函数memcpy是复制内存的函数,其语法为
memcpy(转送源地址,转送目的地址,转送数据的大小);
转送数据大小是以双字为单位的,所以数据大小需要用字节数除以4来指定。第二个memcpy语句的意思是从0x7c00复制512字节到0x00100000。这正好是将启动扇区复制到1MB以后的内存中。第三个memcpy语句的意思是将始于0x00008200的磁盘内容复制到0x00100200的位置。cyls是柱面的个数(10),18个柱头,每个柱面上下两面,最后减一个512是为了减去启动区的大小。
第一个memcpy语句的意思是将bootpack.hrb复制到0x00280000号地址处理。
30天自制操作系统(day8)
这里是对bootpack.hrb的头部内容进行解析,将执行所必须的数据传送过去。EBX里代入的是BOTPAK,所以值如下:
30天自制操作系统(day8)
30天自制操作系统(day8)
这些值是通过二进制编辑器,打开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开始执行的。
我们所要实现的系统的内存分布图如下所示:
30天自制操作系统(day8)
30天自制操作系统(day8)
该段程序是waithbdout所完成的处理,其与wait_KBC_sendready相同,也添加了从0x60号设备进行IN的处理,也就是如果控制器里有键盘代码或者是已经累计了鼠标的数据,就顺便将它们读出来。
Memcy程序具体内容(复制内存):
30天自制操作系统(day8)
30天自制操作系统(day8)
ALIGNB指令的意思是一直添加DBD,直到ALIGNB 16的情况下,地址能够被16整除的时候停止,如果最初的地址能被16整除,那么ALIGNB指令不作任何处理。
GDT0是一种特定的GDT,0号时空区域,不能够在那里定义段,1号和2号分别由下式设定。
30天自制操作系统(day8)
GATR0是LGDT指令,通知GDT0已经有GDT了,在GDT0里,写入了16位的段上限和32位的段起始地址。
总得来说,最初状态时,GDT在asmhead.nas里,不在0x00270000~0x0027ffff的范围里。IDT仍处于中断禁止的状态。因此,在bootpack.c的HariMain里,应该在进行调色板的初始化以及画面的准备之前,先赶紧重新创建GDT和IDT,初始化PIC,并执行io_sti()。
30天自制操作系统(day8)

X问题1:为什么会出现鼠标指针吃掉任务栏的现象?

30天自制操作系统(day8)
解决方法: 因为没有考虑叠加处理,在鼠标指针移动到任务栏之后,任务栏的部分的显示没有进行更新所导致的。

X问题2:关于文件在软盘和内存中的存储地址范围?

Ipl.bin、asmhead.bin、bootpack.hrb三个文件在软盘haribote.img中存储的地址是多少?
Ipl.bin是直接将文件内容写入镜像文件,在镜像文件上不存在文件名。文件内容的存储地址是镜像文件的第一扇区。
asmhead.bin和bootpack.hrb被合并成可执行文件haribote.sys,其文件名位于镜像文件的0x00002600,文件内容位于0x00004200。
30天自制操作系统(day8)
30天自制操作系统(day8)
他们被加载到内存中存储的初始地址和内存复制后的地址是多少?
Ipl.bin被加载到内存中存储的初始地址是0x7c00,内存复制后的地址是0x00100000。
后面两个合并后的haribote.sys被加载到内存中存储的初始地址是0xc200,bootpack.hrb内存复制后的地址是0x00280000,asmhead.bin内存复制后的地址是0x00100200。