上一节我们进行了文件的查找实验,文件查找成功了,这一节,我们将文件的内容加载进内存,再一次将整体的流程给出如下:
读取文件的内容时,我们需要根据FAT表找到存储文件内容的每一个扇区,然后进行内容的读取,在上一节中,我们将整个目录区的内容加载到了内存并根据文件名找到了所在的目录项,为了节省内存,我们将找到的目录项拷贝到另一片内存区域中,因为这个目录项中还有我们需要的内容,比如文件的起始扇区号等。而原来加载目录区的那一部分内存便可以另作他用,比如可以将FAT表加载到该区域。
目标文件的目录信息如下:
内存拷贝时,为了防止已经拷贝的内容将原来还没有拷贝的内容覆盖,我们需要考虑一下拷贝的方向,示意图如下:
当源地址小于等于目标地址时,要从后向前拷贝,如上图中左半部,当源地址大于目标地址时,要从头部向尾部拷贝,如上图右半部。
用到的汇编指令如下所示:
下面直接给出内存拷贝的汇编代码:
1 org 0x7c00 2 3 jmp short start 4 nop 5 6 define: 7 BaseOfStack equ 0x7c00 8 BaseOfLoader equ 0x9000 9 RootEntryOffset equ 19 10 RootEntryLength equ 14 11 EntryItemLength equ 32 12 FatEntryOffset equ 1 13 FatEntryLength equ 9 14 15 header: 16 BS_OEMName db "D.T.Soft" 17 BPB_BytsPerSec dw 512 18 BPB_SecPerClus db 1 19 BPB_RsvdSecCnt dw 1 20 BPB_NumFATs db 2 21 BPB_RootEntCnt dw 224 22 BPB_TotSec16 dw 2880 23 BPB_Media db 0xF0 24 BPB_FATSz16 dw 9 25 BPB_SecPerTrk dw 18 26 BPB_NumHeads dw 2 27 BPB_HiddSec dd 0 28 BPB_TotSec32 dd 0 29 BS_DrvNum db 0 30 BS_Reserved1 db 0 31 BS_BootSig db 0x29 32 BS_VolID dd 0 33 BS_VolLab db "D.T.OS-0.01" 34 BS_FileSysType db "FAT12 " 35 36 start: 37 mov ax, cs 38 mov ss, ax 39 mov ds, ax 40 mov es, ax 41 mov sp, BaseOfStack 42 43 mov ax, RootEntryOffset 44 mov cx, RootEntryLength 45 mov bx, Buf 46 47 call ReadSector 48 49 mov si, Target 50 mov cx, TarLen 51 mov dx, 0 52 53 call FindEntry 54 55 ; cmp dx, 0 56 ; jz output 57 58 mov si, Target 59 mov di, MsgStr 60 mov cx, TarLen 61 62 call MemCpy 63 64 65 output: 66 mov bp, MsgStr 67 mov cx, MsgLen 68 call Print 69 70 last: 71 hlt 72 jmp last 73 74 75 ; ds:si --> source 76 ; es:di --> destination 77 ; cx --> length 78 MemCpy: 79 push si 80 push di 81 push cx 82 push ax 83 84 cmp si, di 85 86 ja btoe 87 88 add si, cx 89 add di, cx 90 dec si 91 dec di 92 93 jmp etob 94 95 btoe: 96 cmp cx, 0 97 jz done 98 mov al, [si] 99 mov byte [di], al 100 inc si 101 inc di 102 dec cx 103 jmp btoe 104 105 etob: 106 cmp cx, 0 107 jz done 108 mov al, [si] 109 mov byte [di], al 110 dec si 111 dec di 112 dec cx 113 jmp etob 114 115 done: 116 pop ax 117 pop cx 118 pop di 119 pop si 120 ret 121 122 ; es:bx --> root entry offset address 123 ; ds:si --> target string 124 ; cx --> target length 125 ; 126 ; return: 127 ; (dx !=0 ) ? exist : noexist 128 ; exist --> bx is the target entry 129 FindEntry: 130 push di 131 push bp 132 push cx 133 134 mov dx, [BPB_RootEntCnt] 135 mov bp, sp 136 137 find: 138 cmp dx, 0 139 jz noexist 140 mov di, bx 141 mov cx, [bp] 142 call MemCmp 143 cmp cx, 0 144 jz exist 145 add bx, 32 146 dec dx 147 jmp find 148 149 exist: 150 noexist: 151 pop cx 152 pop bp 153 pop di 154 155 ret 156 157 ; ds:si --> source 158 ; es:di --> destination 159 ; cx --> length 160 ; 161 ; return: 162 ; (cx == 0) ? equal : noequal 163 MemCmp: 164 push si 165 push di 166 push ax 167 168 compare: 169 cmp cx, 0 170 jz equal 171 mov al, [si] 172 cmp al, byte [di] 173 jz goon 174 jmp noequal 175 goon: 176 inc si 177 inc di 178 dec cx 179 jmp compare 180 181 equal: 182 noequal: 183 pop ax 184 pop di 185 pop si 186 187 ret 188 189 ; es:bp --> string address 190 ; cx --> string length 191 Print: 192 mov dx, 0 193 mov ax, 0x1301 194 mov bx, 0x0007 195 int 0x10 196 ret 197 198 ; no parameter 199 ResetFloppy: 200 push ax 201 push dx 202 203 mov ah, 0x00 204 mov dl, [BS_DrvNum] 205 int 0x13 206 207 pop dx 208 pop ax 209 210 ret 211 212 ; ax --> logic sector number 213 ; cx --> number of sector 214 ; es:bx --> target address 215 ReadSector: 216 push bx 217 push cx 218 push dx 219 push ax 220 221 call ResetFloppy 222 223 push bx 224 push cx 225 226 mov bl, [BPB_SecPerTrk] 227 div bl 228 mov cl, ah 229 add cl, 1 230 mov ch, al 231 shr ch, 1 232 mov dh, al 233 and dh, 1 234 mov dl, [BS_DrvNum] 235 236 pop ax 237 pop bx 238 239 mov ah, 0x02 240 241 read: 242 int 0x13 243 jc read 244 245 pop ax 246 pop dx 247 pop cx 248 pop bx 249 250 ret 251 252 MsgStr db "No LOADER ..." 253 MsgLen equ ($-MsgStr) 254 Target db "LOADER " 255 TarLen equ ($-Target) 256 EntryItem times EntryItemLength db 0x00 257 Buf: 258 times 510-($-$$) db 0x00 259 db 0x55, 0xaa
以上程序将Target处的字符串拷贝到了MsgStr处,然后将MsgStr处的内容打印出来,结果如下:
下一步,我们进行FAT表项的读取,并根据表项的内容加载文件数据,FAT表项中每个表项占用1.5个字节,即使用3个字节表示两个表项,如下图所示:
以前读取a.img的实验中,我们是将FAT表项完全读取后做了一次转换,生成了一个向量,向量中的每一项占用2个字节,那是一次性将所有FAT表项转换好的,而在我们的汇编程序中,我们使用动态组装的方法,即每用到一个表项就进行临时组装,组装后的FAT表项的下标和起始字节的关系图如下:
组装时用到的关系式如下所示:
需要用到的乘法汇编指令如下:
我们先打开之前写的读取a.img的Qt程序,添加一行,如下所示:
我们新添加了224行,将数据所在的扇区号分别打印出来,运行程序,得到如下结果:
上图中只打印出了4,说明数据只存在第4扇区,也就是222行的循环只循环了一次,第二次循环是vec[j]已经不再小于0xFF7,也就是FAT表项不再是一个有效项,而是一个结束项。
下面给出汇编程序:
1 org 0x7c00 2 3 jmp short start 4 nop 5 6 define: 7 BaseOfStack equ 0x7c00 8 BaseOfLoader equ 0x9000 9 RootEntryOffset equ 19 10 RootEntryLength equ 14 11 EntryItemLength equ 32 12 FatEntryOffset equ 1 13 FatEntryLength equ 9 14 15 header: 16 BS_OEMName db "D.T.Soft" 17 BPB_BytsPerSec dw 512 18 BPB_SecPerClus db 1 19 BPB_RsvdSecCnt dw 1 20 BPB_NumFATs db 2 21 BPB_RootEntCnt dw 224 22 BPB_TotSec16 dw 2880 23 BPB_Media db 0xF0 24 BPB_FATSz16 dw 9 25 BPB_SecPerTrk dw 18 26 BPB_NumHeads dw 2 27 BPB_HiddSec dd 0 28 BPB_TotSec32 dd 0 29 BS_DrvNum db 0 30 BS_Reserved1 db 0 31 BS_BootSig db 0x29 32 BS_VolID dd 0 33 BS_VolLab db "D.T.OS-0.01" 34 BS_FileSysType db "FAT12 " 35 36 start: 37 mov ax, cs 38 mov ss, ax 39 mov ds, ax 40 mov es, ax 41 mov sp, BaseOfStack 42 43 mov ax, RootEntryOffset 44 mov cx, RootEntryLength 45 mov bx, Buf 46 47 call ReadSector 48 49 mov si, Target 50 mov cx, TarLen 51 mov dx, 0 52 53 call FindEntry 54 55 cmp dx, 0 56 jz output 57 58 mov si, bx 59 mov di, EntryItem 60 mov cx, EntryItemLength 61 62 call MemCpy 63 64 mov ax, FatEntryLength ; FAT zhan yong 9 ge Sector 65 mov cx, [BPB_BytsPerSec] ; mei ge Sector de da xiao 66 mul cx ; xiang cheng hou de dao FAT biao de zong zi jie da xiao,cun ; zai ax zhong 67 mov bx, BaseOfLoader ; jia zai FAT biao de qi shi di zhi 68 sub bx, ax 69 70 mov ax, FatEntryOffset 71 mov cx, FatEntryLength 72 73 call ReadSector 74 75 mov cx, [EntryItem + 0x1A] ;huo qu qi shi cu hao 76 77 call FatVec 78 79 jmp last 80 81 output: 82 mov bp, MsgStr 83 mov cx, MsgLen 84 call Print 85 86 last: 87 hlt 88 jmp last 89 90 91 ; cx --> index 92 ; bx --> fat table address 93 ; 94 ; return: 95 ; dx --> fat[index] 96 FatVec: 97 mov ax, cx 98 mov cl, 2 99 div cl 100 101 push ax 102 103 mov ah, 0 104 mov cx, 3 105 mul cx 106 mov cx, ax 107 108 pop ax 109 110 cmp ah, 0 111 jz even 112 jmp odd 113 114 even: ; FatVec[j] = ( (Fat[i+1] & 0x0F) << 8 ) | Fat[i]; 115 mov dx, cx 116 add dx, 1 117 add dx, bx 118 mov bp, dx 119 mov dl, byte [bp] 120 and dl, 0x0F 121 shl dx, 8 122 add cx, bx 123 mov bp, cx 124 or dl, byte [bp] 125 jmp return 126 127 odd: ; FatVec[j+1] = (Fat[i+2] << 4) | ( (Fat[i+1] >> 4) & 0x0F ); 128 mov dx, cx 129 add dx, 2 130 add dx, bx 131 mov bp, dx 132 mov dl, byte [bp] 133 mov dh, 0 134 shl dx, 4 135 add cx, 1 136 add cx, bx 137 mov bp, cx 138 mov cl, byte [bp] 139 shr cl, 4 140 and cl, 0x0F 141 mov ch, 0 142 or dx, cx 143 144 return: 145 ret 146 147 ; ds:si --> source 148 ; es:di --> destination 149 ; cx --> length 150 MemCpy: 151 push si 152 push di 153 push cx 154 push ax 155 156 cmp si, di 157 158 ja btoe 159 160 add si, cx 161 add di, cx 162 dec si 163 dec di 164 165 jmp etob 166 167 btoe: 168 cmp cx, 0 169 jz done 170 mov al, [si] 171 mov byte [di], al 172 inc si 173 inc di 174 dec cx 175 jmp btoe 176 177 etob: 178 cmp cx, 0 179 jz done 180 mov al, [si] 181 mov byte [di], al 182 dec si 183 dec di 184 dec cx 185 jmp etob 186 187 done: 188 pop ax 189 pop cx 190 pop di 191 pop si 192 ret 193 194 ; es:bx --> root entry offset address 195 ; ds:si --> target string 196 ; cx --> target length 197 ; 198 ; return: 199 ; (dx !=0 ) ? exist : noexist 200 ; exist --> bx is the target entry 201 FindEntry: 202 push di 203 push bp 204 push cx 205 206 mov dx, [BPB_RootEntCnt] 207 mov bp, sp 208 209 find: 210 cmp dx, 0 211 jz noexist 212 mov di, bx 213 mov cx, [bp] 214 call MemCmp 215 cmp cx, 0 216 jz exist 217 add bx, 32 218 dec dx 219 jmp find 220 221 exist: 222 noexist: 223 pop cx 224 pop bp 225 pop di 226 227 ret 228 229 ; ds:si --> source 230 ; es:di --> destination 231 ; cx --> length 232 ; 233 ; return: 234 ; (cx == 0) ? equal : noequal 235 MemCmp: 236 push si 237 push di 238 push ax 239 240 compare: 241 cmp cx, 0 242 jz equal 243 mov al, [si] 244 cmp al, byte [di] 245 jz goon 246 jmp noequal 247 goon: 248 inc si 249 inc di 250 dec cx 251 jmp compare 252 253 equal: 254 noequal: 255 pop ax 256 pop di 257 pop si 258 259 ret 260 261 ; es:bp --> string address 262 ; cx --> string length 263 Print: 264 mov dx, 0 265 mov ax, 0x1301 266 mov bx, 0x0007 267 int 0x10 268 ret 269 270 ; no parameter 271 ResetFloppy: 272 push ax 273 push dx 274 275 mov ah, 0x00 276 mov dl, [BS_DrvNum] 277 int 0x13 278 279 pop dx 280 pop ax 281 282 ret 283 284 ; ax --> logic sector number 285 ; cx --> number of sector 286 ; es:bx --> target address 287 ReadSector: 288 push bx 289 push cx 290 push dx 291 push ax 292 293 call ResetFloppy 294 295 push bx 296 push cx 297 298 mov bl, [BPB_SecPerTrk] 299 div bl 300 mov cl, ah 301 add cl, 1 302 mov ch, al 303 shr ch, 1 304 mov dh, al 305 and dh, 1 306 mov dl, [BS_DrvNum] 307 308 pop ax 309 pop bx 310 311 mov ah, 0x02 312 313 read: 314 int 0x13 315 jc read 316 317 pop ax 318 pop dx 319 pop cx 320 pop bx 321 322 ret 323 324 MsgStr db "No LOADER ..." 325 MsgLen equ ($-MsgStr) 326 Target db "START " 327 TarLen equ ($-Target) 328 EntryItem times EntryItemLength db 0x00 329 Buf: 330 times 510-($-$$) db 0x00 331 db 0x55, 0xaa
运行bochs,在程序的第77,79行打断点(具体方法见上节反汇编ndisasm使用方法),然后运行到第一个断点处,打印寄存器内容如下:
可以看到ecx中的后两个字节(bochs为16字节操作)确实为4,这就是文件数据起始扇区号,edx此时还为0,继续运行到下一个断点处,打印寄存器如下:
寄存器edx的内容为0xfff,这就是FAT表项中的内容,说明文件结束了,文件的内容只存在了一个扇区中。本节中的FatVec函数并没有完善,本节还没有进行文件内容的读取,只是根据文件数据起始扇区号,读取了下一个FAT表项的内容,下一节继续。。。
参考狄泰软件学院操作系统教程