果冻
QQ:457283
! 本程序完成的主要功能
! 1.bootsect.s从0x7c00处开始执行
! 2.将自己复制到0x90000处
! 3.将setup.s程序从磁盘第2扇区读取到0x90200处
! 4.将system读取到0x10000处
! 5.获取根文件系统设备号
! 6.显示信息
!
! 程序执行示意图
! +---+---+---+ 0xA0000
! | | | |
! | | | |
! +---+---+---+
! | | | |
! | | | |
! | | | S |
! | | | |
! | | | |
! +---+---+---+ 0x90200
! | | B | B |
! +---+---+---+ 0x90000
! | | | |
! | | | |
! +---+---+---+
! | | | |
! | | | |
! | | | |
! | | | |
! | | | K |
! | | | |
! | | | |
! | | | |
! +---+---+---+ 0x10000
! | | | |
! | | | |
! | | | |
! | | | |
! +---+---+---+
! | B | | |
! +---+---+---+ 0x7c00
! | | | |
! +---+---+---+ 0x0000
! 1 2 3
! B - bootsect.s程序
! S - setup.s程序
! K - system模块
!
! 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
!
! 编译连接后system模块的大小
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.
! 定义6个全局标识符 代码段,数据段,未初始化数据段起始结束地址
.globl begtext, begdata, begbss, endtext, enddata, endbss
! 代码段
.text
begtext:
! 数据段
.data
begdata:
! 未初始化数据段
.bss
begbss:
! 代码段
.text
SETUPLEN = 4 ! nr of setup-sectors
! setup程序的扇区数
BOOTSEG = 0x07c0 ! original address of boot-sector
! boot-sector的原始段地址
INITSEG = 0x9000 ! we move boot here - out of the way
! 将boot-sector移动到这里
SETUPSEG = 0x9020 ! setup starts here
! setup程序在这里开始
SYSSEG = 0x1000 ! system loaded at 0x10000 (65536).
! system模块加载到0x10000(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 ! 设备号,指根文件系统是第2个硬盘的第一个分区
! 设备号 = 主设备号*256 + 次设备号(=(major<<8)+ minor)
! 主设备号:1-内存,2-磁盘,3-硬盘,4-ttyx,5-tty,6-并口,7-非命名管道
! 0x300 = 3*256 + 0 第1个硬盘
! 0x301 = 3*256 + 1 第1个硬盘,第1个分区
! 0x306 = 3*256 + 6 第2个硬盘,第1个分区
entry _start ! 程序入口点
_start:
! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! !
mov ax,#BOOTSEG ! 将数据段DS寄存器设置为0x07C0
mov ds,ax
mov ax,#INITSEG ! 将附加段ES寄存器设置为0x9000
mov es,ax
mov cx,#256 ! 移动数值CX=256字 = 512字节
sub si,si ! 源地址 ds:si = 0x07c0:0x0000
sub di,di ! 目的地址 es:di = 0x9000:0x0000
rep ! 重复执行,直到CX=0
movw ! 移动一个字
! 上面的代码作用是将bootsect自身从位置0x07c00移动到0x90000处,共512个字节
jmpi go,INITSEG ! 间接跳转,INITSEG为跳转到的段地址
! 以下代码是从0x90000处执行的
go: mov ax,cs ! 将ds,es和ss设置成0x9000,此时cs跳转到0x9000了
mov ds,ax
mov es,ax
! put stack at 0x9ff00. ! 将堆栈指针指向0x9ff00(0x9000:0xff00)
mov ss,ax
mov sp,#0xFF00 ! arbitrary value >>512
! 因为0x90000 - 0x90200放置bootsect,0x90200开始放置大约4个扇区的setup
! 程序,因此堆栈指针sp要指向0x200 + 0x200 * 4 + 堆栈大小的位置
! load the setup-sectors directly after the bootblock.
! Note that 'es' is already set up.
! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! !
load_setup:
mov dx,#0x0000 ! drive 0, head 0
! 驱动器0,磁头0
mov cx,#0x0002 ! sector 2, track 0
! 扇区2,磁道0
mov bx,#0x0200 ! address = 512, in INITSEG
! 缓冲区偏移量,es在上面的代码已经设置成0x9000
mov ax,#0x0200+SETUPLEN ! service 2, nr of sectors
! 读取4个盘扇区到内存
int 0x13 ! read it
! 调用中断
jnc ok_load_setup ! ok - continue
! CF没置位表示读取成功,跳转到ok_load_setup
mov dx,#0x0000 ! 复位驱动器和磁头
mov ax,#0x0000 ! reset the diskette
int 0x13 ! 复位
j 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标志置位
! 成功载入setup模块
ok_load_setup:
! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! !
! Get disk drive parameters, specifically nr of sectors/track
mov dl,#0x00 ! 设置驱动器号为0
mov ax,#0x0800 ! AH=8 is get drive parameters
int 0x13 ! 调用中断
mov ch,#0x00
seg cs ! 表示下一条语句操作在CS段寄存器所指的段中,
mov sectors,cx ! 保存每磁道扇区数
mov ax,#INITSEG
mov es,ax ! 因为读取磁盘参数时覆盖了ES的值,这里重新改回
! 以上代码是读取磁盘驱动器参数,特别是扇区数量
! INT 0x13
! ah = 0x08 dl = 驱动器号(如果是硬盘则要置位7为1)
! 返回信息:
! 如果出错CF置位,ah = 状态码
! ah = 0, al = 0 bl = 驱动器类型(AT/PS2)
! ch = 最大磁道号低8位 cl = 每磁道最大扇区数(位0-5),最大磁道号高2位(位6-7)
! dh = 最大磁头数 dl = 驱动器数量
! es:di = 软驱磁盘参数表
! 显示信息'Loading system ...回车换行'共24个字符
! Print some inane message
mov ah,#0x03 ! read cursor pos
xor bh,bh ! 读取光标位置
int 0x10
mov cx,#24 ! 共24个字符
mov bx,#0x0007 ! page 0, attribute 7 (normal)
mov bp,#msg1 ! 指向要显示的字符串
mov ax,#0x1301 ! write string, move cursor
int 0x10 ! 写字符串并且移动光标
! ok, we've written the message, now
! 以下代码将system模块加载到0x10000处
! we want to load the system (at 0x10000)
mov ax,#SYSSEG
mov es,ax ! segment of 0x010000
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.
! 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 ! 如果不为0跳转到root_defined,证明已经给定根设备号
jne root_defined
! 检测根设备号
seg cs ! 下面一句代码在CS所指的段中
mov bx,sectors ! 取得每磁道扇区数 15-1.2mb驱动器 18-1.44MB驱动器
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 ! 保存设备号
! after that (everyting loaded), we jump to
! the setup-routine loaded directly after
! the bootblock:
jmpi 0,SETUPSEG ! 跳转到0x9020:0000(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
! 当前磁道中已读扇区数,开始已经读取1个扇区的bootsect
head: .word 0 ! current head
! 当前磁头号
track: .word 0 ! current track
! 当前磁道号
read_it:
mov ax,es
test ax,#0x0fff ! 将es里的值与0x0fff相与,看是否处于0x1000位置
die: jne die ! es must be at 64kB boundary 不处于0x1000位置则进入死循环
xor bx,bx ! bx is starting address within segment
! bx是段内偏移地址
rp_read:
mov ax,es ! 比较es值与ENDSEG,看是否达到段末端,如果不是则跳转到ok1_read继续读取,否则返回
cmp ax,#ENDSEG ! have we loaded all yet?
jb ok1_read
ret
ok1_read:
seg cs ! 设置下面一条指令操作在CS所指的段中
mov ax,sectors ! 读取每磁道扇区数
sub ax,sread ! 减掉当前已读扇区数
mov cx,ax ! cx = 当前未读扇区数
shl cx,#9 ! cx = cx << 9 = cx * 512字节 = 共有多少字节没有读取
add cx,bx ! cx = cx + 段内当前偏移量 判断未读取字节数 + 段内偏移是否超过64KB
jnc ok2_read ! 未超过64KB字节,跳转到ok2_read执行
je ok2_read
! 以下情况是未读取字节数+段内偏移超过了64KB
xor ax,ax ! 清空ax
sub ax,bx ! ax = ax - bx 得到最多还能读入的字节数
shr ax,#9 ! 转换成还需要读取的扇区数
ok2_read:
call read_track
mov cx,ax ! cx = 上面那条调用后已经读出的扇区数
add ax,sread ! 更新已经读取的扇区数
seg cs ! 下一条指令操作在CS所指的段中
cmp ax,sectors ! 如果当前磁道上还有扇区未读,跳转到ok3_read
jne ok3_read
mov ax,#1 ! 判断磁头号
sub ax,head ! 如果为0磁头,则再去读1磁头面上的扇区数据
jne ok4_read
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 ! 跳转到rp_read继续执行
! 读当前磁道上指定开始扇区和需要读扇区数的数据到es:bx开始处
read_track:
! ax,bx,cx,dx如栈
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 ! 弹出dx, cx, bx, ax
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
! /*
! * 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 ! A驱动器,关闭FDC,静止DMA和中断请求,关闭马达
outb ! 将al内容输出到dx指定端口
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: ! 保存根文件系统所在的设备号(init/main.c用到)
.word ROOT_DEV
boot_flag: ! 硬盘有效标识
.word 0xAA55
.text
endtext:
.data
enddata:
.bss
endbss: