Linux 0.11 bootsect.s 学习笔记


!bootsect模块作用是作为引导程序,应该将该模块放置在引导设备的第一个扇区,系统加电后会由bios加载到0x7c00的位置上
! 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 !1单位为16字节
!上面是指定system模块的大小,由于各模块是分开编译的,所以其他模块的大小是可知的
!
!    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
!MOVW:将DS:SI的内容送至ES:DI,是复制过去,原来的代码还在
!将BOOTSEG(0x7c00)起始的256个字节拷到0xINITSEG(0x90000),由于本模块会被bios加载到0x7c00的地址上,以上过程即为复制自身到0x90000
    jmpi    go,INITSEG
!jmpi 长跳转  jmpi 段内偏移,段地址
!跳转到复制的程序的go位置执行,!跳转到复制的程序的go位置执行,长跳转会将目标段的地址赋值给cs
go:    mov    ax,cs
    mov    ds,ax
    mov    es,ax
!将ds和es都赋值为当前段地址
! put stack at 0x9ff00.
    mov    ss,ax
    mov    sp,#0xFF00        ! arbitrary value >>512
!设置ss(堆栈段寄存器)为0x9000,sp=0xff00,即0x9000:ff00,
! load the setup-sectors directly after the bootblock.
! Note that 'es' is already set up.

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
!调用13号中断将setup模块加载到0x90200的位置,失败则复位磁盘,重新尝试加载
!int 0x13主要是磁盘服务
!0x13号中断的使用方法
! ah    0x02    读磁盘扇区到内存
! al    要读出的扇区数    上面 ax=0x0200+SETUPLEN ,SETUPLEN即存放setup模块使用的扇区数
!    ch    磁道号(柱面号)的低八位
!    cl    0-5 开始扇区 6-7磁道号高2位
!    dh     磁头号
!    dl     驱动器号(如果是硬盘则要置位7);
!    es:bx   拷贝到的目标地址  之前的代码以将0x9000赋值给es,这里只需给bx赋值即可
!    该中断出错则将CF置位

ok_load_setup:

! Get disk drive parameters, specifically nr of sectors/track
!使用int 13获取磁盘参数,ah=0x08 dl 驱动器号
! 返回信息 出错CF置位,ah为错误码
! 没有出错 则ax=0, bl驱动器类型
!ch最大磁道号的低8位 cl 0-5 每磁道最大扇区数 6-7 最大磁道号的高2位,最大号即总数,  即ax高位为最大的磁道数低位为磁道内的扇区数,即ax为总扇区数
!dh 最大磁头数 dl 驱动器数量
!es:di 磁盘参数表地址,并不是存到es:di指定的位置去,而是将地址写入es:si这两个寄存器
    mov    dl,#0x00
    mov    ax,#0x0800        ! AH=8 is get drive parameters
    int    0x13
    mov    ch,#0x00                !ch被置0,此时cx  0-5每磁道扇区数 6-7 最大磁道号的高2位
    seg cs !seg 指令指定下一条指令寻址的段地址,即下一条等效于mov ax,cs:sectors
    mov    sectors,cx
!将每磁道扇区数和最大磁道号的高2位存入cs:sectors
    mov    ax,#INITSEG
    mov    es,ax
!读磁盘参数的中断修改了es,这两将es该会INITSEG
! Print some inane message

    mov    ah,#0x03        ! read cursor pos
    xor    bh,bh
    int    0x10
    
    mov    cx,#24
    mov    bx,#0x0007        ! page 0, attribute 7 (normal)
    mov    bp,#msg1
    mov    ax,#0x1301        ! write string, move cursor
    int    0x10
!使用10号中断输出在屏幕上打印字符串,bp存放字符串位置
!0x10号中断主要提供此类服务包括设置显示模式,字符和字符串输出,和基本图形(在图形模式下的读取和写入像素)功能
!ah存放功能号 0x3是读取光标位置形状,0x13输出字符串,AL写模式,BH页码,BL颜色,CX字符串长度,DH行,DL列,ES:BP 字符串起始位置
! ok, we've written the message, now
! we want to load the system (at 0x10000)

    mov    ax,#SYSSEG
    mov    es,ax        ! segment of 0x010000 !将要存放system模块的段地址存入es,调用read_it
    call    read_it        !调用readit 将system模块加载到0x10000开始的内存中
    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        !如果root_def!=0 跳转root_definde,似乎什么也没做直接跳转去执行setup模块
    seg cs
    mov    bx,sectors        !通过磁道扇区数来自行判断root_dev
    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

