!
! 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 /*这里应该是192k,如果每一k 1000b的话,那就是196k*/
!
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.
.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
jmpi go,INITSEG
go: mov ax,cs !jmpi 之后 cs:IP 是 INITSEG:go,即0x9000:go
mov ds,ax
mov es,ax
! put stack at 0x9ff00.
mov ss,ax
mov sp,#0xFF00 ! arbitrary value >>512
! load the setup-sectors directly after the bootblock.
! Note that 'es' is already set up.
/*
功能02H
功能描述:读扇区
入口参数: AH=02H
AL=扇区数 要读取的扇区数 sector
CH=柱面/磁道数 track
CL=扇区数 起始扇区数 sector
DH=磁头数 head
DL=驱动器,00H~7FH:软盘;80H~0FFH:硬盘 drive
ES:BX=缓冲区的地址
出口参数:
CF=0——操作成功,AH=00H,AL=传输的扇区数
否则,AH=状态代码,参见功能号01H中的说明
*/
load_setup:
mov dx,#0x0000 ! drive 0, head 0 DH=磁头数 DL=表示软盘(00H~7FH:软盘;80H~0FFH:硬盘)
/*mov dx,#0x0000 表示0磁头的软盘*/
mov cx,#0x0002 ! sector 2, track 0 表示从0磁道2扇区开始读取 因为第一个扇区已经被bios加载到0x7c00
mov bx,#0x0200 ! address = 512, in INITSEG es:bx指定数据存放目的地,即0x9000:0xff00
mov ax,#0x0200+SETUPLEN ! service 2, nr of sectors al表示要读取的扇区个数
int 0x13 ! read it /*int 0x13读取数据成功之后 会设置EFLAGS寄存器CF=0位, jnc判断CF=0表示成功*/
jnc ok_load_setup ! ok - continue 加载成功将跳转到ok_load_setup
mov dx,#0x0000 /*这里表示加载失败*/
mov ax,#0x0000 ! reset the diskette /*diskette 磁盘*/
int 0x13 /*磁盘系统复位*/
j load_setup /*跳转回去继续执行,就是不知道这里是不是无条件跳转*/
ok_load_setup:
! Get disk drive parameters, specifically nr of sectors/track 获取每个磁道最大多少扇区数
/*
功能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=磁盘驱动器参数表地址
*/
mov dl,#0x00
mov ax,#0x0800 ! AH=8 is get drive parameters
int 0x13 ! 获取软盘的最大扇区数*/
mov ch,#0x00 !最大扇区数存放在cx寄存器中,清空高8位CH,保留低8位CL,CL里面是所有扇区数
seg cs
mov sectors,cx !CL(0-5bits)软盘最大扇区数, 保存软盘驱动器的最大扇区数
/*
seg cs表示后面代码要是用cs 作为段的基地址。
mov sectors,cx ,是要把获取到的drive参数存到数据段,但没有使用ds段,而是强制使用cs段作为基地址,即cs:sectors
其实这里没有必要非一定要使用cs段,上面一次跳转之后,cs==ds,所以是可以使用ds的,这样看代码就方便多了,
不知道作者是不是秀技术。
as86汇编,不知道能不能把数据区和代码区区分开来,这样在阅读代码和代码中处理数据直接饮用ds段,也不用seg cs段超越了。
但,at&t汇编中可以区分,但at&t只能是gnu的32位汇编,唉
谁让电脑启动时候都是实模式16位呢,只能用16位as86汇编(和intel语法很像)
as86编译器 使用minix汇编语法 as86编译器可以生成16位/32位汇编
gnu编译器 使用at&t汇编语法 gnu编译器只能生成32位汇编
seg cs如果不加这段代码,那么后面的
mov sectors, cx会默认保存一个16位到ds:sectors中。
因为此时cs=ds,所以加不加都无所谓,
如果加上这段代码,那么 seg cs 只是提示后面一条指令在操作的时候是存放到cs段中,即cs:sectors
*/
!上面操作处理完之后,那么就已经把drive 参数存储起来了
mov ax,#INITSEG ;指定es 0x9000 因为后面要是用ES:BP指向要写入的字符串的地址,所以要设置下es段
mov es,ax
! Print some inane message /* inane无意义的 */
/*
功能03H
功能描述:在文本坐标下,读取光标各种信息
入口参数:
AH=03H
BH=显示页码
0-3 in modes 2&3
0-7 in modes 0&1
0 in graphics modes
出口参数:
CH=光标的起始行
CL=光标的终止行
DH=行(Y坐标)
DL=列(X坐标)
*/
mov ah,#0x03 ! read cursor pos
xor bh,bh
int 0x10 !获取光标位置
/*
功能13H
功能描述:在Teletype模式下显示字符串
入口参数:
AH=13H
BH=页码
BL=属性(若AL=00H或01H)
CX=显示字符串长度
(DH、DL)=坐标(行、列) 输出到光标所在的行,列。上面已经把获取到的坐标信息存放到dx中,所以这里直接使用。
ES:BP=显示字符串的地址
AL=显示输出方式
0——字符串中只含显示字符,其显示属性在BL中。显示后,光标位置不变
1——字符串中只含显示字符,其显示属性在BL中。显示后,光标位置改变
2——字符串中含显示字符和显示属性。显示后,光标位置不变
3——字符串中含显示字符和显示属性。显示后,光标位置改变
出口参数:无
*/
!光标位置信息已经存放在DX中了,这里直接使用就好了
mov cx,#24 ! 字符串长度
mov bx,#0x0007 ! page 0, attribute 7 (normal) 写到屏幕第0页内存中,第 7个属性(具体什么吗,不知道啊)
mov bp,#msg1 ! msg1指向要输出的信息 ES:BP=显示字符串的地址 ,es前面已经指定为cs,所以这里直接使用
mov ax,#0x1301 ! write string, move cursor AL=显示输出方式 AH=13H
int 0x10 ! 写字符串到屏幕
! ok, we've written the message, now
! we want to load the system (at 0x10000)
mov ax,#SYSSEG
mov es,ax ! segment of 0x010000 此时 es指向0x1000
call read_it !读取磁盘system参数,直到读取128k
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
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:
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.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 已经读取的扇区数
head: .word 0 ! 磁头数
track: .word 0 ! 磁道数
read_it:
mov ax,es !es 是 0x1000
test ax,#0x0fff !ax 是 0x1000 这里的目的就是必须指定es段为0x1000
/*test执行的就是and的指令,只不过不会保存and执行的结果,而是根据and的结果设置flags寄存器的各种标志
这里在计算地址是会是es<<4 + di, di是0,所以 地址是0x10000=64k*/
die:jne die ! es must be at 64kB boundary 如果不是0,继续死循环,相当于参数校验吧
xor bx,bx ! bx is starting address within segment 清空bx
rp_read:
mov ax,es ! 刚开始es是0x1000:0,当读取完64k之后es就是0x2000:0
cmp ax,#ENDSEG ! have we loaded all yet? 从0x1000:0到0x3000:0之间是128k
jb ok1_read ! 如果ax小于ENDSEG,则继续读取, 否则结束
ret
ok1_read:
seg cs
mov ax,sectors !获取前面保存最大扇区数
sub ax,sread !ax = ax - sread; sread是已读扇区数,第一次是5,后面有修改过,变量, 剩下的就是未读取的扇区数
/*
CH=柱面数的低8位
CL的位7-6=柱面数的高2位
CL的位5-0=扇区数
由于上面存储是的cx,获取的是drive上所有的柱面数,
在执行sub ax,sread时候,由于sread是5,cl的0到5位的范围是0-64, 所以在相减的时候,是用
ax = ax - 5;
入口参数: AH=02H
AL=扇区数 要读取的扇区数 sector
CH=柱面/磁道数 track
CL=扇区数 起始扇区数 sector
DH=磁头数 head
DL=驱动器,00H~7FH:软盘;80H~0FFH:硬盘 drive
ES:BX=缓冲区的地址
*/
mov cx,ax !cx表示未读取的扇区数
shl cx,#9 !每个扇区512字节,左移9位之后, cx是未读取的扇区所占的字节数
add cx,bx !循环之前已经清空bx,所以第一次循环bx是0
jnc ok2_read !cx 16位,2的16次方是64k,如果大于64k进位,继续向下执行,否则,继续执行ok2_read
je ok2_read !当读取满足64k之后会继续向下执行,跳转到
/*若加上此次将读磁道上所有未读扇区时未超过64KB,计算最多能读入多少
字节数(64KB-段内读偏移位置),再转换成需要读取的扇区数。
*/
xor ax,ax !清空ax
sub ax,bx !0 - bx 就是取补数,就等于这次还能读入的字节数
shr ax,#9 !右移9位等同于除以512,转换成扇区数
ok2_read: !调用ok2_read的时候把ax未读取的扇区数传递过来了
call read_track !调用read_track的时候把ax未读取的扇区数传递过来了,在read_track中ax就是需要读取的扇区数,如果读取成,说明ax已经被读取
mov cx,ax !所以这里的ax表示已经被成功读取的扇区数
add ax,sread !上面read_track中已经被读取的扇区数ax+sread(原先被读取的扇区数),ax=ax+sread,表示总共读取的扇区数
seg cs
cmp ax,sectors !比对在cs:sectors中存放的最大扇区数和目前已经读取的扇区数是否相等,如果不等则跳转,否则向下执行
jne ok3_read !如果已经读取完最大扇区数,相等了,继续向下执行
mov ax,#1
sub ax,head !读取磁头1
jne ok4_read !如果不相等,即磁头1还未读取,那么跳转继续读取
inc track !增加磁道号
ok4_read:
mov head,ax
xor ax,ax
!这里好像应该加上一句 xor cx, cx 把cx清空
ok3_read:
mov sread,ax !这里的ax是已经读取的扇区数, 从新初始化已经被读取的扇区
shl cx,#9 !cx表示已经读取多少字节了
add bx,cx !由于前面读取之后需要改变es:bx中的bx偏移指针(第一次读取bx是0)
jnc rp_read !由于这里bx是16位,所以最大65535个字节(64k),当大于64k之后,发生进位,这里就是检测有没有读取到64k数据,如果没有继续读取,否则继续向下执行
mov ax,es !此时es是0x1000:0
add ax,#0x1000 !此时ax是0x2000:0
mov es,ax !改变基地址es
xor bx,bx !情况bx
jmp rp_read !无条件跳转继续读取
read_track:
push ax
push bx
push cx
push dx
/*
功能02H
功能描述:读扇区
入口参数: AH=02H
AL=扇区数 要读取的扇区数 sector
CH=柱面/磁道数 低8位磁道号 track
CL=扇区数 起始扇区数(0-5),高两位磁道号(6-7,hard disk only) sector/track
DH=磁头数 head
DL=驱动器,00H~7FH:软盘;80H~0FFH:硬盘 drive (bit 7 set for hard disk)
ES:BX=缓冲区的地址
AH = 02h
AL = number of sectors to read (must be nonzero)
CH = low eight bits of cylinder number
CL = sector number 1-63 (bits 0-5)
high two bits of cylinder (bits 6-7, hard disk only)
DH = head number
DL = drive number (bit 7 set for hard disk)
ES:BX -> data buffer
出口参数:
CF=0——操作成功,AH=00H,AL=传输的扇区数
否则,AH=状态代码,参见功能号01H中的说明
*/
!由于前面已经传递过来需要读取的扇区数ax,所以这里直接使用ax
mov dx,track ! 当前的磁道号 这里dx是变量 /*就是想把磁道号放到cx的高8位,起始扇区放到低8位*/
mov cx,sread ! 当前已读扇区数
inc cx ! 开始读取的扇区,从哪里开始读,因为bit 0-5表示起始扇区,所以inc cx并不会改变cx的高8位ch.
mov ch,dl ! 低8位的磁道号赋值给CH
mov dx,head ! 当前磁头号赋值给dx /*就是想把磁头数存到dx的高8位,低8位表示驱动器属性(软盘 or 硬盘)*/
mov dh,dl ! 当前磁头号的低8位赋值给了高8位
mov dl,#0 ! 低8位置0, 表示这是软盘
and dx,#0x0100 ! mov dx,head 磁头号赋值为0,所以这里&之后还是0,所以是取0磁头的数据
mov ah,#2 ! 功能号
int 0x13 ! es 是 0x1000 bx前面清空了 是0,所以这里缓冲区地址是0x1000:0
jc bad_rt !判断EFLAGS中CF标志,如果出错,跳转到bad_rt
pop dx
pop cx
pop bx
pop ax
ret
bad_rt:
mov ax,#0
mov dx,#0
int 0x13 !磁盘系统复位
pop dx
pop cx
pop bx
pop ax
jmp read_track !跳转到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指定的端口去。
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: