突破512字节的限制(下)

本文介绍了如何在内存中读取文件内容,通过FAT表找到文件起始扇区,包括内存拷贝策略、汇编指令的应用,以及如何利用FAT表项动态加载文件数据的过程。重点展示了文件查找和FAT表操作的汇编实现细节。

上一节我们进行了文件的查找实验,文件查找成功了,这一节,我们将文件的内容加载进内存,再一次将整体的流程给出如下:

读取文件的内容时,我们需要根据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,表明这个文件已经结束了,文件的内容只存在于一个扇区中。 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值