第二章:IA-32处理器基本功能
目录
保护方式和实地址方式
(来源于此博客)
实地址方式——
8086中采用的是16位CPU,但地址总线是20位(采用和1M字节的内存地址空间),所以有些地址CPU访问不到,为了解决这个问题,在8086CPU中设置了四个“段寄存器”:CS、DS、SS、ES,分别用于指令、数据、堆栈和其他。每一个段寄存器都是16位的,对应于地址总线的高16位。
它有1MB寻址能力,那怎么用16位的段寄存器表示呢?
这就引出了分段的概念,8086CPU将1MB存储空间分成许多逻辑段,每个段最大限长为64KB(但不一定就是64KB)。这样每个存储单元就可以用“段基地址+段内偏移地址”表示。这样就实现了从16位内部地址到20位实际地址的转换(映射)。段基地址由16位段寄存器值左移4位表达,段内偏移表示相对于某个段起始位置的偏移量。
段寄存器<<4 + 逻辑地址(16位)= 线性地址 = 物理地址
但是这种操作是没有地址空间保护机制的,对于每一个由段寄存器的内容确定的基地址,一个进程总能访问从此开始的64K字节的联系地址空间,而无法加以限制。
保护方式——
现代操作系统在刚加电时首先运行在实模式下,然后再切换到保护模式下运行。
80386 采用32位CPU,那么他的ALU数据总线是32位的。最自然的地址总线宽度是与数据总线一致。当地址总线宽度达到32位时,其寻址能力达到了4G。因为之后的版本要兼容之前的,所以80386保留了之前的段寄存器,还必须支持实地址模式,与此同时还要能支持保护模式。
保护模式通过“段选择符+段内偏移”寻址最终的线性地址或物理地址。
逻辑地址/线性地址/物理地址
逻辑地址:在有地址变换功能的计算机中,访内指令给出的地址 (操作数) 叫逻辑地址,也叫相对地址。要经过寻址方式的计算或变换才得到内存储器中的实际有效地址,即物理地址。(在某些地方,也叫做有效地址,段内偏移地址。)
线性地址:是逻辑地址到物理地址变换之间的中间层。在分段部件中逻辑地址是段中的偏移地址,然后加上基地址就是线性地址。如果不启用分页,那么此线性地址即物理地址。简单来说,就是保护模式下的“段基址+偏移量”。
物理地址:存储空间的实际地址。线性地址经分页转换后就成了物理地址。
可以简单地认为,在保护方式下,物理地址是32位,段起始地址是32位,偏移也是32位;在实地址方式下,物理地址是20位,段起始地址是20位,而偏移是16位。
通用寄存器
命名于早先的16位CPU8086:
AX累加器
BX基地址寄存器(可作为存储器指针使用)
CX计数寄存器(控制重复循环操作的次数)
DX数据寄存器
SP堆栈指针
BP基指针寄存器
SI源变址寄存器
DI目的变址寄存器
简单传送指令
MOV DEST,SRC
- 两个操作数的尺寸必须一致
- 目的操作数不能是立即数
- 不影响各标志
XCHG OPRD1,OPRD2
- 操作数不能是立即数
- 不影响各标志
除了字符串操作指令外,指令的源操作数和目的操作数都不能同时是存储单元。(可用通用寄存器过渡)
加减指令
ADD DEST,SRC
- 两个操作数的尺寸必须一致
- 影响标志寄存器的有关标志
SUB DEST,SRC
(同上)
ADC DEST,SRC
- 带进位加法指令,DEST=DEST+SRC+CF
SBB DEST,SRC
- 带借位减法指令,DEST=DEST-(SRC+CF)
INC DEST
- 不影响标志寄存器中的进位标志,会影响其他。
- 操作数的类型可以是通用寄存器或存储单元
- 要让变量自加时,需要用寄存器做桥梁(个人经验,未求证)
DEC DEST
(同上)
NEG OPRD
- 取补,0-OPRD,就是取操作数的负数
- 操作数以补码表示的
- 影响有关状态标志。如果操作数为0,则CF为0,否则为1
标志寄存器
进位标志CF
- 运算结果的最高位是否产生进位或借位
- 比较无符号数的大小,要用到CF
- 移位指令也能够把操作数的最高位或最低位移入CF,移位指令和CF配合,可实现操作数之间的位传送
零标志ZF
- 运算结果是否为0
符号标志SF
- 与运算结果最高位相同,正为0,负为1
溢出标志OF
- 反应有符号数加减运算是否引起溢出(即运算结果超出了表示范围。要看真值!而不是单纯的二进制移位什么乱七八糟的/因为有道题做错了来着)
- 判断有符号数的大小时,用到OF和SF
奇偶标志PF
- 最低字节中含有1的位数,偶数个为1,奇数个为0
辅助进位标志AF
- 运算过程中看最后四位,不论长度为多少。最后四位向前有进位或者借位,AF=1,否则AF=0
进位标志操作指令:
清进位标志指令CLC
置进位标志指令STC
进位标志取反指令CMC
获取状态标志操作指令LAHF:把标志寄存器的低八位送到通用寄存器AH中
设置状态标志操作指令SAHF:使状态标志SF ZF AF PF CF分别来自寄存器AH中对应的值
段寄存器
CS代码段寄存器:不能作为目标,不能显式地改变代码段寄存器
SS堆栈段寄存器
DS数据段寄存器
ES附加段寄存器(80386开始,又增加了FS,GS)
- 不能把立即数直接传送倒段寄存器。
- 可见部分的长度都是16位。实地址方式下存放16位段值,保护方式下存放16位段选择子。利用MOV可以存取段寄存器中的段值或段选择子。
在访问存储单元时,CPU先根据对应的段寄存器得到段起始地址,再加上指定的偏移,得到存储单元的物理地址。
寻址方式
立即寻址方式
- 目的操作数不能采用这个方式
寄存器寻址方式
- 操作数在CPU内部的寄存器中
存储器寻址方式
- 是给出存储单元偏移的方式
直接寻址方式——[95780H];在源程序中往往用变量名表示
寄存器间接寻址方式——[EDI],8个通用寄存器之一
(EA=1+2+3)
1寄存器间接寻址
1+2基址加变址寻址
1+3寄存器相对寻址
3直接寻址
取有效地址指令
LEA REG,OPRD
- 把OPRD的有效地址送到REG(通俗来说就是把右边的地址给左边,又因为右边一定是存储器寻址方式,所以基本给的就是方括号里的东西)
- OPRD必须是存储器操作数!
- REG必须是16位或32位的通用寄存器!
- 不影响各标志位
- 两个操作数的位数可以不相同,只取低位,或无符号扩展。
指令指针寄存器和简单控制转移
程序流程的变化是由控制转移指令改变 指令指针寄存器EIP和代码段寄存器CS 实现的。
先从存储器中取指令,就得先知道物理地址,由CS给出代码段的段号,EIP给出偏移。
控制转移指令——条件转移指令,无条件转移指令,循环指令,函数调用及返回指令,中断指令,中断返回指令等
Jcc LABEL
- 条件转移指令
- 根据标志位判断,在条件满足的情况下,只改变EIP(段内转移)
- 本身不影响标志位
CMP DEST SRC
- 比较指令,根据DEST-SRC的差来影响标志寄存器中的各状态标志,但不影响目的操作数。
- 比较数值大小,有符号GEL(用SF和OF判断),无符号AEB(用CF判断),等于用ZF判断
堆栈和堆栈操作
堆栈的栈顶由SS和ESP确定。进栈ESP减小,指向地址更低的存储单元;反之。
堆栈主要用途:
- 保护寄存器内容或者保护现场
- 保存返回地址
- 传递参数
- 安排局部变量或者临时变量
PUSH SRC
- 进栈指令,SRC可以是32位通用寄存器、16位通用寄存器和段寄存器、双字或字存储单元、立即数
- 数据至少是16位
- 将一个双字数据压入栈:先把ESP-4,然后再把数据送到ESP所指示的存储单元。
- ESP始终指向栈顶
POP DEST
- 出栈指令,DEST不能是立即数,也不能是代码段寄存器CS
- 弹栈操作和PUSH相反,先送出,再+4
通用寄存器全进栈和全出栈指令
PUSHA
POPA
- 压入8个16位通用寄存器的内容,顺序为:AX,CX,DX,BX,SP,BP,SI,DI。然后SP-16,所以SP进栈的内容是PUSHA执行之前的值
- 弹出顺序相反,但SP不是由堆栈弹出的,是直接+16来恢复的
PUSHAD
POPAD
- 压入8个32位通用寄存器的内容,顺序为:EAX,ECX,EDX,EBX,ESP,EBP,ESI,EDI。然后ESP-32,同理
- 同理
这四条指令都不影响标志位,也没有显式操作数