保护模式下寻址

合集下载

内存寻址的三种模式

内存寻址的三种模式

内存寻址的三种模型1. 地址的种类首先明确一下逻辑地址和线性地址这两个概念:1. 逻辑地址2. 线性地址3. 物理地址1.1 逻辑地址:逻辑地址是编译器生成的,我们使用在linux环境下,使用C语言指针时,指针的值就是逻辑地址。

对于每个进程而言,他们都有一样的进程地址空间,类似的逻辑地址,甚至很可能相同。

1.2 线性地址:线性地址是由分段机制将逻辑地址转化而来的,如果没有分段机制作用,那么程序的逻辑地址就是线性地址了。

1.3 物理地址物理地址是CPU在地址总线上发出的电平信号,要得到物理地址,必须要将逻辑地址经过分段,分页等机制转化而来。

2. 三种寻址模型x86体系结构下,使用的较多的内存寻址模型主要有三种:1. 实模式扁平模型real mode flat model2. 实模式分段模型real mode segment model3. 保护模式扁平模型protected mode flat model下面是对这三种模型的描述实模式和保护模式相对,实模式运行于20位地址总线,保护模式则启用了32位地址总线,地址使用的是虚拟地址,引入了描述符表;虽然二者都引入了段这样一个概念,但是实模式的段是64KB固定大小,只有16KB个不同的段,CS,DS等存储的是段的序号(想想为什么?)。

保护模式则引入了GDT和LDT段描述符表的数据结构来定义每个段。

扁平模型和分段模型相对,区别在于程序的线性地址是共享一个地址空间还是需要分成多个段,即为多个程序同时运行在同一个CS,DS的范围内还是每个程序都拥有自己的CS,DS:前者(flat)指令的逻辑地址要形成线性地址,不需要切换CS,DS;后者的逻辑地址,必须要经过段选择子去查找段描述符,切换CS,DS,才能形成线性地址。

3. 实模式扁平模型该模式只有在386及更高的处理器中才能出现!80386的实模式,就是指CPU可用的地址线只有20位,能寻址0~1MB的地址空间。

保护模式下寻址(易懂)

保护模式下寻址(易懂)

保护模式下寻址(易懂)保护模式下寻址(易懂):网上看到的一强帖,不转不行了,牛人啊,把这段代码拿捏的相当到位括号中是我的加注段机制轻松体验[内存寻址]实模式下的内存寻址:让我们首先来回顾实模式下的寻址方式段首地址×16+偏移量=物理地址为什么要×16?因为在8086CPU中,地址线是20位,但寄存器是16位的,最高寻址64KB,它无法寻址到1M内存。

于是,Intel设计了这种寻址方式,先缩小4位成16位放入到段寄存器,用到时候,再将其扩大到20位,这也造成了段的首地址必须是16的倍数的限制。

保护模式下分段机制的内存寻址:保护模式下分段机制是利用一个称作段选择符的偏移量,从而到描述符表找到需要的段描述符,而这个段描述符中就存放着真正的段的物理首地址,再加上偏移量一段话,出现了三个新名词:1、段选择子2、描述符表3、段描述符我们现在可以这样来理解这段话:有一个结构体类型,它有三个成员变量:段物理首地址段界限段属性内存中,维护一个该结构体类型的是一个数组。

而分段机制就是利用一个索引,找到该数组对应的结构体,从而得到段的物理首地址,然后加上偏移量,得到真正的物理地址。

公式:xxxx:yyyyyyyy其中,xxxx也就是索引,yyyyyyyy是偏移量(因为32位寄存器,所以8个16进制)xxxx存放在段寄存器中。

现在,我们来到过来分析一下那三个新名词。

段描述符,一个结构体,它有三个成员变量:1、段物理首地址2、段界限3、段属性我们再来重温一遍描述符表,也就是一个数组,什么样的数组呢?是一个段描述符组成的数组。

接下来看看段选择子:段选择子,也就是数组的索引,但这时候的索引不在是高级语言中数组的下标,而是我们将要找的那个段描述符相对于数组首地址(也就是全局描述表的首地址)偏移位置。

就这么简单,如图:图中,通过Selector(段选择子)找到存储在Descriptor Table(描述符表)中某个Descriptor(段描述符),该段描述符中存放有该段的物理首地址,所以就可以找到内存中真正的物理段首地址SegmentOffset(偏移量):就是相对该段的偏移量物理首地址+偏移量就得到了物理地址本图就是DATA但这时,心细的朋友就发现了一个GDTR这个家伙还没有提到!我们来看一下什么是GDTR ?Global Descriptor Table Register(全局描述符表寄存器)但是这个寄存器有什么用呢?大家想一下,段描述符表现在是存放在内存中,那CPU是如何知道它在哪里呢?所以,Intel 公司设计了一个全局描述符表寄存器,专门用来存放段描述符表的首地址,以便找到内存中段描述符表。

asm 32位保护模式下的寻址方式

asm 32位保护模式下的寻址方式

32位保护模式下的寻址方式16位实模式下,一个物理地址由段地址和偏移地址两部分组成,段地址在16位段寄存器中,然后在指令中用16位偏移地址寻址。

物理地址=段地址*0x10+偏移地址32位保护模式下,问题变复杂了。

首先要明白,保护模式保护什么?保护的是:分清楚各个程序使用的存储区域,不允许随便跨界访问。

然后,怎么保护?方式是:为内存里的每段地址空间定义一些安全上的属性,比如可以被多少优先级的代码写入,是不是允许执行等。

这个时候,段寄存器远远不能满足要求了。

原因有二:段寄存器只有32位,保存不了这么多信息;段寄存器个数有限,不能保存内存中所有段的信息。

intel的工程师们就想出了用64位的段描述符表(descriptor table)来存储所有的段信息,段描述符表存放在内存的某个位置。

段寄存器不再表示段首地址了,而是表示这个段在段描述符表的索引信息。

通过段寄存器在段描述符表里找到关于这个段的所有信息。

但是,段描述符表不止一个。

首先有一个全局段描述符表,简称GDT,每个程序都有自己的段描述符表,简称LDT。

相应的,80386里面引入了两个新的寄存器,一个是48位的全局描述符表寄存器GDTR,指向全局描述符表GDT的首地址,一个是16位的局部描述符表寄存器LDTR,它的值随时变化,总是指向CPU当时正在执行的那个程序的局部描述符表LDT。

注意,这里不说指向LDT的首地址,是因为LDTR和CS、DS等段寄存器一样,存放的也是在GDT中的索引值,而不是实际地址。

那么段寄存器里的索引到底是GDT的索引还是LDT得索引呢?下面是实模式下段寄存器的结构:TI位为0 表示从全局描述符表中找TI位为1 表示从局部描述符表中找32位CPU依然兼容实模式,但此时的32位实模式寻址和16位实模式寻址不同。

以实模式下的虚拟地址xxxx:yyyyyyyy(16位段地址,32位偏移)为例,首先看xxxx的TI位,如果为0,那么在GDT中以xxxx的高13位作为索引找出段描述符,这样就得到了段基址、段限长、优先级等信息。

微机原理 概念指令理解

微机原理 概念指令理解

1.Call 指令:两步操作a.将程序当前执行的IP位置压入堆栈。

一般来说,执行一条CALL指令相当于执行一条PUSH指令加一条JMP指令。

2. Ret指令:当执行到RET时,堆栈中的IP出栈,LCALL的继续执行下一条语句。

Sp-2=Sp, 从堆栈中退出pc的高8位和低8位字节,把堆栈指针减2,从pc值开始继续执行程序。

不影响任何标志。

3.宏指令和子程序:a.在源程序中,通过书写宏名来引用宏,而子程序是通过CALL指令来调用; b.宏调用是通过宏扩展来实现的,宏引用多少次,就相应扩展多少次,所以,引用宏不会缩短目标程序;而子程序代码在目标程序中只出现一次,调用子程序是执行同一程序段,因此,目标程序也得到相应的简化子程序优点:模块化,节省内存,可被多次调用,编程效率高。

缺点:额外开销(保存返回地址,计算转向地址,传递参数等)大,增加了执行时间。

适用于子功能代码较长、调用比较频繁的情况。

宏调用:优点:参数传送简单,执行效率高。

缺点:不节省空间,适用于子功能代码较短、传参较多的情况。

4.三态门缓冲器:受到使能输出端控制,当使能端输出有效时,器件实现正常逻辑状态输出(逻辑0 ,1),当使能输入无效时,输出处于高阻状态。

缓冲器是数字元件一种,它对输入值不执行任何运算。

5.实地址模式:实模式,是指寻址采用和8086相同的16位段和偏移量,最大寻址空间1MB,最大分段64KB。

可以使用32位指令。

32位的x86 CPU用做高速的8086。

6.保护模式:保护模式:寻址采用32位段和偏移量,最大寻址空间4GB,最大分段4GB(Pentium Pre及以后为64GB)。

在保护模式下CPU可以进入虚拟8086方式,这是在保护模式下的实模式程序运行环境。

实地址和保护模式最大区别我认为最大的是地址转换方式变化很大.7.。

Gate A20与保护模式

Gate A20与保护模式

Gate A20与保护模式大家都知道,8088/8086只有20位地址线,按理它的寻址空间是2^20,应该是1024KB,但PC机的寻址结构是segment:offset,segment和offset都是16位的寄存器,最大值是0ffffh,换算成物理地址的计算方法是把segment左移4位,再加上offset,所以segment:offset所能表达的寻址空间最大应为0ffff0h +0ffffh = 10ffefh(前面的0ffffh是segment=0ffffh并向左移动4位的结果,后面的0ffffh是可能的最大offset),这个计算出的10ffefh是多大呢?大约是1088KB,就是说,segment:offset的地址表达能力,超过了20位地址线的物理寻址能力,你说这是不是有点麻烦。

在早先,由于所有的机器都没有那么大的内存,加上地址线只有20位,所以当你用segment:offset的方式企图寻址100000h这个地址时,由于没有实际的第21位地址线,你实际寻址的内存是00000h的位置,如果你企图寻址100001h这个地址时,你实际得到的内容是地址00001h上的内容,所以这个事对实际使用几乎没有任何影响,但是后来就不行了,出现了80286,地址线达到了24位,使segment:offset寻址100000h--10ffefh这将近64K的存储器成为可能,为了保持向下兼容,于是出现了A20 Gate,这是后话,我们后面再细说。

我们可能经常听到一些只有在PC机上才有的一些关于存储器的专有名词,包括:常规内存(Conventional Memory)、上位内存区(Upper Memory Area)、高端内存区(High Memory Area)和扩展内存(Extended Memory),我尽量把这几个东东说明白,这需要下面这张著名的图。

这张图很清楚地说明了问题,大家都知道,DOS下的“常规内存”只有640K,这640K就是从0--A0000H这段地址空间;所谓“上位内存区”,指的就是20位地址线所能寻址到的1M地址空间的上面384K空间,就是从A0001H--100000H 这段地址空间,也就是我们说的用于ROM和系统设备的地址区域,这384K空间和常规内存的640K空间加起来就是20位地址线所能寻址的完整空间1024KB;由于80286和80386的出现使PC机的地址线从20位变成24位又变成32位,寻址能力极大地增加,1M以上的内存寻址空间,我们统称为“扩展内存”;这里面绝大部分内存区域只能在保护模式下才能寻址到,但有一部分既可以在保护模式下,也可以在实模式下寻址,这就是我们前面提到过的地址100000h--10ffefh之间的这块内存,为了表明其特殊性,我们把这块有趣的内存区叫做“高端内存”。

第4章指令系统层习题参考解答-汇编语言与计算机组成原理 答案

第4章指令系统层习题参考解答-汇编语言与计算机组成原理 答案

1.什么是“程序可见”的寄存器?程序可见寄存器是指在用户程序中用到的寄存器,它们由指令来指定。

2. 80x86微处理器的基本结构寄存器组包括那些寄存器?各有何用途?基本结构寄存器组按用途分为通用寄存器、专用寄存器和段寄存器3类。

通用寄存器存放操作数或用作地址指针;专用寄存器有EIP和EFLAGS,分别存放将要执行的下一条指令的偏移地址和条件码标志、控制标志和系统标志;段寄存器存放段基址或段选择子。

3.80x86微处理器标志寄存器中各标志位有什么意义?常用的7位:CF进位标志: 在进行算术运算时,如最高位(对字操作是第15位,对字节操作是第7位)产生进位或借位时,则CF置1;否则置0。

在移位类指令中,CF用来存放移出的代码(0或1)。

PF奇偶标志: 为机器中传送信息时可能产生的代码出错情况提供检验条件。

当操作结果的最低位字节中1的个数为偶数时置1,否则置0。

AF辅助进位标志: 在进行算术运算时,如低字节中低4位(第3位向第4位)产生进位或借位时,则AF置1;否则AF置0。

ZF零标志:如指令执行结果各位全为0时,则ZF置1;否则ZF置0。

SF符号标志:其值等于运算结果的最高位。

如果把指令执行结果看作带符号数,就是结果为负,SF置1;结果为正,SF置0。

OF溢出标志: 将参加算术运算的数看作带符号数,如运算结果超出补码表示数的范围N,即溢出时,则OF置1;否则OF置0。

DF方向标志: 用于串处理指令中控制处理信息的方向。

当DF位为1时,每次操作后使变址寄存器SI和DI减小;当DF位为0时,则使SI和DI增大,使串处理从低地址向高地址方向处理。

4.画出示意图,简述实模式下存储器寻址的过程。

20位物理地址如下计算(CPU中自动完成):10H×段基址+偏移地址=物理地址5. 画出示意图,简述保护模式下(无分页机制)存储器寻址的过程。

采用对用户程序透明的机制由选择子从描述子表中选择相应的描述子,得到欲访问段的段基址、段限等有关信息,再根据偏移地址访问目标存储单元。

第2章寻址方式

第2章寻址方式
22
第十九页,编辑于星期二:十九点 四十一分。
寄存器间接寻址方式举例 1
例9. MOV AX,[BX]
; AX ? (DS:[BX] )
注意它与寄存器寻址方式在形式上的区别:
MOV AX ,BX
;(AX) ? (BX)
源操作数的物理地址为:
PA = (DS) × 10H +(BX) 若(DS)= 3000H,(BX)= 78H, (30078H)= 12H 则:PA =3000H ×10H+78H =30078H
找到操作数 ;
? 数据寻址方式以 MOV DST ,SRC 为例讨论。
7
第六页,编辑于星期二:十九点 四十一分。
计算机中操作数保存的地点
1.指令中 2.CPU 的寄存器中 3.存储器中 4.I/O接口寄存器中
4. 在访问I/O 的专用 指令中指定接口中寄 存器的端口号。
1.由程序员直接写在指令中
2. 在指令中指定寄存器名
地址是什么。。。
?操作码 域:指定要完成的操作。
?操作数 2:源操作数 ,表示参与指令操作的一个对象;
?操作数 1:目的操作数 ,它不仅可以作为指令操作的一
个对象,还可以用来存放指令操作的结果。
?分号后的内容是对指令的解释。
例: MOV AH , 10
; (AH)= 10
2
第二页,编辑于星期二:十九点 四十一分。
即用寄存器存放源或目的操作数。
? 存储器操作数
存储器操作数是指操作数存放在主存储器中。
因此在汇编指令中应给出的是存储器的地址。
5
第四页,编辑于星期二:十九点 四十一分。
指令操作数的表达
? r8 —— 任意一个 8位通用寄存器 ? r16—— 任意一个 16位通用寄存器 ? reg—— 代表r8或r16 ? seg—— 段寄存器 CS/DS/ES/SS ? m8 —— 一个 8位存储器操作数单元 ? m16—— 一个16位存储器操作数单元 ? mem—— 代表 m8或m16 ? i8 —— 一个 8位立即数 ? i16 —— 一个 16位立即数 ? imm —— 代表i8或 i16 ? dst /src —— 目的操作数 / 源操作数

Linux0.11——从实模式到保护模式

Linux0.11——从实模式到保护模式

Linux0.11——从实模式到保护模式综述最近在阅读Linux 0.11的源码时,对于setup.s⽂件中设置GDT表的部分不是很理解,后来经过刘国军⽼师的指点,结合赵炯博⼠的《Linux内核完全注释》的第四章《80X86保护模式及其编程》,对于保护模式有了⼀些粗浅的了解和认识。

备忘。

本⽂章主要讲解保护模式的寻址机制与setup.s中的切换部分。

保护模式保护模式运⾏在80286及其之后的所有CPU上,但是为了保证向前兼容性,正常的CPU在启动时并不会默认进⼊保护模式,⽽是会进⼊实模式,随后通过⼀系列设定转⼊保护模式。

在16位实模式下,CPU寻址时使⽤16位段寄存器的内容乘以16当作段基地址,加上16位段偏移地址形成20位的物理地址,所以最⼤寻址仅为1MB字节,最⼤段长度64KB。

在实模式下,所有的段都是可以任意访问的,即任意读、写和执⾏。

虽然在80286点CPU上已经出现了保护模式,但是其寄存器的位宽仍然是16位,只不过其地址线由20位扩⼤到了24位,寻址空间随即扩⼤到了16MB。

真正的32位保护模式出现在80386上,其地址总线和寄存器都是32位宽的,因此寻址空间扩⼤到了4GB。

保护模式下,CPU寻址主要有两种模式,⼀是分段模式,⼆是分段和分页相结合,分页⽆法单独出现。

保护模式的分段模式为每⼀段增加了段属性来限制⽤户程序对内存中⼀些段的操作。

在全局描述符表(GDT)中,每个段的表项存储了⼀个段的基本属性,例如段的基地址、段的界限、段的类型(代码段、数据段)、段的执⾏权限等。

分页模式的出现使得程序员可以编写远远⼤于内存的程序⽽⽆需担⼼内存的容量,在该模式下,内存被划分为“页”存储,磁盘的⼀部分⽤作虚拟内存,当应⽤程序执⾏时需要的某些代码或数据所在的页不在内存中时,CPU就会产⽣⼀个缺页异常,从磁盘中将所需的页调⼊内存中后恢复执⾏,在应⽤程序看来,所需的代码或数据仿佛⼀直存在内存上。

重要数据结构在保护模式中,有⼏个长得很像的名字⼀直是我们⼼头噩梦:GDT、GDTR、LGDT、LDT、LDTR、LLDT……事实上,并不是那么难区分。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

保护模式下寻址网上看到的一强帖,不转不行了,牛人啊,把这段代码拿捏的相当到位括号中是我的加注段机制轻松体验[内存寻址]实模式下的内存寻址:让我们首先来回顾实模式下的寻址方式段首地址×16+偏移量=物理地址为什么要×16?因为在8086CPU中,地址线是20位,但寄存器是16位的,最高寻址64KB,它无法寻址到1M 内存。

于是,Intel设计了这种寻址方式,先缩小4位成16位放入到段寄存器,用到时候,再将其扩大到20位,这也造成了段的首地址必须是16的倍数的限制。

保护模式下分段机制的内存寻址:保护模式下分段机制是利用一个称作段选择符的偏移量,从而到描述符表找到需要的段描述符,而这个段描述符中就存放着真正的段的物理首地址,再加上偏移量一段话,出现了三个新名词:1、段选择子2、描述符表3、段描述符我们现在可以这样来理解这段话:有一个结构体类型,它有三个成员变量:段物理首地址段界限段属性内存中,维护一个该结构体类型的是一个数组。

而分段机制就是利用一个索引,找到该数组对应的结构体,从而得到段的物理首地址,然后加上偏移量,得到真正的物理地址。

公式:xxxx:yyyyyyyy其中,xxxx也就是索引,yyyyyyyy是偏移量(因为32位寄存器,所以8个16进制)xxxx存放在段寄存器中。

现在,我们来到过来分析一下那三个新名词。

段描述符,一个结构体,它有三个成员变量:1、段物理首地址2、段界限3、段属性我们再来重温一遍描述符表,也就是一个数组,什么样的数组呢?是一个段描述符组成的数组。

接下来看看段选择子:段选择子,也就是数组的索引,但这时候的索引不在是高级语言中数组的下标,而是我们将要找的那个段描述符相对于数组首地址(也就是全局描述表的首地址)偏移位置。

就这么简单,如图:图中,通过Selector(段选择子)找到存储在Descriptor Table(描述符表)中某个Descriptor(段描述符),该段描述符中存放有该段的物理首地址,所以就可以找到内存中真正的物理段首地址SegmentOffset(偏移量):就是相对该段的偏移量物理首地址+偏移量就得到了物理地址本图就是DA TA 但这时,心细的朋友就发现了一个GDTR这个家伙还没有提到!我们来看一下什么是GDTR ?Global Descriptor Table Register(全局描述符表寄存器)但是这个寄存器有什么用呢?大家想一下,段描述符表现在是存放在内存中,那CPU是如何知道它在哪里呢?所以,Intel 公司设计了一个全局描述符表寄存器,专门用来存放段描述符表的首地址,以便找到内存中段描述符表。

