汇编语言语法-2
数据传送,寻址和算数运算
写在前面的补充(32位x86处理器):
基本程序执行寄存器
- 通用寄存器:主要用于算数运算和数据传输
- 一些寄存器的组成部分可以处理8位的值,如AX寄存器的高8位被称为AH,低8位被称为AL
32位 | 16位 | 8位(高) | 8位(低) |
---|---|---|---|
EAX | AX | AH | AL |
EBX | BX | BH | BL |
ECX | CX | CH | CL |
EDX | DX | DH | DL |
- 其他通用寄存器只能用32或16位访问
32位 | 16位 | 32位 | 16位 |
---|---|---|---|
ESI | SI | EBP | BP |
EDI | DI | ESP | SP |
-
特殊用法:
- 乘除指令默认使用EAX(extended accumulator)
- CPU默认使用ECX为循环计数器
- ESP(extended stack accumulator)用于寻址堆栈数据,极少用于数据传输和算数运算
- ESI(extended source index)和EDI(extended destination index)用于高速存储传输指令
- 高级语言通过EBP(extended frame pointer)来引用堆栈中的函数参数和局部变量
-
段寄存器
实地址模式中,16位段寄存器表示的是预先分配的内存区域的基址,这个内存区域称为段。一些段中存放程序指令(代码),其他段中存放变量(数据),还有一个堆栈段存放的局部变量和函数参数 -
指令指针
EIP寄存器中包含下一条要执行指令的地址。某些机器指令可以控制EIP,使程序分支转向另一个新位置
数据传送指令
Q: 操作数的三种类型是什么
A:操作数有3种基本形式:
- 立即数——————使用数字文本表达式
- 寄存器操作数————————使用CPU内已经命名的寄存器
- 内存操作数————————引用内存位置
Q:Intel使用的操作数符号中reg/mem32的含义是什么?
K:不用死记:
- reg表示通用寄存器(register):reg8,8位的(AH,AL,BH,BL,CH,CL,DH,DL);reg16,16位的
- sreg表示16位段寄存器(stack reg):CS,DS,SS,ES,GS,FS
- imm立即数(immediate):imm8,8位立即数;imm32,32位立即数(DWORD型)
- reg/mem8:8位操作数,可以是8位通用寄存器或者内存字节
- reg/mem16:16位立即数,可以是16位通用寄存器或者内存字
- reg/mem32:32位立即数,可以是32位通用寄存器或者内存双字
- mem 内存操作数(8位,16位,32位)
A:略…
Q:(判断)
1.MOV指令的目的操作数不能为段寄存器
2.MOV指令中的第二个操作数是目的操作数
3.EIP寄存器不能作为MOV指令的目的操作数
K:
- MOV用于数据传送
MOV destination,source
- MOV操作要满足如下规则:
- 两个操作数必须是同样的大小
- 两个操作数不能同时为内存操作数
- 指令指针寄存器(IP,EIP,RIP)不能作为目标操作数
- MOV不能内存到内存,要先入寄存器
- 由于MOV不能直接将较小的操作数复制到较大的操作数中便有了:
- MOVZX指令将一个较小的操作数(无符号)0扩展为较大的操作数再移动
- MOVSX指令将一个较小的操作数(有符号)符号扩展为较大的操作数再移动
A:F F T
实例
- XCHG指令交换两个操作数的内容,指令中至少有一个操作数是寄存器
1 | .386 |
加减法
算数指令
- INC指令实现操作数加1
- DEC指令实现操作数减1
- ADD指令实现源操作数和目的操作数相加
- SUB指令实现目的操作数减去源操作数
- NEG指令实现操作数符号翻转
状态标志
表示受算数影响的CPU状态标志:
- 算数结果为负,符号标志位(SF)为1
- 与目标操作数相比,无符号算术运算操作结果太大,进位标志位(CF)为1
- 执行算术或布尔指令后,奇偶标志位(PF)能反映出目标操作数最低有效字节中1的个数为奇数还是偶数
- 目的操作数位3有进位或借位,辅助进位标志位(AC)为1,主要用于二进制编码的十进制数(BCD)运算,如1+0Fh,和数在位4上为1,这是位3的进位,AC=1
- 算术操作结果为0,零标志位(ZF)为1
- 有符号算术操作结果超过目标操作数范围时,溢出标志位(OF)为1,典型的就是两个正数相加得到了一个负数,两个负数相加得到了一个正数
ps:若NEG求反后原寄存器无法装下,则OF置1,寄存器内结果不变,如:
1 | mov al,-128 ;AL=10000000b |
Q:写一个汇编程序实现a=R = -X + (Y - Z),其中abcd均为变量,为结果,26, 30, 40
A:
1 | .386 |
与数据相关的运算符和伪指令
- OFFSET运算符返回的是一个变量与其所在段起始地址之间的距离
- PTR可以重写操作数的默认大小类型
- TYPE返回的是一个操作数或者数组中每个元素的大小
- LENGHOF返回数组中元素的个数
- SIZEOF返回数组初始化时使用的字节数
Q:假如有如下定义:
1 | ;Q1 |
Q1: TYPE,LENGHOF,SIZEOF结果为多少?
Q2:AX=?
K:
- 如果数组定义占多行,LENGHOF只针对第一行,故为5,而SIZEOF返回值等于TYPE与LENGHOF的乘积,但如果该定义多行数组第一行结尾加上一个逗号,LENGHOF就为10
- 操作数大小不匹配,则我们无法直接让myDouble的后四位5678h移动到AX中,但使用PTR修改大小即可。为啥是后四位?想想x86处理器的小端存储
A1:1,5,5
A2:5678h
间接寻址
直接寻址很少用于数组的处理,反之,会用寄存器作为指针(称为间接寻址)并控制该寄存器的值。如果一个操作数使用的是间接寻址,就称之为间接操作数
Q:(判断)
1.任何一个32位寄存器都可以用作间接操作数
2.指令inc[esi]非法
3.array[esi]是变址操作数
A:T T T
K:
保护模式
- 任何一个32位通用寄存器(EAX,EBX,ECX,EDX,ESI,EDI,EBP,ESP)加上括号就是一个间接操作数。寄存器中存放的是数据的地址。
1 | .data |
上面代码就是常见用法,MOV指令使用间接操作数作为源操作数,解析ESI中的偏移量,并将该内存的值送入AL,当然也可以mov [esi],bl 即将BL内容复制到ESI寻址的内存地址中
使用PTR
inc [esi]会产生operand must have size的错误,汇编器不知道ESI指针的类型是啥,我们需要使用PTR
1 | inc BYTE PTR [esi] |
变址操作数
变址操作数是指,在寄存器上加上常数产生一个有效地址。每个32位通用寄存器都可以作为变址寄存器。
constant[reg]
[constant + reg]
变址操作非常适合数组处理,在访问第一个元素前,变址寄存器需要初始化为0
1 | .data |
需要注意,arrayB为BYTE型时,下一个为[esi+1],arrayB为WORD型时,下一个为[esi+2],这同样和数组元素大小有关,简化这一步骤,可以使用比例因子
1 | .data |
Q:分析下面程序填结果:
1 | .data |
K:
- myPointer DWORD myDoubles为定义一个指向双字的指针,值为myDoubles(地址),可以联想求数组大小时x = $ - array 中的array就是首地址
- TYPEDEF可以创建用户定义类型,如:PBYTE TYPEDEF PTR BYTE创建了自定义的PBYTE,一个字节的指针
A:a:0010h; b:003B008Ah; c:0; d:0; e:0044h
- 将字类型重定义为双字,则接下来的2字节的内存和该2字节的内存的值一起组成一个双字(小端顺序),自然b就是003B008Ah
- 双字在内存里4个字节存一个数,则该部分排列为00h,00h,00h,01h,00h,00h,00h…,自然[esi+2]得到0,[esi+4]也为0
- .data的数据都是连续存储的,减4得到向前4字节,是44h
JMP&LOOP
Q(判断):1.JMP指令只能跳转到当前过程中的标号。2.JMP是条件跳转指令
A:T F
K:JMP为无条件跳转,目标地址的偏移量直接送入指令指针寄存器,从而从新的地址开始
Q:循环开始时,如果ECX初始化为0,则LOOP指令要循环多少次(假设循环中没有其他指令修改ECX)
A:FFFFFFFFh次
K:LOOP使用ECX计数器循环。LOOP指令为条件跳转,每次执行到LOOP语句时,先使ECX-1,然后判断是否为0,0就结束LOOP,非0跳转到目标给出标号。若循环前初始化ECX=0,则减1变成FFFFFFFFh,真就跑死CPU了。
Q:实地址模式中LOOP和LOOPD分别使用哪个寄存器作为计数器?
A:LOOP–CX; LOOPD–ECX
- 另外还需注意一点,LOOP指令运行跳转目标的范围为-128~127字节内,内存中地址超出跳转范围会报错
Q:分析下面程序:求EAX的最后值
1 | mov eax,0 |
A:你以为是28(3+5*5)?,是死循环哒!L2循环结束时ecx为0,接着执行loop L1,ecx-1,每次循环结束时ecx都会被置成FFFFFFFFh,gg
K:更正方法:循环嵌套时最好使用变量保存进入内层时ecx的值,内层结束后再恢复,模板如下:
1 | .data |
写在后面
- 写loop时一定要注意:
我就是学习汇编语言时长1周的个人练习生,喜欢top:jmp top,loop死循环 - 练习题会随后更