!
! 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 = 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,
mov ds,ax
mov ax,
mov es,ax
mov cx,
sub si,si
sub di,di
rep
movw
jmpi go,INITSEG
;0x7c00
;ds=0x7c0 es=0x9000
;ds[si++]
;最后jmpi跳转到0x9000:go处
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;这段代码用来设置堆栈
;考虑内存空间
;我们目前 bootsect 0x90000~0x90200
;setup 0x90200~0x90a00
;system 0x10000~*
;所以我们把堆栈设置到段0x9000最后即可。
;考虑到我们后面的参数拷贝 需要预留12字节的空间 0x9FF00-12=0X9FEF4
go: mov ax,cs
mov dx,
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
pop fs
mov bx,
seg fs
lgs si,(bx) ! gs:si is source
;使gs=fs=0 si=bx
mov di,dx ! es:di is destination
mov cx,
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,
mov bx,
mov ax,
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,
int 0x13
xor ch,ch
seg cs
mov sectors,cx ;每磁道的扇区数
mov ax,
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,
xor bh,bh
int 0x10
mov cx,
mov bx,
mov bp,
mov ax,
int 0x10
;输出回车+换行+Loading 一共9个字符
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;我们设置es为0x1000 并加载系统段
! ok, we've written the message, now
! we want to load the system (at 0x10000)
mov ax,
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,
cmp bx,
je root_defined
mov ax,
cmp bx,
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,
;保证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,
jb ok1_read
ret
;未加载完毕就跳到这里开始加载
ok1_read:
seg cs
mov ax,sectors
sub ax,sread;得到当前磁道磁头未加载的扇区数
mov cx,ax
shl cx,
add cx,bx ;*512+当前段内偏移值
jnc ok2_read ;如果没有超过了64kb的边界范围 就跳转到ok2
je ok2_read ;如果等于64kb 也跳转到ok2
xor ax,ax
sub ax,bx
shr ax,
;执行到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,
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,
add bx,cx ;调整下次读取数据的开始位置
jnc rp_read ;如果bx不是段末尾的话 就跳回rp——read
;否则说明bx已经位于段末 需要调整段
mov ax,es
add ah,
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,
mov bx,
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,
mov ah,
push dx ! save for error dump
push cx
push bx
push ax
int 0x13
jc bad_rt ;读磁盘 出错则跳转
add sp,
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,
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,
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,
sub al, cl
int 0x10
mov al,
int 0x10
mov al,
int 0x10
no_reg:
add bp,
call print_hex ! print it
pop cx
loop print_loop
ret
print_nl:
mov ax,
int 0x10
mov al,
int 0x10
ret
/*
* print_hex is for debugging purposes, and prints the word
* pointed to by ss:bp in hexadecmial.
*/
print_hex:
mov cx,
mov dx, (bp) ! load word into dx
print_digit:
rol dx,
mov ah,
mov al, dl ! mask off so we have only next nibble
and al,
add al,
cmp al,
jbe good_digit
add al,
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,
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: