卓一笔记---利用boot引导程序加载其他源文件中的代码并执行

      一个人不应望着那山还比这山高,应该慢慢的积蓄自己的力量.生活中总是听到人们说这个人赚了多少多少钱?那个人赚了多少多少钱?这个人怎么怎么厉害?那个人怎么怎么厉害.这么多的诱惑让我们眼花缭乱.<<九方皋相马>>中的说"见其所见,不见其所不见",抓住事物的本质才能有所成就.常立志则无志,一定要耐心地走好自己的那条路,切莫邯郸学步/东施效颦.

      本文主要讲解boot引导程序加载其他文件中的程序并运行,主要模仿了操作系统内核的加载过程.这次实践的成功标志着我们真正迈入了自己写操作系统的大门.

    文中程序主要经历下面的几个步骤:

   初始化boot程序-->在boot程序中寻找loader程序所在的文件-->加载loader程序-->运行loader程序(输出一个字母L).

1.构建loader.asm文件

  org 0100h
  mov ax, 0B800h
  mov gs, ax
  mov ah, 0Fh ;黑底,白字
  mov al, 'L' ;显示一个字符L
  mov [gs: ((80*0 + 39)*2)], ax;屏幕第0行,第39列
  jmp $  ;停住

2.构建boot.asm文件

;%define _BOOT_DEBUG_   ;做Boot Sector时一定将此行注释掉!
%ifdef _BOOT_DEBUG_
    org 0100h    ;调试时,编译成.COM文件时可以用
%else
    org 07c00h   ;Boot状态,Bios将把Boot Sector加载到0:7c00处并开始执行
%endif

%ifdef _Boot_DEBUG_
BaseOfStack  equ  0100h ;调试状态堆栈基地址
%else
BaseOfStack  equ  07c00h ;Boot状态堆栈基地址
%endif

BaseOfLoader   equ 09000h ;LOADER.BIN被加载到的位置---段地址
OffsetOfLoader equ 0100h ;LOADER.BIN 被加载到的位置---偏移地址
RootDirSectors equ 14    ;根目录占用14个扇区
SectorNoOfRootDirectory equ 19  ;Root Directory第一个扇区号
SectorNoOfFAT1 equ  1 ;FAT1的第一个扇区=BPB_RsvdSecCnt
DeltaSectorNo  equ  17 ;DeltaSectorNo=BPB_RsvdSecCnt+(BPB_NumFATS*FATs)-2 
                       ;文件的开始Sector号 = DirEntry中的开始Sector号 + 根目录占用Sector数目 + DeltaSectorNo

jmp short LABEL_START ;Start to boot.
nop 
BS_OEMName     DB 'ForrestY' ;OEM String,必须8个字符
BPB_BytsPerSec DW 512;每个扇区的字节数
BPB_SecPerClus DB 1 ;每蔟多少个扇区
BPB_RsvdSecCnt DW 1;Boot记录占用多少扇区
BPB_NumFATs    DB 2;共有多少FAT表
BPB_RootEntCnt DW 224 ;根目录文件数最大值
BPB_TotSec16   DW 2880;逻辑扇区总数
BPB_Media      DB 0xF0;媒体描述符
BPB_FATSz16    DW 9;每FAT扇区数
BPB_SecPerTrk  DW 18 ;每磁道扇区数
BPB_NumHeads   DW 2 ;磁头数
BPB_HiddSec    DD 0 ;隐藏扇区数
BPB_TotSec32   DD 0 ;wTotalSectorCount为0时这个值记录扇区数
BS_DrvNum      DB 0 ;中断13的驱动器号
BS_Reserved1   DB 0 ;未使用
BS_BootSig     DB 29h ;扩展引导标记(29h)
BS_VolID       DD 0   ;卷序列号
BS_VolLab      DB 'OrangeS0.02' ;卷标,必须11个字节
BS_FileSysType DB 'FAT12   ' ;文件系统类型,必须8个字节

;变量
wSectorNo           dw 0  ;要读取的扇区的号
bOdd                db 0  ;奇数还是偶数
wRootDirSizeForLoop dw RootDirSectors  ;RootDirector的总扇区

LoaderFileName     db "LOADER  BIN",0 ;LOADER.BIN之文件名
MessageLength      equ 9   ;每个字符串的长度设成固定的9个长度
BootMessage:       db "Booting  " 
Message1:          db "Ready.   "
Message2:          db "No Loader"

LABEL_START:
  mov ax, cs
  mov ds, ax
  mov es, ax
  mov ss, ax
  mov sp, BaseOfStack

  ;清屏
  mov ax, 0600h ;AH=6,AL=0h
  mov bx, 0700h ;黑底白字(BL=07h)
  mov cx, 0     ;左上角:(0,0)
  mov dx, 0184fh
  int 10h     ;10号中断
  mov dh, 0 ;"Booting "
  call DispStr  ;显示字符串
  
  xor ah, ah;
  xor dl, dl ;软驱复位
  int 13h

  ;下面在A盘的根目录寻找LOADER.BIN
  mov word [wSectorNo], SectorNoOfRootDirectory;Root目录区开始的位置
  
LABEL_SEARCH_IN_ROOT_DIR_BEGIN:
  cmp word [wRootDirSizeForLoop], 0 ;根目录未读的扇区的个数是否只剩下0个了
  jz  LABEL_NO_LOADERBIN ;没找到
  dec word [wRootDirSizeForLoop]  ;每遍历一次,根目录未读扇区少一个
  mov ax, BaseOfLoader
  mov es, ax
  mov bx, OffsetOfLoader
  mov ax, [wSectorNo]    ;要读取的扇区号
  mov cl, 1   ;读取一个扇区
  call ReadSector  ;将一个扇区中的数据读入到es:bx指向的位置
  mov si, LoaderFileName  ;ds:si指向文件名"LOADER BIN"
  mov di, OffsetOfLoader
  cld
  mov dx, 10h  ;每个目录项32字节,因此一个扇区有16个项
LABEL_SEARCH_FOR_LOADERBIN:
  cmp dx, 0 ;看下32个字节是否读取完了
  jz LABEL_GOTO_NEXT_SECTOR_IN_ROOT_DIR  ;当前目录项遍历完了,遍历该扇区中下一个目录项
  dec dx  ;每读取一个字节,减1
  mov cx, 11  ;LOADER BIN是11个字节
LABEL_CMP_FILENAME:
  cmp cx, 0
  jz LABEL_FILENAME_FOUND ;如果比较了11个字符都相等,表示找到了
  dec cx
  lodsb  ;从ds:si指向的地址读取字符串,每次读取一个字节
  cmp al, byte [es:di]
  jz LABEL_GO_ON
  jmp LABEL_DIFFERENT ;只要发现不一样的字符表明当前目录项不是自己想要的
LABEL_GO_ON:
  inc di
  jmp LABEL_CMP_FILENAME ;循环遍历,将文件名中11个字符遍历完
LABEL_DIFFERENT:
  and di,0FFE0h  ;找不到将di指向当前目录项的起始地址处,
                 ;因为每个目录项占32字节,因此目录项起始地址的最后5位二进制位为0
  add di,20h     ;指向下一个目录项
  mov si,LoaderFileName ;看下一个目录项中是否是想要的文件
  jmp LABEL_SEARCH_FOR_LOADERBIN 
LABEL_GOTO_NEXT_SECTOR_IN_ROOT_DIR:
  add word [wSectorNo], 1 ;读取下一个扇区
  jmp LABEL_SEARCH_IN_ROOT_DIR_BEGIN
LABEL_NO_LOADERBIN:
  mov dh, 2 ;没有找到,显示数组中第2个字符串
  call DispStr    ;显示字符串
