linux-0.12/boot/bootsect.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
!
#include 
    ;#define DEF_INITSEG    0x9000
    ;#define DEF_SYSSEG 0x1000
    ;#define DEF_SETUPSEG   0x9020
    ;#define DEF_SYSSIZE    0x3000
SYSSIZE = DEF_SYSSIZE  ;指明编译链接后system模块的大小 0x3000
!
!   bootsect.s      (C) 1991 Linus Torvalds
!   modified by Drew Eckhardt
!
! 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
;注意 内核系统最大长度限制是512KB(8*64KB)
!
! 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 
;setup程序代码占用4个扇区
BOOTSEG  = 0x07c0           ! original address of boot-sector
INITSEG  = DEF_INITSEG          ! we move boot here - out of the way
;0x9000 我们把bootsect移动到位置0x90000 避开系统模块占用处
SETUPSEG = DEF_SETUPSEG         ! setup starts here
;0x9020 start模块从内存0x90200开始
SYSSEG   = DEF_SYSSEG           ! system loaded at 0x10000 (65536).
;0x1000 系统模块从0x10000开始
ENDSEG   = SYSSEG + SYSSIZE     ! where to stop loading
;0X1000 +0X3000 =0X4000

! ROOT_DEV & SWAP_DEV are now written by "build".
ROOT_DEV = 0
SWAP_DEV = 0

;这部分把bootsect从0x7c00移动到0x90000处
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
;0x7c00---->0x90000 256word
;ds=0x7c0  es=0x9000 
;ds[si++]--->es[[di++]
;最后jmpi跳转到0x9000:go处
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;


;这段代码用来设置堆栈
;考虑内存空间
;我们目前 bootsect 0x90000~0x90200
;setup 0x90200~0x90a00
;system 0x10000~*
;所以我们把堆栈设置到段0x9000最后即可。
;考虑到我们后面的参数拷贝 需要预留12字节的空间 0x9FF00-12=0X9FEF4
go: mov ax,cs       
    mov dx,#0xfef4  ! arbitrary value >>512 - disk parm size

    mov ds,ax 
    mov es,ax
    push    ax;ax=0x9000
    ;保存ax  不过因为后两句重置了堆栈指针  所以这里是错误的。

    mov ss,ax       ! put stack at 0x9ff00 - 12.
    mov sp,dx
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
/*
 *  Many BIOS's default disk parameter tables will not 
 *  recognize multi-sector reads beyond the maximum sector number
 *  specified in the default diskette parameter tables - this may
 *  mean 7 sectors in some cases.
 *
 *  Since single sector reads are slow and out of the question,
 *  we must take care of this by creating new parameter tables
 *  (for the first disk) in RAM.  We will set the maximum sector
 *  count to 18 - the most we will encounter on an HD 1.44.  
 *
 *  High doesn't hurt.  Low does.
 *
 *  Segments are as follows: ds=es=ss=cs - INITSEG,
 *      fs = 0, gs = parameter table segment
 */

;在多扇区读时 当所读的扇区数超过磁盘参数表中的指定最大扇区数时,很多BIOS不能正确识别
;单扇区读取速度太慢,所以我们要为第一个驱动器(我们的启动盘,软驱)重置参数表,把最大扇区数设置为18(在1.44MB下会遇到)
    push    #0
    pop fs
    mov bx,#0x78        ! fs:bx is parameter table address
    seg fs
    lgs si,(bx)         ! gs:si is source
    ;使gs=fs=0 si=bx

    mov di,dx           ! es:di is destination
    mov cx,#6           ! copy 12 bytes
    cld

    rep
    seg gs
    movw
    ;gs:si=0:0x78
    ;es:di=0x9000:0xfef4

    mov di,dx
    ;di=0xfef4
    movb    4(di),*18       ! patch sector count

    ;bx=0x78
    seg fs
    mov (bx),di
    seg fs
    mov 2(bx),es
    ;es:di=0x9000:0xfef4

    pop ax;恢复0x9000
    mov fs,ax
    mov gs,ax

    xor ah,ah           ! reset FDC 
    xor dl,dl
    int     0x13
    ;INT 1EH — 指向磁盘驱动器参数表指针 
    ;功能 00H 
    ;功能描述:磁盘系统复位 
    ;入口参数:AH=00H 
    ;DL=驱动器,00H~7FH:软盘;80H~0FFH:硬盘 
    ;出口参数:CF=0——操作成功,AH=00H,否则,AH=状态代码,参见功能号 01H 中的说明   

;重置成功 我们开始加载我们的setup模块
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
! load the setup-sectors directly after the bootblock.
! Note that 'es' is already set up.

;功能 02H 
;功能描述:读扇区 
;入口参数:AH=02H 
;AL=扇区数 
;CH=柱面 
;CL=扇区 
;DH=磁头 
;DL=驱动器,00H~7FH:软盘;80H~0FFH:硬盘 
;ES:BX=缓冲区的地址 
;出口参数:CF=0——操作成功,AH=00H,AL=传输的扇区数,否则,AH=状态代码,参见
;功能号 01H 中的说明 

load_setup:
    xor dx, dx          ! drive 0, head 0  ;驱动器0 磁头0
    mov cx,#0x0002      ! sector 2, track 0;扇区2 柱面0
    mov bx,#0x0200      ! address = 512, in INITSEG; ES:BX=缓冲区的地址
    mov ax,#0x0200+SETUPLEN ! service 2, nr of sectors ;功能号2 读取4个扇区 
    int 0x13            ! read it
    jnc ok_load_setup       ! ok - continue
    ;发生错误则继续执行 不发生错误 跳转

    push    ax          ! dump error code
    call    print_nl
    mov bp, sp
    call    print_hex
    pop ax  

    xor dl, dl          ! reset FDC
    xor ah, ah
    int 0x13
    j   load_setup

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;已经成功加载setup模块
;接下来我们要加载system模块 system模块较大 我们要获取磁盘驱动器的参数
;功能 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=磁盘驱动器参数表地址

ok_load_setup:

! Get disk drive parameters, specifically nr of sectors/track
;sectors是一个word类型的内存变量

    xor dl,dl
    mov ah,#0x08        ! AH=8 is get drive parameters
    int 0x13
    xor ch,ch
    seg cs
    mov sectors,cx ;每磁道的扇区数
    mov ax,#INITSEG
    mov es,ax

! Print some inane message
;功能 03H 
;功能描述:在文本坐标下,读取光标各种信息 
;入口参数:AH=03H 
;BH=显示页码 
;出口参数:CH=光标的起始行 
;CL=光标的终止行 
;DH=行(Y 坐标) 
;DL=列(X 坐标) 

;功能 13H 
;功能描述:在 Teletype 模式下显示字符串 
;入口参数:AH=13H 
;BH=页码 
;BL=属性(若 AL=00H 或 01H) 
;CX=显示字符串长度 
;(DH、DL)=坐标(行、列) 
;ES:BP=显示字符串的地址 AL=显示输出方式 
;0——字符串中只含显示字符,其显示属性在 BL 中。显示后,光标位置不变 
;1——字符串中只含显示字符,其显示属性在 BL 中。显示后,光标位置改变 
;2——字符串中含显示字符和显示属性。显示后,光标位置不变 
;3——字符串中含显示字符和显示属性。显示后,光标位置改变 
;出口参数:无 

    mov ah,#0x03        ! read cursor pos
    xor bh,bh
    int 0x10

    mov cx,#9   ;显示9个字符
    mov bx,#0x0007      ! page 0, attribute 7 (normal)
    mov bp,#msg1
    mov ax,#0x1301      ! write string, move cursor
    int 0x10
;输出回车+换行+Loading  一共9个字符
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;我们设置es为0x1000 并加载系统段
! ok, we've written the message, now
! we want to load the system (at 0x10000)

    mov ax,#SYSSEG
    mov es,ax       ! segment of 0x010000
    call    read_it     ;成功读取了 system模块
    call    kill_motor
    call    print_nl  ;输出回车换行的东东

;至此我们成功的加载了system模块
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

! 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
    or  ax,ax
    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:
    :fail
    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 system 确定根文件系统设备等等)

! 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

;加载system es已经设置好
read_it:
    mov ax,es
    test ax,#0x0fff
    ;保证es在64kb边界上
die:    jne die         ! es must be at 64kB boundary
    xor bx,bx       ! bx is starting address within segment
rp_read:
    ;判断我们是否已经全部加载完毕  比较当前加载的段寄存器是否等于ENDSEG
    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   ;*512+当前段内偏移值
    jnc ok2_read  ;如果没有超过了64kb的边界范围 就跳转到ok2
    je ok2_read   ;如果等于64kb 也跳转到ok2
    xor ax,ax
    sub ax,bx
    shr ax,#9   ;如果超过64kb就算出本次最多能读取几个扇区

;执行到ok2的时候 ax保存的是本次要读取的扇区数目
ok2_read:
    call read_track
    mov cx,ax;al本次传送的扇区数
    add ax,sread ;得到当前已经读到的扇区数
    seg cs
    cmp ax,sectors ;比较是否已经读完当前磁道磁头的所有扇区
    jne ok3_read ;没有读完跳到ok3
    mov ax,#1    ;读完了就切换磁头
    sub ax,head
    jne ok4_read ;如果切换后磁头是1 表明应该继续读 跳到ok4
    inc track  ;切换后磁头如果是0 表明应该增加磁道  track+1

