linux0.11启动第一步:bootsect.s

来源:互联网 发布:湖人火箭7场大战数据 编辑:程序博客网 时间:2024/05/16 18:48
!
!#2014-12-13:16-53《开始小结linux启动汇编之旅》

!#我们按下开关,执行BIOS程序(上电自检等),最后BIOS自动
!#加载MBR(即主引导分区,主引导分区上的512个字节放着我们
!#的bootsect.s程序--是编译好的二进制码)
!#将bootsect.s程序加载到内存0x07c0处(这是BIOS厂商与OS之间
!#的一个协定,BIOS相当一个裸机程序,由主板厂商编写,也是一个庞大的系统,
!#而os由其他团队编写,BIOS就相当于为我们封装好的一些功能接口,
!#让我们不必关心底层硬件,直接调用,明确的分工,减少工作量,把每一步分做到极致,
!#然后加上相互之间的一个约定--这里即0x07c0。。。大饼分着吃,事情多了一个人也完成不了,需要多人合作)
!#
!#在这里bootsect.s主要完成的功能:
!#1.移动自己从0x07c0-->0x9000
! #2.加载setup.s到0x9020
!#3.加载system到0x10000
!#完成后跳到setup.s执行

! SYS_SIZE is the number of clicks (16 bytes) to be loaded.
! 0x3000 is 0x30000 bytes = 196kB, more than enough for current
! versions of linux
! #SYSSIZE 是要被装载的clicks的数量。每一个clicks是16个字节。
! #例如:0x3000是0x30000个字节。十六进制的0x30000,十进制数是196608,196608/1024=196KB
! #对于当前的linux版本是足够了。
SYSSIZE = 0x3000
!
! bootsect.s (C) 1991 Linus Torvalds
!
! bootsect.s is loaded at 0x7c00 by the bios-startup routines, and moves
! iself out of the way to address 0x90000, and jumps there.
!#bootsect.s是被装载在0x7c00,这是通过bios-启动例程装载。
!#并将自己移动到地址0x90000,然后跳到那里。
!
! It then loads 'setup' directly after itself (0x90200), and the system
! at 0x10000, using BIOS interrupts. 
!#在自己(bootsect.s)后面直接装载‘setup’地址为0x90200,然后系统装载在0x0000,
!#都是使用BIOS中断。
!
! NOTE! currently system is at most 8*65536 bytes long. This should be no
! problem, even in the future. I want to keep it simple. This 512 kB
! kernel size should be enough, especially as this doesn't contain the
! buffer cache as in minix
!#注意,当前系统最多是8*65536字节长。这应该不成问题,即使是在未来。我想保持它的简单。
!#512KB的内核大小应该是够了,尤其是这里不像minix系统包含缓冲区高速缓冲。
!
! The loader has been made as simple as possible, and continuos
! read errors will result in a unbreakable loop. Reboot by hand. It
! loads pretty fast by getting whole sectors at a time whenever possible.
!#加载程序已经做得尽可能的简单,持续的读错误,将导致系统进入一个不可断的循环中。
!#只能手动重启。装载相当的快,每当有可能在一次中获取整个扇区。


.globl begtext, begdata, begbss, endtext, enddata, endbss  !#定义6个全局描述符
.text    !#文本段
begtext:
.data !#数据段
begdata:
.bss     !#未初始化数据段
begbss:
.text


SETUPLEN = 4 ! nr of setup-sectors   !#setup-扇区的数量
BOOTSEG  = 0x07c0 ! original address of boot-sector !#boot-扇区的原始地址
INITSEG  = 0x9000 ! we move boot here - out of the way !#boot移动到这儿
SETUPSEG = 0x9020 ! setup starts here  !#setup开始在这儿
SYSSEG   = 0x1000 ! system loaded at 0x10000 (65536). !#系统装载在0x10000
ENDSEG   = SYSSEG + SYSSIZE ! where to stop loading  !#停止加载在这儿


! ROOT_DEV: 0x000 - same type of floppy as boot.
! 0x301 - first partition on first drive etc !#第一个硬盘的第一个分区
ROOT_DEV = 0x306   !#指定根文件系统设备是第2个硬盘的第1个分区。


