日历

2008 7.5 Sat
  12345
6789101112
13141516171819
20212223242526
2728293031  
«» 2008 - 7 «»

文章搜索

日志文章

2007年09月21日 16:12:29

bootsect.s

!此文件为linux0.11版本下的原始文件,注释是参照赵炯的<Linux内核0.11完全注释>添加的

! 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 = 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.
!
! It then loads 'setup' directly after itself (0x90200), and the system
! at 0x10000, using BIOS interrupts.
!
! 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
!
! 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或.global用于定义随后的标识符是外部的或全局的,即使不使用也强制引入.
!.test .data .bss用于分别定义当前的代码段,数据段和未初始化数据段.在联结多个目标模块时,连接程序
!(Id86)会根据他们的类别八各个目标模块中的相应段分别组合(合并)在一起,这里八伞个段都定义为同一重
!叠地址范围,因此本程序实际上并不分段.另外,后面带冒号的字符串是标号:用来表示地址.
.globl begtext, begdata, begbss, endtext, enddata, endbss !定义了6个全局标识符;
.text                                     !文本段;
begtext:
.data                                     !数据段
begdata:
.bss                                     !未初始化数据段(Block Started By Symbol)
begbss:
.text                                     !文本段
SETUPLEN = 4   ! nr of setup-sectors
                      !setup的扇区数目
BOOTSEG = 0x07c0   ! original address of boot-sector
                      !bootsect的原始地址(是段地址,以下同)
INITSEG = 0x9000   ! we move boot here - out of the way
                      !将bootsect移到这里-避开:
SETUPSEG = 0x9020   ! setup starts here
                      !setup的其实地址;
SYSSEG   = 0x1000   ! system loaded at 0x10000 (65536).
                      !系统起始加载位置(64k处)
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
!设备号0x306指定根文件系统设备是第二个硬盘的第一个分区.当年linus是在第2个硬盘上安装的
!linux0.11系统,所以这里ROOT_DEV被设置为0x306.在编译这个内核时你可以根据自己的根文件系统所在
!设备修改这个设备号.这个设备号是linux系统老式的硬盘号命名方式,硬盘设备号具体值的含义如下:
!设备号=主设备号*256+次设备号(也即dev_no = (major<<8)+minor))
!(主设备号:1-内存,2-磁盘,3-硬盘,4-ttyx,5-tty,6-并行口,7-非命名管道)
!0x300-/dev/hd0-代表整个第一个硬盘;
!0x301-/dev/hd1-第一个硬盘的第一个分区;
!...
!0x304-/dev/hd4-第一个硬盘的第4个分区;
!0x305-/dev/hd5-代表第二个硬盘;
!0x306-/dev/hd6-第二个硬盘的第一个分区;
!从linux0.95版后就已经使用与现在内核相同明明方法了.
!伪指令entry迫使连接程序在生成的执行程序(a.out)中包含指定的表识符或符号.
!第一个go语句之前的部分的作用是将自身(bootsect)从目前段位置0x7c0(31k)移动到
!0x9000(576kb)处,工256字,512字节,然后跳转到移动后代码的go标号处
entry start             !告知连接程序,程序从标号start开始.
start:
mov ax,#BOOTSEG     !将ds段寄存器置为0x7c0;
mov ds,ax
mov ax,#INITSEG     !将es段寄存器置为0x9000;
mov es,ax
mov cx,#256       !设置移动计数值为256
sub si,si         !源地址 ds:si=0x07c0:0x0000
sub di,di         !目的地址es:di=0x9000:0x0000
rep               !重复执行并递减cx的值 ,知道 cx=0为止.
movw             !即movs指令,这里从内存[si]处移动cx个字到[di]处
. jmpi go,INITSEG     !段间跳转(jump intersegment).这里INITSEG指出跳转到的段地址
                    !,标号go是段内偏移地址.
!从下面开始,cpu在已移动到0x90000位置处的代码中执行.
!这段代码设置几个段寄存器,包括栈寄存器ss和sp.栈指针sp只要指向远大于512字节偏移(即地址0x90200)
!处都可以.因为从0x90200处都可以.因为从0x90200地址开始还要放置setup程序,而此时setup程序大约为
!4个扇区,因此sp要指向大于(0x200+0x200*4+堆栈大小)处.
!实际上bios把引导扇区加载0x7c00处并把执行权交给引导程序时,ss=0x00,sp=0xfffe.
go: mov ax,cs         !将ds,es和ss都置成移动后所在的段处(0x9000).
mov ds,ax         !由于程序中有堆栈操作,因此必须设置堆栈.
mov es,ax
! put stack at 0x9ff00.     !将堆栈指针sp指向0x9ff00(即0x9000:0xff00)处
mov ss,ax
mov sp,#0xFF00 ! arbitrary value >>512
! load the setup-sectors directly after the bootblock.
! Note that 'es' is already set up.
! 在bootsect程序块后紧跟着加载setup模块的代码数据.
! es已经设置好了.(在移动代码时es已经指想目的地段地址处0x9000)
!下面几行的用途是利用bios中断INT0x13将setup模块从磁盘第二个扇区开始读到0x90200开始处,共读四个扇区.
!如果读出错,则复位驱动器,并重试,没有退路.INT 0x13的使用方法如下:
!读扇区:
!ah = 0x02 - 读磁盘扇区到内存; al = 需要读出的扇区数量;
!ch = 磁道(柱面)号的低8位;   cl = 开始扇区(位0-5),磁道高2位(位6-7)
!dh = 磁头号;           dl =驱动器号(如果是磁盘,则要位7置1);
!es:bx->指乡向数据缓冲区;如果没有出错则cf标志置位,ah中是出错码;
load_setup:
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
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
!取磁盘驱动器的参数,特别是每道的扇区数量.
!取磁盘驱动器参数INT 0x13调用格式和返回信息如下:
!ah = 0x08   dl = 驱动器号(如果是硬盘则要置位7为1);
!返回信息:
!如果出错则cf置位,并且 ah = 状态码.
!ah = 0,al = 0,     bl = 驱动器类型(AT/PS2)
!ch 最大磁道号的低8位,c1 = 每磁道最大扇区数(位0-5),最大磁道号高2位(位6-7)
!dh = 最大磁头树,     d1 = 驱动器数量
!es:di ->软驱磁盘参数表.
mov dl,#0x00
mov ax,#0x0800 ! AH=8 is get drive parameters
int 0x13
mov ch,#0x00
!下面指令表示下一条语句的操作树在cs段寄存器所指的段中.它只影响下一条语句,实际上
!由于本程序和数据都被设置处于同一段中,即cs ds es的值相同,因此本程序中此处可以不同此语句
seg cs
!下句保存每磁道扇区数.对于软盘来说(dl=0),其最大磁道号不会超过256,ch已经足够表示她,因此cl的为6-7
!肯定为0.有上面到数第二句已置ch=0,因此此时cs中是每磁道扇区数.
mov sectors,cx
mov ax,#INITSEG
mov es,ax           !因为上面取磁盘参数中断改掉了es的值,这里重新改回.
! Print some inane message
!显示信息;"'loading system . . .'回车换行",共显示包括回车和换行控制字符在内的24个字符.
!bios中断0x10功能号 ah = 0x03,读光标位置.
!输入:bh = 页号
!返回:ch = 扫描开始线; ch = 扫描开始线;cl = 扫描结束线; dh = 行号(0x00顶端);dl = 列号(0x00最左边).
!
!BIOS中断0x10功能号 ah = 0x13, 显示字符串.
!输入:al = 防止光标的方式及规定属性.0x01- 表示使用b1中的属性值,光标停在字符串结尾处.
!es:bp 次寄存器对指向要显示的字符串起始位置处.cx = 显示字符串字符数.bh = 显示页面号;
!bl = 字符属性.dh = 行号.dl =列号;
mov ah,#0x03 ! read cursor pos
xor bh,bh           !首先读光标位置.返回光标位置值在dx中.
int 0x10           !dh -行(0--24);dl-列(0--79).供显示串用.