ok4_read:
    ;这里处理磁头head
    ;如果是切换后磁头是1 跳过来的  原来的head是0 这里就置一
    ;如果切换后磁头是0 原来head是1 这里把head置0
    mov head,ax
    xor ax,ax

ok3_read:
    ;这里应该是处理扇区
    mov sread,ax ;ax存储的是已经读取的扇区数
    shl cx,#9   ;上次已经读取的扇区*512
    add bx,cx   ;调整下次读取数据的开始位置
    jnc rp_read ;如果bx不是段末尾的话 就跳回rp——read
    ;否则说明bx已经位于段末  需要调整段

    mov ax,es
    add ah,#0x10 ;eg: 0x9020-->0x9030         0x100-256B
    mov es,ax
    xor bx,bx   ;清除偏移
    jmp rp_read

;ax 保存要读取的扇区数目
;功能 0EH 
;功能描述:在 Teletype 模式下显示字符 
;入口参数:AH=0EH 
;AL=字符 
;BH=页码 
;BL=前景色(图形模式) 
;出口参数:无 

;功能 02H 
;功能描述:读扇区 
;入口参数:AH=02H 
;AL=扇区数 
;CH=柱面 
;CL=扇区 
;DH=磁头 
;DL=驱动器,00H~7FH:软盘;80H~0FFH:硬盘 
;ES:BX=缓冲区的地址 ;
;出口参数:CF=0——操作成功,AH=00H,AL=传输的扇区数,否则,AH=状态代码,参见
;功能号 01H 中的说明
read_track:
    pusha
    ;输出一个  .
    pusha   
    mov ax, #0xe2e  ! loading... message 2e = .
    mov bx, #7
    int 0x10
    popa        

    mov dx,track ;磁道
    mov cx,sread ;已读的扇区数
    inc cx       ;cl开始读的扇区
    mov ch,dl    ;当前的磁道
    mov dx,head  ;当前的磁头
    mov dh,dl    ;dh =磁头号  dl=驱动器号
    and dx,#0x0100  ;保证磁头号不大于1  驱动器号=0
    mov ah,#2

    push    dx              ! save for error dump
    push    cx
    push    bx
    push    ax

    int 0x13
    jc bad_rt   ;读磁盘 出错则跳转
    add sp, #8
    popa
    ret

bad_rt: push    ax              ! save error code
    call    print_all           ! ah = error, al = read


    xor ah,ah
    xor dl,dl
    int 0x13


    add sp, #10
    popa    
    jmp read_track

/*
 *  print_all is for debugging purposes.  
 *  It will print out all of the registers.  The assumption is that this is
 *  called from a routine, with a stack frame like
 *  dx 
 *  cx
 *  bx
 *  ax
 *  error
 *  ret <- sp
 *
*/

print_all:
    mov cx, #5      ! error code + 4 registers
    mov bp, sp  

print_loop:
    push    cx      ! save count left
    call    print_nl    ! nl for readability
    jae no_reg      ! see if register name is needed

    mov ax, #0xe05 + 0x41 - 1
    sub al, cl
    int 0x10

    mov al, #0x58   ! X
    int 0x10

    mov al, #0x3a   ! :
    int 0x10

no_reg:
    add bp, #2      ! next register
    call    print_hex   ! print it
    pop cx
    loop    print_loop
    ret

print_nl:
    mov ax, #0xe0d  ! CR
    int 0x10
    mov al, #0xa    ! LF
    int     0x10
    ret

/*
 *  print_hex is for debugging purposes, and prints the word
 *  pointed to by ss:bp in hexadecmial.
*/

print_hex:
    mov cx, #4      ! 4 hex digits
    mov dx, (bp)    ! load word into dx
print_digit:
    rol dx, #4      ! rotate so that lowest 4 bits are used
    mov ah, #0xe    
    mov al, dl      ! mask off so we have only next nibble
    and al, #0xf
    add al, #0x30   ! convert to 0 based digit, '0'
    cmp al, #0x39   ! check for overflow
    jbe good_digit
    add al, #0x41 - 0x30 - 0xa  ! 'A' - '0' - 0xa

good_digit:
    int 0x10
    loop    print_digit
    ret


/*
 * 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;软驱数字卡的输出寄存器端口
    xor al, al
    ;a驱动器 关闭FDC 禁止DMA 和中断请求  关闭电动机
    outb
    pop dx
    ret

sectors:
    .word 0

msg1:
    .byte 13,10
    .ascii "Loading"

.org 506
swap_dev:
    .word SWAP_DEV  ;2
root_dev:
    .word ROOT_DEV  ;2
boot_flag:
    .word 0xAA55    ;2

.text
endtext:
.data
enddata:
.bss
endbss:

你可能感兴趣的:(linux-0-12)