本文主要是介绍《汇编语言》chapter3 note,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
第三章:程序设计初步
目录
第三章:程序设计初步
堆栈的作用
过程调用和返回指令
参数传递
局部变量
算术逻辑运算指令
乘除运算指令
符号扩展指令
扩展传送指令
逻辑运算指令
一般移位指令
循环移位指令
双精度移位指令
分支程序设计
无条件和条件转移指令
多路分支的实现
循环程序设计
循环指令
计数器转移指令
多重循环程序
子程序设计
传递参数方法
安排局部变量地方法
保护寄存器的约定
堆栈的作用
- 保存函数的返回地址
- 用于向函数传递参数
- 安排函数的局部变量
过程调用和返回指令
CALL LABEL
- 不影响状态标志
- 段内直接调用指令:①把返回地址偏移压入堆栈;②使得EIP的内容为目标地址偏移(实现转移,和JMP指令操作是一样的)
- 段内间接调用指令:CALL OPRD 由OPRD给出入口地址偏移的子程序,把OPRD的内容送到EIP,操作数可以是32位的通用寄存器或者双字存储单元。
- 段间调用指令:相似,先把CS压入堆栈,再把EIP压入堆栈。
RET
- 不影响标志寄存器中的状态标志
- 从堆栈弹出地址偏移(调整ESP),送到指令指针寄存器EIP
- 段内带立即数返回指令:RET count ,其中count是一个16位的立即数,第一步不变,再把count加到ESP上
- 函数的返回值在寄存器EAX中
- 调用子程序的过程本质上是控制转移
- 在保护方式下(32位代码段),地址偏移是一个双字。
- 特别注意,子程序的嵌入汇编代码应该安排在C函数的return语句之后,以免不经过调用直接进入子程序。
参数传递
方法:
- 寄存器传递法
- 把参数放在约定的寄存器中
- 由于寄存器数量有限,只适用于传递少量参数的情形
- 堆栈传递法
- C语言的函数通常利用堆栈传递入口参数,而利用寄存器传递出口参数
- 用堆栈传递入口参数时,主程序在调用子程序之前,把需要传递的参数依次压入堆栈,然后子程序从堆栈中取入口参数
- 有时候需要考虑保护寄存器
- 约定内存单元传递法、CALL后续区传递法等
局部变量
局部变量放堆栈中,EBP指向堆栈,能够以EBP作为基址寄存器来访问局部变量。
算术逻辑运算指令
乘除运算指令
有时操作数是隐含的
MUL OPRD
- 无符号数乘法指令,另一个操作数隐含,位于寄存器AL,AX,EAX中
- OPRD可以是通用寄存器、存储单元,但不能是立即数!
- 乘积的高半部分不等于0,则CF=1,OF=1(表示高位含有结果的有效数呗);否则都为0。对其他状态标志无定义
IMUL OPRD
IMUL DEST(只能是通用寄存器),SRC(可以是立即数,尺寸不能超过目的操作数)
IMUL DEST(只能是通用寄存器),SRC1(不能为立即数),SRC2(只能为立即数)
- 有符号数乘法指令
- 双数或三数,乘积有可能溢出,那么高位部分将被截掉
- 单数和无符号乘法指令一样,高位含有效位,则CF=OF=1;双数和三数,溢出高位截断则CF=OF=1。对其他状态标志无定义
DIV OPRD
- 无符号数除法指令
- 和mul相反,被除数位于AX,DX:AX,EDX:EAX
- 字节操作数:AX除以OPRD,商送到AL,余数送到AH
- 字操作数:DX:AX除以OPRD,商送到AX,余数送到DX
- 双字操作数:EDX:EAX除以OPRD,商送到EAX,余数送到EDX
- OPRD不能是立即数
- 对状态标志的影响无定义
- 除数为0或商太大,则引起除法出错异常
IDIV OPRD
- 有符号数除法指令
- 被除数位于和DIV一样,商和余数也都和DIV一样
- 如果不能整除,余数的符号与被除数一致,而且余数的绝对值小于除数的绝对值
- 对状态标志无影响
- 有时需要在除操作之前扩展被除数
符号扩展指令
CBW
- 字节→字
- AL符号扩展到AH
CWD
- 字→双字
- AX符号扩展到DX
CWDE
- 字→双字
- AX符号扩展到EAX的高16位
CDQ
- 双字→四字
- EAX符号扩展到EDX
- 四条符号扩展指令均不影响各状态标志。
- 在无符号数除操作之前,不宜利用CBW、CWD、CDQ指令来扩展符号位,一般采用逻辑异或XOR指令把高位直接清零(即无符号扩展)
扩展传送指令
MOVSX DEST,SRC
- 符号扩展传送指令,扩展SRC的符号后送至DEST
- SRC可以是通用寄存器或存储单元,但DEST只能是通用寄存器,且尺寸必须大于SRC
- 不改变源操作数,也不影响状态标志
MOVZX DEST,SRC
- 零扩展传送指令
- SRC、DEST要求同上,也两不
逻辑运算指令
- 按位进行
- 目的操作数:只能是通用寄存器或存储单元,用于存放运算结果
- 如果只有一个操作数,则该操作数既是源又是目的
- 两个操作数,最多只能有一个存储单元。操作数们尺寸必须一致。源操作数可以是立即数
NOT OPRD
- 将OPRD按位取反
- 对标志无影响
AND DEST,SRC
- 与运算
- 使CF=OF=0,PF、ZF、SF反应运算结果,AF无定义
- 操作数自己与自己and,值不变,可以使CF清零
- 常用于使操作数若干位不变,另外若干位清零的场合
OR DEST,SRC
- 或运算
- 使CF=OF=0,PF、ZF、SF反应运算结果,AF无定义
XOR DEST,SRC
- 异或运算
- 使CF=OF=0,PF、ZF、SF反应运算结果,AF无定义
- 自己与自己异或,结果总为0。经常会采用xor把寄存器清零
TEST DEST,SRC
- 和and类似,但结果不送到dest,仅影响标志位,使CF=OF=0,PF、ZF、SF反应运算结果,AF无定义
- 通常用于检测某些位是否为1,但又不希望改变操作数值的场合
一般移位指令
移位指令需要标明移动的位数,可以是8位立即数,也可以是CL。通过截取count的第五位,实际的移位数被限制于0~31之间。
SAL OPRD,count //算数左移指令(同逻辑左移)
SHL OPRD,count //逻辑左移指令(同算数左移)
SAR OPRD,count //算数右移指令
SHR OPRD,count //逻辑右移指令
- CF受影响,SF、ZF、PF反应移位后的结果,OF很复杂,AF未定义
- 左移都是一样的,每向左移动一位,右边用0补足,最高位进入CF
- 算数右移左边的符号位保持不变,最低位进入CF
- 逻辑右移左边用0补足,最低位进入CF
循环移位指令
ROL OPRD,count //左循环移位指令
ROR OPRD,count //右循环移位指令
RCL OPRD,count //带进位左循环移位指令
RCR OPRD,count //带进位右循环移位指令
- CF受影响, OF很复杂,其他状态标志不受影响
- 前两条没有把CF包含在循环的环中,CF和一般循环的变化一样
- 每移动一位,CF都会变
- 循环移位指令,方便实现一个操作数内部的移位
- 带进位循环指令,能够实现跨操作数之间的移位。(将CF作为中间桥梁)
双精度移位指令
老师说不建议使用
SHLD OPRD1,OPRD2,count
SHRD OPRD1,OPRD2,count
- OPRD1作为目的操作数可以是通用寄存器或存储单元,字或双字
- OPRD2只能是寄存器。操作数尺寸一致。
- count都同理,如果超过操作数的尺寸,则目的操作数的结果无定义,状态标志也无定义
- 对目的操作数操作,左移把OPRD2放右边,右移把OPRD2放左边
- 仅移动一位,若符号位发生变化,则置OF,否则清OF;SF、ZF、PF反应移位后的结果,AF未定义
分支程序设计
- 利用条件转移指令和无条件转移指令实现分支
- 减少转移(在判断之前先假设满足简单的情形),充分利用寄存器(可以把寄存器作为形参变量之类的)等,都有助于提高执行的效率。
无条件和条件转移指令
(简单转移指令见此处)
条件转移指令和循环指令只能实现段内转移,无条件转移指令和过程调用返回指令都可以实现,软中断指令和中断指令一定是段间转移。
又分为直接转移和间接转移。
JMP LABEL
- 无条件段内直接转移指令机器码:操作码OP+地址差rel——地址差是转移目标地址偏移与紧随JMP指令的下一条指令的地址偏移之间的差值。执行无条件段内直接转移指令时,实际上时rel+EIP——相对转移
JMP OPRD
- 无条件段内间接转移:在保护方式下,OPRD是32位通用寄存器或双字存储单元,其内容直接被装入EIP
多路分支的实现
- switch
- 当多路分支在5路以上时,可以考虑通过无条件间接转移指令和目标地址表实现
循环程序设计
循环=初始化+循环体+调整+控制(计数控制法和条件控制法)
循环指令
LOOP LABEL
- =DEC ECX ; JNZ LABEL
- 段内转移,相对转移,转移范围-128~+127
- 自动以ECX作为循环计数器
- 不影响各状态标志
- 先要设置好ECX的初值
- 执行时,首先使ECX减1(不影响标志!),再判断是否为0,所以循环最多可进行232次
LOOPE LABEL //等于循环指令
LOOPZ LABEL //全零循环指令
还要看ZF等于1,则跳转
LOOPNE LABEL //不等于循环指令
LOOPNZ LABEL //非全零循环指令
还要看ZF等于0,则跳转
计数器转移指令
JECXZ LABEL
- 当寄存器ECX=0时,转移到LABEL(即循环次数为0,不进行循环)
- 通常在循环体开始之前使用该指令,当循环次数为0时,就可以跳过循环体
多重循环程序
实践过后再说吧。
子程序设计
- 必须遵循与主程序的约定
- 注意传递参数的方法、安排全局变量的方式、保护寄存器的约定、表述子程序的说明
传递参数方法
- 希望利用寄存器传递入口参数:在C源程序中明确_fastcall的调用规定
- 给动态局部变量分配寄存器,能有效地提高执行效率
安排局部变量地方法
- 利用堆栈来安排局部变量
保护寄存器的约定
- 函数代码虽然使用寄存器eax、ecx、edx,但并不事先保护他们;
- 函数代码只要使用了寄存器ebx、esi、edi、ebp,总是先保护它们,过后再恢复(这就是默契)
- 函数代码对堆栈指针ESP的使用是极其谨慎的
- 保护和恢复的时候一定要注意堆栈平衡
这篇关于《汇编语言》chapter3 note的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!