1、突破512字节的限制

2、加载Loader进入内存

一、突破512字节的限制

一个操作系统从开机到开始运行,大致经历"引导—》加载内核入内存—》跳入保护模式—》开始执行内核"这样一个过程。也就是说,在内核开始执行之前不但要加载内核,还要准备保护模式等一系列工作,如果全部交给引导扇区来做,512字节很可能不够用,所以,不放把这个过程交给另外的模块来完成,我们把这个模块叫做Loader。引导扇区负责把Loader加载如内存并且把控制权交它,其他的工作放心地交给 Loader来做,因为它没有512字节的限制,将会灵活很多。

二、加载Loader进入内存

上一节我们已经详细介绍了FAT12文件系统的数据结构,下面我们需要思考的是两个问题:1、引导扇区通过怎样的步骤才能找到文件;2、如何能够把文件内容全都读出来并加载进入内存。

下面我们先解决第一个问题:

1、  如何读取软盘?

(1)    我们需要使用BIOS中断int 13h来读取软盘。它的用法如下表所示:

在这里我们只介绍了2种工作方式,中断int 13h还有其他的工作方式,如果需要可以自行查看内容。

(2)    由上表我们可以知道:当读取某些扇区时,需要柱面(磁道)号(ch),起始扇区号(cl),磁头号(dh)。我们如何通过计算得到这些数据呢?

(3)    现在万事俱备只欠东风了,下面我们就书写读取软盘扇区的函数ReadSector。

首先我们要知道该函数需要什么参数,这些参数存储在什么位置?

参数1:扇区号,存储在ax中

参数2:要读取扇区的个数,存储在cl中

参数3:数据缓冲区,即读取扇区数据后,将其存储在什么位置,用es:bx指向该缓冲区。

即函数的作用:从第ax个Sector开始,将cl个Sector读入es:bx中。

;函数名:ReadSector
;------------------------------------------------------
;作用:从第ax个Sector开始,将cl个Sector读入es:bx中
ReadSector:  ;---------------------------------------------  ;怎样由扇区号求扇区在磁盘中的位置(扇区号->柱面号,起始扇区,磁头号)  ;---------------------------------------------  ;设扇区号为x  ;                           ┌ 柱面号 = y >> 1  ;       x           ┌ 商 y ┤  ; -------------- => ┤      └ 磁头号 = y & 1  ;  每磁道扇区数     │  ;                   └ 余 z => 起始扇区号 = z + 1  ;辟出两个字节的堆栈区间保存要读取的扇区数:byte[bp-2]  push bp  mov bp, sp  sub esp, 2            mov byte[bp-2], cl      ;将参数cl,存储在byte[bp-2],将要读取扇区的个数。  push bx             ;保存bx,因为下面要使用bx进行计算。  mov bl, [BPB_SecPerTrk]     ;bl:除数=18  ;ax存储的是扇区号,bl是每磁道扇区数,执行ax/bl=al----ah,  ;即商y在al中,商z在ah中。  div bl                inc ah              ;ah(z)++,即起始扇区号=z+1,  mov cl, ah          ;将ah值赋值给cl,中断int 13h中,cl保存的恰好是起始扇区号  mov dh, al          ;将al(y),赋值给dh  shr al, 1           ;对al(y)进行右移一位,即得到柱面号=y>>1,  mov ch, al          ;然后将al赋值给ch,在中断int 13h中,ch保存着柱面(磁道)号  and dh, 1           ;将dl(y)进行&1运算,即得到磁头号=y&1,在中断int 13h中,dh保存着  ;磁头号  pop bx              ;恢复bx值  ;到此为止,“柱面(磁道)号(ch),起始扇区号(cl),磁头号(dh),缓冲地址(es:bx)”全部准备就绪  mov dl, [BS_DrvNum]     ;在中断int 13中,dl保存着驱动器号。此时dl=[BS_DrvNum]=0
.GoOnReading:  ;下面对ah,al进行赋值,ah=2,al=要读取的扇区数,前面将参数cl存储在byte[bp-2],现在从这里重新获取  ;并赋值给al。  mov ah, 2  mov al, byte[bp-2]  ;中断int 13一切准备就绪,然后执行int 13  int 13h  jc  .GoOnReading        ;如果读取错误,CF会被置为1,这时就不停地读,直到正确为止。  add esp, 2          ;恢复堆栈  pop bp  ret

2、  如何在软盘中寻找Loader.bin文件

(1)    结合上一节所介绍的FAT12数据结构,从中我们可以知道,要寻找一个文件,首先需要在根目录区中寻找该文件的根目录条目;然后根据根目录条目获取文件开始簇数(也就是在数据区中存储的扇区);最后读取文件内容到内存。

(2)    嗯,是的,下面就让我们来完成第一步-----在根目录区中寻找该文件的根目录条目。

让我们开始思考这个问题,

首先要知道根目录区的开始扇区号是19,也就是说从第19扇区开始,根目录区占用扇区总数为14,也就是说,如果不能发现Loader.bin,需要将14个扇区都进行查找,于是需要一个大的循环在外围,控制着扇区的读取。

紧接着,我们每读取一个扇区,一个扇区是512个字节,一个根目录条目占32个字节,故一个扇区中存在512/32=16个根目录条目,所以需要添加一个循环,控制根目录条目的变化,从0—16进行循环。

最后,针对每一个根目录条目,我们只是要比较文件名,一个根目录条目的文件名占用11个字节,所以需要对每一个字节与"LOADER   BIN"中的每一个字节进行比较,所以还是要添加一个循环,来控制字符的变化,即从0—11.

用C语言来表示该问题就是:

for( i = 根目录区的起始扇区号(19); i < 根目录区占有的扇区数(14);  i++)      {

for( j = 0;j < 一个扇区内存储的根目录条目个数(512/32=16); j++)    {

for(k =0; k < 根目录条目中文件名占用的空间(11个字符); k++)      {

if(k=10)jmp LABEL_FILENAMEFOUND

if(ds:si= es:di) si++; di++;

else  break;

}

}

}

(3)    下面让我们来分析代码:

首先需要介绍下面可能需要用到的几个变量值:

BaseOfLoader           equ  09000h     ;LOADER.BIN被加载到的位置---段地址

OffsetOfLoader        equ  0100h       ;LOADER.BIN被加载到的位置---偏移地址

RootDirSectors         equ  14     ;根目录占用空间(BPB_RootEntCnt*32+511)/512

SectorNoOfRootDirectory        equ  19     ;Root Directory 的第一个扇区号

;变量

wRootDirSizeForLoop      dw    RootDirSectors         ;Root Directory占用的扇区数,在循环中会递减至0

wSectorNo                 dw    0                ;要读取的扇区号

bOdd                           db     0                ;奇数还是偶数

;字符串

LoaderFileName       db     "LOADER  BIN",     0       ;LOADER.BIN之文件名