这时,段描述符表地址被存到GDTR寄存器中了。

好了,分析就到这,我们来看一下正式的定义:当x86 CPU 工作在保护模式时,可以使用全部32根地址线访问4GB的内存,因为80386的所有通用寄存器都是32位的,所以用任何一个通用寄存器来间接寻址,不用分段就可以访问4G空间中任意的内存地址。

也就是说我们直接可以用Eip寄存器就可以找到茫茫内存里面所有的值!但这并不意味着,此时段寄存器就不再有用了[其实还有部分原因是要与8086兼容] 。

实际上,段寄存器更加有用了,虽然再寻址上没有分段的限制了,但在保护模式下,一个地址空间是否可以被写入,可以被多少优先级的代码写入,是不是允许执行等等涉及保护的问题就出来了。

[想想吧,单单就是靠eip找到所有内存的值显然不够的,醒醒吧,我们到了80386时代了,我们需要保护模式,要指示出来那些内存段是操作系统核心用的,那些是你打游戏时用的,打游戏时的cpu不能访问到操作系统核心所用的内存段。

我们需要分出"级别"来] 。

要解决这些问题,必须对一个地址空间定义一些安全上的属性。

段寄存器这时就派上了用场。

但是设计属性和保护模式下段的参数,要表示的信息太多了,要用64位长的数据才能表示。

我们把着64位的属性数据叫做段描述符,上面说过,它包含3个变量:段物理首地址、段界限、段属性80386的段寄存器是16位(注意:通用寄存器在保护模式下都是32位,但段寄存器没有被改变,比如cs还是16位的,16位的段寄存器怎么可能装下一个64位的段描述符)的,无法放下保护模式下64位的段描述符。

如何解决这个问题呢?方法是把所有段的段描述符顺序存放在内存中的指定位置,组成一个段描述符表(Descriptor Table);而段寄存器中的16位用来做索引信息,这时,段寄存器中的信息不再是段地址了,而是段选择子(Selector)。

可以通过它在段描述符表中“选择”一个项目已得到段的全部信息。

也就是说我们在另一个地方把段描述符放好,然后通过选择子来找到这个段描述符。

那么段描述符表存放在哪里呢?80386引入了两个新的寄存器来管理段描述符,就是GDTR和LDTR,(LDTR 大家先忘记它,随着学习的深入,我们会在以后学习)。

这样,用以下几步来总体体验下保护模式下寻址的机制1、段寄存器中存放段选择子Selector2、GDTR中存放着段描述符表的首地址3、通过选择子根据GDTR中的首地址,就能找到对应的段描述符4、段描述符中有段的物理首地址,就得到段在内存中的首地址5、加上偏移量,就找到在这个段中存放的数据的真正物理地址。

=================================好的,那我们开始编码,看看如何实现先前描述的内容首先,既然我们需要一个数组,全局描述符表,那我们就定义一块连续的结构体:[SECTION .gdt] ;为了代码可读性,我们将这个数组放到一个节中;由一块连续的地址组成的,不就是一个数组吗?看下面代码,^_^段基地址段界限段属性GDT_BEGIN: Descriptor 0, 0, 0GDT_CODE32: Descriptor 0, 0, DA_C;上面,我定义了二个连续地址的结构体,大家先认为Descriptor就是一个结构体类型,我们会在以后详细讲述;第一个结构体,全部是0,是为了遵循Interl规范,先记得就OK;第二个定义了一个代码段,段基地址和段界限我们暂且还不知道,先初始化为0,但是因为是个代码段,代码段具备执行的属性,那么DA_C就代表是一个可执行代码段,DA_C是一个预先定义好的常量,我们会在详细讲解段描述符中讲解。

我们继续来实现,那么下面,我们就需要设计段选择子了,因为上面代码已经包含了段描述符和全局描述符表还记得选择子是个什么东西吗?段选择子:也就是数组的索引,但这时候的索引不在是高级语言中数组的下标,而是我们将要找的那个段描述符相对于数组首地址(也就是全局描述表的首地址)偏移位置。

看我代码怎么实现,包含以上代码不再说明:[SECTION .gdt]GDT_BEGIN: Descriptor 0, 0, 0GDT_CODE32: Descriptor 0, 0, DA_C;下面是定义代码段选择子,它就是相对数组首地址的偏移量SelectorCode32 equ GDT_CODE32 - GDT_BEGIN;因为第一个段描述符,不被使用,所以就不比设置段选择子了。

=================================偏移地址:注意一点,我们在程序中使用的都是偏移地址,相对于段的偏移地址,用上面的例子来说,象GDT_CODE32 GDT_BEGIN 这些结构体的首地址都是相对于数据段的偏移量。

什么意思呢?因为我们的程序到底加载到内存的哪个地方是不固定,不知道的,只需使用偏移地址操作就行了,如:SelectorCode32 ,它本身就是一个偏移地址但是SelectorCode32 equ GDT_CODE32 - GDT_BEGIN怎么解释呢?GDT_CODE32是相对于数据段的偏移量,GDT_BEGIN也是相对于数据段的偏移量,虽然它是数组的首地址,说的罗索一些,GDT_BEGIN是数组的首地址,但是它是相对于数据段的偏移量那么两个偏移量相减就是GDT_CODE32 相对于GDT_BEGIN的偏移量所以,我们要时时刻刻记得,在程序中,我们永远使用的是偏移量,因为我们不知道程序将要被加载内存那块地方。

好了,基础也学的差不多了,下面我们要自己动手写一段程序,实现实模式到保护模式之间的跳转============================================================= ========;实现从实模式到保护模式之间的跳转;参考:《自己动手写操作系统》----------------------------------------------------------------------%include "pm.inc"org 0100hjmp LABEL_BEGIN[SECTION .gdt]GDT_BEGIN: Descriptor 0, 0, 0GDT_CODE32: Descriptor 0, LenOfCode32 - 1, DA_C + DA_32GDT_VIDEO: Descriptor 0B8000H, 0FFFFH, DA_DRWGdtLen equ $ - GDT_BEGINGdtPtr dw GdtLen - 1dd 0;定义段选择子SelectorCode32 equ GDT_CODE32 - GDT_BEGINSelectorVideo equ GDT_VIDEO - GDT_BEGIN[SECTION .main][BITS 16]LABEL_BEGIN:mov ax, csmov ds, axmov es, axmov ss, ax;初始化32位代码段选择子;我们可以在实模式下通过段寄存器×16 +偏移两得到物理地址,;那么,我们就可以将这个物理地址放到段描述符中,以供保护模式下使用,;因为保护模式下只能通过段选择子+偏移量xor eax, eaxmov ax, csshl eax, 4add eax, LABEL_CODE32mov word [GDT_CODE32 + 2],axshr eax, 16mov byte [GDT_CODE32 + 4],almov byte [GDT_CODE32 + 7],ah;得到段描述符表的物理地址,并将其放到GdtPtr中xor eax, eaxmov ax, dsshl eax, 4add eax, GDT_BEGINmov dword [GdtPtr + 2],eax;加载到gdtr,因为现在段描述符表在内存中,我们必须要让CPU知道段描述符表在哪个位置;通过使用lgdtr就可以将源加载到gdtr寄存器中lgdt [GdtPtr];关中断cli;打开A20线in al, 92hor al, 00000010bout 92h, al;准备切换到保护模式,设置PE为1mov eax, cr0or eax, 1mov cr0, eax;现在已经处在保护模式分段机制下,所以寻址必须使用段选择子:偏移量来寻址;跳转到32位代码段中;因为此时偏移量位32位,所以必须dword告诉编译器,不然,编译器将阶段成16位jmp dword SelectorCode32:0;跳转到32位代码段第一条指令开始执行[SECTION .code32][BITS 32]LABEL_CODE32:mov ax, SelectorVideomov es, axxor edi, edimov edi, (80 * 10 + 10)mov ah, 0chmov al, 'G'mov [es:edi],axjmp $LenOfCode32 equ $ - LABEL_CODE32这段代码的大概意思是:先在16位代码段,实模式下运行,在实模式下,通过段寄存器×16+偏移量得到32位代码的真正物理首地址,并将放入到段描述符表中,以供在保护模式下使用,上面说过了,保护模式下寻址,是通过段选择子,段描述符表,段描述符一起工作寻址的。

相关文档
最新文档