linux-0.11中bootsect.s分析

来源:互联网 发布:mac 泳道图 工具 编辑:程序博客网 时间:2024/05/21 14:07


!
! 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   /*这里应该是192k,如果每一k 1000b的话,那就是196k*/
!
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 begtext, begdata, begbss, endtext, enddata, endbss
.text
begtext:
.data
begdata:
.bss
begbss:
.text

SETUPLEN = 4    ! nr of setup-sectors   四个扇区
BOOTSEG  = 0x07c0   ! original address of boot-sector
INITSEG  = 0x9000   ! we move boot here - out of the way
SETUPSEG = 0x9020   ! setup starts here
SYSSEG   = 0x1000   ! system loaded at 0x10000 (65536).
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

entry start
start:
 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           !jmpi 之后 cs:IP  是 INITSEG:go,即0x9000:go
 mov ds,ax
 mov es,ax
! put stack at 0x9ff00.
 mov ss,ax
 mov sp,#0xFF00  ! arbitrary value >>512

! load the setup-sectors directly after the bootblock.
! Note that 'es' is already set up.

/*
功能02H
功能描述:读扇区
入口参数:  AH=02H
            AL=扇区数      要读取的扇区数              sector
            CH=柱面/磁道数                             track
            CL=扇区数      起始扇区数                  sector
            DH=磁头数                                  head
            DL=驱动器,00H~7FH:软盘;80H~0FFH:硬盘   drive
            ES:BX=缓冲区的地址
出口参数:
    CF=0——操作成功,AH=00H,AL=传输的扇区数
    否则,AH=状态代码,参见功能号01H中的说明
*/
load_setup:
 mov dx,#0x0000  ! drive 0, head 0 DH=磁头数 DL=表示软盘(00H~7FH:软盘;80H~0FFH:硬盘)
                        /*mov dx,#0x0000 表示0磁头的软盘*/ 
 mov cx,#0x0002  ! sector 2, track 0 表示从0磁道2扇区开始读取  因为第一个扇区已经被bios加载到0x7c00
 mov bx,#0x0200  ! address = 512, in INITSEG  es:bx指定数据存放目的地,即0x9000:0xff00
 mov ax,#0x0200+SETUPLEN ! service 2, nr of sectors al表示要读取的扇区个数
 int 0x13   ! read it   /*int 0x13读取数据成功之后 会设置EFLAGS寄存器CF=0位, jnc判断CF=0表示成功*/
 jnc ok_load_setup  ! ok - continue 加载成功将跳转到ok_load_setup
 mov dx,#0x0000      /*这里表示加载失败*/
 mov ax,#0x0000  ! reset the diskette /*diskette  磁盘*/
 int 0x13            /*磁盘系统复位*/
 j load_setup   /*跳转回去继续执行,就是不知道这里是不是无条件跳转*/

ok_load_setup:

! Get disk drive parameters, specifically nr of sectors/track  获取每个磁道最大多少扇区数
/*
功能08H
功能描述:读取软盘驱动器参数
入口参数:
    AH=08H
    DL=驱动器,00H~7FH:软盘;80H~0FFH:硬盘
出口参数:
    CF=1——操作失败,AH=状态代码,参见功能号01H中的说明,
否则:
    BL  =01H — 360K
        =02H — 1.2M
        =03H — 720K
        =04H — 1.44M
    CH=最大柱面数/磁道号的低8位
    CL的位7-6=最大柱面数的高2位
    CL的位5-0=最大扇区数
    DH=最大磁头数
    DL=最大驱动器数
    ES:DI=磁盘驱动器参数表地址
*/
 mov dl,#0x00
 mov ax,#0x0800  ! AH=8 is get drive parameters
 int 0x13            ! 获取软盘的最大扇区数*/
 
 mov ch,#0x00        !最大扇区数存放在cx寄存器中,清空高8位CH,保留低8位CL,CL里面是所有扇区数
 seg cs     
 mov sectors,cx      !CL(0-5bits)软盘最大扇区数, 保存软盘驱动器的最大扇区数
 
/*
    seg cs表示后面代码要是用cs 作为段的基地址。
    mov sectors,cx ,是要把获取到的drive参数存到数据段,但没有使用ds段,而是强制使用cs段作为基地址,即cs:sectors
    其实这里没有必要非一定要使用cs段,上面一次跳转之后,cs==ds,所以是可以使用ds的,这样看代码就方便多了,
    不知道作者是不是秀技术。
    as86汇编,不知道能不能把数据区和代码区区分开来,这样在阅读代码和代码中处理数据直接饮用ds段,也不用seg cs段超越了。
    但,at&t汇编中可以区分,但at&t只能是gnu的32位汇编,唉
    谁让电脑启动时候都是实模式16位呢,只能用16位as86汇编(和intel语法很像)
    as86编译器  使用minix汇编语法        as86编译器可以生成16位/32位汇编
    gnu编译器   使用at&t汇编语法         gnu编译器只能生成32位汇编 
    seg cs如果不加这段代码,那么后面的
    mov sectors, cx会默认保存一个16位到ds:sectors中。
    因为此时cs=ds,所以加不加都无所谓,
    如果加上这段代码,那么 seg cs 只是提示后面一条指令在操作的时候是存放到cs段中,即cs:sectors
*/

 !上面操作处理完之后,那么就已经把drive 参数存储起来了
 mov ax,#INITSEG     ;指定es 0x9000 因为后面要是用ES:BP指向要写入的字符串的地址,所以要设置下es段
 mov es,ax

! Print some inane message /* inane无意义的 */

/*
功能03H
功能描述:在文本坐标下,读取光标各种信息
入口参数:
        AH=03H
        BH=显示页码
            0-3 in modes 2&3
            0-7 in modes 0&1
            0 in graphics modes
出口参数:
        CH=光标的起始行
        CL=光标的终止行
        DH=行(Y坐标)
        DL=列(X坐标)
*/
 mov ah,#0x03  ! read cursor pos
 xor bh,bh          
 int 0x10            !获取光标位置


/*
功能13H
功能描述:在Teletype模式下显示字符串
入口参数:
    AH=13H
    BH=页码
    BL=属性(若AL=00H或01H)
    CX=显示字符串长度
    (DH、DL)=坐标(行、列)  输出到光标所在的行,列。上面已经把获取到的坐标信息存放到dx中,所以这里直接使用。
    ES:BP=显示字符串的地址
    AL=显示输出方式
        0——字符串中只含显示字符,其显示属性在BL中。显示后,光标位置不变
        1——字符串中只含显示字符,其显示属性在BL中。显示后,光标位置改变
        2——字符串中含显示字符和显示属性。显示后,光标位置不变
        3——字符串中含显示字符和显示属性。显示后,光标位置改变
出口参数:无
*/
    !光标位置信息已经存放在DX中了,这里直接使用就好了
 mov cx,#24          ! 字符串长度
 mov bx,#0x0007  ! page 0, attribute 7 (normal) 写到屏幕第0页内存中,第 7个属性(具体什么吗,不知道啊)
 mov bp,#msg1        ! msg1指向要输出的信息 ES:BP=显示字符串的地址 ,es前面已经指定为cs,所以这里直接使用
 mov ax,#0x1301  ! write string, move cursor     AL=显示输出方式    AH=13H
 int 0x10            ! 写字符串到屏幕


! ok, we've written the message, now
! we want to load the system (at 0x10000)

 mov ax,#SYSSEG
 mov es,ax  ! segment of 0x010000   此时 es指向0x1000
 call read_it !读取磁盘system参数,直到读取128k
 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.

 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:

 jmpi 0,SETUPSEG  !跳转到setup.s

! 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   ! 磁头数
track: .word 0   ! 磁道数

read_it:
 mov ax,es           !es  是 0x1000
 test ax,#0x0fff     !ax  是 0x1000  这里的目的就是必须指定es段为0x1000
    /*test执行的就是and的指令,只不过不会保存and执行的结果,而是根据and的结果设置flags寄存器的各种标志
    这里在计算地址是会是es<<4 + di, di是0,所以 地址是0x10000=64k*/
die:jne die   ! es must be at 64kB boundary  如果不是0,继续死循环,相当于参数校验吧
 xor bx,bx  ! bx is starting address within segment  清空bx
rp_read:
 mov ax,es       ! 刚开始es是0x1000:0,当读取完64k之后es就是0x2000:0
 cmp ax,#ENDSEG ! have we loaded all yet?  从0x1000:0到0x3000:0之间是128k
 jb ok1_read     ! 如果ax小于ENDSEG,则继续读取, 否则结束
 ret
ok1_read:
 seg cs
 mov ax,sectors !获取前面保存最大扇区数
 sub ax,sread   !ax = ax - sread;  sread是已读扇区数,第一次是5,后面有修改过,变量, 剩下的就是未读取的扇区数
/*
CH=柱面数的低8位
CL的位7-6=柱面数的高2位
CL的位5-0=扇区数
    由于上面存储是的cx,获取的是drive上所有的柱面数,
    在执行sub ax,sread时候,由于sread是5,cl的0到5位的范围是0-64, 所以在相减的时候,是用
    ax = ax - 5;
入口参数:  AH=02H
            AL=扇区数      要读取的扇区数              sector
            CH=柱面/磁道数                             track
            CL=扇区数      起始扇区数                  sector
            DH=磁头数                                  head
            DL=驱动器,00H~7FH:软盘;80H~0FFH:硬盘   drive
            ES:BX=缓冲区的地址
*/   
 mov cx,ax   !cx表示未读取的扇区数
 shl cx,#9   !每个扇区512字节,左移9位之后, cx是未读取的扇区所占的字节数
 add cx,bx   !循环之前已经清空bx,所以第一次循环bx是0
 jnc ok2_read !cx 16位,2的16次方是64k,如果大于64k进位,继续向下执行,否则,继续执行ok2_read
 je ok2_read  !当读取满足64k之后会继续向下执行,跳转到

/*若加上此次将读磁道上所有未读扇区时未超过64KB,计算最多能读入多少
  字节数(64KB-段内读偏移位置),再转换成需要读取的扇区数。
*/
 xor ax,ax   !清空ax
 sub ax,bx   !0 - bx 就是取补数,就等于这次还能读入的字节数
 shr ax,#9   !右移9位等同于除以512,转换成扇区数

ok2_read:           !调用ok2_read的时候把ax未读取的扇区数传递过来了
 call read_track !调用read_track的时候把ax未读取的扇区数传递过来了,在read_track中ax就是需要读取的扇区数,如果读取成,说明ax已经被读取
 mov cx,ax       !所以这里的ax表示已经被成功读取的扇区数
 add ax,sread    !上面read_track中已经被读取的扇区数ax+sread(原先被读取的扇区数),ax=ax+sread,表示总共读取的扇区数
 seg cs
 cmp ax,sectors  !比对在cs:sectors中存放的最大扇区数和目前已经读取的扇区数是否相等,如果不等则跳转,否则向下执行
 jne ok3_read    !如果已经读取完最大扇区数,相等了,继续向下执行  
 mov ax,#1      
 sub ax,head     !读取磁头1
 jne ok4_read    !如果不相等,即磁头1还未读取,那么跳转继续读取
 inc track       !增加磁道号
ok4_read:
 mov head,ax
 xor ax,ax
 !这里好像应该加上一句  xor  cx, cx 把cx清空
ok3_read:
 mov sread,ax    !这里的ax是已经读取的扇区数, 从新初始化已经被读取的扇区
 shl cx,#9       !cx表示已经读取多少字节了
 add bx,cx       !由于前面读取之后需要改变es:bx中的bx偏移指针(第一次读取bx是0)
 jnc rp_read     !由于这里bx是16位,所以最大65535个字节(64k),当大于64k之后,发生进位,这里就是检测有没有读取到64k数据,如果没有继续读取,否则继续向下执行
 mov ax,es       !此时es是0x1000:0
 add ax,#0x1000  !此时ax是0x2000:0
 mov es,ax       !改变基地址es
 xor bx,bx       !情况bx
 jmp rp_read     !无条件跳转继续读取

read_track:
 push ax
 push bx
 push cx
 push dx
/*
功能02H
功能描述:读扇区
入口参数:  AH=02H
            AL=扇区数      要读取的扇区数              sector
            CH=柱面/磁道数 低8位磁道号                 track
            CL=扇区数      起始扇区数(0-5),高两位磁道号(6-7,hard disk only) sector/track
            DH=磁头数                                  head
            DL=驱动器,00H~7FH:软盘;80H~0FFH:硬盘   drive   (bit 7 set for hard disk)
            ES:BX=缓冲区的地址
AH = 02h
AL = number of sectors to read (must be nonzero)
CH = low eight bits of cylinder number
CL = sector number 1-63 (bits 0-5)
    high two bits of cylinder (bits 6-7, hard disk only)
DH = head number
DL = drive number (bit 7 set for hard disk)
ES:BX -> data buffer           
出口参数:
    CF=0——操作成功,AH=00H,AL=传输的扇区数
    否则,AH=状态代码,参见功能号01H中的说明
*/ 
    !由于前面已经传递过来需要读取的扇区数ax,所以这里直接使用ax
 mov dx,track    ! 当前的磁道号  这里dx是变量    /*就是想把磁道号放到cx的高8位,起始扇区放到低8位*/
 mov cx,sread    ! 当前已读扇区数
 inc cx          ! 开始读取的扇区,从哪里开始读,因为bit 0-5表示起始扇区,所以inc cx并不会改变cx的高8位ch.
 mov ch,dl       ! 低8位的磁道号赋值给CH
 
 mov dx,head     ! 当前磁头号赋值给dx        /*就是想把磁头数存到dx的高8位,低8位表示驱动器属性(软盘 or 硬盘)*/
 mov dh,dl       ! 当前磁头号的低8位赋值给了高8位
 mov dl,#0       ! 低8位置0, 表示这是软盘
 and dx,#0x0100  ! mov dx,head 磁头号赋值为0,所以这里&之后还是0,所以是取0磁头的数据
 mov ah,#2       ! 功能号
 int 0x13        ! es 是 0x1000  bx前面清空了 是0,所以这里缓冲区地址是0x1000:0
 
 jc bad_rt       !判断EFLAGS中CF标志,如果出错,跳转到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  !跳转到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.
 */
kill_motor:
 push dx
 mov dx,#0x3f2   !写入到这个端口
 mov al,#0 
 outb            !将al中的值输出到dx指定的端口去。
 pop dx
 ret

sectors:        !扇区数
 .word 0

msg1:
 .byte 13,10
 .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:

原创粉丝点击