突破512
上一节我们写了字符串打印的函数和软盘读取函数,在软盘读取函数中,我们是直接给定了要读的逻辑扇区号,这一节我们来实现一个查找文件的功能,也就是根据给定的文件名,在根目录区中查找指定的文件是否存在,涉及到文件名的查找,就会涉及到内存的比较,因此,我们需要实现两个函数,分别为内存比较函数和根目录区查找函数。
整体的流程如下:
首先将根目录区加载到内存中的指定位置上,这里面包含了一定数目的根目录项,而根目录项的前11个字节为文件名,因此,查找文件的工作就可以进行了,我们只需要将目标文件名和每一个根目录项的前11个字节进行比较即可,如果文件存在,必定会在某一个目录项中找到,只需将目录项的偏移位置通过寄存器返回即可。
内存比较的示意图如下:
需要用到的两个指令如下:
比较指令:cmp cx, 0 ; 比较cx的值是否为零
跳转:jz equal ; 如果比较的结果为真,则跳转到equal处
用于比较的汇编程序片段如下:
1 org 0x7c00 2 3 jmp short start 4 nop 5 6 define: 7 BaseOfStack equ 0x7c00 8 RootEntryOffset equ 19 9 RootEntryLength equ 14 10 11 header: 12 BS_OEMName db "D.T.Soft" 13 BPB_BytsPerSec dw 512 14 BPB_SecPerClus db 1 15 BPB_RsvdSecCnt dw 1 16 BPB_NumFATs db 2 17 BPB_RootEntCnt dw 224 18 BPB_TotSec16 dw 2880 19 BPB_Media db 0xF0 20 BPB_FATSz16 dw 9 21 BPB_SecPerTrk dw 18 22 BPB_NumHeads dw 2 23 BPB_HiddSec dd 0 24 BPB_TotSec32 dd 0 25 BS_DrvNum db 0 26 BS_Reserved1 db 0 27 BS_BootSig db 0x29 28 BS_VolID dd 0 29 BS_VolLab db "D.T.OS-0.01" 30 BS_FileSysType db "FAT12 " 31 32 start: 33 mov ax, cs 34 mov ss, ax 35 mov ds, ax 36 mov es, ax 37 mov sp, BaseOfStack 38 39 mov si, MsgStr 40 mov di, DEST 41 mov cx, MsgLen 42 43 call MemCmp 44 45 cmp cx, 0 46 jz label 47 jmp last 48 49 50 label: 51 mov bp, MsgStr 52 mov cx, MsgLen 53 54 call Print 55 56 output: 57 mov bp, MsgStr 58 mov cx, MsgLen 59 call Print 60 61 last: 62 hlt 63 jmp last 64 65 66 ; ds:si --> source 67 ; es:di --> destination 68 ; cx --> length 69 ; 70 ; return: 71 ; (cx == 0) ? equal : noequal 72 MemCmp: 73 push si 74 push di 75 push ax 76 77 compare: 78 cmp cx, 0 79 jz equal 80 mov al, [si] 81 cmp al, byte [di] 82 jz goon 83 jmp noequal 84 goon: 85 inc si 86 inc di 87 dec cx 88 jmp compare 89 90 equal: 91 noequal: 92 pop ax 93 pop di 94 pop si 95 96 ret 97 98 ; es:bp --> string address 99 ; cx --> string length 100 Print: 101 mov ax, 0x1301 102 mov bx, 0x0007 103 int 0x10 104 ret 105 106 ; no parameter 107 ResetFloppy: 108 push ax 109 push dx 110 111 mov ah, 0x00 112 mov dl, [BS_DrvNum] 113 int 0x13 114 115 pop dx 116 pop ax 117 118 ret 119 120 ; ax --> logic sector number 121 ; cx --> number of sector 122 ; es:bx --> target address 123 ReadSector: 124 push bx 125 push cx 126 push dx 127 push ax 128 129 call ResetFloppy 130 131 push bx 132 push cx 133 134 mov bl, [BPB_SecPerTrk] 135 div bl 136 mov cl, ah 137 add cl, 1 138 mov ch, al 139 shr ch, 1 140 mov dh, al 141 and dh, 1 142 mov dl, [BS_DrvNum] 143 144 pop ax 145 pop bx 146 147 mov ah, 0x02 148 149 read: 150 int 0x13 151 jc read 152 153 pop ax 154 pop dx 155 pop cx 156 pop bx 157 158 ret 159 160 MsgStr db "Hello World" 161 MsgLen equ ($-MsgStr) 162 DEST db "Hello World" 163 164 Buf: 165 times 510-($-$$) db 0x00 166 db 0x55, 0xaa
运行结果如下:
上图的程序中,我们直接比较的字符串“Hello World”,如果比较成功,就将字符串打印出来。
下面我们进行文件的查找,具体流程如下图:
首先加载根目录区,将根目录区加载到指定的缓冲区中,程序片段如下:
访问栈顶数据时,不能通过sp直接访问,而要经过其他寄存器间接访问,如下:
有了以上的思路后,我们直接给出查找文件的汇编程序:
1 org 0x7c00 2 3 jmp short start 4 nop 5 6 define: 7 BaseOfStack equ 0x7c00 8 RootEntryOffset equ 19 9 RootEntryLength equ 14 10 11 header: 12 BS_OEMName db "D.T.Soft" 13 BPB_BytsPerSec dw 512 14 BPB_SecPerClus db 1 15 BPB_RsvdSecCnt dw 1 16 BPB_NumFATs db 2 17 BPB_RootEntCnt dw 224 18 BPB_TotSec16 dw 2880 19 BPB_Media db 0xF0 20 BPB_FATSz16 dw 9 21 BPB_SecPerTrk dw 18 22 BPB_NumHeads dw 2 23 BPB_HiddSec dd 0 24 BPB_TotSec32 dd 0 25 BS_DrvNum db 0 26 BS_Reserved1 db 0 27 BS_BootSig db 0x29 28 BS_VolID dd 0 29 BS_VolLab db "D.T.OS-0.01" 30 BS_FileSysType db "FAT12 " 31 32 start: 33 mov ax, cs 34 mov ss, ax 35 mov ds, ax 36 mov es, ax 37 mov sp, BaseOfStack 38 39 mov ax, RootEntryOffset 40 mov cx, RootEntryLength 41 mov bx, Buf 42 43 call ReadSector 44 45 mov si, Target 46 mov cx, TarLen 47 mov dx, 0 48 49 call FindEntry 50 51 cmp dx, 0 52 jz output 53 jmp last 54 55 output: 56 mov bp, MsgStr 57 mov cx, MsgLen 58 call Print 59 60 last: 61 hlt 62 jmp last 63 64 ; es:bx --> root entry offset address 65 ; ds:si --> target string 66 ; cx --> target length 67 ; 68 ; return: 69 ; (dx != 0) ? exist : noexist 70 ; exist --> bx is the target entry 71 FindEntry: 72 push di 73 push bp 74 push cx 75 76 mov dx, [BPB_RootEntCnt] 77 mov bp, sp 78 79 find: 80 cmp dx, 0 81 jz noexist 82 mov di, bx 83 mov cx, [bp] 84 call MemCmp 85 cmp cx, 0 86 jz exist 87 add bx, 32 88 dec dx 89 jmp find 90 91 exist: 92 noexist: 93 pop cx 94 pop bp 95 pop di 96 97 ret 98 99 ; ds:si --> source 100 ; es:di --> destination 101 ; cx --> length 102 ; 103 ; return: 104 ; (cx == 0) ? equal : noequal 105 MemCmp: 106 push si 107 push di 108 push ax 109 110 compare: 111 cmp cx, 0 112 jz equal 113 mov al, [si] 114 cmp al, byte [di] 115 jz goon 116 jmp noequal 117 goon: 118 inc si 119 inc di 120 dec cx 121 jmp compare 122 123 equal: 124 noequal: 125 pop ax 126 pop di 127 pop si 128 129 ret 130 131 ; es:bp --> string address 132 ; cx --> string length 133 Print: 134 mov ax, 0x1301 135 mov bx, 0x0007 136 int 0x10 137 ret 138 139 ; no parameter 140 ResetFloppy: 141 push ax 142 push dx 143 144 mov ah, 0x00 145 mov dl, [BS_DrvNum] 146 int 0x13 147 148 pop dx 149 pop ax 150 151 ret 152 153 ; ax --> logic sector number 154 ; cx --> number of sector 155 ; es:bx --> target address 156 ReadSector: 157 push bx 158 push cx 159 push dx 160 push ax 161 162 call ResetFloppy 163 164 push bx 165 push cx 166 167 mov bl, [BPB_SecPerTrk] 168 div bl 169 mov cl, ah 170 add cl, 1 171 mov ch, al 172 shr ch, 1 173 mov dh, al 174 and dh, 1 175 mov dl, [BS_DrvNum] 176 177 pop ax 178 pop bx 179 180 mov ah, 0x02 181 182 read: 183 int 0x13 184 jc read 185 186 pop ax 187 pop dx 188 pop cx 189 pop bx 190 191 ret 192 193 MsgStr db "No START ..." 194 MsgLen equ ($-MsgStr) 195 Target db "START " 196 TarLen equ ($-Target) 197 Buf: 198 times 510-($-$$) db 0x00 199 db 0x55, 0xaa
如果找不到文件会打印出“No START”,如果能找到,则不打印任何字符。不打印字符的话却无法证明我们的程序是正确的,我们下面进行断点调试,看一下寄存器内容,首先使用ndisasm -o 0x7c00 boot.bin > boot.txt进行反编译,找到适合的断点。
我们应该在第51行cmp dx, 0处对应的地方打断点,根据boot.txt文件中的内容,我们来确定断点地址,如下:
可以看到,断点地址应该为0x7c61,接下来,启动bochs,进行调试,结果如下:
根据上图可以看出,dx寄存器的值为219,dx为输出寄存器,也就是文件所在的目录项的位置,如果dx为0,说明文件不存在,dx不为0,则说明文件找到了,到此为止,我们查找文件的实验成功了。