博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
3.3 实模式
阅读量:5081 次
发布时间:2019-06-13

本文共 3645 字,大约阅读时间需要 12 分钟。

目录

实模式

实模式分段机制

1. 实模式

在32位系统中,CPU有2种工作模式,分别为实模式和保护模式。当电脑一开始启动时,就默认进入实模式下。

2. 为什么要分段

8086cpu有20根地址总线。使用20根CPU总线的寻址范围就为 1<<20 = 1M 。

而段寄存器(例如DS段寄存器),只有16位大小。如果只使用单独的段寄存器寻址,使用寻址范围为1<<16 = 64kb 。

CPU总线总线寻址能力:1<<20 = 1M

段寄存器寻址能力 : 1<<16 = 64kb

这此时引入分段机制来解决这个问题,使用一个基址段寄存器,配合一个偏移地址,来配合使用共同寻址。从而达到20位的寻址能力(归根结底还是段寄存器不够大,导致单独一个段寄存器不够用的)。使用DS段值+偏移地址进行计算,换算成20位总线地址。

CPU总线寻址: 20位寻址能力 段寄存器址: 段寄存器(16位) << 4 +  偏移地址(16位) = 总线寻址能力(20位)

使用一个段寄存器存储基址,配合一个数据寄存器存储偏移地址,来配合使用共同寻址,刚好达到20位寻址能力。

在这种模式下,CPU提供了3个段寄存器:

段寄存器:

  • 代码段寄存器CS:用于指令寻址
  • 数据段寄存器DS:用于数据寻址
  • 附加段寄存器ES:用于其他寻址、

偏移地址:

- 寄存器:  偏移地址使用寄存器时,只能为BX,SI,DI,BP中之一.- 立即数: 使用立即数作为为偏移地址

3. 代码段和数据段

一个可执行程序一般包含代码段和数据段两部分。比如显示字符串的功能,将字符串文本数据和显示函数放在2个位置。当程序执行时,可以设置一个段地址为段基址,其他的段内地址为段基址+段内偏移地址。这样来决定一条指令的位置。

  1. 代码段

    因为处理器是自动从一个开始地址中取出指令开始执行,如果没有指令进行跳转的话。则依次取出下一条指令继续执行。而这些完成某个工作的指令集中在内存的连续一段区域,称为代码段。

    内存中指令位置:

    [CS段寄存器 : IP指令指针寄存器]

  2. 数据段

    程序操作的数据也集中一起,放在内存的连续一段区域,称为数据段。

    内存中数据位置:

    [DS段寄存器:偏移地址]

指令执行

段寄存器赋值

  1. 段寄存器赋值

    因为intel处理器不允许直接将段寄存器进行'立即数'赋值,因此如果使用段寄存器的话,必须先将'立即数'放到通用寄存器,然后复制通用寄存器的数据到段寄存器中。

    比如:

    MOV 通用寄存器 , 0x7c00
    MOV 段寄存器,通用寄存器
    或者:
    MOV 段寄存器,内存地址

  2. 段寄存器范围

    实模式下,段的寄存器为16位,范围为 0x0000 ~ 0xFFFF,当超出0xFFFF就会进位,继续回到 0x0000的值。

  3. 段内地址赋值

    当我们使用一个段地址时,可以使用一下方式

    MOV [0x1000:0x000b], 0x100

    其中0x1000为段地址,0x000b为偏移地址,0x100为操作数的值。

    这个表达式意思为设置地址 DS * 0xf + 0x000b 的值为0x100。

    当然,不过也可以不指定 0x1000,那么偏移会默认以DS中的值为基点,计算偏移后的地址

    比如

    MOV [0x000b], 0x100
* 关于[]表名这个是地址,而不是操作数。如果在右边不加[]时为操作数,而加入[]则表示一个地址,当运算会以地址指向的数据来进行计算。

指令执行

执行指令,需要使用到段寄存器:CS段寄存器 和 IP指令指针寄存器

CPU每次执行内存中指令,都需要一个内存地址。这个地址为内存中的指令代码地址。

CPU是根据寄存器来获取到这个地址并且执行这个地址的指令的。使用到2个寄存器来获取指令地址:CS和IP

  1. 指令执行

1.首先CPU根据CS和IP获取到内存地址,将内存中对应的指令放入指令缓冲器等待执行。

2.之后IP寄存器的值会自动增加,使得CS和IP的地址指向内存中的下一条指令。
3.使用JMP指令可以修改CS,IP的值

例如,执行CS:0x0003H,IP:0x0016H地址的指令

JMP 0x0003:0x0016    ;跳转到内存 0x0003H<<4 + 0x0016H处
  1. 指令的内存地址

    指令的内存地址使用:CS段寄存器:IP指令指针寄存器 来表示。
    例如:

    0x0003:0x0016 ;代表内存 0x0003<< 4 + 0x0016处的指令。

数据访问

1. 内存单元:

内存单元,一个内存单元的大小是一字节:1B(1BYTE)。

内存单元的地址:

内存单元表示方式为:

[基本地址 : 偏移地址]