;调用中断int 13h,实现软驱复位  xor ah, ah  xor dl, dl  int 13h   ;下面在A盘的根目录中寻找LOADER.BIN  ;wSectorNo表示要读取的扇区号,SectorNoOfRootDirectory  ;表示根目录区的开始扇区号=19  mov word[wSectorNo], SectorNoOfRootDirectory
LABEL_SEARCH_IN_ROOT_DIR_BEGIN:  ;wRootDirSizeForLoop=RootDirSectors,表示根目录占用的扇区数,即表示要读取的扇区数;也就是  ;最外部循环中的控制变量(相当于i)。  ;判断根目录区所有扇区是不是已经读取完毕,如果读完表示没有找到LOADER.BIN,  ;跳入到LABEL_NO_LOADERBIN,否则,减1。  cmp word[wRootDirSizeForLoop], 0  jz  LABEL_NO_LOADERBIN            dec word[wRootDirSizeForLoop]  ;为ReadSector函数准备参数,从第ax个Sector开始读,将cl个Sector读入es:bx中  mov ax, BaseOfLoader  mov es, ax      ;es<-BaseOfLoader  mov bx, OffsetOfLoader  ;bx<-OffsetOfLoader,于是es:bx=BaseOfLoader:OffsetOfLoader  mov ax, [wSectorNo] ;ax<-Root Directory中的某Sector号,表示要读取的扇区号  mov cl, 1       ;cl表示要读取扇区个数=1  call    ReadSector  ;调用ReadSector函数之后,es:bx将存储该扇区数据。  mov si, LoaderFileName  ;ds:si->"LOADER  BIN"  mov di, OffsetOfLoader  ;es:di->BaseOfLoader:OffsetOfLoader=es:bx  ;即es:di指向存储的该扇区数据  cld  ;一个扇区是512个字节,一个根目录项占32个字节,故512/32=16,因此需要比较16个根目录项的文件名,  ;故赋值dx=16,由dx来控制循环次数  mov dx, 10h
LABEL_SEARCH_FOR_LOADERBIN:  ;判断dx是否为0,0意味着这个扇区内的所有根目录项进行比较完毕,然后跳入到下一个扇区,继续进行比较,  ;dx=0,则跳入LABEL_GOTO_NEXT_SECTOR_IN_ROOT_DIR;否则,dx--  cmp dx, 0  jz  LABEL_GOTO_NEXT_SECTOR_IN_ROOT_DIR    dec dx  ;一个根目录项的文件名占用11个字节,故必须对其每个字节与"LOADER  BIN"一一对比  ;故赋值cx=11,由cx来控制循环次数  mov cx, 11
LABEL_CMP_FILENAME:  cmp cx, 0  jz LABEL_FILENAME_FOUND ;如果cx=0,意味着11个字符都相等,表示找到,跳转到LABEL_FILENAME_FOUND  dec cx          ;否则,cx--  lodsb           ;ds:si->al,ds:si指向的是字符串"LOADER  BIN"  cmp al, byte[es:di] ;进行一个字符的比较,如果相等,则比较下一个字符,  jz LABEL_GO_ON      ;跳入到LABEL_GO_ON  jmp LABEL_DIFFERENT ;只要发现有一个不相等的字符就表明本Directory Entry不是我们要  ;找的LOADER.BIN,跳转到LABEL_DIFFERENT,进如下一个Directory Entry比较。
LABEL_GO_ON:              inc di          ;将di++,进行一个字符的比较。  jmp LABEL_CMP_FILENAME  ;跳转到LABEL_CMP_FILENAME,继续进行文件名比较。  LABEL_DIFFERENT:  ;di&=E0是为了让它指向本条目开头,di初始化为某个条目开头,  ;在比较过程中,会将它不断增1,当失败之后,必须进行重新初始化  ;因为一个条目占用32个字节,故and di,0FFE0h   add di, 20h  ;之后,di就指向了下一个条目  and di, 0FFE0h                add di, 20h   ;重新初始化si,使其指向"LOADER  BIN"的开始位置  mov si, LoaderFileName            jmp LABEL_SEARCH_FOR_LOADERBIN  ;跳转到LABEL_SEARCH_FOR_LOADERBIN  LABEL_GOTO_NEXT_SECTOR_IN_ROOT_DIR:   add word[wSectorNo], 1      ;将要读取的扇区号+1,进行下一个扇区的比较  jmp LABEL_SEARCH_IN_ROOT_DIR_BEGIN  ;跳转到LABEL_SEARCH_IN_ROOT_DIR_BEGIN,开始一个扇区的比较  ;如果最后没有找到"LOADER   BIN",则显示“NO LOADER”字符串来表示。
LABEL_NO_LOADERBIN:  mov dh, 2               ;"NO LOADER"  call DispStr                ;显示字符串
%ifdef  _BOOT_DEBUG_  mov dh, 2               ;"NO LOADER"  call DispStr                ;显示字符串  mov ax, 4C00h  int 21h                 ;没有找到LOADER.BIN,返回到DOS
%else     jmp $                   ;没有找到LOADER.BIN,死循环在这里
%endif  ;如果找到"LOADER   BIN",则跳转到LABEL_FILENAME_FOUNT,然后进行第二步骤,从  ;Directory Entry中读取文件在数据区的开始簇号。
LABEL_FILENAME_FOUND:

