开发操作系统实践(三)——LinDos引导扇区内容及运行环境搭建

时间:2022-09-02 18:40:13

        几乎用了一周的时间才将引导扇区源代码编写、测试完成。期间不知道经历了多少痛苦和磨难。终究原因,是因为基础知识的不扎实。因为在编写过程中有很多转移指令,因为掌握的不够牢固,不能灵活运用,导致出错频繁,精神几乎崩溃开发操作系统实践(三)——LinDos引导扇区内容及运行环境搭建!还好,毕竟坚持了下来,基本测试通过了。

       一、编译环境搭建

       先说一下,我的开发环境。我用的是Windows7系统,用visual Studio2008编写源代码,因为它里面有个很不错的文本编辑器,如下图:

开发操作系统实践(三)——LinDos引导扇区内容及运行环境搭建

          你可以在visual Studio2008中通过如下设置方式,使其看起来跟上面一样。打开文件菜单,新建——文件——文本文件,编写好后,将其保存为.asm文件。通过点击工具——选项——环境——字体和颜色,可以设置前景色和背景色,及字体大小。另外点击工具菜单——选项——文本编辑器——所有语言,可以显示行号。通过上面的调整,你可以发现,你的文本编辑器和上图看起来一样了。

         二、在上一篇文章里,我已经说过,我的LinDos0.01要运行在软盘上。考虑到现在大多数计算机几乎不再配置软盘驱动器了,我搭建了一个虚拟计算机,来运行它。我用的是VMWare Workstation 8来搭建一个虚拟计算机。通过下面的步骤,来创建一个适合LinDos运行的虚拟硬件环境。打开VMware,点击文件菜单——新的虚拟机——自定义——WorkStation8.0——我以后再安装操作系统——选择一个客户机操作系统:其他,版本:其他——虚拟机名称:LinDos——处理器数量:1——内存:4M——不使用网络连接——BUslogic——创建一个新的逻辑磁盘——IDE——最大磁盘空间:1G,单个文件虚拟成磁盘——磁盘文件:你自己命名——完成

        好了你已经搭建好我们的运行环境了。如下图:

开发操作系统实践(三)——LinDos引导扇区内容及运行环境搭建

       三、创建软盘镜像文件

       因为我们没有软盘驱动器,所以要在虚拟环境中运行软盘镜像文件。创建一个软盘镜像文件,很简单,用WinHex就可以办到。打开WinHex,点击文件菜单——新建——创建的文件大小:1440KB。将该文件保存为boot.img文件。好了,这就是一个软盘镜像文件了。

       四、文件在磁盘上的存放

       参见上篇文章:LinDos文件系统

       五、引导扇区源代码

       引导程序主要完成了下面的工作:

       1、设置DS、SS与CS在一个代码段,设置堆栈指针到7C00H处

       2、将引导程序前N个字节(里面包含有磁盘的信息,操作系统名称等)移入内存0050:0000处

       3、根据根目录表所在的逻辑扇区号,计算它的物理磁头号、磁道号、逻辑扇区号

       4、将根目录表所在的第一个扇区的内容读入内存0070:0000

       5、要想引导系统,必须将引导文件放在根目录表的第一个目录项中。所以这一步分析根目录表的第一项文件名是否是系统引导文件“LDloader.sys”

       6、计算系统引导文件LDloader.sys占用的扇区数,目录项偏移110B处是文件的大小

       7、将LDloader.sys文件读入系统内存0070:0000。这里又有一个先决条件,即LDloader.sys文件放在用户数据区开头的连续扇区中。

       8、JMP 0070:0000,完成系统引导,转去执行LDloader.sys。

       

;########################################
;#                                                                             #
;#    软盘引导扇区源代码                                        #
;#                                                                            #
;#  2012年3月05日                                                  #
;########################################


org 7c00h

jmp start

;系统数据区

BootSecCount db 1   ;引导程序占用的扇区数
FATtype db 12    ;文件分配表FAT类型,这里是FAT12类型
FATcount db 2    ;FAT表个数
SecsOfCluster db 1   ;每簇扇区数
BytesOfSector dw 512  ;每扇区字节数
Heads db 2     ;磁头数
CylinderOfHead dw 80  ;每面磁道数
SecsOfCylinder dw 18  ;每磁道扇区数
DiskType db 0    ;磁盘类型,驱动器号(编号法则为:磁盘的第一个分区为0,第二个为1,依次类推。若为硬盘,则第7位置1
       ;也就是若是硬盘第一个分区则设成0x80,软盘则为0x00)
