基本概念
程序:由一系列有序的指令构成
指令:指示计算机硬件完成指定的基本操作的命令
指令系统
又叫指令集,是一台计算机所有指令的集合
位于软件硬件交界面上
计算机的主要属性,指出计算机有哪些基本的硬件功能
指令系统应具备的特征
完备性:功能齐全
高效性:编写的程序占据空间小,执行速度快
规整性
对称性:所有寄存器和存储单元可同等对待;所有指令可使用各种寻址方式
匀齐性:可以支持各自数据类型
一致性:指令格式和数据格式一致
兼容性:系列机各种机型有相同的基本机构和共同的基本指令集
指令格式
基本格式
操作码+地址码
操作码指出操作的类型
地址码给出被操作的信息的地址
指令长度指的是一条指令的二进制代码长度
取决于操作码长度、地址码长度、地址码个数
可能大于、等于或小于机器字长,如双字长指令、字长指令、半字长指令
指令系统的所有指令长度相等,称为定长指令字结构,执行快,结构简单
指令系统的指令长度随指令而异,称为变长指令字结构,一般是字节的整数倍(考虑主存按字节编址)
根据操作数地址码的个数,指令分为
零地址指令
不需要操作数的指令。如空指令、停机指令、关中断指令
涉及堆栈的运算指令。
一地址指令:
单操作数指令。如自增、自减、求反、求补,形式为
隐含的双操作数指令。一般另一个操作数由ACC(累加器)提供,运算结果也存到ACC中,形式为
二地址指令:比如常用的逻辑运算,算术运算,形式为
三地址指令:比如常用的逻辑运算,算术运算,形式为
四地址指令:形式为,是下一条指令的地址
定长操作码指令格式
在指令高位分配固定长度的若干位表示操作码
位操作码字段可以表示个指令
定长操作码简化计算机硬件设计,提高指令译码识别速度,当计算机字节为32位和更长时,这是常规用法
拓展操作码指令格式
当指令字长有限时,为了丰富指令种类,可以采用可变长度操作码
拓展操作码是最常用的可变长操作码,操作码长度根据地址码的减少而增加
一般全1留作拓展操作码使用
比如0000 - 1110是4位操作码;11110000 -
11111110是8位操作码;111111110000 - 111111111110是12位操作码
除了拓展操作码,还有其他的拓展方法,比如哈夫曼编码的思想,给高频指令短的操作码
拓展编码必须是前缀码,即不存在短码是长码的前缀,且各指令操作码不重复
指令的操作类型
数据传送
寄存器之间的传送 MOV
从内存读数据到CPU寄存器 LOAD
从CPU寄存器写数据到内存 STORE
算术和逻辑运算
算术运算:ADD(加)、SUB(减)、CMP(比较)、MUL(乘)、DIV(除)、INC(自增1)、DEC(自减1)
逻辑运算:AND(与)、OR(或)、NOT(非)、XOR(异或)
移位操作
算术移位
逻辑移位
循环移位
转移操作
无条件转移 JMP:任意条件都会转移
条件转移 BRANCH:满足条件才会转移
调用
CALL:调用指令需要保存下一条指令的地址,方便调用结束后返回。
返回 RET:完成调用后根据之前保存的地址返回
陷阱 TRAP
输入输出操作:用于CPU和外部设备交换数据、传送控制命令和状态信息
寻址方式
有效地址的概念
指令中的地址码字段并不代表操作数真实地址,而是形式地址
形式地址需要结合寻址方式,算出操作数在存储器中的真实地址,即有效地址EA
若考虑虚拟存储机制,有效地址本质上是段内偏移量,即线性地址等于段基地址加有效地址。
本章内容除非特殊说明,默认先不考虑虚拟存储,即段基地址从0开始,且不采用分页机制,此时有效地址就是真实的物理地址
指令寻址和数据寻址
指令寻址
含义:寻找下一条需要执行的指令地址
分为
顺序寻址:程序计数器PC加1
跳跃寻址:通过本条转移指令算出下一条指令地址,可能跳跃到绝对地址(根据标记符),也可能跳跃到相对地址(距当前的指令偏移量),算出后修改程序计数器PC
数据寻址
含义:根据指令中操作数的形式地址得到其有效地址
数据寻址方式很多,通常在指令中设置一个字段(即寻址特征),用以指明寻址方式的类型
指令的格式为:操作码、寻址特征、形式地址A
常见寻址方式
隐含寻址
不显式给出所有操作数地址,指令中隐含操作数的地址
比如累加器ACC作为第二个操作数的地址,结果也存放到ACC中
优点是缩短指令长度;缺点是需要增加存储隐含地址的硬件
访存0次
立即(数)寻址
地址字段指出的是操作数本身,称为立即数
数据用补码形式存放
优点是不需要访问内存;缺点是立即数的位数收到指令长度限制
访存0次
直接寻址
地址字段就是操作数的真实地址
优点是寻址简单,只需要访问一次内存;缺点是寻址范围受指令位数限制,地址也不容易修改
访存1次
间接寻址
指令中给出操作数真实地址所存放位置的真实地址
间接寻址可以是一次,也可以是多次
如果内存寻址得到的内容第一位是1,表示多次间接寻址,需要继续寻址
如果内存寻址得到的内容第一位是0,表示得到的就是操作数的地址
优点是扩大了寻址范围,EA位数大于A的位数;缺点是需要多次访存(至少2次)
这种寻址方式不常用,通常使用寄存器间接寻址扩大寻址范围
访存至少2次
寄存器寻址
指令中给出操作数所在的寄存器编号
优点是不访问主存,且地址码很短(因为寄存器不多);缺点是寄存器价格贵,个数有限
访存0次
寄存器间接寻址
指令中给出操作数真实地址所存放的寄存器编号
特点是比间接寻址快,但仍需要访问主存
访存1次
相对寻址
指令中给出的是偏移地址(可正可负),基地址在PC中
操作数地址不是固定的,广泛应用于转移指令
注意
转移指令取出后,PC会立刻更新到下一行指令的位置,此后再计算相对偏移量。
比如转移指令2个字节,转移指令地址为X,则执行完后,
访存1次
基址寻址
指令给出偏移地址,基地址在基址寄存器BR中
面向操作系统的寻址方式,基址寄存器由操作系统管理,用户程序运行时通常BR不可变
该寻址方法扩大了寻址范围,用户不需要考虑编程的地址范围;缺点是偏移量的位数短
访存1次
变址寻址
指令给出基地址,偏移地址在变址寄存器IX中
面向用户的寻址方式,变址寄存器用户可以更改,指令中的A一般不变
该寻址方法扩大了寻址范围,常用于数组(A为数组地址,IX存放元素偏移量)、循环;缺点是偏移量的位数短
访存1次
堆栈寻址
堆栈是存储器(或专用寄存器组)中特定的按后进先出原则管理的存储区
该存储的读写一般通过栈顶指针寄存器SP
分为
硬堆栈:寄存器堆栈,成本高,不适合大容量堆栈
软堆栈:主存中划分一块区域作为堆栈,划算且常用
本寻址方式一般指令中都无操作数,操作数隐含在堆栈中,在读写堆栈的单元前后会相应对SP内容进行增减
x86汇编指令入门
相关寄存器
8个32位的通用寄存器,分别为
EAX 累加器(Accumulator)
EBX 基地址寄存器(Base Register)
ECX 计数寄存器(Count Register)
EDX 数据寄存器(Data Register)
ESI、EDI 变址寄存器(Index Register)
EBP 堆栈基指针(Base Pointer)
ESP 堆栈顶指针(Stack Pointer)
功能上作为程序计数器PC的寄存器:IP或EIP,只能使用控制指令修改
字母表示可以大写也可以小写,第一个字母E表示Extended(拓展的)
为了兼容性,EAX、EBX、ECX、EDX的低两个字节可以单独使用,以EAX为例
EAX低2字节称为AX
AX的高字节和低字节分别称为AH和AL
除了EBP和ESP,其他寄存器的使用实际上是任意的
寻址模式
如果是两个地址参数,第一个为目的地址,第二个为源地址
中括号内是地址,整体表示取括号中地址对应的存储空间
计算地址最多只能用2个32位寄存器和1个32位有符号常数相加
内存分配
汇编语言中声明内存大小,显示的使用
DB:Data Byte,单字节
DW:Data Word,双字节
DD:Double Word,四字节
对于常数的分配空间大小,可以使用标识符
BYTE PTR:常数以单字节形式
WORD PTR:常数以双字节形式
DWORD PTR:常数以四字节形式
指令可以有后缀指明空间大小,以传送指令mov为例
movb:传送单字节
movw:传送双字节
movl:传送四字节
movq:传送八字节
常用指令
以
以
数据传送指令
mov指令:将第二个操作数复制到第一个操作数。要求目的操作数不为常数,且不能从内存到内存
mov
mov
mov
mov
mov
push指令:ESP值减4后把操作数压入栈中,可以看出栈增长方向是从大地址到小地址
push
push
push
pop指令:把操作数弹出栈后ESP加4
pop
pop
算术和逻辑计算指令
add/sub指令:第一个操作数加上/减去第二个操作数,结果保存在第一个操作数位置
add/sub
add/sub
add/sub
add/sub
add/sub
inc/dec指令:操作数自增1/自减1
inc/dec
inc/dec
imul指令:带符号整数乘法指令。第一个(目的)操作数必须是寄存器;可以有一个或两个源操作数;一个源操作数时不使用常数,其与目的操作数的积放到目的操作数;两个源操作数时使用常数,把积放到目的操作数;如果溢出则OF=1
imul
imul
imul
imul
idiv指令:带符号除法指令。一个操作数表示除数。被除数为EDX:EAX。结果商存入EAX,余数存入EDX。
idiv
idiv
and/or/xor指令:逻辑与/或/异或,结果放在第一个操作数
and/or/xor
and/or/xor
and/or/xor
and/or/xor
and/or/xor
not指令:取反(位翻转)指令
not
not
neg指令:取负指令
neg
neg
shl/shr指令:逻辑移位指令,结果存放在第一个操作数,第二个操作数为移位的位数
shl/shr
shl/shr
shl/shr
shl/shr
控制流指令
jmp指令:控制IP转移到label指示的地址执行
jmp
cmp指令:第一个操作数和第二个操作数比较,根据结果设置处理机状态字条件码
cmp
cmp
cmp
cmp
jcondition指令:根据处理机状态字进行条件转移
je
jne
jz
jg
jge
jl
jle
ja
jae
jb
jbe
call/ret指令:用于函数的调用和返回。call把当前指令地址入栈后,无条件转移到标签处;ret从栈中弹出之前保存的地址,无条件转移回之前的地址位置
call
ret
数据对齐和大小端存放
详见第二章笔记中,“数据的存储和排列”小节
CISC和RISC的基本概念
复杂指令系统计算机(CISC)
增强原有指令的功能,设置更复杂的新指令,使软件功能硬件化实现
比如X86架构计算机
精简指令系统计算机(RISC)
减少指令的种类、简化指令的功能,使指令的速度提高
比如ARM、MIPS架构计算机
复杂指令系统计算机
CISC的特点如下:
指令系统:复杂庞大
指令数目:一般在200条以上
指令字长:不固定。指令格式多,寻址方式多
可访存的指令:不受限制
各指令使用频度:差别大,遵循28定律(20%的指令使用频率80%,80%的指令很少使用)
各指令执行时间:差别大,大部分需要多个时钟周期
CPU中通用寄存器数量:较少
控制器控制方式:大多数采用微程序控制。有些指令很复杂,无法用硬连线控制。
目标代码优化:难以用优化编译生成高效的目标代码程序
指令流水线技术:可以通过一定方式实现
软件兼容性:比较好,高档机可保护低档机全部指令并加以扩充
精简指令系统计算机
RISC的特点如下:
指令系统:选取使用频率高的简单指令,复杂的指令由简单指令组合实现
指令数目:一般在100条以下
指令字长:固定。指令格式少,寻址方式少
可访存的指令:只有LOAD/STORE(取数存数)允许访存。其余指令的操作在寄存器之间进行
各指令使用频度:都经常使用
各指令执行时间:大部分指令在一个时钟周期内完成
CPU中通用寄存器数量:非常多
控制器控制方式:以硬布线控制(组合逻辑控制)为主,基本不用微程序控制
目标代码优化:特别重视编译优化工作,以减少程序执行时间
指令流水线技术:一定采用
软件兼容性:大多数RISC不能和老机器兼容,但因为实用性强,是未来的发展方向
高级语言程序与机器代码之间的对应
编译器、汇编器和链接器的基本概念
详见第一章笔记“高级语言程序与机器语言程序转换”小节
过程(函数)调用的机器级表示
假设P调用Q,则步骤如下
P保存现场:当需要保留调用者保存寄存器(包括EAX、ECX、EDX)的值,进行此步骤
P压参数:把调用参数按从右到左的顺序压入栈中(如果寄存器数量充足,也可能把部分参数用寄存器保存)
P执行CALL指令
存旧PC:把返回地址(调用指令后一条指令的位置)压栈
更新PC:修改PC至跳转处,此后Q过程开始执行
Q准备阶段
存原栈底:将EBP(P的栈底)压栈(此时ESP为栈顶,所指位置存放P的栈底)
更新栈底:更新EBP为ESP位置(即Q的栈底中存放P的栈底)
更新栈顶:修改ESP,为自己分配栈空间(一般减去大小为16字节的倍数,以便对齐)
保存现场:如果需要用被调用者保存寄存器(包括EBX、ESI、EDI),进行此步骤
Q过程体阶段
局部变量空间分配:一般按低地址到高地址的顺序使用栈空间(和栈增长方向相反)
通常按小端存放,考虑对齐
结束时设置返回值(通常是放到EAX寄存器中)
Q结束阶段
Q恢复P的现场
Q执行leave指令
恢复栈顶:修改ESP等于EBP,以释放Q的栈空间
恢复栈底:弹出Q的EBP所指向P的EBP的值给EBP,即恢复P的栈空间
Q执行ret指令
恢复PC:弹出此时ESP所指向的返回地址给PC,以返回P执行
P恢复现场
P继续执行CALL指令的下一条指令
注:关于一个过程P的栈帧 +
P栈帧头:P过程EBP指向空间(存放P过程的调用者的旧EBP) +
P栈帧尾:下一个过程Q的EBP指向空间(存放的是P的EBP)的前一个空间(存放的是P的返回地址)
+ 栈帧是周期性的,P的栈帧尾后面就是Q的栈帧头
选择结构语句的机器级表示
条件码
即标志位寄存器,包括
CF 进位标志:用于无符号数
ZF 零标志:最近运算是否为0
SF 符号标志:最近运算结果的符号
OF 溢出标志:用于带符号数
cmp和sub对条件码的行为一致
test和and对条件码行为一致
jcondition指令结合条件码的ZF和SF可以实现跳转
if语句
可以利用if-goto语句分析从高级语言到汇编的过程
如果if条件不满足,则goto跳转
然后把if-goto转换到对应的汇编中,使用cmp/test、jcondition
Label的方式
switch语句
相比于if语句多次条件判断来跳转,switch是多路选择,一次直接跳到某个条件处的语句执行
需要用到跳转表
段属性为只读,即.section .rodata
跳转地址在4字节边界上,即align 4
跳转表的头设置一个标记Label,头之后的每一行(项)都是一个标签Labeli,i=0,1,...,7
给出一个例子
设switch输入10、12、14、15、17分别对应情况L2、L3、L4、L1、L3,其他输入对应情况L5
跳转表Label标签后面的每一行分别表示情况10、11、12、...、17需要跳到的标记Labeli,即L2、L5、L3、L5、L4、L1、L5、L3
汇编代码中,首先把判断的输入减10,记为t。
比较t和7的关系(条件码设置按无符号减法)。
如果大于7(根据无符号数运算,这里包括t是负数的情况),跳转到L5
否则根据t作为索引查跳转表对应表项标签Labelt,跳转到跳转表此项对应的标签位置执行即可
循环结构语句的机器级表示
高级语言转汇编可以借助if-goto作为中间代码,方便分析转换
循环结构有三种情况
do-while循环:一次goto即可
while循环:相当于在do-while前先判断条件一次。需要两个goto
for循环:相当于while语句前先做一个初始化语句,可以先转成while循环。需要两个goto。
最后把if-goto转换到对应的汇编中,使用cmp/test、jcondition
Label的方式