(4)    对上面这段代码画出它的简易流程图如下:

3、  如何将Loader.bin文件加载到内存?

现在我们已经有了Loader.bin的起始扇区号,我们需要用这个扇区号来做两件事情:一件是把起始扇区装入内存,另一件则是通过它找到FAT中的项,从而找到Loader占用的其余所有扇区。

此时装入一个扇区对我们来说已经是很轻松的事了,可从FAT中找到一个项还是多少有些麻烦,下面我们就根据扇区号去FAT表中找到相应的项。在这里,将要写一个函数GetFATEntry,函数的输入就是扇区号(ax),输出则是其对应的FAT项的值(ax)。

我们一起来思考这个函数如何去实现,我们知道了扇区号x,然后我们去FAT1中寻找x所对应的FATEntry,我们已经知道一个FAT项占1.5个字节。所以我们用x*3/2=y………z,y为商(偏移量)(字节),相对于FAT1的初始位置的偏移量;Z为余数(0或者1),是判断FATEntry是奇数还是偶数,0表示偶数,1表示奇数。然后我们让y/512=m………n,m为商,n为余数,此时m为FATEntry所在的相对扇区,n为在此扇区内的偏移量(字节)。因为FAT1表前面还有1个引导扇区,所以FATEntry所在的实际扇区号为m+1。然后读取m+1和m+2两个扇区,然后在偏移n个字节处,取出FATEntry,相当于读取两个字节。此时再利用z,如果z为0的话,此FAT项为前一个字节和后一个字节的后4位,如果z为1的话,此FATEntry取前一个字节的前4位和后一个字节。

下面我们实现GetFATEntry函数,函数的输入就是扇区号,输出则是其对应的FATEntry的值。

加载Loader.bin相关推荐

  1. 全面剖析《自己动手写操作系统》第四章---加载Loader.bin

    全面剖析<自己动手写操作系统>第四章--FAT12文件系统    http://blog.csdn.net/zgh1988/article/details/7284834 1.突破512字 ...

  2. 《一个64位操作系统的设计与实现》学习实践3-boot加载loader

    1.boot.asm源码开发,编写一个能加载loader的boot. root@ubuntu:~# vi boot.asm org 0x7c00 BaseOfStack equ 0x7c00BaseO ...

  3. 使用c#封装海康SDK出现无法加载 DLL“..\bin\HCNetSDK.dll”: 找不到指定的模块

    最近在研究网络摄像头的二次开发,测试了一款海康威视的网络摄像头,程序调试的时候,出现如题的报错. 调试随机自带的demo时,程序运行正常,但当把该程序引入到我自己的程序中时,就开始报错.根据开发软件包 ...

  4. 【实现操作系统 03】使用 FAT12 文件系统实现简单的 Boot 加载 Loader 到内存

    系统环境: OS:CentOS Stream release 9 (cmd: cat /etc/redhat-release) Linux Kernel:Linux 5.14.0-142.el9.x8 ...

  5. [操作系统] 操作系统真相还原读书笔记三:MBR加载loader到内存并跳转到loader执行

    为什么要有loader程序? 通过操作系统真相还原读书笔记二:编写MBR主引导记录我们已经能够正常运行MBR主引导记录(有些书籍也叫做boot)程序了,但该程序什么也没做.我们的MBR 受限于 512 ...

  6. 杰里之ANC 加载 anc_gains.bin anc_coeff.bin【篇】

    注意:无需在下载批处理 -res 后面添加任何文件

  7. os引导程序boot 在根目录区寻找os加载程序文件loader 对应的根目录条目

    [0]README 0.0) source code from orange's implemention of a os and for complete code , please visit h ...

  8. thinkphp 框架自动加载原理_这下你应该理解ThinkPHP的Loader自动加载了

    想了很久终于要开始系列文章的编写了,期望是写出提升和面试都可以搞定的系列文章. 当你看到本文时,如果你发现咔咔没有编写到的面试热点问题或者技术难点,期待评论区指出,一起完善. 前言 目前再整理PHP进 ...

  9. babylonjs 分部加载模型_babylonjs使用笔记

    目录 一.介绍了解 1.游乐场:playground 2.沙盒:sanbox 1)Nodes 2)Materials 3)Textrues 3.在线例子:examples 二.模型文件 三.项目使用 ...

