上节详细分析了FAT12文件系统。
这节具体讲解:主引导程序如何①从存储介质中加载程序到内存 ②将控制权交由新加载的程序执行
目录
1、突破512字节限制
1、将文件系统的根目录区加载到内存
1、字符串打印
2、软盘读取
2、在根目录区找到目标文件
1、内存比较
2、根目录的查找
3、通过FAT表将文件的数据全部加载到内存
1、内存拷贝
2、Fat表项的读取
4、小结
2、 主引导程序控制权的转移
整体思路
1、将文件系统的根目录区加载到内存
2、在根目录区找到目标文件
3、通过FAT表将文件的数据全部加载到内存,执行跳转
主引导程序中如何进行字符串打印?
主引导程序中如何读取指定扇区处的数据?
我们将会编写两个函数,一个进行字符串打印,一个进行软盘读取
BIOS中的字符串打印
- 指定打印参数(AX=0x1301,BX=0x0007)
- 指定字符串的内存地址(ES:BP = 串地址)
- 指定字符串的长度(CX = 串长度)
- 中断调用(int 0x10)
编程实验
定义打印函数 print
boot.asm
org 0x7c00 ; IP = 0x7c00
jmp short start ;占用两字节
nop ;占用一字节
define:
BaseOfStack equ 0x7c00 ;#define BaseOfStack 0x7c00 栈从高地址向低地址增长,与代码段无影响
;主引导区(0扇区)的必要信息
header:
BS_OEMName db "D.T.Soft"
BPB_BytsPerSec dw 512
BPB_SecPerClus db 1
BPB_RsvdSecCnt dw 1
BPB_NumFATs db 2
BPB_RootEntCnt dw 224
BPB_TotSec16 dw 2880
BPB_Media db 0xF0
BPB_FATSz16 dw 9
BPB_SecPerTrk dw 18
BPB_NumHeads dw 2
BPB_HiddSec dd 0
BPB_TotSec32 dd 0
BS_DrvNum db 0
BS_Reserved1 db 0
BS_BootSig db 0x29
BS_VolID dd 0
BS_VolLab db "D.T.OS-0.01"
BS_FileSysType db "FAT12 "
;开始就跳转到这里执行
start:
mov ax, cs
mov ss, ax
mov ds, ax
mov es, ax
mov sp, BaseOfStack ;指定栈顶指针
mov bp, MsgStr ;指定字符串地址
mov cx, MsgLen ;指定字符串长度
call Print
last:
hlt
jmp last
;打印字符串函数
; es:bp --> string address
; cx --> string length
Print:
mov ax, 0x1301 ;指定打印参数
mov bx, 0x0007 ;指定打印参数
int 0x10 ;执行0x10号中断
ret
MsgStr db "Hello, DTOS!"
MsgLen equ ($-MsgStr)
Buf:
times 510-($-$$) db 0x00
db 0x55, 0xaa
makefile
.PHONY : all clean rebuild
SRC := boot.asm
OUT := boot.bin
IMG := data.img
RM := rm -fr
all : $(OUT) $(IMG)
dd if=$(OUT) of=$(IMG) bs=512 count=1 conv=notrunc
@echo "Success!"
$(IMG) :
bximage $@ -q -fd -size=1.44
$(OUT) : $(SRC)
nasm $^ -o $@
clean :
$(RM) $(IMG) $(OUT)
rebuild :
@$(MAKE) clean
@$(MAKE) all
bochs配置文件插入data.img
软盘的构造
- 一个软盘有2个盘面,每个盘面对应1个磁头
- 每一个盘面被划分为若干个圆圈,成为柱面(磁道)
- 每一个柱面被划分为若干个扇区,每个扇区512字节
3.5寸软盘的数据特性
- 每个盘面一共80个柱面(编号为0 - 79)
- 每个柱面有18个扇区(编号为1 - 18)
- 存储大小:2 * 80 * 18 * 512 = 1474560Bytes = 1440KB
软盘数据的读取
- 读取数据前,逻辑扇区号需要转化为磁盘的物理位置
- 物理软盘上的数据位置由磁头号,柱面号,扇区号唯一确定
- 软盘数据以扇区(512字节)为单位进行读取
- 指定数据所在位置的磁头号,柱面号,扇区号
- 计算公式:
何为逻辑扇区号?举个简单栗子
BIOS 中的软盘数据读取(int 0x13)
软盘数据读取流程
汇编中的16位除法操作(div)
- 被除数放到AX寄存器
- 除数放到通用寄存器或内存单元(8位)
- 结果:商位于AL,余数位于AH
编程实验
磁盘数据的读取 boot.asm
org 0x7c00
jmp short start ;占用两个字节
nop ;占用一个字节
define:
BaseOfStack equ 0x7c00 ;#define BaseOfStack 0x7c00 栈从高地址向低地址增长,所以与代码段无影响
;主引导区(0扇区)的必要信息
header:
BS_OEMName db "D.T.Soft"
BPB_BytsPerSec dw 512
BPB_SecPerClus db 1
BPB_RsvdSecCnt dw 1
BPB_NumFATs db 2
BPB_RootEntCnt dw 224
BPB_TotSec16 dw 2880
BPB_Media db 0xF0
BPB_FATSz16 dw 9
BPB_SecPerTrk dw 18 ;每柱面扇区数
BPB_NumHeads dw 2
BPB_HiddSec dd 0
BPB_TotSec32 dd 0
BS_DrvNum db 0 ;软驱编号
BS_Reserved1 db 0
BS_BootSig db 0x29
BS_VolID dd 0
BS_VolLab db "D.T.OS-0.01"
BS_FileSysType db "FAT12 "
start:
mov ax, cs
mov ss, ax
mov ds, ax
mov es, ax
mov sp, BaseOfStack ;指定栈顶指针
mov ax, 34 ;指定要读取的逻辑扇区号34
mov cx, 1 ;读取1个扇区
mov bx, Buf
call ReadSector ;读到es:bx所指向的Buf
mov bp, Buf
mov cx, 21
call Print ;打印es:bp所指向的Buf中的21个字符
last:
hlt
jmp last
;打印字符串函数
; es:bp --> string address
; cx --> string length
Print:
mov ax, 0x1301 ;指定打印参数
mov bx, 0x0007 ;指定打印参数-
int 0x10 ;执行0x10号中断
ret
;重置软驱
; no parameter
ResetFloppy:
push ax
push dx
mov ah, 0x00 ;13号中断处理程序的0功能:磁盘系统复位
mov dl, [BS_DrvNum] ;必要参数: dl = 驱动器
int 0x13
pop dx
pop ax
ret
; ax --> logic sector number
; cx --> number of sector
; es:bx --> target address
ReadSector:
push bx
push cx
push dx
push ax
call ResetFloppy
push bx ;目标地址和读取扇区数量入栈
push cx
mov bl, [BPB_SecPerTrk]
div bl ;逻辑扇区号(AX) / 柱面扇区数 => 余数放到AH,商放到AL
mov cl, ah ;
add cl, 1 ;余数值+1得到扇区号
mov ch, al ;
shr ch, 1 ;商值右移一位得到柱面号
mov dh, al
and dh, 1 ;得到磁头号
mov dl, [BS_DrvNum] ;得到驱动器号
pop ax ;读取扇区数(CX)
pop bx ;确保bx继续指向目标地址
mov ah, 0x02 ;13号中断处理程序的02号功能:读扇区
;参数:AL=扇区数,CH=柱面号,CL=扇区号,DH=磁头号,DL=驱动器号,ES:BX指向目标地址
read:
int 0x13 ;读取数据到Buf
jc read ;可能读取失败,重读(CF=0 操作成功)
pop ax
pop dx
pop cx
pop bx
ret
MsgStr db "Hello, DTOS!"
MsgLen equ ($-MsgStr)
Buf:
times 510-($-$$) db 0x00
db 0x55, 0xaa
成功从文件系统的34扇区读取一个扇区数据到内存Buf,并打印21个字符
内存比较函数的创建 MemCmp
查找根目录区是否存在目标文件
; ds:si --> source
; es:di --> destination
; cx --> length
;
; return:
; (cx == 0) ? equal : noequal
MemCmp:
push si
push di
push ax
compare:
cmp cx, 0
jz equal
mov al, [si]
cmp al, byte [di]
jz goon
jmp noequal
goon:
inc si
inc di
dec cx
jmp compare
equal:
noequal:
pop ax
pop di
pop si
ret
根目录区查找函数的创建 FindEntry
加载根目录区
org 0x7c00
jmp short start
nop
define:
BaseOfStack equ 0x7c00
RootEntryOffset equ 19 ;根目录区偏移
RootEntryLength equ 14 ;根目录区大小
header:
BS_OEMName db "D.T.Soft"
BPB_BytsPerSec dw 512
BPB_SecPerClus db 1
BPB_RsvdSecCnt dw 1
BPB_NumFATs db 2
BPB_RootEntCnt dw 224
BPB_TotSec16 dw 2880
BPB_Media db 0xF0
BPB_FATSz16 dw 9
BPB_SecPerTrk dw 18
BPB_NumHeads dw 2
BPB_HiddSec dd 0
BPB_TotSec32 dd 0
BS_DrvNum db 0
BS_Reserved1 db 0
BS_BootSig db 0x29
BS_VolID dd 0
BS_VolLab db "D.T.OS-0.01"
BS_FileSysType db "FAT12 "
start:
mov ax, cs
mov ss, ax
mov ds, ax
mov es, ax
mov sp, BaseOfStack
mov ax, RootEntryOffset ;从第19扇区读
mov cx, RootEntryLength ;读14个扇区
mov bx, Buf
call ReadSector ;软盘读取,将根目录区读到Buf
mov si, Target ;指定要查找的文件名字符串
mov cx, TarLen ;长度
mov dx, 0 ;作为FindEntry返回值
call FindEntry ;根据文件名和长度查找对应目录项(bx)
cmp dx, 0
jz output ;没找到,输出错误信息
jmp last
output:
mov bp, MsgStr
mov cx, MsgLen
call Print
last:
hlt
jmp last
; es:bx --> root entry offset address
; ds:si --> target string
; cx --> target length
;
; return:
; (dx != 0) ? exist : noexist
; exist --> bx is the target entry
FindEntry:
push di
push bp
push cx
mov dx, [BPB_RootEntCnt] ;目录项个数,即dx保存着最大查找次数
mov bp, sp ;不能使用sp直接访问栈顶数据
find:
cmp dx, 0
jz noexist ;找不到目标文件,跳出
mov di, bx ;根目录区第i个目录项地址
mov cx, [bp]
call MemCmp
cmp cx, 0
jz exist ;查找目标存在,跳转
add bx, 32 ;进行下一个目录项
dec dx
jmp find
exist:
noexist:
pop cx
pop bp
pop di
ret
; ds:si --> source
; es:di --> destination
; cx --> length
;
; return:
; (cx == 0) ? equal : noequal
MemCmp:
push si
push di
push ax
compare:
cmp cx, 0
jz equal
mov al, [si]
cmp al, byte [di]
jz goon
jmp noequal
goon:
inc si
inc di
dec cx
jmp compare
equal:
noequal:
pop ax
pop di
pop si
ret
; es:bp --> string address
; cx --> string length
Print:
mov ax, 0x1301
mov bx, 0x0007
int 0x10
ret
; no parameter
ResetFloppy:
push ax
push dx
mov ah, 0x00
mov dl, [BS_DrvNum]
int 0x13
pop dx
pop ax
ret
; ax --> logic sector number
; cx --> number of sector
; es:bx --> target address
ReadSector:
push bx
push cx
push dx
push ax
call ResetFloppy
push bx
push cx
mov bl, [BPB_SecPerTrk]
div bl
mov cl, ah
add cl, 1
mov ch, al
shr ch, 1
mov dh, al
and dh, 1
mov dl, [BS_DrvNum]
pop ax
pop bx
mov ah, 0x02
read:
int 0x13
jc read
pop ax
pop dx
pop cx
pop bx
ret
MsgStr db "No LOADER ..."
MsgLen equ ($-MsgStr)
Target db "LOADER "
TarLen equ ($-Target)
Buf:
times 510-($-$$) db 0x00
db 0x55, 0xaa
备份目标文件的目录信息(MemCpy)
拷贝方向
编程实验
内存拷贝函数 MemCpy
; ds:si --> source
; es:di --> destination
; cx --> length
MemCpy:
push si
push di
push cx
push ax
cmp si, di
ja btoe ;si > di 从头部开始拷贝
add si, cx ;指向源内存尾部
add di, cx ;指向目标内存尾部
dec si ;源内存最后一个有效字节
dec di
jmp etob ;从尾部开始拷贝
btoe:
cmp cx, 0
jz done
mov al, [si]
mov byte [di], al
inc si
inc di
dec cx
jmp btoe
etob:
cmp cx, 0
jz done
mov al, [si]
mov byte [di], al
dec si
dec di
dec cx
jmp etob
done:
pop ax
pop cx
pop di
pop si
ret
加载Fat表,并完成Fat 表项的查找与读取(FatVec)
Fat表项的读取
必须清楚的是这里的Fat表均代表是载入内存后的,FatVec代表其具体的组织结构
boot.asm
org 0x7c00
jmp short start
nop
define:
BaseOfStack equ 0x7c00 ;栈基址
BaseOfLoader equ 0x9000 ;最终加载地址
RootEntryOffset equ 19 ;根目录区扇区起始扇区号
RootEntryLength equ 14 ;根目录区长度
EntryItemLength equ 32 ;每个目录项大小(32字节)
FatEntryOffset equ 1 ;FAT表起始扇区号
FatEntryLength equ 9 ;FAT表长度9扇区
header:
BS_OEMName db "D.T.Soft"
BPB_BytsPerSec dw 512
BPB_SecPerClus db 1
BPB_RsvdSecCnt dw 1
BPB_NumFATs db 2
BPB_RootEntCnt dw 224
BPB_TotSec16 dw 2880
BPB_Media db 0xF0
BPB_FATSz16 dw 9
BPB_SecPerTrk dw 18
BPB_NumHeads dw 2
BPB_HiddSec dd 0
BPB_TotSec32 dd 0
BS_DrvNum db 0
BS_Reserved1 db 0
BS_BootSig db 0x29
BS_VolID dd 0
BS_VolLab db "D.T.OS-0.01"
BS_FileSysType db "FAT12 "
start:
mov ax, cs
mov ss, ax
mov ds, ax
mov es, ax
mov sp, BaseOfStack
mov ax, RootEntryOffset ;指定读取的逻辑扇区号
mov cx, RootEntryLength ;指定读取的长度
mov bx, Buf
call ReadSector ;读取根目录区所有数据到Buf
mov si, Target ;指定要查找的目标文件名
mov cx, TarLen ;指定长度
mov dx, 0
call FindEntry ;根据文件名在Buf查找该文件的目录项
cmp dx, 0
jz output ;找不到该文件,输出错误信息; 找到了bx指向该目录项
mov si, bx ;指定拷贝源
mov di, EntryItem ;拷贝的目的地址
mov cx, EntryItemLength ;长度
call MemCpy ;将Buf的该目录项(bx指向)拷到另一片内存
;加载FAT表
mov ax, FatEntryLength ;指定FAT表长度(9)
mov cx, [BPB_BytsPerSec] ;指定每扇区大小(512字节)
mul cx ;ax = FAT表字节数
mov bx, BaseOfLoader
sub bx, ax ;指定FAT表加载的内存起始位置(bx)
mov ax, FatEntryOffset ;FAT表在文件系统的逻辑扇区号
mov cx, FatEntryLength ;读取的扇区数
call ReadSector ;读取FAT表到bx所指内存
mov cx, [EntryItem + 0x1A];该目标文件对应目录项的起始簇号
call FatVec ;查表 cx对应的FAT表项
jmp last
output:
mov bp, MsgStr
mov cx, MsgLen
call Print
last:
hlt
jmp last
;下面所说的FAT表都是加载到内存后称呼,之前的称为FatVec
; cx --> FatVec表项下标j 即目录项中的簇号
; bx --> fat table address fat表加载到内存后的起始地址
;
; return:
; dx --> FatVec[j]
FatVec:
mov ax, cx
mov cl, 2
div cl ;j/2
push ax ;备份商和余数
mov ah, 0
mov cx, 3
mul cx ;ax = al * 3 对应表项在内存的起始位置(i = j / 2 * 3)
mov cx, ax
pop ax
cmp ah, 0;余数为0,FatVec下标j是偶数,不为0是奇数
jz even
jmp odd
even: ; FatVec[j] = ( (Fat[i+1] & 0x0F) << 8 ) | Fat[i];
mov dx, cx
add dx, 1
add dx, bx ;FAT表的基址 + (i+1) = Fat[i+1]的地址
mov bp, dx
mov dl, byte [bp] ;dl = FAT[i+1]的值
and dl, 0x0F
shl dx, 8
add cx, bx ;Fat[i]的地址
mov bp, cx
or dl, byte [bp]
jmp return
odd: ; FatVec[j+1] = (Fat[i+2] << 4) | ( (Fat[i+1] >> 4) & 0x0F );
mov dx, cx
add dx, 2
add dx, bx ;Fat[i+2]的地址
mov bp, dx
mov dl, byte [bp]
mov dh, 0
shl dx, 4
add cx, 1
add cx, bx ;Fat[i+1]的地址
mov bp, cx
mov cl, byte [bp]
shr cl, 4
and cl, 0x0F
mov ch, 0
or dx, cx
return:
ret
; ds:si --> source
; es:di --> destination
; cx --> length
MemCpy:
push si
push di
push cx
push ax
cmp si, di
ja btoe
add si, cx
add di, cx
dec si
dec di
jmp etob
btoe:
cmp cx, 0
jz done
mov al, [si]
mov byte [di], al
inc si
inc di
dec cx
jmp btoe
etob:
cmp cx, 0
jz done
mov al, [si]
mov byte [di], al
dec si
dec di
dec cx
jmp etob
done:
pop ax
pop cx
pop di
pop si
ret
; es:bx --> root entry offset address
; ds:si --> target string
; cx --> target length
;
; return:
; (dx !=0 ) ? exist : noexist
; exist --> bx is the target entry
FindEntry:
push di
push bp
push cx
mov dx, [BPB_RootEntCnt]
mov bp, sp
find:
cmp dx, 0
jz noexist
mov di, bx
mov cx, [bp]
call MemCmp
cmp cx, 0
jz exist
add bx, 32
dec dx
jmp find
exist:
noexist:
pop cx
pop bp
pop di
ret
; ds:si --> source
; es:di --> destination
; cx --> length
;
; return:
; (cx == 0) ? equal : noequal
MemCmp:
push si
push di
push ax
compare:
cmp cx, 0
jz equal
mov al, [si]
cmp al, byte [di]
jz goon
jmp noequal
goon:
inc si
inc di
dec cx
jmp compare
equal:
noequal:
pop ax
pop di
pop si
ret
; es:bp --> string address
; cx --> string length
Print:
mov dx, 0
mov ax, 0x1301
mov bx, 0x0007
int 0x10
ret
; no parameter
ResetFloppy:
push ax
push dx
mov ah, 0x00
mov dl, [BS_DrvNum]
int 0x13
pop dx
pop ax
ret
; ax --> logic sector number
; cx --> number of sector
; es:bx --> target address
ReadSector:
push bx
push cx
push dx
push ax
call ResetFloppy
push bx
push cx
mov bl, [BPB_SecPerTrk]
div bl
mov cl, ah
add cl, 1
mov ch, al
shr ch, 1
mov dh, al
and dh, 1
mov dl, [BS_DrvNum]
pop ax
pop bx
mov ah, 0x02
read:
int 0x13
jc read
pop ax
pop dx
pop cx
pop bx
ret
MsgStr db "No LOADER ..."
MsgLen equ ($-MsgStr)
Target db "LOADER "
TarLen equ ($-Target)
EntryItem times EntryItemLength db 0x00
Buf:
times 510-($-$$) db 0x00
db 0x55, 0xaa
当汇编代码中定义了函数,那么也需要定义栈空间
读取数据前,逻辑扇区号需要转化为磁盘的物理位置
物理软盘上的数据位置由磁头号,柱面号,扇区号唯一确定
软盘数据以扇区(512字节)为单位进行读取
可通过查找根目录区判断是否存在目标文件
- 加载根目录区至内存中(ReadSector)
- 遍历根目录区中的每一项(FindEntry)
- 通过每一项的前11个字节进行判断(MemCmp)
- 当目标不存在时,打印错误信息(Print)
内存拷贝时需要考虑进行拷贝的方向
- 当si > di时,从前向后拷贝
- 当si <= di时,从后向前拷贝
Fat 表加载到内存中之后,需要"动态组装”表项
- Fat表中使用3个字节表示2个表项
- 起始字节 = 表项下标 / 2 * 3 →(运算结果取整)
BootLoader内存布局
弄完了才发现描述有误,文件系统改为软盘
通过FAT表加载文件内容
实验步骤
1. 在虚拟软盘中创建体积较大的文本文件(Loader)
2. 将Loader的内容加载到BaseOfLoader地址处
3. 打印Loader中的文本(判断加载是否完全)
先编写loader.asm
org 0x9000
begin:
mov si, msg
print:
mov al, [si]
add si, 1
cmp al, 0x00
je end
mov ah, 0x0E
mov bx, 0x0F
int 0x10
jmp print
end:
hlt
jmp end
msg:
db 0x0a, 0x0a
db "Hello, D.T.OS!"
db 0x0a, 0x0a
db 0x00
由于0扇区代码量需要控制在512字节所以之前的代码删除一些非必要代码如push pop(重构)
boot.asm
org 0x7c00
jmp short start
nop
define:
BaseOfStack equ 0x7c00
BaseOfLoader equ 0x9000
RootEntryOffset equ 19
RootEntryLength equ 14
EntryItemLength equ 32
FatEntryOffset equ 1
FatEntryLength equ 9
header:
BS_OEMName db "D.T.Soft"
BPB_BytsPerSec dw 512
BPB_SecPerClus db 1
BPB_RsvdSecCnt dw 1
BPB_NumFATs db 2
BPB_RootEntCnt dw 224
BPB_TotSec16 dw 2880
BPB_Media db 0xF0
BPB_FATSz16 dw 9
BPB_SecPerTrk dw 18
BPB_NumHeads dw 2
BPB_HiddSec dd 0
BPB_TotSec32 dd 0
BS_DrvNum db 0
BS_Reserved1 db 0
BS_BootSig db 0x29
BS_VolID dd 0
BS_VolLab db "D.T.OS-0.01"
BS_FileSysType db "FAT12 "
start:
mov ax, cs
mov ss, ax
mov ds, ax
mov es, ax
mov sp, BaseOfStack
mov ax, RootEntryOffset
mov cx, RootEntryLength
mov bx, Buf
call ReadSector
mov si, Target
mov cx, TarLen
mov dx, 0
call FindEntry
cmp dx, 0
jz output
mov si, bx
mov di, EntryItem
mov cx, EntryItemLength
call MemCpy
mov ax, FatEntryLength
mov cx, [BPB_BytsPerSec]
mul cx
mov bx, BaseOfLoader
sub bx, ax
mov ax, FatEntryOffset
mov cx, FatEntryLength
call ReadSector
mov dx, [EntryItem + 0x1A] ;簇号,即表项下标j
mov si, BaseOfLoader ;指定目标地址
loading:
mov ax, dx
add ax, 31 ;33 - 2 + j
mov cx, 1 ;读取一个扇区
push dx
push bx
mov bx, si ;target address
call ReadSector ;读取一个扇区到si
pop bx
pop cx ;pop dx ,mov cx dx
call FatVec ;读取j表项的内容
cmp dx, 0xFF7
jnb BaseOfLoader
add si, 512 ; 开始下一个扇区
jmp loading
output:
mov bp, MsgStr
mov cx, MsgLen
call Print
last:
hlt
jmp last
; cx --> index
; bx --> fat table address
;
; return:
; dx --> fat[index]
FatVec:
mov ax, cx
mov cl, 2
div cl
push ax
mov ah, 0
mov cx, 3
mul cx
mov cx, ax
pop ax
cmp ah, 0
jz even
jmp odd
even: ; FatVec[j] = ( (Fat[i+1] & 0x0F) << 8 ) | Fat[i];
mov dx, cx
add dx, 1
add dx, bx
mov bp, dx
mov dl, byte [bp]
and dl, 0x0F
shl dx, 8
add cx, bx
mov bp, cx
or dl, byte [bp]
jmp return
odd: ; FatVec[j+1] = (Fat[i+2] << 4) | ( (Fat[i+1] >> 4) & 0x0F );
mov dx, cx
add dx, 2
add dx, bx
mov bp, dx
mov dl, byte [bp]
mov dh, 0
shl dx, 4
add cx, 1
add cx, bx
mov bp, cx
mov cl, byte [bp]
shr cl, 4
and cl, 0x0F
mov ch, 0
or dx, cx
return:
ret
; ds:si --> source
; es:di --> destination
; cx --> length
MemCpy:
cmp si, di
ja btoe
add si, cx
add di, cx
dec si
dec di
jmp etob
btoe:
cmp cx, 0
jz done
mov al, [si]
mov byte [di], al
inc si
inc di
dec cx
jmp btoe
etob:
cmp cx, 0
jz done
mov al, [si]
mov byte [di], al
dec si
dec di
dec cx
jmp etob
done:
ret
; es:bx --> root entry offset address
; ds:si --> target string
; cx --> target length
;
; return:
; (dx !=0 ) ? exist : noexist
; exist --> bx is the target entry
FindEntry:
push cx
mov dx, [BPB_RootEntCnt]
mov bp, sp
find:
cmp dx, 0
jz noexist
mov di, bx
mov cx, [bp]
push si
call MemCmp
pop si
cmp cx, 0
jz exist
add bx, 32
dec dx
jmp find
exist:
noexist:
pop cx
ret
; ds:si --> source
; es:di --> destination
; cx --> length
;
; return:
; (cx == 0) ? equal : noequal
MemCmp:
compare:
cmp cx, 0
jz equal
mov al, [si]
cmp al, byte [di]
jz goon
jmp noequal
goon:
inc si
inc di
dec cx
jmp compare
equal:
noequal:
ret
; es:bp --> string address
; cx --> string length
Print:
mov dx, 0
mov ax, 0x1301
mov bx, 0x0007
int 0x10
ret
; no parameter
ResetFloppy:
mov ah, 0x00
mov dl, [BS_DrvNum]
int 0x13
ret
; ax --> logic sector number
; cx --> number of sector
; es:bx --> target address
ReadSector:
call ResetFloppy
push bx
push cx
mov bl, [BPB_SecPerTrk]
div bl
mov cl, ah
add cl, 1
mov ch, al
shr ch, 1
mov dh, al
and dh, 1
mov dl, [BS_DrvNum]
pop ax
pop bx
mov ah, 0x02
read:
int 0x13
jc read
ret
MsgStr db "No LOADER ..."
MsgLen equ ($-MsgStr)
Target db "LOADER "
TarLen equ ($-Target)
EntryItem times EntryItemLength db 0x00
Buf:
times 510-($-$$) db 0x00
db 0x55, 0xaa
makefile
.PHONY : all clean rebuild
BOOT_SRC := boot.asm
BOOT_OUT := boot
LOADER_SRC := loader.asm
LOADER_OUT := loader
IMG := data.img
IMG_PATH := /mnt/hgfs
RM := rm -fr
all : $(IMG) $(BOOT_OUT) $(LOADER_OUT)
@echo "Build Success ==> D.T.OS!"
$(IMG) :
bximage $@ -q -fd -size=1.44
$(BOOT_OUT) : $(BOOT_SRC)
nasm $^ -o $@
dd if=$@ of=$(IMG) bs=512 count=1 conv=notrunc
$(LOADER_OUT) : $(LOADER_SRC)
nasm $^ -o $@
sudo mount -o loop $(IMG) $(IMG_PATH)
sudo cp $@ $(IMG_PATH)/$@
sudo umount $(IMG_PATH)
clean :
$(RM) $(IMG) $(BOOT_OUT) $(LOADER_OUT)
rebuild :
@$(MAKE) clean
@$(MAKE) all
成功将loader程序加载到内存并读取其中的内容
小结
- Boot 需要进行重构保证在512字节内完成功能
- 在汇编程序中尽量确保函数调用前后通用寄存器的状态不变
- Boot 成功加载Loader 后将控制权转移
- Loader 程序没有代码体积上的限制
下一节 - 实模式到保护模式