计算方式:

内存单元地址 = 基本地址 << 4+偏移地址

2. 内存单元的数据

内存单元的数据,大小为16B,内存单元的数据,是根据[内存单元地址]来获取的。

CPU是根据DS段地址和偏移地址来定位内存单元的地址/

访问内存单元的数据使用

[段寄存器值 : 偏移地址]

来表示根据

得到的内存地址处的内存单元 = 段寄存器值 << 4 + 偏移地址。

如果不指定段寄存器,CPU执行时默认会取DS段寄存器值进行计算。

默认使用DS段寄存器

内存单元的数据: [偏移地址] = [DS段寄存器:偏移地址]

3. 访问内存数据(16位)

访问内存,需要使用到段寄存器:DS段寄存器

内存单元读写

读取内存单元的数据到寄存器中:

例如

mov ds,0x10000mov ax,[0x10]       ;ax = [0x10000:0x10]

段寄存器不能使用常量立即数赋值,所以必须使用一个中间的数据寄存器来操作

例如读取[0x10000H:0x0016H]的数据到al寄存器中

mov bx,0x10000mov ds,bx                   ;ds = 0x10000mov al,[0x0016]         ;al = [0x10000:0x0016WW]

访问范围

实模式下,偏移地址也是16位的,限制位 0x0000 ~ 0xffff。所以如果访问超出这个范围的内存地址,只使用偏移地址是不行的。

如下:

[偏移地址] 寻址范围: 0x0000 ~ 0xffff
[段寄存器值 :偏移地址] 寻址范围 : 0x0_0000 ~ 0xf_ffff

实模式下内存访问范围

在实模式下:

内存单元的数据大小为1个字节 (16B).

基本地址是使用一个16位的段寄存器的值,而偏移地址是一个16位的值。

如果没有显式说明段寄存器,默认基本地址使用DS段寄存器。

访问范围 0x0000 ~ 0x10FFEF (0xFFFF << 4 + 0xFFFF )

当程序访问0x100000~0x10FFEF这一段地址时,因为其逻辑上是正常的,CPU并不会认为其访问越界而产生异常,但这段地址确实没有实际的物理地址与其对应,会截取掉最高的一位,进行回绕访问。

实模式内存分配

首先我们需要对程序在内存中分配的位置有个大概的定义。

首先是实模式下的1M空间的内存,分配

这1M大小的内存区域位并不完全位于通常的内存条中,会被BIOS以及显卡等占据一部分。

其中0x00000~0x9FFFF位置是属于内存条的地址。

0xA0000~0xEFFFF提供给外围设备使用,例如显卡等。
而0xF0000~0xFFFFF是属于BIOS的ROM地址。

内存地址 空间大小 用途
0x00000 ~ 0x003FF 1KB 中断向量表
0x00400 ~ 0x004FF 256B BIOS数据区
0x00500 ~ 0x07BFF 大概30KB 可用区域
0x07C00 ~ 0x07DFF 512B MBR引导加载位置
0x07E00 ~ 0x9FBFF 大概608KB 可用区域
0x9FC00 ~ 0x9FFFF 1KB BIOS扩展数据区
--- --- ---
0xA0000 ~ 0xAFFFF 64KB 显示适配器-彩色模式
0xB0000 ~ 0xB7FFF 32KB 显示适配器-黑白模式
0xB8000 ~ 0xBFFFF 32KB 显示适配器-文本模式
0xC0000 ~ 0xC7FFF 32KB 显示适配器
0xC8000 ~ 0xEFFFF 160KB 硬件适配器ROM
0xF0000 ~ 0xFFFFF 64KB BIOS程序。BOIS入口地址为: 0xF0000-0xFFFFF

boot :0x07C00 ~ 0x07DFF 1个扇区(512_字节)

loader :0x90000 ~ 0x907FF 4个扇区(2048_字节)
kernel :0x10000 ~ 24个扇区(12288_字节)

运行后应该是进入黑屏无光标的页面(区别是没有光标的哦)。

转载于:https://www.cnblogs.com/mlzrq/p/10223044.html

你可能感兴趣的文章
POP的Stroke动画
查看>>
SQL语句在查询分析器中可以执行,代码中不能执行
查看>>
yii 1.x 添加 rules 验证url数组
查看>>
html+css 布局篇
查看>>
SQL优化
查看>>
用C语言操纵Mysql
查看>>
轻松学MVC4.0–6 MVC的执行流程
查看>>
redis集群如何清理前缀相同的key
查看>>
Python 集合(Set)、字典(Dictionary)
查看>>
获取元素
查看>>
proxy写监听方法,实现响应式
查看>>
第一阶段冲刺06
查看>>
十个免费的 Web 压力测试工具
查看>>
EOS生产区块:解析插件producer_plugin
查看>>
mysql重置密码
查看>>
jQuery轮 播的封装
查看>>
一天一道算法题--5.30---递归
查看>>
JS取得绝对路径
查看>>
排球积分程序(三)——模型类的设计
查看>>
python numpy sum函数用法
查看>>