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

!
!#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
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值