几乎用了一周的时间才将引导扇区源代码编写、测试完成。期间不知道经历了多少痛苦和磨难。终究原因,是因为基础知识的不扎实。因为在编写过程中有很多转移指令,因为掌握的不够牢固,不能灵活运用,导致出错频繁,精神几乎崩溃!还好,毕竟坚持了下来,基本测试通过了。
一、编译环境搭建
先说一下,我的开发环境。我用的是Windows7系统,用visual Studio2008编写源代码,因为它里面有个很不错的文本编辑器,如下图:
你可以在visual Studio2008中通过如下设置方式,使其看起来跟上面一样。打开文件菜单,新建——文件——文本文件,编写好后,将其保存为.asm文件。通过点击工具——选项——环境——字体和颜色,可以设置前景色和背景色,及字体大小。另外点击工具菜单——选项——文本编辑器——所有语言,可以显示行号。通过上面的调整,你可以发现,你的文本编辑器和上图看起来一样了。
二、在上一篇文章里,我已经说过,我的LinDos0.01要运行在软盘上。考虑到现在大多数计算机几乎不再配置软盘驱动器了,我搭建了一个虚拟计算机,来运行它。我用的是VMWare Workstation 8来搭建一个虚拟计算机。通过下面的步骤,来创建一个适合LinDos运行的虚拟硬件环境。打开VMware,点击文件菜单——新的虚拟机——自定义——WorkStation8.0——我以后再安装操作系统——选择一个客户机操作系统:其他,版本:其他——虚拟机名称:LinDos——处理器数量:1——内存:4M——不使用网络连接——BUslogic——创建一个新的逻辑磁盘——IDE——最大磁盘空间:1G,单个文件虚拟成磁盘——磁盘文件:你自己命名——完成
好了你已经搭建好我们的运行环境了。如下图:
三、创建软盘镜像文件
因为我们没有软盘驱动器,所以要在虚拟环境中运行软盘镜像文件。创建一个软盘镜像文件,很简单,用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文件。记住在设备状态中一定要选择“打开电源时连接”选项。好了打开电源,你就会看到下面的内容:
上面的源文件及软盘镜像文件您可以点击下面的连接下载。欢迎有软盘驱动器的留言测试情况。我只在我的虚拟系统中引导成功。