这一节,我们来真正的读取文件中的内容到内存中,首先来看一下内存布局是什么样的,如下所示:
Boot占用了512字节,Fat Table占用了4KB,而真正的文件中的内容,我们把它存在0x9000开始的内存地址处。
加载文件内容的过程如下:
实验步骤如下:
1、在虚拟软盘中创建体积较大的文本文件,使之内容大小超过一个扇区。
2、将文件的内容加载到BaseOfLoader地址处。
3、打印加载的内容,判断是否加载完全。
读取文件内容的代码如下所示
start:
mov ax, cs
mov ss, ax
mov ds, ax
mov es, ax
mov sp, BaseOfStack
mov ax, RootEntryOffest
mov cx, RootEntryLength
mov bx, Buf
call ReadSection
mov bx, Buf
mov si, Target
mov cx, TarLen
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 cx, FatEntryLength
mov ax, FatEntryOffset
call ReadSection
mov dx, [EntryItem + 0x1a]
mov si, BaseOfLoader
loading:
mov ax, dx
add ax, 31 # ax = 逻辑扇区号
mov cx, 1
push dx
push bx
mov bx, si
call ReadSection
pop bx
pop cx # cx = 文件扇区下标(数据区的扇区号)
call FatVec
cmp dx, 0xff7 # 判断下一个文件扇区下标是否合法
jnb Output
add si, 512 # si = dest + 512
jmp loading
Output:
mov bp, BaseOfLoader
mov cx, [EntryItem + 0x1c]
call Print
last:
hlt
jmp last
我们在make时出现了一个错误
这个错误告诉我们已经使用的内存超出了512字节,所以我们就需要代码重构,删除一些不必要的代码,于是我们就把一些函数调用时不必要的入栈和出栈操作给删除了。
完整汇编代码如下所示
org 0x7c00
define:
BaseOfStack equ 0x7c00
BaseOfLoader equ 0x9000
RootEntryOffest equ 19
RootEntryLength equ 14
EntryItemLength equ 32
FatEntryOffset equ 1
FatEntryLength equ 9
jmp short start
nop
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, RootEntryOffest
mov cx, RootEntryLength
mov bx, Buf
call ReadSection
mov bx, Buf
mov si, Target
mov cx, TarLen
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 cx, FatEntryLength
mov ax, FatEntryOffset
call ReadSection
mov dx, [EntryItem + 0x1a]
mov si, BaseOfLoader
loading:
mov ax, dx
add ax, 31
mov cx, 1
push dx
push bx
mov bx, si
call ReadSection
pop bx
pop cx
call FatVec
cmp dx, 0xff7
jnb Output
add si, 512
jmp loading
Output:
mov bp, BaseOfLoader
mov cx, [EntryItem + 0x1c]
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: ; ((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: ; (fat[i + 2] << 4) | ((fat[i + 1] & 0xf0) >> 4);
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]
and cl, 0xf0
mov ch, 0
shr cx, 4
or dx, cx
return:
ret
; ds:si --> src
; es:di --> dest
; 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 --> entry root offset address
; ds:si --> target string
; cx --> target length
;
; return
; (dx != 0) ? exist : noexist;
; exist --> bx is target entry
FindEntry:
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
noexist:
exist:
pop cx
ret
; ds:si --> source address
; es:di --> destination address
; 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, 0x007
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
ReadSection:
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 "Not Exist! ..."
MsgLen equ ($-MsgStr)
Target db "LOADER BIN"
TarLen equ ($-Target)
EntryItem times EntryItemLength db 0x00
Buf:
times 510-($-$$) db 0x00
db 0x55, 0xaa
下面是程序的输出结果
输出结果打印了loader.bin这个文件的内容,正确地打印了文件的内容。此文件的大小不足512字节,为了验证该程序能否正常的打印大小大于512字节的文件,我们就需要修改loader.bin这个文件。
于是我们通过 sudo mount -o loop data.img /mnt/hgfs/ 命令,将这个软盘挂载到虚拟文件系统,我们来修改loader.bin这个文件的内容,把boot.asm的内容复制到此文件中。
boot.asm的大小为3960字节,大于了512字节,我们重新加载程序
正确打印了loader.bin的文件内容,两次正确的打印说明了该程序的正确性。
我们修改了主程序的第86行代码,当找到目标文件时,主引导程序将控制权转交给0x9000处的地址片段。
以下是我们的测试程序,该程序打印一个"hello, DTOS!",来验证主引导程序是否把控制权转交给0x9000处的地址片段
org 0x9000
begin:
mov si, MsgStr
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
MsgStr:
db 0x0a, 0x0a
db "hello, DTOS!"
db 0x0a, 0x0a
db 0x00
由于我们需要把上面的程序编译成可执行文件,并且需要把可执行文件拷贝到data.img这个软盘中,为了不每次进行重复的步骤,我们需要修改makefile,修改后的makefile如下所示
.PHONY : all clean rebuild
BOOT_SRC := boot.asm
BOOT_OUT := boot.bin
LOADER_SRC := loader.asm
LOADER_OUT := loader
IMG := data.img
IMG_PATH := /mnt/hgfs
RM := rm -fr
all : $(IMG) $(BOOT_OUT) $(LOADER_OUT)
@echo "Success!"
$(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
我们在bochs上运行data.img
程序运行结果如下
成功跳转到0x9000处去执行我们测试用的可执行程序,打印出了字符串。
我们把data.img复制到window上,去VMware上测试,测试结果如下
也打印出来相应的字符串,到此为止,我们已经突破了512字节的限制!!!