【OS学习笔记】六 实模式:编写主引导扇区代码

上一篇文章学习了:计算机的启动过程(点击链接查看上一篇文章)

这篇文章学习记录为:编写主引导扇区代码。

参考:《X86汇编语言-从实模式到保护模式》-李忠。纯学习笔记,更详细内容请阅读正版书籍。如有侵权请联系我删除文章。

实际上,从这篇文章开始,我们才开始进入到实模式的学习。之前的五篇文章,都是预备的学习知识。点击下面链接复习相关的预备知识:

  1. 处理器、内存和指令
  2. 汇编语言和汇编软件
  3. 计算机的启动过程
  4. 什么是虚拟机
  5. VirtualBox的下载、安装和配置

1、回顾主引导扇区

在前面的学习中,我们知道在计算机重新启动后,如果硬盘是首选的启动设备,那么处理器就会跳转到硬盘的0面0道1扇区去执行代码。这里成为主引导扇区。

主引导扇区的大小为512字节、ROM-BIOS将它加载到处理器的地址空间的逻辑地址0x0000:0x7c00处,也就是物理地址0x07c00处,然后判断它是否有效。

而判断一个主引导扇区是否有效的方法是判断它最后的两个字节是否是0x550xAA。ROM-BIOS首先检测这两个位置是否正确,如果正确,则以一个段间转移指令jmp 0x0000:0x7c00处执行代码。

一般来说,主引导扇区的代码,负责计算出操作系统所在的硬盘位置,然后将操作系统的自举代码加载到内存,也用一个jmp指令跳转到那里继续执行,直到操作系统完成启动。

我们本篇文章的主要内容就是,编写一段代码,将它写到主引导扇区,让处理器执行。为了更加明显的显示我们的代码是正确的,我们选择在屏幕上显示一行字符串。

2、在屏幕上显示文字

在编写代码之前,我们首先来了解一下如何在屏幕上显示文字。

请注意,这里可不是使用printf或者cout或者System.out.println的地方。我们这里是在没有操作系统的情况下,想要在显示屏上显示文字。

想要显示文字,就要把想要显示的内容写到显存即可。显存是什么?也是一种存储器,只不过专门存储需要在显示器上显示的内容的。其他详细原理自己百度吧,或者看本文的参考书籍,有详细的解释。

如下图所示,是一个字符在屏幕上显示的简单的原理图:
在这里插入图片描述

处理器为了直接访问显存,将显存映射到处理器的寻址空间中。如下图:
在这里插入图片描述

我们知道8086可以访问1M的内存空间。其中0x00000-0x9FFFF属于常规内存,由内存条提供。0xF0000-0xFFFFF由主板上的ROM-BIOS提供。

中间还剩余的320KB的空洞,即0xA0000-0xEFFFF,这段空间就由外设来提供,其中就包括显卡的显存部分。

由于历史原因,一直以来0xB8000-0xBFFFF这段物理地址空间,是留给显卡的。

3、分析主引导扇区代码

