主引导程序控制权的转移,彻底突破512字节限制

学习自 狄泰软件

主引导程序控制权的转移,彻底突破512字节限制
Stack 空间 是栈空间,用于函数调用

0x7c00 : 主引导程序的起始地址

0x7e00 – 0x9000 : 是FAT 表,占据4KB 空间

0x9000 : 我们需要将目标程序(Loader程序)加载到 0x9000地址处,我们最终将控制权从 主引导程序(Boot程序)里面 跳转到 0x9000处,开始向后执行,也就是将控制权从Boot程序 转交给 Loader程序。

主引导程序控制权的转移,彻底突破512字节限制
主引导程序控制权的转移,彻底突破512字节限制
主引导程序控制权的转移,彻底突破512字节限制

1 用bx寄存器 拿到 FAT表的起始地址

2 拿到目标文件的 文件目录项起始地址
拿到目标文件的 所在的第一个簇(目标文件所在的第一个扇区,也就是对应的FAT表第一个表项存储的扇区号),所以就拿到了 该目标文件所在的第一个扇区。

3 获取目标地址 0x9000, 将目标文件(Loader程序)加载到 0x9000地址处

4 开始循环 ,FAT表项的值(扇区号)要小于 0xFF7,否则表示文件内容已经读完了

/*开始读取扇区内容*/
4.1  通过扇区号 得到 逻辑扇区号  并读取扇区数据
4.2  通过当前扇区号 作为下标 去拿到对应FAT表项的值,即下一个扇区号
4.3  将 目标地址 向后偏移512字节,即向后偏移一个扇区,准备好读取下一个扇区数据

主引导程序控制权的转移,彻底突破512字节限制
主引导程序控制权的转移,彻底突破512字节限制
主引导程序控制权的转移,彻底突破512字节限制

主引导程序控制权的转移,彻底突破512字节限制

主引导程序控制权的转移,彻底突破512字节限制

loader.asm

org 0x9000
;将当前程序 放到 0x9000 地址处执行

begin:
	;获取 msg标签所对应的地址,指向打印内容
    mov si, msg

print:
	;一个字符一个字符的进行打印
    mov al, [si]
	;移位 到下一个字符
    add si, 1
	;是否打印结束
    cmp al, 0x00
	;如果相等 则跳转到 end
    je end
	;不为0 则继续打印
    mov ah, 0x0E
    mov bx, 0x0F
    int 0x10
    jmp print

end:
	;使程序停止运行,处理器进入暂停状态,不执行任何操作,使 CPU 进入这么一个状态:既不取指令,也不读写数据
    hlt
    jmp end
    
msg:
	;
    db 0x0a, 0x0a
    db "Hello, D.T.OS!"
    db 0x0a, 0x0a
    db 0x00

makefile

.PHONY : all clean rebuild

#Boot 编译
BOOT_SRC := boot.asm
BOOT_OUT := boot

#Loader 编译
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程序,并烧写到虚拟软盘中
$(BOOT_OUT) : $(BOOT_SRC)
	nasm $^ -o $@
	dd if=$@ of=$(IMG) bs=512 count=1 conv=notrunc

#编译 Loader程序,将虚拟软盘挂载到 指定路径,并将Loader 程序拷贝到 虚拟软盘,最后卸载虚拟软盘
$(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

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
	
	;获取目标文件 起始簇,目标文件所在的第一个扇区的位置,即扇区号(该文件数据起始FAT表表项数据,扇区号),
	mov dx, [EntryItem + 0x1A]
	;获取目标地址 0x9000
	mov si, BaseOfLoader
	
loading:
    mov ax, dx
	;逻辑扇区号
    add ax, 31
	;只读取一个扇区
    mov cx, 1
	;备份 寄存器 dx bx
    push dx
    push bx
    mov bx, si
	;读取扇区内容
    call ReadSector
	;恢复寄存器
    pop bx
    pop cx
	;通过当前扇区号 作为下标 访问FAT表,去拿到对应FAT表项的值,即下一个扇区号
    call FatVec
	;验证读取FAT表项到的值,下一个扇区号,验证是否结束
    cmp dx, 0xFF7
	;>= 如果内容已经读完,所有数据都读取到目标地址0x9000后,跳转到 BaseOfLoader 0x9000地址处 继续执行
    jnb BaseOfLoader
	; 将 目标地址 向后偏移512字节,即向后偏移一个扇区,准备好读取下一个扇区数据
    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
上一篇:实验2 多个逻辑段的汇编源程序编写与调试


下一篇:微型计算机原理第三版 课后答案 王忠民