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

一、实验主要内容

1、内容1:制作真正的IPL
因为磁盘最初的512字节是启动区,但操作系统的大小远超过512字节,因此要装载操作系统后面的内容,于是在IPL.nas文件中添加以下指令用于装载下一个512字节的内容:
在这里插入图片描述

其中JC为jump if carry”意思是如果进位标志(carry flag)是1的话,就跳转而CF的值表示装载是否成功,成功CF赋值为0,失败为1同时将错误号码赋值给AH(这里相当于提供一个失败原因)。INT 0x13表示调用BIOS的0x13号函数。在此函数下对于AH寄存器进行不同的赋值有不同的功能,具体功能可查看此网站:https://blog.csdn.net/ludylu/article/details/6593707
这里使用的功能是AH=0x02;(读盘)在此功能下相应的参数含义如下
AL=处理对象的扇区数;(只能同时处理连续的扇区,但连续处理时可能出错)
CH=柱面号 & 0xff;
CL=扇区号(0-5位)|(柱面号&0x300)>>2;
DH=磁头号;
DL=驱动器号;
ES:BX=缓冲地址;(校验及寻道时不使用)其代表着ES16+BX的内存地址。因为BX能表示的数据量太小,所以增加一个寄存器ES用于扩大范围。
所以这里操作的具体含义如下:
MOV CH,0
MOV DH,0
MOV CL,2//代表柱面0磁头0扇区2,这里意义是指定要读取的扇区
MOV AH,0x02//代表着读盘功能。
MOV AL,1//代表处理1个扇区。
MOV DL,0x00//代表着调用0号软盘驱动器,因为现在的计算机一般只有一个软盘所以这里一般都是0号。
INT 0x13//代表着调用磁盘BIOS的0x13号函数。
MOV AX,0x0820
MOV ES,AX//因为不能对ES直接赋值(上次实验中发现的不能直接使用MOV对ES寄存器赋常数),所以这里只能先通过AX再赋给ES。
而0x0820表示的是(后面BX赋值的是0)0x0820
16+0=0x8020,所以这里表示的是0x8020号内存地址。这里意义是作为装载程序的内存起始地址。
选则0x8200是因为0x8000-0x81ff的512字节是留给启动区的,且0x8000以后的内存区域没有人使用,因此便将程序装载到该处。
这里软盘中含有IPL的启动区位于C0-H0-S1(即柱面0,磁头0,扇区1)也就是第一扇区,下一个扇区是C0-H0-S2,也就是我们这里程序所装载的扇区。
2、内容2:建立容错机制:
软盘有时候会偶尔发生不能读数据的状况,但这时候只需让其重读即可,软盘不一定真的损坏了。但是若是磁盘真的损坏,程序便会进入死循环。因此,我们可以给程序一定的容错的次数,失败后让软盘重读一次,如果重读次数超过一定次数,那我们就认为软盘真的损坏了。在该程序中,设置的读数据失败次数为5次,此时将程序更改为:
在这里插入图片描述

MOV SI,0//代表记录失败次数的寄存器SI,初始值为0
如果JNC=0,即读磁盘没有出错,就跳转到fin继续执行,否则,SI就加1,并与5进行比较,如果SI<=5,即失败次数没有达到5次,那么就重置驱动器(这里用到了新的功能AH=0x00,DL=0x00表示复位软盘状态),再读一次,否则报错(这里是进入错误处理程序,即运行error处的代码这里是显示hello world)。运行结果如下,没有报错或输出hello world就说明没问题了
在这里插入图片描述

3、内容3:增加装载的扇区:
(1)增加到18扇区
因为1个柱面有18个扇区,因此读扇区时只需依次扇区加1直至第18号扇区即可。前面代码中entry部分是寄存器初始化和设置读入的扇区和存入的内存地址。所以我们要修改的地方就是entry部分的相关寄存器的值,相关代码如下:
在这里插入图片描述

MOV AX,EX
ADD AX,0x0020
MOV ES,AX//通过AX来给ES赋值并使其增加0x20(相当于设置存入的内存地址[ES:BX]加了0x200即512个字节,就是下一个扇区的范围)
CL寄存器的值表示扇区号,当CL<=18,代表着扇区还未读到18号,因此跳转到readloop继续读扇区,当超过18时就停止。运行结果和前面一样只要不报错或者显示hello world就表示没有出错。
(2)读入更多的内容(读入10个柱面)
前面读盘的指令没有变化,只需增加读18个扇区,2个磁头和10个柱面的指令即可。
在这里插入图片描述

DH存储的是磁头号,CH存储的是柱面号,代码的具体含义如下:
先读入18个扇区,如果扇区号超过18,那么就磁头号加1,当磁头号大于等于2时(表示以前磁头号为1),那么就柱面加1,同时将磁头号赋为0,如果读入柱面超过10时,就停止。这里的CYLS其实在开始处就用QUL声明为10了。
现在通过读入10个柱面,已经将软盘最初的10x2x18x512=183 320Byte=180KB的内容装载到内存中了,运行结果和前面一样只要不报错或者显示hello world就表示没有出错。
4、内容4:开始开发操作系统:
现在这里开始编写操作系统的运行代码
编写程序,使cpu暂时处于待机状态
fin:
HLT
JMP fin
代码写好后需要将代码保存在系统文件中然后保存在磁盘映像里然后需要查看我们写的代码保存在磁盘中的哪个位置,这样才方便我们后续运行我们的代码
过程为将其保存为haribote.nas,再用nask编译,输出成haribote.sys。将该文件保存到磁盘映像haribote.img中。用二进制编辑器打开该映像文件,可以查看haribote.sys文件在磁盘中是什么样的。
注意到在0x002600附近,磁盘保存着文件名,在0x004200附近,可以看到F4 EB FD的内容。
在这里插入图片描述

