参考
https://www.cnblogs.com/wanmeishenghuo/tag/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F/
https://blog.51cto.com/13475106/category6.html
这一节,我们来真正的读取文件中的内容到内存中,首先来看一下内存布局是什么样的,如下所示:
Boot占用了512字节,Fat Table占用了4KB,而真正的文件中的内容,我们把它存在0x9000开始的内存地址处。
加载文件内容的过程如下:
实验步骤如下:
1、在虚拟软盘中创建体积较大的文本文件,使之内容大小超过一个扇区。
2、将文件的内容加载到BaseOfLoader地址处。
3、打印加载的内容,判断是否加载完全。
org 0x7c00 jmp short start nop define: BaseOfStack equ 0x7c00 BaseOfLoader equ 0x9000 RootEntryOffset equ 19 RootEntryLength equ 14 EntryItemLength equ 32 FatEntryOffset equ 1 FatEntryLength equ 9 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, RootEntryOffset mov cx, RootEntryLength mov bx, Buf call ReadSector mov si, Target mov cx, TarLen mov dx, 0 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 ax, FatEntryOffset mov cx, FatEntryLength call ReadSector 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 ReadSector 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: ; FatVec[j] = ( (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: ; FatVec[j+1] = (Fat[i+2] << 4) | ( (Fat[i+1] >> 4) & 0x0F ); 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] shr cl, 4 and cl, 0x0F mov ch, 0 or dx, cx return: ret ; ds:si --> source ; es:di --> destination ; 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 --> root entry offset address ; ds:si --> target string ; cx --> target length ; ; return: ; (dx !=0 ) ? exist : noexist ; exist --> bx is the target entry FindEntry: push cx mov dx, [BPB_RootEntCnt] mov bp, sp find: cmp dx, 0 jz noexist mov di, bx mov cx, [bp] push si call MemCmp pop si cmp cx, 0 jz exist add bx, 32 dec dx jmp find exist: noexist: pop cx ret ; ds:si --> source ; es:di --> destination ; 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, 0x0007 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 ReadSector: 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 "No LOADER ..." MsgLen equ ($-MsgStr) Target db "LOADER " TarLen equ ($-Target) EntryItem times EntryItemLength db 0x00 Buf: times 510-($-$$) db 0x00 db 0x55, 0xaa
95行跳转到加载文件内容的地址处,96行为文件的长度。运行bochs,可以看到文件内容全部打印出来了,结果如下:
以上我们加载的文件只是文本内容,下面我们在文件中写入程序,并编译成可执行程序,然后将这个可执行程序文件放到虚拟软盘中,并加载到指定地址,然后跳转到这个地址执行。
待加载文件中的内容如下:
org 0x9000 begin: mov si, msg 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 msg: db 0x0a, 0x0a db "Hello, D.T.OS!" db 0x0a, 0x0a db 0x00
为了方便起见,我们修改makefile,如下:
.PHONY : all clean rebuild BOOT_SRC := boot.asm BOOT_OUT := boot LOADER_SRC := loader.asm LOADER_OUT := loader IMG := data.img IMG_PATH := /mnt/hgfs RM := rm -fr all : $(IMG) $(BOOT_OUT) $(LOADER_OUT) @echo "Build Success ==> D.T.OS!" $(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
makefile完成编译boot.asm,编译loader.asm,写入boot.bin到第一个扇区,挂载a.img到/mnt/hgfs目录,写入loader可执行文件,卸载a.img等。
我们修改boot.asm,程序如下:
org 0x7c00 jmp short start nop define: BaseOfStack equ 0x7c00 BaseOfLoader equ 0x9000 RootEntryOffset equ 19 RootEntryLength equ 14 EntryItemLength equ 32 FatEntryOffset equ 1 FatEntryLength equ 9 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, RootEntryOffset mov cx, RootEntryLength mov bx, Buf call ReadSector mov si, Target mov cx, TarLen mov dx, 0 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 ax, FatEntryOffset mov cx, FatEntryLength call ReadSector 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 ReadSector pop bx pop cx call FatVec cmp dx, 0xFF7 jnb BaseOfLoader add si, 512 jmp loading 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 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: ; FatVec[j] = ( (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: ; FatVec[j+1] = (Fat[i+2] << 4) | ( (Fat[i+1] >> 4) & 0x0F ); 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] shr cl, 4 and cl, 0x0F mov ch, 0 or dx, cx return: ret ; ds:si --> source ; es:di --> destination ; 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 --> root entry offset address ; ds:si --> target string ; cx --> target length ; ; return: ; (dx !=0 ) ? exist : noexist ; exist --> bx is the target entry FindEntry: push cx mov dx, [BPB_RootEntCnt] mov bp, sp find: cmp dx, 0 jz noexist mov di, bx mov cx, [bp] push si call MemCmp pop si cmp cx, 0 jz exist add bx, 32 dec dx jmp find exist: noexist: pop cx ret ; ds:si --> source ; es:di --> destination ; 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, 0x0007 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 ReadSector: 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 "No LOADER ..." MsgLen equ ($-MsgStr) Target db "LOADER " TarLen equ ($-Target) EntryItem times EntryItemLength db 0x00 Buf: times 510-($-$$) db 0x00 db 0x55, 0xaa
第90行改为跳转到文件内容加载地址处,而不再跳转到output。第309行文件的名字也改为LOADER。
执行make,然后运行bochs,结果如下:
可以看到,文件中的可执行程序被加载并成功执行了,成功打印出字符串。此时的打印是文件中的可执行程序中print函数实现的,而不是我们的加载程序中的Print函数实现的。
我们将data.img拷贝到windows下,在真正的机器上试一下,设置如下:
启动虚拟机,我们看到了以下结果:
我们的加载程序和可执行文件都成功运行了。