RDTcount dw 128    ;根目录表中的目录项数
BytesOfDT dw 128   ;每个目录项占用的字节数
RDTlogicSecLow dw 19  ;根目录表开始逻辑扇区号低地址值,(本来该值可以根据上面的参数计算出来,但会提高引导程序的复杂度,为了保证它的内容在
RDTlogicSecHigh dw 0  ;根目录表开始逻辑扇区号高地址值, 512字节内,我们用现成的值代替繁琐的计算过程)
UserDataLogicSecLow dw 51 ;用户数据区开始逻辑扇区号低16位
UserDataLogicSecHigh dw 0 ;用户数据区开始逻辑扇区号高16位
SystemVer db "LinDos 0.01" ;系统版本号,占用11字节
Volume db "System Disk"  ;磁盘名称,最大11字节

;系统数据区结束
start:
 mov ax,cs
 mov ds,ax
 mov ss,ax
 mov sp,7c00h
 
;将系统数据区保存至内存0050:0000
 mov si,BootSecCount
 mov di,0
 mov ax,50h
 mov es,ax
 mov cx,start-BootSecCount
 cld
 rep movsb
 
;调用SubDiskAddress子程序,计算根目录表所在的磁头号、磁道号、扇区号
ReadLBR:
 mov dx,[RDTlogicSecHigh]
 mov ax,[RDTlogicSecLow]
 call SubDiskAddress
;将根目录表所在的第一个扇区的内容读入内存0070:0000
 mov ax,70h
 mov es,ax
 mov bx,0
 call ReadDisk
 jnc next2      ;读磁盘成功跳转,否则显示错误信息
NonSysErr:
 mov ax,cs
 mov es,ax
 mov ax,ErrorMSG
 mov bp,ax
 mov cx,SystemName-ErrorMSG
 call TypeMSG
;获取键盘输入,按任意键重新读取磁盘
 xor ax,ax
 int 16h
 jmp ReadLBR
next2:
 ;分析根目录表的第一项文件名是否是系统引导文件“LDloader.sys”
 mov ax,70h
 mov es,ax
 mov di,0
 mov si,SystemName
 mov cx,11
CMPstr:
 mov dl,[es:di]
 mov dh,[ds:si]
 cmp dl,dh
 jne NonSysErr
 inc di
 inc si
loop CMPstr
 mov dl,[es:di+1] ;检查该目录项最后一个字符是否是0
 cmp dl,0
 jne NonSysErr
 ;下面的程序,要将系统引导程序LDloader.sys从磁盘读入内存0070:0000
;1、计算LDloader.sys占用的扇区数,目录项偏移110B处是文件的大小
 
 mov dx,[es:112]
 mov ax,[es:110]
 mov cx,512
 call subn_32v16
 cmp bx,0
 je next3
 inc ax    ;这里我们假设LDloader.sys不会超过2^16个扇区
next3:
 push ax
;显示载入系统文件提示信息
 mov ax,cs
 mov es,ax
 mov ax,LoadMSG
 mov bp,ax
 mov cx,16
 call TypeMSG
 mov ax,70h
 mov es,ax
;2、将用户数据区开始的N个扇区读入内存0070:0000
 pop cx    ;要读入的扇区数
 mov ax,[UserDataLogicSecLow]
 mov dx,[UserDataLogicSecHigh]
 xor bx,bx
 
ReadLDlod:
 push cx
 push dx
 push ax
 push bx
 call SubDiskAddress ;计算扇区物理地址
 pop bx
 push bx
 call ReadDisk
 jc NonSysErr
 pop bx
 add bx,512
 pop ax
 pop dx
 inc ax
 adc dx,0
 pop cx
 loop ReadLDlod
 
jmp 0070h:0000
  
;=============================================================================================
;根据逻辑扇区号计算对应磁盘的磁头号、磁道号、扇区号
;逻辑扇区号与物理扇区换算关系:逻辑扇区按照扇区号、磁头号、柱面号(或磁道号)增长的顺序连续分配
 ;假设:LH---LinDos逻辑扇区0的磁头号
 ;      LC---LinDos逻辑扇区0的柱面号
 ;      LS---LinDos逻辑扇区0的扇区号
 ;      NS---每磁道扇区数
 ;      NH---磁盘总的磁头数
 ;若已知某扇区柱面号C,磁头号H,扇区号S,则其对应的逻辑扇区号RS公式为:
 ; RS=NH*NS*(C-LC)+NS*(H-LH)+(S-DS)
 ;若已知某扇区的逻辑扇区号RS,则其对应的柱面号C,磁头号H,扇区号S公式为:
 ; S=(RS MOD NS)+LS
 ; H=((RS DIV NS) MOD NH)+LH
 ; C=((RS DIV NS)DIV NH)+LC
;入口参数:DX:逻辑扇区号高16位
;     AX:逻辑扇区号低16位
;出口参数:CH—柱面,CL—扇区,DH—磁头
;=============================================================================================

SubDiskAddress:

;1、计算物理扇区号
 push dx
 push ax
 mov cx,[SecsOfCylinder]
 call subn_32v16
 add bx,1
 mov [Volume],bl     ;保存物理扇区号