这段代码是本文参考书籍的代码,先把代码贴上,不算长,如果看不懂,不要被吓跑了。下面的分析,肯定可以让你明白这个程序的意思。

  1          ;代码清单5-1 2          ;文件名:c05_mbr.asm3          ;文件说明:硬盘主引导扇区代码4          ;创建日期:2011-3-31 21:15 5  6          mov ax,0xb800                 ;指向文本模式的显示缓冲区,显存的段地址,7          mov es,ax                                      ;一般用DS段寄存器,但是DS有其他用处,这里我们使用ES寄存器8 9          ;以下是显示字符串"Label offset:"10          mov byte [es:0x00],'L'11          mov byte [es:0x01],0x0712          mov byte [es:0x02],'a'13          mov byte [es:0x03],0x0714          mov byte [es:0x04],'b'15          mov byte [es:0x05],0x0716          mov byte [es:0x06],'e'17          mov byte [es:0x07],0x0718          mov byte [es:0x08],'l'19          mov byte [es:0x09],0x0720          mov byte [es:0x0a],' '21          mov byte [es:0x0b],0x0722          mov byte [es:0x0c],"o"23          mov byte [es:0x0d],0x0724          mov byte [es:0x0e],'f'25          mov byte [es:0x0f],0x0726          mov byte [es:0x10],'f'27          mov byte [es:0x11],0x0728          mov byte [es:0x12],'s'29          mov byte [es:0x13],0x0730          mov byte [es:0x14],'e'31          mov byte [es:0x15],0x0732          mov byte [es:0x16],'t'33          mov byte [es:0x17],0x0734          mov byte [es:0x18],':'35          mov byte [es:0x19],0x0736 37          mov ax,number                 ;取得标号number的偏移地址38          mov bx,1039 40          ;设置数据段的基地址,只是在同一个段,偏移地址是不一样的41          mov cx,cs42          mov ds,cx43 44          ;求个位上的数字45          mov dx,046          div bx47          mov [0x7c00+number+0x00],dl   ;保存个位上的数字48 49          ;求十位上的数字50          xor dx,dx51          div bx52          mov [0x7c00+number+0x01],dl   ;保存十位上的数字53 54          ;求百位上的数字55          xor dx,dx56          div bx57          mov [0x7c00+number+0x02],dl   ;保存百位上的数字58 59          ;求千位上的数字60          xor dx,dx61          div bx62          mov [0x7c00+number+0x03],dl   ;保存千位上的数字63 64          ;求万位上的数字 65          xor dx,dx66          div bx67          mov [0x7c00+number+0x04],dl   ;保存万位上的数字68 69          ;以下用十进制显示标号的偏移地址70          mov al,[0x7c00+number+0x04]71          add al,0x3072          mov [es:0x1a],al              ;将al寄存器中的ASCII数字传送到显示缓冲区73          mov byte [es:0x1b],0x04             ;下一字节存放显示属性,0x04代表:黑底红字,无闪烁,无加亮74 75          mov al,[0x7c00+number+0x03]76          add al,0x3077          mov [es:0x1c],al78          mov byte [es:0x1d],0x0479 80          mov al,[0x7c00+number+0x02]81          add al,0x3082          mov [es:0x1e],al83          mov byte [es:0x1f],0x0484 85          mov al,[0x7c00+number+0x01]86          add al,0x3087          mov [es:0x20],al88          mov byte [es:0x21],0x0489 90          mov al,[0x7c00+number+0x00]91          add al,0x3092          mov [es:0x22],al93          mov byte [es:0x23],0x0494 95          mov byte [es:0x24],'D'96          mov byte [es:0x25],0x0797 98    infi: jmp near infi                 ;无限循环,防止处理器再接着取下面的数据,数据当成指令取执行会导致错误或运行不正常99 
100    number db 0,0,0,0,0
101 
102    times 203 db 0
103             db 0x55,0xaa
  1. 首先我们是要在屏幕上显示字符串,所以需要将需要显示的字符串的字符传送到显存中
  2. 6行7行代码:由第2节的内容知显存位于处理器寻址空间的0xB8000处。所以我们需要设置显存的段地址为:0xb800 ,这里我们使用ES寄存器来表示显存段地址(当然也可以使用DS,但是DS还有其他用处,所以我们就使用ES寄存器)。
  3. 10行-35行:显示字符串"Label offset:"

那么为什么每将一个字符传送到显存后,后面要继续传动一个0x07呢?实际上是这样的:

显存中,每一个字符的ASCII码后面跟的是该字符的显示属性。包括字符的颜色和背景色。如下图:
在这里插入图片描述

在8086下,80x25文本模式下的颜色表如下:
在这里插入图片描述

由以上可知,我们显示的字符属性是0x07,黑底白字,无闪烁,无加亮。也就是黑底白字。

10行-35行依次将字符写入到缓存中,后面依次写入字符的属性。这很好理解!!!

  1. 37行:取得标号number的汇编地址。本代码不光想在屏幕上显示字符串Label offset:,还想将number的汇编地址显示出来。number是一个标号,标号是它所在的地方的汇编地址。什么是汇编地址?

实际上一个程序经过编译后,编译器会给每一条代码一个汇编地址,这个汇编地址实际上是从0开始。

在分段机制中,偏移地址也是从0开始。实际上,这个汇编地址就是与偏移地址是对应的。如下图:
在这里插入图片描述

理解了什么是汇编地址与偏移地址的关系后(不理解的看原书第五章),我们就来将number处的汇编地址在屏幕上显示出来。

number就代表那个地址的值。我这里已经提前知道这个地址是:0x012E也就是十进制302

由之前的学习内容知道直接将302传送到显存的话,是不可能在屏幕上显示302的。我们只能将302进行拆分,将每一个数位都拆解出来,一个一个传送给显存。如何拆解?每次除以10…太简单了就不写了。

  1. 38行:将bx寄存器赋值为10 ,作为下面除以10 的时候的除数。
  2. 100行:这里为什么突然到100行了?不急,慢慢来,你分析程序也是跳来跳去的分析吧。

我们既然想将number的汇编地址分解为一个个数位,就得找一个地方,将分解后的数字先咱是存起来。你可以想到用寄存器先存起来,但是寄存器,毕竟就那么8个通用的寄存器,而且本段代码也用了好几个了,所以这里无法使用寄存器来暂时存我们的数据。