%ifdef _BOOT_DEBUG_
  mov ax, 4c00h
  int 21h  ;回到dos
%else
  jmp $  ;到这里停止
%endif 

LABEL_FILENAME_FOUND:  ;找到LOADER.BIN后便开始加载这个文件
  mov ax, RootDirSectors
  and di, 0FFE0h ;di->当前目录条目的开始地址(即LOADER.BIN文件的目录项)
  add di, 01Ah   ;di->移动到目录项中,首蔟所在的位置中
  mov cx, word [es:di]
  push cx    ;将首蔟在FAT中的序号压入堆栈
  add cx, ax
  add cx, DeltaSectorNo ;cl<-LOADER.BIN的起始扇区号
  mov ax, BaseOfLoader
  mov es, ax       
  mov bx, OffsetOfLoader ;数据会加载到BaseOfLoader:OffsetOfLoader指定的位置处
  mov ax, cx   ;ax<- 读取数据的起始Setctor号

LABEL_GOON_LOADING_FILE:
  push ax  
  push bx
  mov ah, 0Eh
  mov al, '.'
  mov bl, 0Fh  ;/Booting......
  int 10h   ;
  pop bx
  pop ax
  
  mov cl, 1
  call ReadSector
  pop ax     ;取出此Sector在FAT中的扇区号
  call GetFATEntry  ;获取下一个扇区号,保存到ax中
  cmp ax, 0FFFh
  jz  LABEL_FILE_LOADED
  push ax  ;保存Sector在FAT中的序号
  mov  dx, RootDirSectors
  add  ax, dx
  add  ax, DeltaSectorNo
  add  bx, [BPB_BytsPerSec] ;es:bx偏移512字节,用来存储下一个扇区
  jmp  LABEL_GOON_LOADING_FILE

LABEL_FILE_LOADED:
  mov dh, 1
  call DispStr  ;显示字符串
  
  jmp BaseOfLoader: OffsetOfLoader ;这一句正式跳转到已加载到内存中LOADER.BIN的开始处,开始执行LOADER.BIN的代码
  ;Boot Sector的使命到此结束
DispStr:
  mov ax,MessageLength
  mul dh
  add ax,BootMessage
  mov bp, ax
  mov ax, ds
  mov es, ax  ;es:bp指向字符串地址
  mov cx, MessageLength 
  mov ax, 01301h ;AH=13h,AL=01h 
  mov bx, 0007h  ;页号0(BH=0),黑底白字(BL=07h)
  mov dl, 0
  int 10h   ;用10号中断显示字符串
  
  ret

GetFATEntry:
  push es
  push bx
  push ax
  mov  ax, BaseOfLoader
  sub  ax, 0100h ;基地址后移,腾出4k空间用来读取LOADER.BIN数据
  mov  es, ax
  pop  ax  ;ax为蔟号
  mov  byte [bOdd], 0
  mov  bx, 3
  mul  bx   ;ds:ax*3
  mov  bx, 2
  div  bx  ;ds:ax*3/2  指向的是一个蔟的起始地址字节
  cmp  dx,0
  jz   LABEL_EVEN
  mov  byte [bOdd], 1
LABEL_EVEN: ;偶数
  xor dx, dx
  mov bx, [BPB_BytsPerSec]
  div bx  ; 商ax对应于FAT扇区号,余数dx对应扇区中的偏移字节数
  push dx
  mov  bx,0 ;es:bx=(BaseOfLoader-100):00
  add  ax,SectorNoOfFAT1 ;此句之后的ax就是FATEntry所在的扇区号
  mov  cl,2
  call ReadSector ;读取FATEntry所在的扇区,一次读取两个,避免在边界发生错,
                  ;因为一个FATEntry可能跨越两个扇区(512/12有余数)
  pop dx
  add bx,dx  ;指向扇区偏移地址
  mov ax,[es:bx] ;
  cmp byte [bOdd], 1
  jnz  LABEL_EVEN_2
  shr ax,4   ;若是奇数,则取2个字节的的高12位
