读bootsect.s

原文出处 linux内核0.11完全注释。重写一遍!!

 

! bootsect.s 2011-04-06
! 处理基本步骤:
! 1. bootsect.s 被BIOS-启动子程序加载至0x7c00(31k)处,并将自己
! 移到了地址0x90000(576k)处,并跳转至那里;
!
! 2. 使用BIOS中断将setup直接加载到自己的后面(0x90200)(576.5k),
! 并将system加载至地址0x10000处;
!
! 注意,目前的内核系统最大长度限制为(8*65536)(512k)字节,即使是
! 在将来也应该没有问题的,我想让它保持简单明了。这样512k的最大内核
! 长度应该是足够的,尤其是这里没有像minix中一样包含缓冲区高速缓冲。
!
! 加载程序已经做的够简单了,所有持续的读出错将导致死循环。只能手工
! 重启。只要可能,通过一次读取素有扇区,加载过程可以做的很快。

 

SYSSIZE = 0x3000 ! 指编译连接后system模块的大小,这里给出最大的默认值

! 定义了6个全局标识符
.globl begtext, begdata, begbss, endtext, enddata, endbss
.text         ! 文本段
begtext:
.data         ! 数据段
begdata:
.bss          ! 未初始化数据段(Block Started by Symbol)
begbss:
.text         ! 文本段

SETUPLEN = 4                ! setup程序的扇区数

BOOTSEG  = 0x07c0           ! bootsect的原始地址

INITSEG  = 0x9000           ! 将bootsect移到这里

SETUPSEG = 0x9020           ! setup程序从这里开始

SYSSEG   = 0x1000           ! system模块加载到0x10000(64kB)处

ENDSEG   = SYSSEG + SYSSIZE ! 停止加载的段地址

ROOT_DEV = 0x306            ! 指定根文件系统设备是第2个硬盘第1个分区,
                            ! 这是linux老式硬盘命名方式,具体值的含义如下:
                            ! 设备号 = 主设备号*256 + 次设备号
                            ! (主设备号: 1-内存 2-磁盘 3-硬盘 4-ttyx 
                            !            5-tty 6-并行口 7-非命名管道)
                            ! 一个硬盘有1-4个分区,即次设备号
                            ! 从linux内核0.95版本后已经使用与现在相同的命名方法

entry start                 ! 告知链接程序,程序从start标号开始执行
start:                      ! 以下代码是将自身(bootsect)从0x07c0(31k)移动到
                            ! 0x9000(576k)处,共256字(512字节),然后跳转到移动后
                            ! 代码的go标号处
    mov ax, #BOOTSEG        ! 将es段寄存器置为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 = 0
    movw                    ! 移动1个字
    jmpi go,INITSEG         ! 间接跳转

                            ! 从下面开始,CPU执行已移动到0x9000段处得代码
go:
    mov ax, cs
    mov ds, ax
    mov es, ax
    mov ss, ax              ! 将堆栈指针sp指向0x9ff00(0x9000:0xff00)处
    mov sp, #0xFF00

! 在bootsect程序块后紧跟着setup模块的代码数据
! 注意es已经设置好了

load_setup:
! 利用BIOS中断INT 0x13将setup模块从磁盘第2个扇区开始读到0x90200开始处,
! 共读4个扇区。如果读出错,则复位驱动器,并重试,没有退路。
! INT 0x13用法如下:
! 读扇区:
! ah = 0x02 - 读磁盘扇区到内存; al = 需要读出的扇区数量;
! ch = 磁道(柱面)号的低8位;     cl = 开始扇区(0-5位),磁道号高2位(6-7)
! dh = 磁头号;                  dl = 驱动器号(如果是硬盘则位7要置位);
! es:bx -->指向数据缓冲区;  如果出错则CF标志置位
    mov dx, #0x0000
    mov cx, #0x0002
    mov bx, #0x0200
    mov ax, #0x0200+SETUPLEN
    int 0x13
    jnc ok_load_setup
    mov dx, #0x0000
    mov ax, #0x0000
    int 0x13
    j   load_setup


ok_load_setup:
! 取磁盘驱动器的参数,特别是每道的扇区数量
! INT 0x13调用格式和返回信息如下:
! ah = 0x08  dl = 驱动器号(如果是硬盘则要置位7为1)
! 返回信息:
! 如果出错则CF置位,并且ah = 状态码
! ah = 0, al = 0,         bl = 驱动器类型(AT/PS2)
! ch = 最大磁道号的低8位  cl = 每磁道最大扇区数(位0-5),最大磁道号高2位(bit 6-7)
! dh = 最大磁头数         dl = 驱动器数量
! es:di ---> 软驱磁盘参数表

    mov dl, #0x00
    mov ax, #0x0800
    int 0x13
    mov ch, #0x00
    seg cs                    ! 表示下一条语句的操作数在cs段寄存器所指的段中
    mov sectors, cx           ! 保存每磁道扇区数
    mov ax, #INITSEG
    mov es, ax                ! 因为上面取磁盘参数中断改掉了es的值,这里重新改回

