!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: