《Orange‘s》Loader
Loader
作用
引导扇区只有512个字节,能做的事情很少,局限性太大。所以需要一个程序,通过引导扇区加载入内存,然后将控制权交给它,这样就突破了512字节的限制。这个程序便是loader。
加载过程
首先我们需要把loader程序复制到软盘上,并让引导扇区找到并加载它。
为了简单起见,就把loader放在根目录中。根目录区目录项格式:
要加载一个文件到内存中,需要读取软盘,这时候就需要BIOS中断INT 13h。用法如下表:
中断需要的参数不是从第0扇区开始的扇区号,而是柱面号,磁头号和当前柱面的扇区号。对于1.44MB的软盘,有2个面(磁头号0和1),每面80个磁道(磁道0~79),每个磁道有18个扇区(扇区号1~18)。所以软盘容量=2×80×18×512=1.44MB
所以磁头号、柱面(磁道)号和起始扇区号可以用如图所示方法来计算:
接下来分析一下代码:
首先实现将给定扇区号转化为13号中断所需参数并从软盘读取数据的函数:
扇区号放在ax中,读取扇区数目放在cl中,数据被读取到es:bx指向的缓冲区。
ReadSector:
; -----------------------------------------------------------------------
; 怎样由扇区号求扇区在磁盘中的位置 (扇区号 -> 柱面号, 起始扇区, 磁头号)
; -----------------------------------------------------------------------
; 设扇区号为 x
; ┌ 柱面号 = y >> 1
; x ┌ 商 y ┤
; -------------- => ┤ └ 磁头号 = y & 1
; 每磁道扇区数 │
; └ 余 z => 起始扇区号 = z + 1
push bp
mov bp, sp
sub esp, 2 ; 辟出两个字节的堆栈区域保存要读的扇区数: byte [bp-2]
mov byte [bp-2], cl ;要读cl个扇区
push bx ; 保存 bx
mov bl, [BPB_SecPerTrk] ; bl: 除数 每个磁道的扇区数目
div bl ; 商 在 al 中, 余数 在 ah 中
inc ah ; 余数 ++
mov cl, ah ; cl <- 起始扇区号
mov dh, al ; dh <- 商Q
shr al, 1 ; y >> 1 (y/BPB_NumHeads)
mov ch, al ; ch <- 柱面号
and dh, 1 ; dh & 1 = 磁头号
pop bx ; 恢复 bx
; 至此, "柱面号, 起始扇区, 磁头号" 全部得到
mov dl, [BS_DrvNum] ; 驱动器号 (0 表示 A 盘) 也叫磁头号
.GoOnReading:
mov ah, 2 ; 读
mov al, byte [bp-2] ; 读 al 个扇区,读到es:bx对应的缓冲区中
int 13h
jc .GoOnReading ; 如果读取错误 CF 会被置为 1,
; 这时就不停地读, 直到正确为止
;到了此处,代表读取正确
add esp, 2
pop bp
ret
接下来就要在软盘根目录区寻找Loader.bin文件。
LABEL_START:
mov ax, cs
mov ds, ax
mov es, ax
mov ss, ax
mov sp, BaseOfStack
xor ah, ah
xor dl, dl
int 13h ; 软驱复位
; 下面在 A 盘的根目录寻找 LOADER.BIN
mov word [wSectorNo], SectorNoOfRootDirectory ;根目录的第一个扇区号
LABEL_SEARCH_IN_ROOT_DIR_BEGIN:
cmp word [wRootDirSizeForLoop], 0 ;判断根目录区是不是已经读完
jz LABEL_NO_LOADERBIN ;如果读完表示没有找到 LOADER.BIN
dec word [wRootDirSizeForLoop] ;循环次数,初始为根目录所占的扇区数目
mov ax, BaseOfLoader
mov es, ax ; es <- BaseOfLoader
mov bx, OffsetOfLoader ; bx <- OffsetOfLoader
mov ax, [wSectorNo] ; ax <- Root Directory 中的某 Sector 号
mov cl, 1 ;读取一个扇区
call ReadSector
;此时es:bx开始的缓冲区内容为读取扇区的内容
mov si, LoaderFileName ; ds:si -> "LOADER BIN"
mov di, OffsetOfLoader ; es:di -> BaseOfLoader:0100
cld ;决定块传送方向
mov dx, 10h ;一个扇区512B,一个文件条目32B,所以一个扇区中有16个条目
LABEL_SEARCH_FOR_LOADERBIN:
cmp dx, 0
jz LABEL_GOTO_NEXT_SECTOR_IN_ROOT_DIR ;如果已经读完了一个 Sector,
dec dx ; 就跳到下一个 Sector
mov cx, 11 ;LOADER__BIN 的长度
LABEL_CMP_FILENAME:
cmp cx, 0
jz LABEL_FILENAME_FOUND ; 如果比较了 11 个字符都相等, 表示找到
dec cx
lodsb ; ds:si -> al
cmp al, byte [es:di]
jz LABEL_GO_ON
jmp LABEL_DIFFERENT ; 发现不同字符,不是要找的Loader.bin条目
LABEL_GO_ON: ;开始比较下一个字符
inc di
jmp LABEL_CMP_FILENAME ; 继续循环
LABEL_DIFFERENT: ;当前条目不是Loader.bin
and di, 0FFE0h ; di &= E0 为了让它指向本条目开头
add di, 20h ; 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 ; "No LOADER."
call DispStr ; 显示字符串
%ifdef _BOOT_DEBUG_
mov ax, 4c00h ; `.
int 21h ; / 没有找到 LOADER.BIN, 回到 DOS
%else
jmp $ ; 没有找到 LOADER.BIN, 死循环在这里
%endif
LABEL_FILENAME_FOUND: ; 找到 LOADER.BIN 后便来到这里继续
jmp $ ; 代码暂时停在这里
上述程序段共有2个循环,外层循环遍历根目录区的每个扇区,内层循环遍历扇区内的每个条目。通过逐字符比较文件名,寻找Loader.bin。如果找到,此时的di就为Loader.bin文件名最后一个字符的偏移,和0xffe0相与之后便可以得到Loader.bin的条目的地址es:di,进而可以得到Loader.bin的开始簇号。
找到文件后,就开始将Loader.bin载入内存。现在我们已经获得了Loader.bin的开始簇号,可以计算得到Loader.bin的起始扇区号。同时可以通过开始簇号找到FAT表中对应的项目以获得下一个簇号,从而找到Loader占用的其余所有扇区。
为了方便起见,先实现一个通过簇号来在FAT表中获取下一个簇号的函数。
ax中存放簇号,返回值为下一个簇号也放在ax中。
GetFATEntry:
push es
push bx
push ax
mov ax, BaseOfLoader; `.
sub ax, 0100h ; | 在 BaseOfLoader 后面留出 4K 空间用于存放 FAT
mov es, ax ; /
pop ax
mov byte [bOdd], 0
mov bx, 3
mul bx ; dx:ax = ax * 3
mov bx, 2
div bx ; dx:ax / 2 ==> ax <- 商, dx <- 余数
; 这里相当于簇号*1.5,因为一个fat项占12位即1.5字节
cmp dx, 0
jz LABEL_EVEN ;簇号为偶数,偏移刚好是整字节
mov byte [bOdd], 1 ;簇号为奇数,标志位置1
LABEL_EVEN:
; 现在 ax 中是 FATEntry 在 FAT 中的偏移量,下面来
; 计算 FATEntry 在哪个扇区中(FAT占用不止一个扇区)
xor dx, dx
mov bx, [BPB_BytsPerSec] ; 一个扇区的字节数
div bx ; dx:ax / BPB_BytsPerSec
; ax <- 商 (FATEntry 所在的扇区相对于 FAT 的扇区号)
; dx <- 余数 (FATEntry 在扇区内的偏移)
push dx
mov bx, 0 ; bx <- 0 于是, es:bx = (BaseOfLoader - 100):00
add ax, SectorNoOfFAT1 ; 此句之后的 ax 就是 FATEntry 所在的扇区号
mov cl, 2
call ReadSector ; 读取 FATEntry 所在的扇区, 一次读两个, 避免在边界
; 发生错误, 因为一个 FATEntry 可能跨越两个扇区
pop dx
add bx, dx
mov ax, [es:bx] ;得到在扇区内的偏移,为16位
cmp byte [bOdd], 1
jnz LABEL_EVEN_2 ; 偶数
shr ax, 4 ; 奇数,低4位为前一个FAT项的高4位,所以右移4位
LABEL_EVEN_2:
and ax, 0FFFh ; 只保留低12位
LABEL_GET_FAT_ENRY_OK:
pop bx
pop es
ret
要注意的是,如果簇号为奇数,取到的16位二进制数中低4位属于前一个FAT项,所以要右移去掉。
现在可以加载Loader到内存了:
LABEL_FILENAME_FOUND: ; 找到 LOADER.BIN 后便来到这里继续
mov ax, RootDirSectors ;根目录占用Sector数目
and di, 0FFE0h ; di -> 当前条目的开始
add di, 01Ah ; di -> 首 Sector DIR_FstClus 偏移为1ah,此条目对应的开始簇号
mov cx, word [es:di] ;cx为开始的簇号
push cx ; 保存此 Sector 在 FAT 中的序号
add cx, ax
add cx, DeltaSectorNo ; cl <- LOADER.BIN的起始扇区号(0-based)
mov ax, BaseOfLoader
mov es, ax ; es <- BaseOfLoader
mov bx, OffsetOfLoader ; bx <- OffsetOfLoader
mov ax, cx ; ax <- Sector 号
LABEL_GOON_LOADING_FILE:
push ax ; `.
push bx ; |
mov ah, 0Eh ; | 每读一个扇区就在 "Booting " 后面
mov al, '.' ; | 打一个点, 形成这样的效果:
mov bl, 0Fh ; | Booting ......
int 10h ; |
pop bx ; |
pop ax ; /
mov cl, 1
call ReadSector
pop ax ; 取出此 Sector 在 FAT 中的序号
call GetFATEntry ;获取下一个簇号
cmp ax, 0FFFh ; 0fffh代表结束
jz LABEL_FILE_LOADED
push ax ; 保存 Sector 在 FAT 中的序号
mov dx, RootDirSectors
add ax, dx
add ax, DeltaSectorNo
add bx, [BPB_BytsPerSec]
jmp LABEL_GOON_LOADING_FILE
LABEL_FILE_LOADED: ;loader已经被加载到内存中
mov dh, 1 ; "Ready."
call DispStr ; 显示字符串
; *****************************************************************************************************
jmp BaseOfLoader:OffsetOfLoader ; 这一句正式跳转到已加载到内
; 存中的 LOADER.BIN 的开始处,
; 开始执行 LOADER.BIN 的代码。
; Boot Sector 的使命到此结束
; *****************************************************************************************************
终于看到这里了,不容易啊。。。
《Orange‘s》Loader的更多相关文章
- 《Orange‘s》FAT12文件系统
FAT12 层次 扇区(Sector):磁盘上的最小数据单元 簇(Cluster):一个或多个扇区 分区(Partition):通常指整个文件系统 引导扇区 引导扇区是整块软盘的第0个扇区,在这个扇区 ...
- 《Orange’s》保护模式
保护模式 完整代码 ; ========================================== ; pmtest1.asm ; 编译方法:nasm pmtest1.asm -o pmte ...
- 《Orange‘s》 Bochs环境配置
安装配置bochs之前先写一个简单的引导扇区用来测试: org 07c00h mov ax,cs mov ds,ax mov es,ax call DispStr jmp $ DispStr: mov ...
- 《Orange'S:一个操作系统的实现》笔记(一)
感觉自己对于操作系统始终没有一个清楚的概念,尤其最近困扰于实模式.保护模式以及寻址方式等一些概念.转而一想,所有的程序,最终都是操作的计算机资源,需要和操作系统打交道,所以操作系统有必要深入了解一下. ...
- 配置《Orange's一个操作系统的实现》环境心得
<Orange>这本书开篇第一章就做了一个实例,编写了一段引导扇区的代码,但是引导介质仍然采用了已被淘汰多年的软盘.在经历了两天的痛苦查找后终于找到了最方便的解决办法,在此做一下记录,希望 ...
- 闲扯游戏编程之html5篇--山寨版《flappy bird》源码
新年新气象,最近事情不多,继续闲暇学习记点随笔,欢迎拍砖.之前的〈简单游戏学编程语言python篇〉写的比较幼稚和粗糙,且告一段落.开启新的一篇关于javascript+html5的从零开始的学习.仍 ...
- Node.js Web 开发框架大全《中间件篇》
这篇文章与大家分享优秀的 Node.js 中间件模块.Node 是一个服务器端 JavaScript 解释器,它将改变服务器应该如何工作的概念.它的目标是帮助程序员构建高度可伸缩的应用程序,编写能够处 ...
- 20145206《Java程序设计》第9周学习总结
20145206 <Java程序设计>第9周学习总结 教材学习内容总结 第十六章 整合数据库 JDBC是用于执行SQL的解决方案,开发人员使用JDBC的标准接口,数据库厂商则对接口进行操作 ...
- 推荐--《Android深入浅出》
基本信息 书名:Android深入浅出 作者:张旸 著 页数: 661 出版社: 机械工业出版社; 第1版 (2014年4月17日) 语种: 简体中文 ASIN: B00JR3P8X0 品牌: 北京华 ...
随机推荐
- 工控随笔_08_西门子_Win10安装Step7.V5.6中文版授权管理器不能正常启动
随着Windows系统的不断升级,西门子工控软件也不断升级,但是有时候在安装西门子 软件的时候会出现授权管理器不能正常启动的情况. 图 Step7 因为自动许可证管理器不能正常打开 如上图所示,报S ...
- c# excel如何导入到sqlserver数据库
最近在做这个如何把excel导入到数据库中,经过多方查找,终于找到一个适合的,并且经过自己的完善可以正常使用(忘记原作者博客的链接地址了,敬请见谅) 首先是窗体的创建,文本框显示文件的路径,按钮执行操 ...
- Apartment 2019:(1)创建墙体
墙体建模 The Walls 软件:SketchUp Pro 2017 墙体模型 建模过程: 一.导入图像并调整大小 导入公寓平面参考图/户型图(来自网络),导入为图像.连续三击鼠标左键,选中所有的几 ...
- tigervnc-server 无法启动问题
[root@moodle-bak .X11-unix]# vncserver WARNING: The first attempt to start Xvnc failed, possibly bec ...
- Python 爬虫之下载图片
from urllib import request import json #---------获取网页源代码-------------- def getHtml(url): response=re ...
- 在下载SOPC代码的过程中遇到的一些错误
(1)Error (209015): Can't configure device. Expected JTAG ID code 0x02D120DD for device 2, but found ...
- void类型及void指针(转载)
转载 https://www.cnblogs.com/pengyingh/articles/2407267.html 2.void的含义 void的字面意思是“无类型”,void *则为“无类型指针” ...
- Linux部署笔记分享
# Linux部署 ## 安装lrzsz1. 安装lrzsz: yum -y install lrzsz2. 进入tmp目录3. rz 上传安装文件 jdk-8u65-linux-x64.tar.gz ...
- 6Linux用户身份与文件权限
3类用户身份: (1)管理员UID为0,root (2)系统用户UID为1-999:nologin不能登录系统,老版本5.6中是1-499 (3)普通用户UID为1000开始,老版本5.6中是1000 ...
- EF_简单的增删改查
EF分为三种code_first,model_first,dabase_first这三种模式,网上的例子有好多,但是用了之后感觉实际中都不是这么用的,此处记录写下来日后用的着了可以快速应用,记录如下: ...