最新文章

  1. 【python教程入门学习】PyCharm 如何使用
  2. 网页瀑布流效果实现的几种方式
  3. 提高你的Java代码质量吧:推荐在复杂字符串操作中使用正则表达式
  4. Microservices Reference Architecture - with Spring Boot, Spring Cloud and Netflix OSS--转
  5. c语言猴子吃桃嵌套调用编程,C语言实现猴子吃桃问题(循环、递归两种方法)...
  6. JS制作字体图(文字图)
  7. 赢在 CSDN:我在 CSDN 的成长,“长风破浪会有时”,如何保证自己有持续写作的动力?
  8. 数据科学和人工智能技术笔记 一、向量、矩阵和数组
  9. Java Swing Mysql实现的员工工资管理系统项目源码附带视频指导运行教程
  10. oracle如何查看某个时间段调用的函数的异常信息_每天14点遭遇惊魂时刻,如何一步一步揪出真凶?...
  11. CreateThread _beginThread _begintheadex AfxBeginThead思考
  12. 刘徽与《九章算术》《海岛算经》简介
  13. 中华人民共和国行政区划(五级):省级、地级、县级、乡级和村级。
  14. 计算机专业大一上学期的c语言难吗,我想知道大学里面C语言考试时怎么弄的!难么?还有就是计算机二级考试必须要报考还是?...
  15. 宝德银河麒麟系统打印机安装及共享方法(一)
  16. python基础教程:Python实现动态给类和对象添加属性和方法操作示例
  17. Unity开发VR项目(二)——SteamVR按键设置
  18. 一个或多个listeners启动失败,更多详细信息查看对应的容器日志文件
  19. 多益2980邮箱集合专业游戏服务免费安全的电子邮箱
  20. 论文参考文献格式说明

热门文章

  1. 【Windows 逆向】使用 CE 工具挖掘关键数据内存真实地址 ( 逐层分析分析 静态地址 到 动态地址 的寻址 + 偏移 过程 ) ★
  2. 【Windows 逆向】使用 CE 工具挖掘关键数据内存真实地址 ( 完整流程演示 | 查找临时内存地址 | 查找真实指针地址 )
  3. 【错误记录】Android Studio 编译报错 ( VirtualApp 编译 NDK 报错 | Error:A problem occurred configuring project ‘: )
  4. 【SeeMusic】视频编辑 ( 顶部裁剪 | 底部裁剪 | 左侧裁剪 | 右侧裁剪 | 明亮度 | 对比度 | 色调 | 饱和度 )
  5. 【Android】Source Insight 基本用法 ( 导入 Android 源码 | 设置字体 | 显示行号 | 搜索功能 | 快捷键设置 )
  6. 1642: [Usaco2007 Nov]Milking Time 挤奶时间(dp)
  7. centos7.3安装MongoDB
  8. JSP自定义标签开发与打包
  9. 一步一步学习Bootstrap系列--表单布局
  10. DataSet.Relations一例