一个办法就是在内存找到一个地方,来存储。这里,我们的主引导扇区是512字节,我们写的代码很少不到300字节,所以我们选择在主引导扇区的最后先开辟一个空间用于存储number的分解后的数字。

那么第100行,就定义了五字节的数据,赋值为0。当然你也可以赋值为其他值,反正后面是呀被覆盖的。

  1. 41-42行:我们将DS寄存器指向代码段,就是让数据段寄存器DS与代码段寄存器CS保持一致。因为我们这里将数据与代码都放到一个段里面了,所以数据段与代码段是一个段(正常不能不放到一个段,我们初学,先这么写,后面会分段) 其实用CS来访问数据也可以,但是我们还是习惯用DS来访问数据,所以这里就有这么两句赋值代码。

  2. 44-67行:求numberi的各个数位的数字,然后存到我们预先开辟好的空间中。

  3. 70-93行:先将各个数位转化成十进制显示,然后送入到显存,在每一个字符后面写入显示属性0x04,代表黑底红字。

  4. 95-96行:显示字符D 以代表我们前面显示的number地址是10进制显示的。黑底白字。

  5. 98行:无限循环,防止处理器再接着取下面的数据,数据当成指令取与执行会导致错误或运行不正常

  6. 102行:由于主引导扇区是512字节的,我们写的程序并没有达到512字节。所以我们应该将主引导扇区未满的地方填满。我们这里采取了一些特殊手段得知有203字节未填满,搜易我们了连续声明203个字节用于存储0. 至于使用了什么特殊手段,不必要知道,因为后面的学习中会学习使用正常的的手段来得知这个未填满的字节有多少。

  7. 103行:一个有效的主引导扇区,它的最后必须是0x55和0xaa

4、编译主引导扇区代码并加载运行

在上一篇文章,我们已经安装了VirtualBox 虚拟机软件,并在里面创建了一台名为LEARNASM的虚拟计算机。除此之外,还为它创建了一块虚拟硬盘。

然后我们参考书上4.2.4节的内容,将我们汇编代码编译好的二进制bin文件写到虚拟硬盘的主引导扇区中。启动虚拟机,就会运行我们写的代码,运行结果如下:
在这里插入图片描述

今天的程序运行的很顺利。

5、总结

了解汇编的运行机制,对以后深入学习高级语言,很有帮助:比如JVM。

笔记记得不是很全,像汇编的语法以及如何将代码写到虚拟硬盘的主引导扇区这些都没有写。如果又不懂的可以加我联系方式一起交流。

学习探讨加个人:
qq:1126137994
微信:liu1126137994

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/423058.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【OS学习笔记】七 Bochs的下载、安装和配置

参考:《X86汇编语言-从实模式到保护模式》-李忠。纯学习笔记,更详细内容请阅读正版书籍。 1 开源的BOCHS虚拟机软件 Bochs是开源软件。它用软件来模拟处理器取指令和执行指令的过程,以及整个计算机硬件。当它开始运行时,就直接模…

【OS学习笔记】八 实模式:编写主引导扇区代码-另一种更高效的写法

学习交流加 个人qq: 1126137994个人微信: liu1126137994学习交流资源分享qq群: 962535112 上一篇文章,我们用比较原始的方法编写了主引导扇区的代码。点击链接查看上一篇文章:编写主引导扇区代码 本片文章将学习以下内…

【OS学习笔记】九 实模式:从汇编的角度理解栈结构

上一篇文章以一种更加高效的方法编写了主引导扇区的代码。主要是引入了循环和跳转指令。点击链接查看上一篇文章:编写主引导扇区代码-另一种更高效的写法 本篇文章,继续上一篇文章的学习。同样还是编写汇编代码加载到主引导扇区让CPU直接执行。但是我们…

【OS学习笔记】十 实模式:实现一个程序加载器-程序加载器如何将用户程序加载到内存并执行

上一篇文章学习了以下内容: 用一种不同的分段方法,从另一个不同的的角度理解处理器的分段内存访问机制使用循环和条件转移指令来优化主引导扇区代码 点击链接查看上一篇文章:点击链接查看 对于主引导扇区部分。大概前几篇文章已经学的差不…

【OS学习笔记】十一 实模式:中断-软中断和硬中断基本原理

上一篇文章我们模拟操作系统的加载器程序,使用汇编语言实现了一个程序加载器:点击链接查看上一篇文章:程序加载器的实现原理 本篇文章,是实模式学习的结尾。在经过了那么多坎坷,终于学完了8086的实模式!&a…

配置节处理程序时出错,未能加载文件或程序集

