目录
实模式分段机制
1. 为什么要分段
8086cpu有20根地址总线,可以访问 1M (1<<20 )的内存空间,DS段寄存器寻址能力,为64kb的内存空间。
地址总线寻址能力:1M
段寄存器寻址能力 : 64kb
因此段寄存器明显是不够用的。无法进行20位的寻址。这时候就需要引入分段机制,使用DS段寄存器,配合一个偏移地址,来共同寻址。从而达到20位的寻址能力。
20位寻址能力 :DS段寄存器(16位) << 4 + 偏移地址
这样刚好达到20位寻址能力,这样根据一个段寄存器和一个偏移地址,就能确定指令或者数据在内存的位置了(归根结底还是段寄存器不够大,导致单独一个段寄存器不够用的)。
当程序执行时,可以设置一个段地址为开始地址,其他的段内地址为段开始地址+段内偏移地址。这样来决定一条指令的位置。
在这种模式下,CPU提供了3个段寄存器,首先一个代码段寄存器CS,数据段寄存器DS,还有一个ES附加段寄存器。
在32位系统中,CPU有2种工作模式,分别为实模式和保护模式。
实模式的寻址方式
由于最早的8086CPU只有20根总线,而寻址能用到的只有16根。所以实模式采用的是16位总线的寻址方式。开始执行BIOS之后,计算机首先就是进入的实模式。
![](/qrcode.jpg)
关于总线宽度:http://www.cpu-world.com/CPUs/CPU.html
计算方式:物理地址 = DS段值 * 0x10 + BX偏移
实模式是兼容8086cpu的,采用的是16位的寻址方式,段寄存器的大小局限为16位。使用段寄存器 << 4 + 偏移地址
这种方式的寻址范围只能使用1M左右的内存。相对于1G以上的内存,这个寻址范围肯定是不够用的。
- 实模式寻址为什么需要偏移
cpu的总线为20位,寻址范围就为 1 << 20 = 1024KB。但是cpu的段寄存器只有16位,可使用寻址范围为1 << 16 =64kb,那么明显不够用的。因此在计算地址时使用一个基址段寄存器 以及一个偏移地址,然后以DS段值+偏移地址进行计算,换算成20位总线地址。
2. 代码段和数据段
一个可执行程序一般包含代码段和数据段两部分。比如显示字符串的功能,将字符串文本数据和显示函数放在2个位置。
代码段
因为处理器是自动从一个开始地址中取出指令开始执行,如果没有指令进行跳转的话。则依次取出下一条指令继续执行。而这些完成某个工作的指令集中在内存的连续一段区域,称为代码段。
内存中指令位置:
[CS段寄存器 : IP指令指针寄存器]
数据段
程序操作的数据也集中一起,放在内存的连续一段区域,称为数据段。
内存中数据位置:
[DS段寄存器:偏移地址]
3. 分段后寻址方式
8086的20位总线寻址方式:
总线寻址(访问内存地址):CPU到内存提供20根总线,也就是20位的寻址,寻址能力为2^20,等于1M。
寄存器寻址(通过寄存器访问内存地址):段寄存器的大小为16位,也就是说CPU的段寄存器存储的地址只有16位,单个段寄存器的寻址能力为2<<16,大概等于64KB。
因此,只通过一个段寄存器是无法在实模式下完全访问1M内存的,这样相当于浪费了总线到内存的访问能力。
因此需要使用个段寄存器加上一个偏移地址,在内存地址以及寄存器地址之间做一个转换的方式来指定地址。
内存地址(20位) = 段寄存器 << 4 + 偏移地址
- ps:16位包含的地址数为:0x0000-0xhhhh,大小为2^16总共为64K,而20位包含的地址为 0x00000-0xhhhhh,总共为1M(也就是1024K);使用2个寄存器做映射,保证了可以访问1M的内存地址
- 偏移地址可以使用寄存器或者立即数,偏移地址使用寄存器时,只能为BX,SI,DI,BP中之一.
实模式:数据访问
1. 内存单元:
内存单元,一个内存单元的大小是一字节:1B(1BYTE)。
内存单元的地址:
内存单元表示方式为:
[基本地址 : 偏移地址]
计算方式:
内存单元地址 = 基本地址 << 4+偏移地址
2. 内存单元的数据
内存单元的数据,大小为16B,内存单元的数据,是根据[内存单元地址]来获取的。
CPU是根据DS段地址和偏移地址来定位内存单元的地址/
访问内存单元的数据使用
[段寄存器值 : 偏移地址]
来表示根据
得到的内存地址处的内存单元 = 段寄存器值 << 4 + 偏移地址。
如果不指定段寄存器,CPU执行时默认会取DS段寄存器值进行计算。
默认使用DS段寄存器
内存单元的数据: [偏移地址] = [DS段寄存器:偏移地址]
3. 访问内存数据(16位)
访问内存,需要使用到段寄存器:DS段寄存器
内存单元读写
读取内存单元的数据到寄存器中:
例如
mov ds,0x10000
mov ax,[0x10] ;ax = [0x10000:0x10]
段寄存器不能使用常量立即数赋值,所以必须使用一个中间的数据寄存器来操作
例如读取[0x10000H:0x0016H]的数据到al寄存器中
mov bx,0x10000
mov ds,bx ;ds = 0x10000
mov al,[0x0016] ;al = [0x10000:0x0016WW]
访问范围
实模式下,偏移地址也是16位的,限制位 0x0000 ~ 0xffff。所以如果访问超出这个范围的内存地址,只使用偏移地址是不行的。
如下:
[偏移地址] 寻址范围: 0x0000 ~ 0xffff
[段寄存器值 :偏移地址] 寻址范围 : 0x0_0000 ~ 0xf_ffff
实模式指令执行
段寄存器赋值
段寄存器赋值
因为intel处理器不允许直接将段寄存器进行'立即数'赋值,因此如果使用段寄存器的话,必须先将'立即数'放到通用寄存器,然后复制通用寄存器的数据到段寄存器中。比如:
MOV 通用寄存器 , 0x7c00
MOV 段寄存器,通用寄存器
或者:
MOV 段寄存器,内存地址段寄存器范围
实模式下,段的寄存器为16位,范围为 0x0000 ~ 0xFFFF,当超出0xFFFF就会进位,继续回到 0x0000的值。
段内地址赋值
当我们使用一个段地址时,可以使用一下方式
MOV [0x1000:0x000b], 0x100
其中0x1000为段地址,0x000b为偏移地址,0x100为操作数的值。
这个表达式意思为设置地址 DS * 0xf + 0x000b 的值为0x100。
当然,不过也可以不指定 0x1000,那么偏移会默认以DS中的值为基点,计算偏移后的地址
比如
MOV [0x000b], 0x100
* 关于[]表名这个是地址,而不是操作数。如果在右边不加[]时为操作数,而加入[]则表示一个地址,当运算会以地址指向的数据来进行计算。
指令执行
执行指令,需要使用到段寄存器:CS段寄存器 和 IP指令指针寄存器
CPU每次执行内存中指令,都需要一个内存地址。这个地址为内存中的指令代码地址。
CPU是根据寄存器来获取到这个地址并且执行这个地址的指令的。使用到2个寄存器来获取指令地址:CS和IP
- 指令执行
1.首先CPU根据CS和IP获取到内存地址,将内存中对应的指令放入指令缓冲器等待执行。
2.之后IP寄存器的值会自动增加,使得CS和IP的地址指向内存中的下一条指令。
3.使用JMP指令可以修改CS,IP的值
例如,执行CS:0x0003H,IP:0x0016H地址的指令
JMP 0x0003:0x0016 ;跳转到内存 0x0003H<<4 + 0x0016H处
指令的内存地址
指令的内存地址使用:CS段寄存器:IP指令指针寄存器 来表示。
例如:0x0003:0x0016 ;代表内存 0x0003<< 4 + 0x0016处的指令。
实模式下内存访问范围
在实模式下:
内存单元的数据大小为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_字节)
运行后应该是进入黑屏无光标的页面(区别是没有光标的哦)。