突破512字节的限制

上节详细分析了FAT12文件系统。

这节具体讲解:主引导程序如何①从存储介质中加载程序到内存 ②将控制权交由新加载的程序执行

突破512字节的限制_第1张图片

目录

1、突破512字节限制

1、将文件系统的根目录区加载到内存

1、字符串打印

2、软盘读取

2、在根目录区找到目标文件

1、内存比较

2、根目录的查找

3、通过FAT表将文件的数据全部加载到内存

1、内存拷贝

2、Fat表项的读取

4、小结

2、 主引导程序控制权的转移


1、突破512字节限制

整体思路

突破512字节的限制_第2张图片

1、将文件系统的根目录区加载到内存

2、在根目录区找到目标文件

3、通过FAT表将文件的数据全部加载到内存,执行跳转 

1、将文件系统的根目录区加载到内存

突破512字节的限制_第3张图片

1、字符串打印

主引导程序中如何进行字符串打印?

主引导程序中如何读取指定扇区处的数据?

我们将会编写两个函数,一个进行字符串打印,一个进行软盘读取

突破512字节的限制_第4张图片

 

BIOS中的字符串打印   

- 指定打印参数(AX=0x1301,BX=0x0007

    - 指定字符串的内存地址(ES:BP = 串地址

    - 指定字符串的长度(CX = 串长度

    - 中断调用(int 0x10

编程实验

定义打印函数 print

boot.asm

org 0x7c00      ; IP = 0x7c00

jmp short start ;占用两字节
nop             ;占用一字节

define:
    BaseOfStack equ 0x7c00 ;#define BaseOfStack 0x7c00 栈从高地址向低地址增长,与代码段无影响

;主引导区(0扇区)的必要信息
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 bp, MsgStr ;指定字符串地址
    mov cx, MsgLen ;指定字符串长度
    
    call Print 
    
last:
    hlt
    jmp last    

;打印字符串函数
; es:bp --> string address
; cx    --> string length
Print:
    mov ax, 0x1301 ;指定打印参数
    mov bx, 0x0007 ;指定打印参数
    int 0x10 ;执行0x10号中断
    ret

MsgStr db  "Hello, DTOS!"    
MsgLen equ ($-MsgStr)

Buf:
    times 510-($-$$) db 0x00
    db 0x55, 0xaa

makefile


.PHONY : all clean rebuild

SRC := boot.asm
OUT := boot.bin
IMG := data.img

RM := rm -fr

all : $(OUT) $(IMG)
	dd if=$(OUT) of=$(IMG) bs=512 count=1 conv=notrunc
	@echo "Success!"

$(IMG) :
	bximage $@ -q -fd -size=1.44

$(OUT) : $(SRC)
	nasm $^ -o $@

clean :
	$(RM) $(IMG) $(OUT)

rebuild :
	@$(MAKE) clean
	@$(MAKE) all
	

突破512字节的限制_第5张图片

                              bochs配置文件插入data.img

突破512字节的限制_第6张图片

2、软盘读取

软盘的构造

    - 一个软盘有2个盘面,每个盘面对应1个磁头

    - 每一个盘面被划分为若干个圆圈,成为柱面(磁道)

    - 每一个柱面被划分为若干个扇区,每个扇区512字节

突破512字节的限制_第7张图片

3.5寸软盘的数据特性

    - 每个盘面一共80个柱面(编号为0 - 79)

    - 每个柱面有18个扇区(编号为1 - 18)

    - 存储大小:2 * 80 * 18 * 512 = 1474560Bytes = 1440KB

软盘数据的读取

    - 读取数据前,逻辑扇区号需要转化为磁盘的物理位置

    - 物理软盘上的数据位置由磁头号,柱面号,扇区号唯一确定

    - 软盘数据以扇区(512字节)为单位进行读取

    - 指定数据所在位置的磁头号,柱面号,扇区号

    - 计算公式:

突破512字节的限制_第8张图片

何为逻辑扇区号?举个简单栗子

突破512字节的限制_第9张图片

 

BIOS 中的软盘数据读取(int 0x13)

突破512字节的限制_第10张图片

软盘数据读取流程

突破512字节的限制_第11张图片

汇编中的16位除法操作(div)

    - 被除数放到AX寄存器

    - 除数放到通用寄存器或内存单元(8位)

    - 结果:商位于AL,余数位于AH

编程实验

磁盘数据的读取 boot.asm

org 0x7c00

jmp short start ;占用两个字节
nop             ;占用一个字节

define:
    BaseOfStack equ 0x7c00 ;#define BaseOfStack 0x7c00   栈从高地址向低地址增长,所以与代码段无影响

;主引导区(0扇区)的必要信息
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, 34      ;指定要读取的逻辑扇区号34
    mov cx, 1       ;读取1个扇区
    mov bx, Buf 
    
    call ReadSector ;读到es:bx所指向的Buf
    
    mov bp, Buf
    mov cx, 21
    
    call Print      ;打印es:bp所指向的Buf中的21个字符
    
last:
    hlt
    jmp last    

;打印字符串函数
; es:bp --> string address
; cx    --> string length
Print:
    mov ax, 0x1301 ;指定打印参数
    mov bx, 0x0007 ;指定打印参数-
    int 0x10       ;执行0x10号中断
    ret

;重置软驱
; no parameter
ResetFloppy:
    push ax 
    push dx
    
    mov ah, 0x00        ;13号中断处理程序的0功能:磁盘系统复位
    mov dl, [BS_DrvNum] ;必要参数: dl = 驱动器
    int 0x13
    
    pop dx 
    pop ax
    
    ret

; ax    --> logic sector number
; cx    --> number of sector
; es:bx --> target address
ReadSector:
    push bx
    push cx
    push dx
    push ax
    
    call ResetFloppy
    
    push bx                  ;目标地址和读取扇区数量入栈
    push cx
    
    mov bl, [BPB_SecPerTrk]
    div bl                   ;逻辑扇区号(AX) / 柱面扇区数 => 余数放到AH,商放到AL
    mov cl, ah ;
    add cl, 1                ;余数值+1得到扇区号
    mov ch, al ;
    shr ch, 1                ;商值右移一位得到柱面号
    mov dh, al
    and dh, 1                ;得到磁头号
    mov dl, [BS_DrvNum]      ;得到驱动器号
    
    pop ax                   ;读取扇区数(CX)
    pop bx                   ;确保bx继续指向目标地址
    
    mov ah, 0x02             ;13号中断处理程序的02号功能:读扇区
                             ;参数:AL=扇区数,CH=柱面号,CL=扇区号,DH=磁头号,DL=驱动器号,ES:BX指向目标地址

read:    
    int 0x13 ;读取数据到Buf
    jc read  ;可能读取失败,重读(CF=0 操作成功)
    
    pop ax
    pop dx
    pop cx
    pop bx
    
    ret

MsgStr db  "Hello, DTOS!"    
MsgLen equ ($-MsgStr)
Buf:
    times 510-($-$$) db 0x00
    db 0x55, 0xaa

突破512字节的限制_第12张图片

             成功从文件系统的34扇区读取一个扇区数据到内存Buf,并打印21个字符

2、在根目录区找到目标文件

1、内存比较

内存比较函数的创建 MemCmp

突破512字节的限制_第13张图片

突破512字节的限制_第14张图片

 

查找根目录区是否存在目标文件

突破512字节的限制_第15张图片

; ds:si --> source
; es:di --> destination
; 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

2、根目录的查找

根目录区查找函数的创建 FindEntry

加载根目录区

org 0x7c00

jmp short start
nop

define:
    BaseOfStack      equ 0x7c00
    RootEntryOffset  equ 19 ;根目录区偏移
    RootEntryLength  equ 14 ;根目录区大小

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 ;从第19扇区读
    mov cx, RootEntryLength ;读14个扇区
    mov bx, Buf
    
    call ReadSector         ;软盘读取,将根目录区读到Buf
    
    mov si, Target ;指定要查找的文件名字符串
    mov cx, TarLen ;长度
    mov dx, 0      ;作为FindEntry返回值
    
    call FindEntry ;根据文件名和长度查找对应目录项(bx)
    
    cmp dx, 0
    jz output      ;没找到,输出错误信息
    jmp last
    
output:    
    mov bp, MsgStr
    mov cx, MsgLen
    call Print
    
last:
    hlt
    jmp last    

; 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 di
    push bp
    push cx 
    
    mov dx, [BPB_RootEntCnt] ;目录项个数,即dx保存着最大查找次数
    mov bp, sp               ;不能使用sp直接访问栈顶数据
    
find:
    cmp dx, 0
    jz noexist    ;找不到目标文件,跳出
    mov di, bx    ;根目录区第i个目录项地址
    mov cx, [bp] 
    call MemCmp  
    cmp cx, 0
    jz exist      ;查找目标存在,跳转
    add bx, 32    ;进行下一个目录项
    dec dx
    jmp find

exist:
noexist:
    pop cx
    pop bp
    pop di
       
    ret

; ds:si --> source
; es:di --> destination
; 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 ax, 0x1301
    mov bx, 0x0007
    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
ReadSector:
    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  "No LOADER ..."    
MsgLen equ ($-MsgStr)
Target db  "LOADER     "
TarLen equ ($-Target)
Buf:
    times 510-($-$$) db 0x00
    db 0x55, 0xaa

突破512字节的限制_第16张图片

3、通过FAT表将文件的数据全部加载到内存

1、内存拷贝

备份目标文件的目录信息(MemCpy)

突破512字节的限制_第17张图片

拷贝方向

突破512字节的限制_第18张图片

编程实验
内存拷贝函数 MemCpy

; ds:si --> source
; es:di --> destination
; cx    --> length
MemCpy:
    push si
    push di
    push cx
    push ax
    
    cmp si, di
    
    ja btoe ;si > di 从头部开始拷贝
    
    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

2、Fat表项的读取

加载Fat表,并完成Fat 表项的查找与读取(FatVec)

Fat表项的读取

          必须清楚的是这里的Fat表均代表是载入内存后的,FatVec代表其具体的组织结构

突破512字节的限制_第19张图片

突破512字节的限制_第20张图片

 boot.asm

org 0x7c00

jmp short start
nop

define:
    BaseOfStack      equ 0x7c00 ;栈基址
    BaseOfLoader     equ 0x9000 ;最终加载地址
    RootEntryOffset  equ 19     ;根目录区扇区起始扇区号
    RootEntryLength  equ 14     ;根目录区长度
    EntryItemLength  equ 32     ;每个目录项大小(32字节)
    FatEntryOffset   equ 1      ;FAT表起始扇区号
    FatEntryLength   equ 9      ;FAT表长度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 	    ;读取根目录区所有数据到Buf
	
	mov si, Target          ;指定要查找的目标文件名
	mov cx, TarLen          ;指定长度
	mov dx, 0 
	
	call FindEntry			;根据文件名在Buf查找该文件的目录项
	
	cmp dx, 0
	jz output               ;找不到该文件,输出错误信息; 找到了bx指向该目录项
	
	mov si, bx              ;指定拷贝源
	mov di, EntryItem       ;拷贝的目的地址
	mov cx, EntryItemLength ;长度
	
	call MemCpy			    ;将Buf的该目录项(bx指向)拷到另一片内存
	
	;加载FAT表
	mov ax, FatEntryLength    ;指定FAT表长度(9)
	mov cx, [BPB_BytsPerSec]  ;指定每扇区大小(512字节)
	mul cx                    ;ax = FAT表字节数
	mov bx, BaseOfLoader 
	sub bx, ax 			      ;指定FAT表加载的内存起始位置(bx)
	
	mov ax, FatEntryOffset    ;FAT表在文件系统的逻辑扇区号
	mov cx, FatEntryLength    ;读取的扇区数
	 
	call ReadSector 	      ;读取FAT表到bx所指内存
	
	mov cx, [EntryItem + 0x1A];该目标文件对应目录项的起始簇号
	
	call FatVec 			  ;查表 cx对应的FAT表项
	
	jmp last
	
output:	
    mov bp, MsgStr
    mov cx, MsgLen
	call Print
	
last:
    hlt
	jmp last	


;下面所说的FAT表都是加载到内存后称呼,之前的称为FatVec
; cx --> FatVec表项下标j 即目录项中的簇号
; bx --> fat table address fat表加载到内存后的起始地址
;
; return:
;     dx --> FatVec[j] 
FatVec:
    mov ax, cx
    mov cl, 2
    div cl  ;j/2
    
    push ax  ;备份商和余数
    
    mov ah, 0
    mov cx, 3
    mul cx ;ax = al * 3 对应表项在内存的起始位置(i = j / 2 * 3)
    mov cx, ax
    
    pop ax
    
    cmp ah, 0;余数为0,FatVec下标j是偶数,不为0是奇数
    jz even
    jmp odd

even:    ; FatVec[j] = ( (Fat[i+1] & 0x0F) << 8 ) | Fat[i];
    mov dx, cx
    add dx, 1
    add dx, bx ;FAT表的基址 + (i+1) = Fat[i+1]的地址
    mov bp, dx
    mov dl, byte [bp] ;dl = FAT[i+1]的值
    and dl, 0x0F
    shl dx, 8
    add cx, bx ;Fat[i]的地址
    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 ;Fat[i+2]的地址
    mov bp, dx
    mov dl, byte [bp]
    mov dh, 0
    shl dx, 4
    add cx, 1
    add cx, bx ;Fat[i+1]的地址
    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:
    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 --> root entry offset address
; ds:si --> target string
; cx    --> target length
;
; return:
;     (dx !=0 ) ? exist : noexist
;        exist --> bx is the 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

exist:
noexist: 
    pop cx
    pop bp
    pop di
       
    ret

; ds:si --> source
; es:di --> destination
; 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, 0x0007
	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
ReadSector:
    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  "No LOADER ..."	
MsgLen equ ($-MsgStr)
Target db  "LOADER     "
TarLen equ ($-Target)
EntryItem times EntryItemLength db 0x00
Buf:
	times 510-($-$$) db 0x00
	db 0x55, 0xaa

 

4、小结

当汇编代码中定义了函数,那么也需要定义栈空间

读取数据前,逻辑扇区号需要转化为磁盘的物理位置

物理软盘上的数据位置由磁头号,柱面号,扇区号唯一确定

软盘数据以扇区(512字节)为单位进行读取

可通过查找根目录区判断是否存在目标文件

      - 加载根目录区至内存中(ReadSector)

      - 遍历根目录区中的每一项(FindEntry)

      - 通过每一项的前11个字节进行判断(MemCmp)

      - 当目标不存在时,打印错误信息(Print)

内存拷贝时需要考虑进行拷贝的方向

      - 当si > di时,从前向后拷贝

      - 当si <= di时,从后向前拷贝

Fat 表加载到内存中之后,需要"动态组装”表项

      - Fat表中使用3个字节表示2个表项

      - 起始字节 = 表项下标 / 2 * 3 →(运算结果取整)

 

2、 主引导程序控制权的转移

BootLoader内存布局

突破512字节的限制_第21张图片

                                         弄完了才发现描述有误,文件系统改为软盘

通过FAT表加载文件内容

突破512字节的限制_第22张图片

实验步骤

1. 在虚拟软盘中创建体积较大的文本文件(Loader)

2. 将Loader的内容加载到BaseOfLoader地址处

3. 打印Loader中的文本(判断加载是否完全)

 

先编写loader.asm

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

由于0扇区代码量需要控制在512字节所以之前的代码删除一些非必要代码如push pop(重构)

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]   ;簇号,即表项下标j
	mov si, BaseOfLoader ;指定目标地址
	
loading:
    mov ax, dx
    add ax, 31 ;33 - 2 + j
    mov cx, 1 ;读取一个扇区

    push dx
    push bx 

    mov bx, si ;target address
    call ReadSector	;读取一个扇区到si
    pop bx
    pop cx ;pop dx ,mov cx dx

    call FatVec ;读取j表项的内容
    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

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

突破512字节的限制_第23张图片

突破512字节的限制_第24张图片

                              成功将loader程序加载到内存并读取其中的内容

小结

   - Boot 需要进行重构保证在512字节内完成功能

   - 在汇编程序中尽量确保函数调用前后通用寄存器的状态不变

   - Boot 成功加载Loader 后将控制权转移

   - Loader 程序没有代码体积上的限制

 


下一节 - 实模式到保护模式

你可能感兴趣的:(操作系统专题【笔记】)