entry start  !#告诉连接程序,程序从start的标签处开始。
start:
!#移动bootsect.s从0x07c0到0x9000
!#下面的程序ds->0x07c0(源地址),es->0x9000(目的地址),cx->移动的数量
!#movw以字为单位移动(x80386中一个字代表两个字节)。
!#rep重复执行下面的movw,执行cx的次数。
mov ax,#BOOTSEG
mov ds,ax
mov ax,#INITSEG
mov es,ax
mov cx,#256
sub si,si
sub di,di
rep
movw
jmpi go,INITSEG  !#段间跳转,跳到新地方执行。
go: mov ax,cs !#ds,es,ss赋值成移动后的代码所在段处
mov ds,ax
mov es,ax
! put stack at 0x9ff00.  !# 栈段0x9000,sp指向栈底0x9ff00,入栈由高地址向低
mov ss,ax
mov sp,#0xFF00! arbitrary value >>512
!#sp的大小大于512,十六进制的0x200.(bootsect.s 占0x200 + setup.s占4*0x200 + 堆栈段大小)
! load the setup-sectors directly after the bootblock.
! Note that 'es' is already set up.
!#装载setup扇区,直接在boot-程序块的后面。
!#注意“es”已经被设置,在移动代码的时候es已经被设置好了,这里又设置了一次。




load_setup:
!#程序段说明,利用bios中断int 0x13将setup模块从磁盘的第二个扇区开始读到0x90200开始
!#处,共四个扇区。如果读取出错则复位磁盘,并重试。该中断用法:
!#ah=0x02-->读取磁盘扇区到内存(表动作);al=需要读取的扇区数量;ax=0x0200+SETUPLEN:表示读4个扇区到内存
!#ch=磁道号的低8位;cl=开始扇区(位0-5),磁道号高两位(位6-7)。这里cx=0x0200:表示扇区2,磁道0;
!#dh=磁头号;dl=驱动器号(若是硬盘则要置位7)
!#es:bx->指向数据缓冲区。若出错则CF标志置位。

mov dx,#0x0000! drive 0, head 0
mov cx,#0x0002! sector 2, track 0
mov bx,#0x0200! address = 512, in INITSEG
mov ax,#0x0200+SETUPLEN! service 2, nr of sectors
int 0x13! read it
jnc ok_load_setup! ok - continue !#成功,继续跳转到ok_load_setup
mov dx,#0x0000
mov ax,#0x0000! reset the diskette
int 0x13
j load_setup


ok_load_setup:


! Get disk drive parameters, specifically nr of sectors/track
! #获取磁盘驱动的参数,尤其是扇区/(track)的数量。
!#取磁盘参数INT 0x13调用格式和返回信息如下:
!#输入:
!# ah=0x08;dl=驱动器号(若是硬盘则要置位7为1)
!#输出(返回):
!# ah=0,al=0;
!# bl=驱动器类型(AT/PS2);
! # ch=最大磁道号的低8位;cl=每磁道最大的扇区数(位0-5),最大磁道号高2位(位6-7)
!# dh=最大磁头数,dl=驱动器数量;
!# es:di--->软驱磁盘参数表。
!#若出错则CF置位,且ah=状态码。
mov dl,#0x00
mov ax,#0x0800! AH=8 is get drive parameters
int 0x13
mov ch,#0x00
seg cs   !#表示下一条语句的操作数在cs段寄存器所指的段中。
mov sectors,cx  !#保存每磁道扇区数(上面的中断就是为了读取这个数***)
mov ax,#INITSEG
mov es,ax     !#读取磁盘参数中断修改了es,这里重新改回。


! Print some inane message  
!#inane [ɪ'neɪn]:adj. 空洞的,空虚的;愚蠢的
!#调用INT 0x10中断显示“Loading system ...”


mov ah,#0x03! read cursor pos !#ah=0x03表示读取光标位置
xor bh,bh
int 0x10

mov cx,#24!#共24个字符
mov bx,#0x0007! page 0, attribute 7 (normal)
mov bp,#msg1!#指向要显示的字符串
mov ax,#0x1301! write string, move cursor !#写字符串,移动光标
int 0x10