安装sql server2008时,出现下面的错误: 按照上面的提示,查看那个路径下的文件,根据文件名判断可能是临时文件,于是首先将上面的文件剪切出来,然后安装,成功。 转载于:https://www.cnblogs.com/x…

【软件开发底层知识修炼】六 Binutils辅助工具之- addr2line与strip工具

学习交流加 个人qq: 1126137994个人微信: liu1126137994学习交流资源分享qq群: 962535112 上一篇文章我们学习了gcc编译器的相关内容。点击查看上一篇文章:gcc编译器。本篇文章接着上一篇文章,学习GNU为GCC提供的辅助开…

Eboot 中给nandflash分区实现

提到分区就不得不提到MBR,不得不提到分区表。 什么是MBR 硬盘的0柱面、0磁头、1扇区称为主引导扇区,NANDFLASH由BLOCK和Sector组成,所以NANDFLASH的第0 BLOCK,第1 Sector为主引导扇区,FDISK程序写到该扇区的内容称为主…

kmp匹配算法

kmp匹配算法1.第一种方式是暴利匹配方式2.第二种方式采用kmp 方式进行匹配3. 相应的代码1.第一种方式是暴利匹配方式 暴利匹配规则 模型: str1 位源字符串下标为i,str2位匹配字符串,下标为j 。 假设 str1 匹配到i , str2 匹配到j 则有 (1)当 …

【软件开发底层知识修炼】七 Binutils辅助工具之- ar工具与nm工具

学习交流加 个人qq: 1126137994个人微信: liu1126137994学习交流资源分享qq群: 962535112 上一篇文章学习addr2line与strip工具。点击链接查看上一篇文章:点击查看 本篇文章学习两个工具:ar与nm工具。 文章目录1、ar工…

【软件开发底层知识修炼】八 Binutils辅助工具之- objdump工具 与 size,strings工具

上一篇文章学习了ar工具与nm工具,点击链接查看上一篇文章:点击链接 本片文章学习记录以下三个工具: objdumpsizestrings 1、objdump工具 用法: 反汇编目标文件,查看汇编到源码的映射(后面代码案例分析看具体区别&a…

【C++深度剖析教程39】实现C++数组类模板

上一篇文章在那个学习了多参数类模板与特化的分析:点击链接查看上一篇文章:类模板深度剖析 本篇文章学习记录: 数值型模板参数实现C数组类模板 1、模板中的数值型参数 模板参数可以是数值型参数。也就是非类型参数。如下图所示&#xff1…

前端学习(168)全局事件属性

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/ TR/html4/strict.dtd"> <html><head><meta http-equiv"content-type" content"text/html; charsetutf-8"><title>事件</ti…

【原】两个时间相加的运算符重载实现

要求&#xff1a;两个时间相加&#xff0c;得到的时间形如“xx时&#xff1a;xx分&#xff1a;xx秒” -------------------------------------------------------------------------------------------------------------- 解答&#xff1a; 首先编写时间处理类 时间类 1 ///&l…

IOT变现

IOT&#xff08;物联网&#xff09;变现1. IOT 设备基础结构2.IOT 组成元素3.从M2M扩展到IOT的世界4. 工业4.0最近在看IOT变现&#xff0c;记录一下核心的知识点概念&#xff1a;IOT 是利用无线标签&#xff0c;传感器&#xff0c;MEMS(micro-ElectroMechanical System&#xf…

【C++深度剖析教程40】使用数值型模板技术计算1+2+3+...+N的值

上一篇文章学习了数值型模板技术&#xff0c;并利用相关技术&#xff0c;实现了C的数组类模板。点击文章查看上一篇文章&#xff1a;点击链接查看 本篇文章&#xff0c;继续利用模板技术来解决一个问题。 如果想求123…N的结果&#xff0c;有很多种方法。可以循环遍历&#x…

前端学习(169):无语义元素

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/ html4/strict.dtd"> <html><head><meta http-equiv"content-type" content"text/html; charsetutf-8"><title>无语义标签&…

dubbo思维导图

dubbo思维导图之前总结的 后续持续更新中

前端学习(170):无语义元素二

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/ html4/strict.dtd"> <html><head><meta http-equiv"content-type" content"text/html; charsetutf-8"><title>DIVCSS布局&…

【软件开发底层知识修炼】九 链接器-可重定位文件与可执行文件

上几篇文章学习了Binutils辅助工具里面的几个实用的工具&#xff0c;那些工具对于以后的学习都是非常有帮助的&#xff0c;尤其是C语、C语言的学习以及调试是非常有帮助的。点击链接查看上一篇文章&#xff1a;点击查看 本篇文章开始一个新的知识的学习&#xff0c;链接器的学习…