在0x004200附近看到的内容其实就是haribote.sys中的内容。
在这里插入图片描述

因此可以总结为:一般向一个空软盘保存文件时,文件名会写在0x002600以后的地方,文件内容会写在0x004200以后的地方。
执行结果如下:

在这里插入图片描述

5、内容5:从启动区执行操作系统及确定操作系统执行情况:
现在的程序是从启动区开始,将磁盘上的内容装载到内存0x8000号地址,因此磁盘0x4200处的内容应该位于内存0x8000+0x4200=0xc200号地址。因此只需在haribote.nas中加上跳转。
JMP 0xc200
在这里插入图片描述

同时再在编写的程序上添加ORG 0xc200确定程序的装载到内存的位置,就可以运行我们编写的程序。
在这里插入图片描述

扫描二维码关注公众号,回复: 13188280 查看本文章

为了确定我们编写的操作系统的运行情况这里添加了新的功能使用了0x10号中断
其中MOV AL,0x13表示VGA显卡(AH选择功能这里为0x00),代表设置0x13的画面模式,也就是320x200x8的彩色模式,如果画面模式切换正常,画面应该是一片漆黑。图形模式光标会消失。
运行结果如下:
在这里插入图片描述

6、内容6: 32位模式前期准备:
如果使用32位模式就不能调用BIOS功能,因为BIOS是用16位机器语言写的,如果要用,就全部都放在开头先做。这里先设置画面模式,为了后续可能需要适应不同的画面模式,将画面模式的信息保存在内存中。
在这里插入图片描述

在这里插入图片描述

先使用EQU宏定义一些内存地址方便后续调试
然后将画面模式信息存入到相应内存地址中去
[VRAM]当中保存的是0xa0000,VRam指的是显卡内存,用来显示画面的内存,里面的各个地址都对应着画面上的像素,具体地址是由于“VRam是0xa0000~0xaffff的64KB”,INT 0x16键盘服务的中断函数,AH=0x02读取键盘状态,这里用于是记录LED灯的状态,同时我们把画面的像素数、颜色数、以及从BIOS取得的键盘信息都保存起来,保存在内存的0x0ff0附近。
7、内容7:导入c语言:
修改程序,在haribote.sys中用汇编语言编写前半部分,用C语言编写后半部分。并且为了调用C语言写的程序,添加了100行左右的汇编代码(比较困难在后面再进行理解)。
C语言部分的程序文件为bootpack.c
在这里插入图片描述

goto指令相当于JMP指令。但我们想要我们的程序能够成功运行我们还需要将C语言转化Wie机器语言。
bootpack.c变成机器语言的过程:
(1)通过编译器ccl.exe转化为汇编语言bootpack.gas
(2)使用gas2nask.exe转化为bootpack.nas
(3)使用nask.exe转化为bootpack.obj
(4)使用obi2bim.exe转化为bootpack.bim
(5)使用bim2hrb.exe转化为bookpack.hrb
最终转化为bookpack.hrb机器语言
8、内容8:实现HLT:
为了能使计算机处于HALT状态,但C语言不能用HLT,因此先用汇编语言编写io_hlt函数,实现HLT功能,实现链接之后,便可以直接调用该函数,实现HALT功能。
在这里插入图片描述

[FORMAT “WCOFF”] 制作目标文件的模式
[BITS 32] 制作32位模式用的机器语言
[FILE “naskfunc.nas”] 源文件名信息
GLOBAL io_hlt 程序中包含的函数名
[SECTION .text] 代码内容
用汇编写的函数,之后还要与bootpack.obj链接,所以也要编译成目标文件。在nask目标文件的模式下,必须设定文件名信息,然后再写明下面程序的函数名,注意要在函数名的前面加上“_”,否则就不能很好地与C语言函数链接,需要链接的函数名都要用GLOBAL指令声明,如上代码所示,先写一个与用GLOBAL声明的函数名相同的标号(label)再从此往下写代码。
在C语言中要先将要调用的函数进行声明,告诉C编译器,这个函数的实现在别的文件里
运行结果如下:
在这里插入图片描述

二、遇到的问题及解决方法

1、描述问题1:在nas文件中用汇编语言编写好了函数,但在实际运行当中程序卡死:
代码:
在这里插入图片描述
在这里插入图片描述

运行结果如下(模拟器无法响应):
在这里插入图片描述

解决方案:
原因:在naskfunc.nas文件中设定的是32位模式但BIOS中断是16位的所以在这里是无法运行的
解决办法:
在启用32位模式之前编写代码这里可以在asmhead.nas文件中编写代码但要注意不能破坏后续代码程序的正常运行。
2、描述问题2:在asmhead.nas文件编程时显示字符串失败,代码如下;
在这里插入图片描述

运行情况如下(程序未能成功运行):
在这里插入图片描述

解决方案:
原因:要显示的字符标号放在太上面了没能和后面的代码区分开,导致后面的声明指令也被作为要显示的字符,从而导致程序无法正常运行
解决办法:将地址标号放到putloop后面就可以了

三、程序设计创新点

1、描述创新点1:在系统待机的时候显示字符串
因为中断不能在32位下使用,所以在asmhead.nas添加代码如下:
在这里插入图片描述

运行结果如下:
在这里插入图片描述

四、实验心得体会

猜你喜欢

转载自blog.csdn.net/qq_49327751/article/details/120734352