! ok, we've written the message, now
! we want to load the system (at 0x10000)
!#好,我们现在写这个消息,立刻。
!#我们想去装载这个系统在0x10000
mov ax,#SYSSEG
mov es,ax! segment of 0x010000 !#ex=system段
call read_it!#读取磁盘上的system模块,es为输入参数。
call kill_motor


! After that we check which root-device to use. If the device is
! defined (!= 0), nothing is done and the given device is used.
! Otherwise, either /dev/PS0 (2,28) or /dev/at0 (2,8), depending
! on the number of sectors that the BIOS reports currently.
!#后面我们检查要使用哪个根文件系统设备(根-设备)
!#如果设备已经定义(!=0),什么也不做,使用给定的设备。
!#否则,可能使用/dev/PS0(2,28),或是/dev/at0(2,8),使用哪个依赖当前BIOS报告的每磁道扇区数。
seg cs
mov ax,root_dev
cmp ax,#0
jne root_defined
seg cs
mov bx,sectors
mov ax,#0x0208! /dev/ps0 - 1.2Mb
cmp bx,#15
je root_defined
mov ax,#0x021c! /dev/PS0 - 1.44Mb
cmp bx,#18
je root_defined
undef_root:
jmp undef_root!#没有定义设备号,进入死循环
root_defined:
seg cs
mov root_dev,ax   !#将检查过的设备号保存起来


! after that (everyting loaded), we jump to
! the setup-routine loaded directly after
! the bootblock:
!#所有都装载后,我们跳到被直接装载在boot-块后面的setup-例程。


jmpi 0,SETUPSEG  !# =====》跳到setup程序处执行,此程序功能完成。


! This routine loads the system at address 0x10000, making sure
! no 64kB boundaries are crossed. We try to load it as fast as
! possible, loading whole tracks whenever we can.
!
! in: es - starting address segment (normally 0x1000)
!
sread: .word 1+SETUPLEN! sectors read of current track
head: .word 0 ! current head
track: .word 0! current track




!#测试输入的段值。从盘上读入的数据必须存放在位于内存地址64KB(65536字节,0x10000)的边界开始处。
!#否则进入死循环。
read_it:
mov ax,es
test ax,#0x0fff
die: jne die ! es must be at 64kB boundary
xor bx,bx ! bx is starting address within segment #bx为段内的开始地址


rp_read: !#读system数据的入口点。
!#判断数据是否已经读入全部数据。比较当前所读段是否就是系统数据末端所处的段(#ENDSEG)
!#如果不是就跳转至ok1_read标号处继续读,否这退出返回至调用处。
mov ax,es
cmp ax,#ENDSEG! have we loaded all yet?
jb ok1_read
ret
ok1_read:
!#计算和验证当前磁道需要读取的扇区数,放在ax寄存器中。根据当前磁道还未读取的扇区数以及
!#段内数据字节开始偏移位置,计算如果全部读取这些未读扇区,所读的总字节数是否会超过64KB
!#段长限制。若超过,则根据此次最多能读入的字节数(64KB-段内偏移位置),反算出此次需要
!#读取的扇区数。
seg cs
mov ax,sectors  !#取每磁道扇区数
sub ax,sread !#减去当前磁道已经读取的扇区数。
mov cx,ax !#cx=ax=当前为读取的扇区数。
shl cx,#9 !#cx=cx*512字节(cx上移9位,每一个扇区是512个字节,所以每一个扇区乘以512,表示共有多少的字节)
add cx,bx !#cx=cx+段内偏移值(bx),即此次读操作后段内读入的字节数
jnc ok2_read ! #若没有超过64KB字节,则跳转到ok2_read处执行。
je ok2_read
xor ax,ax !#若加上此次将读磁道上所有未读扇区时会超过64KB
sub ax,bx !#则计算此时最多能读取的字节数(64KB-段内偏移位置),在转换成需要读取的扇区数。
shr ax,#9
ok2_read:
call read_track
mov cx,ax !#cx=该次操作已读的操作数。
add ax,sread !#sread当前磁道上已经读取的扇区数。
seg cs
cmp ax,sectors!#判段如果磁道上还有扇区未读,则跳转到ok3_read
jne ok3_read
mov ax,#1 !#下面读磁道另一磁头(1号磁头)上的数据。如果已经完成,读下一磁道。
sub ax,head !#判断当前磁头号。
jne ok4_read !#如果是0磁头,则再去读1磁头上面的扇区数据。
inc track !#否则取读下一磁道。
ok4_read:
mov head,ax
xor ax,ax
ok3_read:
mov sread,ax ! #保存当前磁道已读扇区数。
shl cx,#9 !#上次已读扇区数*512B
add bx,cx !#调整段内偏移bx的值
jnc rp_read !#若小于64KB则跳到rp_read。否则调整当前段为读下一段做准备。
mov ax,es
add ax,#0x1000!#将段基址调整为指向下一个64KB内存开始处。
mov es,ax
xor bx,bx !#清段内数据开始偏移值。
jmp rp_read !#跳到rp_read,继续读取数据。