! 显示信息('Loading system ...' 回车换行,共24个字符)
    mov 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           ! 写字符串并移动光标
    int 0x10

   
! 现在开始将system模块加载到0x10000(64k)处
    mov ax, #SYSSEG
    mov es, ax
    call read_it              ! 读磁盘上system模块,es为输入参数
    call kill_motor           ! 关闭驱动器马达,这样就可以知道驱动器状态了

! 此后,我们检查要使用哪个根文件系统设备(简称根设备)。
! 如果已经指定了设备(!=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.2M 或 7->1.44M等)
! 因为7*4 + 0 = 28, 所以:
! /dev/PS0(2,28)指的是1.44M A驱动器,其设备号为0x21c,同理
! /dev/at0(2,8) 指的是1.2M A驱动器, 其设备号是0x0208

    seg cs
    mov ax, root_dev
    cmp ax, #0
    jne root_defined
    seg cs
    mov bx, sectors
    mov ax, #0x208               ! 如果sectors=15,则说明是1.2M的驱动器
    cmp bx, #15
    je root_defined
    mov ax, #0x021c              ! 如果sectors=18,则说明是1.44Mb软驱
    cmp bx, #18
    je root_defined
undef_root:                      ! 如果都不一样,死循环,死机
    jmp undef_root
root_defined:
    seg cs
    mov root_dev, ax             ! 将检查过的设备号保存起来

! 到此,所有程序都加载完毕,跳转到bootsect后面的setup程序去
    jmpi 0, SETUPSEG             ! 跳转到0x9020:0000(setup.s程序的开始处)
                                 !!!! 本程序到此结束了

! 下面是两个子程序。
! 该子程序是将system模块加载到内存0x10000处,并确定没有跨越64KB的内存边界。
! 试图尽快的进行加载,只要可能,就每次加载整条磁道的数据
! 输入: es -- 开始内存地址段值(通常是0x1000)
sread: .word 1+SETUPLEN          ! 当前磁道中已读的扇区数
                                 ! 开始已经读了1扇区的引导扇区,加上setup
head:  .word 0                   ! 当前磁头号
track: .word 0                   ! 当前磁道号

read_it:
    mov ax, es
    test ax, #0x0fff
die:
    jne die
    xor bx, bx
rp_read:
    mov ax, es
    cmp ax, #ENDSEG               ! 是否已经加载了全部数据
    jb ok1_read
    ret
ok1_read:
! 计算和验证当前磁道需要读取的扇区数,放在ax寄存器中
! 根据当前磁道中还未读取的扇区数以及段内数据字节开始偏移位置,
! 计算如果全部读取这些未读扇区所读总字节数,是否会超过64KB段长度的限制。
! 若会超过,则根据此次最多能读入的字节数(64KB - 段内偏移),反算出此次需要读取的扇区数
    seg cs
    mov ax, sectors               ! 取每磁道扇区数
    sub ax, sread                 ! 减去当前磁道已读扇区数
    mov cx, ax
    shl cx, #9                    ! cx = cx * 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                 ! 当前磁道上读取的扇区数
    seg cs
    cmp ax, sectors               ! 如果当前磁道上的还有扇区未读,则跳转到ok3_read
    jne ok3_read
! 读该磁道的下一磁头面(1号磁头)上的数据。如果已经完成,则去读下一磁道
    mov ax, #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                    ! 上次已读扇区数*512字节
    add bx, cx                    ! 调整当前段内数据开始位置
    jnc rp_read                   ! 若小于64KB边界值,则跳转到rp_read,继续读数据
                                  ! 否则调整当前段,为读下一段数据做准备
    mov ax, es
    add ax, #0x1000               ! 将段基址调整为指向下一64KB内存开始处
    mov es, ax
    xor bx, bx                    ! 清段内数据开始偏移处
    jmp rp_read

! 读当前磁道上指定开始扇区和需读扇区数的数据至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              ! cl = 开始读扇区
    mov ch, dl          ! ch = 当前磁道号
    mov dx, head        ! 取当前磁头号
    mov dh, dl          ! dh = 磁头号
    mov dl, #0          ! dl = 驱动器号(为0表示当前A驱动器)
    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
! 执行驱动器复位操作(磁盘中断功能号0),再跳转到read_track处重试
bad_rt:
    mov ax, #0
    mov dx, #0
    int 0x13
    pop dx
    pop cx
    pop bx
    pop ax
    jmp read_track


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        ! 共24个ASCII码字符

.org 508                     ! 表示下面语句从地址508(0x1FC)开始,所以root_dev
                             ! 在启动扇区的第508开始的2个字节中

root_dev:
    .word ROOT_DEV           ! 这里存放根文件系统所在的设备号(init/main.c中会用到)


boot_flag:
    .word 0xAA55             ! 硬盘有效标识

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

   

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

robbie1314

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值