LABEL_EVEN_2:
  and ax, 0FFFh  ;若是偶数,则取2个字节的低12位
LABEL_GET_FAT_ENRY_OK:
  pop bx
  pop es
  ret
ReadSector:
  push bp
  mov  bp, sp
  sub  esp, 2  ;堆栈中留两个字节区域存储要读取的扇区书目
  
  mov  byte [bp-2], cl ;cl为要读取的扇区数目
  push bx      ;保存bx,下面要给int 13h中断用
  mov  bl, [BPB_SecPerTrk] ;每个磁道的扇区数
  div  bl   ;扇区编号:是按照柱面,磁头,磁道内扇区偏移的顺序编号的
            ;ax=2*柱面*bl + 磁头*bl + 扇区偏移   
  inc  ah  ; 余数在ah中,代表扇区偏移
  mov  cl, ah ;磁道内扇区偏移
  mov  dh, al ;商在al中2*柱面(最右边一位肯定为0)
              ; + 磁头(只有两个磁头,所以此值最多为1)
  shr  al, 1  ;向右平移一位则得到柱面号  
  mov  ch, al ;柱面号
  and  dh, 1 ;商 & 1得到磁头号
  pop  bx    ;用完之后,将bx中的值还原回去
             ;柱面好,磁头号,起始扇区号 给int 13h赋值好了
  mov  dl, [BS_DrvNum] ;驱动器号赋好值,用来读取数据
.GoOnReading:
  mov ah, 2 ;2类型代表int 13h是读取操作
  mov al, byte [bp-2] ;读al个扇区
  int 13h             ;读取数据放到[es:bx]指向的地址中
  jc  .GoOnReading ;如果读取错误 CF会被置1,然后重新读取
  add esp, 2
  pop bp
  ret
  
times 	510-($-$$)	db	0	; 填充剩下的空间,使生成的二进制代码恰好为512字节
dw 	0xaa55				; 结束标志 

3.构建MakeFile文件

##################################################
# Makefile of boot.asm (x=[1,2,3...])
##################################################

SRCBOOT:=boot.asm
BINBOOT:=$(subst .asm,.bin,$(SRCBOOT)) #${SRCBOOT}中的.asm会替换成.bin
SRCLOADER:=loader.asm
BINLOADER:=$(subst .asm,.bin,$(SRCLOADER)) #${SRCLOADER}中的.asm会替换成.bin

IMG:=a.img
FLOPPY:=/mnt/floppy/

.PHONY : everything

everything : $(BINBOOT) $(BINLOADER)
	dd if=$(BINBOOT) of=$(IMG) bs=512 count=1 conv=notrunc
	sudo mount -o loop $(IMG) $(FLOPPY)
	sudo cp $(BINLOADER) $(FLOPPY) -v
	sudo umount $(FLOPPY)

$(BINBOOT) : $(SRCBOOT)
	nasm $< -o $@ #等价与nasm boot.asm -o boot.bin
$(BINLOADER) : $(SRCLOADER)
	nasm $< -o $@ #等价与nasm loader.asm -o loader.bin

4.构建bashsrc文件

#Configuration file for Bochs

#how much memory the emulated machine will have
megs: 32

#filename of ROM images
romimage: file=$BXSHARE/BIOS-bochs-latest
vgaromimage: file=$BXSHARE/VGABIOS-lgpl-latest

#what disk images will be used
floppya: 1_44=a.img, status=inserted

#choose the boot disk.
boot: a

#where do we send log messages
log: bochsout.txt

#disable the mouse
mouse: enabled=0

#enable key mapping, using US layout as default
keyboard:keymap=$BXSHARE/keymaps/x11-pc-us.map

5.执行make -f MakeFile编译文件,运行命令bochs -f bochsr得到如下结果

 

源码地址:

https://github.com/Foolegend/aos/tree/master/chapter04 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值