上一节我们进行了文件的查找实验,文件查找成功了,这一节,我们将文件的内容加载进内存,再一次将整体的流程给出如下:
读取文件的内容时,我们需要根据FAT表找到存储文件内容的每一个扇区,然后进行内容的读取,在上一节中,我们将整个目录区的内容加载到了内存并根据文件名找到了所在的目录项,为了节省内存,我们将找到的目录项拷贝到另一片内存区域中,因为这个目录项中还有我们需要的内容,比如文件的起始扇区号等。而原来加载目录区的那一部分内存便可以另作他用,比如可以将FAT表加载到该区域。
目标文件的目录信息如下:

内存拷贝时,为了防止已经拷贝的内容将原来还没有拷贝的内容覆盖,我们需要考虑一下拷贝的方向,示意图如下:

当源地址小于等于目标地址时,要从后向前拷贝,如上图中左半部,当源地址大于目标地址时,要从头部向尾部拷贝,如上图右半部。
用到的汇编指令如下所示:

汇编程序如下所示
org 0x7c00
define:
BaseOfStack equ 0x7c00
RootEntryOffest equ 19
RootEntryLength equ 14
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
;jmp last
mov si, Target
mov di, MsgStr
mov cx, MsgLen
call MemCpy
Output:
mov bp, MsgStr
mov cx, MsgLen
call Print
last:
hlt
jmp last
; ds:si --> src
; es:di --> dest
; 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 --> entry root offset address
; ds:si --> target string
; cx --> target length
;
; return
; (dx != 0) ? exist : noexist;
; exist --> bx is 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
noexist:
exist:
pop cx
pop bp
pop di
ret
; ds:si --> source address
; es:di --> destination address
; 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, 0x007
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
ReadSection:
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 "Not Exist! ..."
MsgLen equ ($-MsgStr)
Target db "LOADER BIN"
TarLen equ ($-Target)
Buf:
times 510-($-$$) db 0x00
db 0x55, 0xaa
下一步,我们进行FAT表项的读取,并根据表项的内容加载文件数据,FAT表项中每个表项占用1.5个字节,即使用3个字节表示两个表项,如下图所示:

以前读取data.img的实验中,我们是将FAT表项完全读取后做了一次转换,生成了一个向量,向量中的每一项占用2个字节,那是一次性将所有FAT表项转换好的,而在我们的汇编程序中,我们使用动态组装的方法,即每用到一个表项就进行临时组装,组装后的FAT表项的下标和起始字节的关系图如下:
组装时用到的关系式如下所示:

需要用到的乘法汇编指令如下:

我们先打开之前写的读取data.img的Qt程序,添加一行,如下所示:

我们在原本的Qt程序中添加了一条打印语句,打印的数据是相对于数据区的偏移扇区号 + 2

打印输出表明:只循环了一次,这个文件的数据只占用一个扇区的大小,并且它的数据存放在距离数据区的第 2 个扇区大小的偏移,第二次去Fat表查找剩余数据时,Fat[j] > 0xff7,则表明当前文件已经没有剩余内容了。
下面是汇编程序
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 # src: 查找到的目录项地址
mov di, EntryItem # dest
mov cx, EntryItemLength # length: 32
call MemCpy
mov ax, FatEntryLength # 9个扇区大小
mov cx, [BPB_BytsPerSec] # 每个扇区512字节
mul cx # 计算出整个Fat表的大小
mov bx, BaseOfLoader
sub bx, ax # Fat表的加载地址: (0x9000 - 512 * 9)
mov cx, FatEntryLength
mov ax, FatEntryOffset
call ReadSection # 读取Fat表
mov cx, [EntryItem + 0x1a] # 查找的的文件所对应的开始簇号
call FatVec # 根据Fat表查找下一个簇号
jmp last
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 # index / 2
mov cl, 2
div cl
push ax # al存放商,ah存放余数
mov ah, 0
mov cx, 3
mul cx
mov cx, ax # cx = index / 2 * 3,cx存放了物理Fat号地址的偏移量
pop ax
cmp ah, 0 # 判断逻辑Fat号是奇数还是偶数
jz even
jmp odd
even: ; ((fat[i + 1] & 0x0f) << 8) | fat[i]
mov dx, cx
add dx, 1 # dx = i + 1
add dx, bx # dx = FatAddr +i + 1
mov bp, dx
mov dl, byte [bp] # dl = fat[i + 1]
and dl, 0x0f # fat[i + 1] & 0x0f
shl dx, 8 # (fat[i + 1] & 0x0f) <<8
add cx, bx # cx = FatAddr + i
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:
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 --> entry root offset address
; ds:si --> target string
; cx --> target length
;
; return
; (dx != 0) ? exist : noexist;
; exist --> bx is 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
noexist:
exist:
pop cx
pop bp
pop di
ret
; ds:si --> source address
; es:di --> destination address
; 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, 0x007
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
ReadSection:
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 "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

我们在58行和59行对应的地址处打两个断点,来查看寄存器的值。

cx寄存器的值为4,这和我们之前在Qt平台上打印出的数据是一样的,表明这个文件的开始簇号为4,我们来观察下一个断点对应的寄存器信息

我们观察到dx的寄存器的值为0xfff,大于0xff7,表明这个文件已经结束了,文件的内容只存在于一个扇区中。
本文介绍了如何在内存中读取文件内容,通过FAT表找到文件起始扇区,包括内存拷贝策略、汇编指令的应用,以及如何利用FAT表项动态加载文件数据的过程。重点展示了文件查找和FAT表操作的汇编实现细节。
628

被折叠的 条评论
为什么被折叠?