! 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 当前磁道号

read_it:
    mov ax,es
    test ax,#0x0fff        !判断es段是否是64k对齐的,段寄存器存储的是20位地址的高16位,不是64k对齐就死循环。。。
die:    jne die            ! es must be at 64kB boundary
    xor bx,bx        ! bx is starting address within segment!清空bx,es:bs指向es段首,bx被用作段内基址
rp_read:
    mov ax,es
    cmp ax,#ENDSEG        ! have we loaded all yet?
    jb ok1_read        ! ENGSEG=system模块的大小+加载到的段地址,即上一条指令是在判断是否读取完成,没有完成则跳转到ok1_read,完成则返回
    ret
ok1_read:
    seg cs            
    mov ax,sectors        !从cs:sector中取出之前存入的每磁道扇区数和磁道号的高2位,存入ax
    sub ax,sread
    mov cx,ax        !将未读取的扇区数存入cx
    shl cx,#9        !左移9位得到剩余的字节数     ?最大磁道号的最高2位中应该还有一位残留在cx首
    add cx,bx
    jnc ok2_read
    je ok2_read        !当cx+bx没有溢出是跳转到ok2_read,16位寄存器64k溢出
    xor ax,ax        !如果剩余扇区大小超过64k
    sub ax,bx        !ax为当前段可剩下可用内存的大小
    shr ax,#9        !右移9位得到剩余内存可拷入的扇区的数量
ok2_read:
    call read_track        !调用read_track从当前磁道读入数据到es:bx
!若当前磁道剩余大小超过64k则读取64k内容填满当前段,否则将当前磁道剩余的所有扇区读取内存
    mov cx,ax        !可分析得出ax存放着此次读取的扇区的数量
    add ax,sread        ! ax为当前已读取扇区数    
    seg cs
    cmp ax,sectors        
    jne ok3_read        !当已读取扇区数不等于一个磁道中最大扇区数,即当前磁道内扇区未被读完则跳转到ok3_read
    mov ax,#1        ! 如果是0号磁头则换磁头,不换磁道
    sub ax,head        ! 如果是1号磁头则,换磁道,再换回0号磁头
    jne ok4_read
    inc track
ok4_read:
    mov head,ax        !磁头转换,0->1   1->0  ,每个磁道可以分别用两个磁头读,这里读取的方式 先用0读某磁道,在用1读该磁道,读完还磁道,在0,1
    xor ax,ax
ok3_read:
    mov sread,ax        !更新当前磁道已读扇区数
    shl cx,#9        !cx为此次读取的字节数
    add bx,cx        !更新基址寄存器bx
    jnc rp_read        !为超出当前段则跳回rp_read继续读取
    mov ax,es        
    add ax,#0x1000
    mov es,ax        !超出当前段,则移动将断寄存器指向下一个64k的段继续读取
    xor bx,bx        !切换到了新段,清空基址bx
    jmp rp_read

read_track:
    push ax
    push bx
    push cx
    push dx
    mov dx,track    
    mov cx,sread
    inc cx        !cx 要读取的扇区号
    mov ch,dl    !ch 磁道号
    mov dx,head
    mov dh,dl
    mov dl,#0    !dl 驱动器号
    and dx,#0x0100  !dh 磁头号,同时也设置了dl
    mov ah,#2 !在调用read_track之前ax已经右移过9位,al为剩余可读扇区数
    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 !磁盘复位,在跳转会readtrack
    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.
 */
kill_motor:
    push dx
    mov dx,#0x3f2
    mov al,#0
    outb    !将al输出到dx指定的I/o端口中,I/O端口是个啥
    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:

你可能感兴趣的:(Linux 0.11 bootsect.s 学习笔记)