read_track:
!#读当前磁道上指定开始扇区和需读扇区数的数据到es:bx开始处。


push ax
push bx
push cx
push dx !#填充读取磁道中扇区数据的中断调用参数
mov dx,track    !#当前磁道号。
mov cx,sread !#取当前磁道上已经读取的扇区数。
inc cx !#cl = 开始读扇区
mov ch,dl !#ch = 当前的磁道号
mov dx,head !#取当前的磁头号
mov dh,dl !#dh = 磁头号
mov dl,#0 !#dl = 驱动器号(0表示当前驱动器)
and dx,#0x0100!#磁头号不大于1
mov ah,#2 !#ah = 2:读取扇区功能号
int 0x13
jc bad_rt  !#如果出错,则跳转至bad_rt处重试。
pop dx
pop cx
pop bx
pop ax
ret
bad_rt: mov ax,#0
mov dx,#0 !#执行驱动器复位操作。
int 0x13
pop dx
pop cx
pop bx
pop ax
jmp read_track


/*
 * This procedure turns off the floppy drive motor, so
 * that we enter the kernel in a known state, and
 * don't have to worry about it later.
 */
!#motor ['məʊtə]n. 发动机,马达;汽车
!#这个子程序关掉软驱电动机。以致我们进入内核是一个已知的状态。
!#以后就不用担心它了。
kill_motor:
push dx
mov dx,#0x3f2
mov al,#0
outb
pop dx
ret


sectors:
.word 0


msg1:
.byte 13,10    !#回车、换行的ASCII码
.ascii "Loading system ..."
.byte 13,10,13,10


.org 508  !#表示下语句从地址508(0x1fc)开始,所以root_dev在启动扇区第508开始的2字节。
root_dev:
.word ROOT_DEV
boot_flag:
.word 0xAA55


.text
endtext:
.data
enddata:
.bss
endbss:
0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 小米手机wifi慢怎么办 小米wifi网速慢怎么办 华为mate9网络差怎么办 大王卡信号差怎么办 华为P9Plus忘记密码怎么办 华为手机音质差怎么办 三星c5手机发烫怎么办 华为手机老卡怎么办 小米手机慢卡怎么办 华为指纹识别不灵敏了怎么办 金立手机信号不好怎么办 手机边框坏了怎么办 手机保护膜划了怎么办 车膜贴的有气泡怎么办 手机膜进气泡怎么办 贴的手机膜翘角怎么办 全屏膜出现气泡怎么办 手机膜的气泡怎么办 透明手机壳气泡怎么办 钢化膜边缘有气泡怎么办 贴钢化膜边缘有气泡怎么办 钢化膜边上有泡泡怎么办 贴钢化膜周边有气泡怎么办 钢化膜里面有气泡怎么办 手机保护膜破了怎么办 手机触摸屏没反应怎么办 苹果手机触屏坏了怎么办 手机边缘有气泡怎么办 手机膜有空气怎么办 电脑膜有气泡怎么办 汽车贴膜起泡怎么办 汽车玻璃膜用久了起泡怎么办 车窗玻璃膜起泡怎么办 新车贴膜气泡怎么办 贴手机钢化膜有灰尘怎么办 戒指砖石掉了怎么办 寄手机没有包装怎么办 手机背面有划痕怎么办 oopo手机声音小怎么办 手机屏幕被划了怎么办 oppo手机组装屏卡顿怎么办