;2、计算磁头号
 pop ax
 pop dx
 push dx
 push ax
 mov cx,[SecsOfCylinder]
 call subn_32v16
 xor cx,cx
 mov cl,[Heads]
 call subn_32v16
 mov [Volume+1],bl    ;保存物理磁头号
;3、计算物理磁道号
 pop ax
 pop dx
 mov cx,[SecsOfCylinder]
 call subn_32v16
 xor cx,cx
 mov cl,[Heads]
 call subn_32v16
 mov ch,al      ;返回柱面号
 mov dh,[Volume+1]    ;返回磁头号
 mov cl,[Volume]     ;返回扇区号
 ret

;===============================
;显示字符串函数
;入口参数:ES:BP,字符串地址
;     CX,字符串长度
;
;
;===============================

TypeMSG:
 push cx
 mov ah,3  ;调用int 10H 03H功能,获得光标坐标
 mov bh,0
 int 10h
 pop cx
 mov ax,1301h ;调用int 10H 13H功能,显示字符串
 mov bx,0fh
 int 10h
ret

;===============================
;无符号数的32位值除以16位值
;入:DXAX=被除数
;    CX=除数
;出:DXAX=商
;    BX=余数
;===============================
subn_32v16:
 push ax
 mov ax,dx
 mov dx,0
 div cx
 mov bx,ax
 pop ax
 div cx
 xchg bx,dx
 ret
 
;===============================
;读磁盘扇区功能,读入一个扇区
;入:
;    CH—柱面,CL—扇区,DH—磁头
;  ES:BX=缓冲区地址
;出:CF=0—操作成功,AH=00H,AL=传输的扇区数
;    CF=1—操作失败,AH=状态码
;===============================

ReadDisk:
 mov ax,0201h
 mov dl,[DiskType]
 int 13h
ret


ErrorMSG db "None system or disk error!",10,13,"Replace disk press anykey when ready.",10,13
SystemName db "LDloader.sys"
LoadMSG db "Loading... ...",10,13

times 510-($-$$) db 0

dw 0aa55h

    将上面的程序保存为boot.asm。

      六、编译程序

      利用nasm  for win32,在windows7环境中直接编译。执行如下的命令nasm boot.asm -o boot.bin

      七、将引导程序写入磁盘镜像文件

      打开WinHex,打开磁盘镜像文件,boot.bin,打开编译好的引导程序boot.bin。选择boot.bin的所有内容,将光标移动到boot.img文件0字节处,并选择该字节,点击鼠标右键——编辑——剪贴板数据——写入。好了,引导扇区的内容已经写入磁盘0面0道1扇区了。

      八、系统引导程序

       这里仅仅是个测试版本,用来检测能否引导系统,所以它的内容很简单。只是显示了一段欢迎信息。源代码如下:

org 0

jmp start

Message db "Welcome to our new operation system"

start:
 mov ax,70h
 mov es,ax
 mov ax,Message
 mov bp,ax
 mov cx,start-Message
 call TypeMSG
 jmp $

;===============================
;显示字符串函数
;入口参数:ES:BP,字符串地址
;     CX,字符串长度
;
;
;===============================

TypeMSG:
 push cx
 mov ah,3  ;调用int 10H 03H功能,获得光标坐标
 mov bh,0
 int 10h
 pop cx
 mov ax,1301h ;调用int 10H 13H功能,显示字符串
 mov bx,0fh
 int 10h
ret

将上面的程序,编译为LDloader.bin文件,并在winhex中打开,同时打开boot.bin文件。LDloader.sys文件应为于磁盘逻辑扇区51号(0面1道16扇区,至于如何计算,可参见上面的引导程序源代码)。所以我们在boot.bin文件中,打开位置菜单——转到偏移地址——26112处,将LDloader.sys文件的内容写入该处。

      九、进一步完善boot.bin

       因为引导程序还要检测根目录表的第一目录项的文件名是否是LDLoader.sys,并且要计算它的大小。所以我们要在根目录表的第一个目录项处填入字符串“LDloader.sys”。打开winhex,并打开文件boot.bin。将位置定位于偏移地址9728处(即逻辑19扇区,1面0道2扇区),从偏移9728开始,分别写入16进制:4C  44  6C  6F  61  64  65  72  2E  73  79  73  00。然后定位于9838处,写入00  02  00  00,这里是该文件的大小。

      十、在虚拟系统中引导系统

       打开vmware,选择我们刚才创建的虚拟计算机LinDos,点击右键——设置——软盘驱动器——使用软盘镜像文件:选择我们创建的boot.img文件。记住在设备状态中一定要选择“打开电源时连接”选项。好了打开电源,你就会看到下面的内容:

开发操作系统实践(三)——LinDos引导扇区内容及运行环境搭建

       上面的源文件及软盘镜像文件您可以点击下面的连接下载。欢迎有软盘驱动器的留言测试情况。我只在我的虚拟系统中引导成功。

         下载源文件及磁盘镜像文件