mov cx,#24         !共显示24个字符
mov bx,#0x0007 ! page 0, attribute 7 (normal)
mov bp,#msg1         !es:bp指向要显示的字符串.
mov ax,#0x1301 ! write string, move cursor
int 0x10
! ok, we've written the message, now
! we want to load the system (at 0x10000)
! 现在将system模块加载到0x10000(64kb)处,
mov ax,#SYSSEG
mov es,ax ! segment of 0x010000   !es = 存放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)
!就直接使用给定的设备.否则就需要知道根据bios报告的每磁道扇区数来确定到底使用/dev/ps0(2,28)还是
!/dev/at0(2,8).
!!上面一行中两个设备文件的含义:
!!在linux中软驱的主设备号是2,次设备号 = type*4+nr,其中nr为0-3分别对应软驱A B C 或 D;type是软驱的类型(2->1.2MB
!!或7-->1.44MB等)因为7*4+0=28,所以/dev/ps0(2,28)指的是1.44MB A驱动器,其设备号是0x021c
!!同理 /dev/at0(2,8)指的是1.28MB A驱动器,其设备好为0x0208
seg cs
mov ax,root_dev       !取508,509字节处的根设备号并判断是否已被定义.
cmp ax,#0
jne root_defined
!取上面第88行保存的每磁道扇区数.如果sectors=15则说明是1.2MB的驱动器;如果sectors=18,则说明是1.4MB软驱.
!因为是可引导的驱动器,所以肯定是A驱.
seg cs
mov bx,sectors
mov ax,#0x0208 ! /dev/ps0 - 1.2Mb
cmp bx,#15           !判断每磁道扇区树是否是15
je root_defined       !如果等于,则ax中就是引导驱动器的设备号.
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       !将检查过的设备号保存到root_dev中.
! after that (everyting loaded), we jump to
! the setup-routine loaded directly after
! the bootblock:
!到此,所有程序都加载完毕,我们就跳转到被加载在bootsect后面的程序上
!段间跳转指令(jump intersegment).跳转到0x9020:0000去执行.
jmpi 0,SETUPSEG           !!!over
! 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)
!
!该子程序将系统模块加载到内存地址0x10000出,并确定没有跨越64KB的内存边界
!输入 es - 开始内存地址段值(通常是0x1000)
!下面伪操作.word定义一个2字节目标.相当于C语言程序中定义的变量和所占内存空间的大小.
!'1+SETUPLEN'表示开始时已经读进1个引导扇区和setup程序所占的扇区数SETUPLEN

!!主要寄存器含义
!!sectors                 磁道扇区数目
!!sread: .word 1+SETUPLEN 当前磁道中已读扇区数
!!head: .word 0   当前磁头号
!!track: .word 0   当前磁道号
!!es                     当前段


sread: .word 1+SETUPLEN ! sectors read of current track !当前磁道中已读扇区数
head: .word 0   ! current head         !当前磁头号
track: .word 0   ! current track       !当前磁道号
read_it:

!首先测试输入的段值,从盘上读入的数据必须存放在位与64kb的边界开始处,否则进入死循环.
!test以比特位相与两个操作数,结果只影响标志位ZF.
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:
mov ax,es
cmp ax,#ENDSEG ! have we loaded all yet?
jb ok1_read
ret
ok1_read:
seg cs
mov ax,sectors
sub ax,sread
mov cx,ax
shl cx,#9
add cx,bx
jnc ok2_read
je ok2_read
xor ax,ax
sub ax,bx
shr ax,#9
ok2_read:
call read_track
mov cx,ax
add ax,sread
seg cs
cmp ax,sectors
jne ok3_read
mov ax,#1
sub ax,head
jne ok4_read
inc track
ok4_read:
mov head,ax
xor ax,ax
ok3_read:
mov sread,ax
shl cx,#9
add bx,cx
jnc rp_read
mov ax,es
add ax,#0x1000
mov es,ax
xor bx,bx
jmp rp_read
!read_track子程序.
!读当前磁道上指定开始扇区和需读扇区数的数据到es:bx开始处.
!int 0x13, ah = 2 的说明
!al - 需读扇区数;es:bx - 缓冲区开始位置.
read_track:
push ax
push bx
push cx
push dx
mov dx,track
mov cx,sread
inc cx
mov ch,dl
mov dx,head
mov dh,dl
mov dl,#0
and dx,#0x0100
mov ah,#2
int 0x13
jc 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.
*/
!下面的程序中0x3f2是软盘控制器的一个端口,被成为数字输出寄存器(DOR)断口.它是一个8位的寄存器.
!其7-4位分别用于控制4个软驱(D-A)的启动和关闭.位3-2用于允许禁止DMA和中断请求以及启动/复位软盘控制
!器FDC.位1-0用于选择操作的软驱.下面的设置al 为0值,就是用于选择A驱动器,关闭FDC,禁止DMA和中断请求,
!关闭马达.
kill_motor:
push dx
mov dx,#0x3f2
mov al,#0     !A驱动器 关闭FDC,禁止DMA和中断请求,关闭马达.
outb         !将al中的内容输出到 dx指定端口去
pop dx
ret
sectors:
.word 0       !存放当前启动软盘每磁道的扇区数
msg1:             !调用BIOS中断显示的信息  
.byte 13,10   !回车 换行的ASCII码
.ascii "Loading system ..."
.byte 13,10,13,10
.org 508
root_dev:
.word ROOT_DEV   !存放根文件系统所在设备号
boot_flag:
.word 0xAA55     !引导扇区标识的两个字节
 
.text
endtext:
.data
enddata:
.bss
endbss:

Tags: bootsect  

类别: Linux |  评论(1) |  浏览(2000) |  收藏
一共有 1 条评论
1楼 三笑数码科技--电脑扩展主机Ne.. 2007年09月25日 13:21:19 Says:
我看不懂到此一游
发表评论