数据传送、寻址和算数运算
与 8086汇编重复且用法上没有变化的指令这里就不过多介绍。
操作数类型
操作数类型有:立即操作数(immediate)、寄存器操作数(register)和内存操作数(memory)。
整数的零/符号扩展
- MOVZX 指令将源操作数的内容复制的目的操作数中,并将该值零扩展至 16 位或 32位。
- MOVSX 指令将源操作数的内容复制到目的操作数中,并将该值符号位扩展至 16 位或 32 位。
MOVZX 和 MOVSX 常用在除法指令前
LAHF 和 SAHF 指令
与 8086汇编中的指令一样,这里复习一下
- LAHF 指令将 EFLAGS 寄存器的低字节复制到 AH 寄存器
- SAHF 指令复制 AH 寄存器的值到 EFLAGS 寄存器的低字节
这两个指令主要用于修改标志寄存器,或判断标志寄存器中哪个位为 1
案例
例1:演示数据传送指令
1 | ;--------------------------- |
在 win32 汇编中寻址方式可以使用 “*” 和 “/“,对内存地址对进行乘法和除法运算。
加法和减法指令
INC和DEC指令
INC 和 DEC 指令从操作数中加1 或 减1,指令格式如下:
1 | INC reg/mem |
NEG指令
NEG 指令通过将数字转换为对应的补码而求得其相反数,指令格式如下:
1 | NEG reg |
在 win32 汇编中,INC、DEC、NEG 指令可对内存操作数进行操作。
案例:(AddSub3)
1 | ;--------------------------- |
除 INC 和 DEC 指令外,其他加减指令都影响进位标志位。
和数据操作相关的伪指令
指令 | 功能 |
---|---|
OFFSET | 返回偏移地址 |
PTR | 允许改变变量的默认大小 |
TYPE | 返回变量类型的大小 |
LENGTHOF | 返回数组内元素的数目 |
SIZEOF | 返回数组初始化时占用的字节数 |
LABEL | 对同一变量重新定义不同大小的方法 |
ALIGN | 将变量的位置按字节、字、双字或段边界对齐 |
这里 8086 汇编中都已经学过,如果忘了再看一遍回顾一下。
间接寻址
这里主要把 win32汇编中需要编写代码不一样的地方看一下。
- 通用保护故障:在保护模式下,如果有效程序指向数据段以外的区域,CPU 就有可能会产生通用保护故障(GP,General Protection Fault)。即使指令不修改内存,这种情况也有可能会发生。例如,如果 ESI 未初始化,下面的指令就有可能产生通用保护故障:
1 | MOV ax,[esi] |
因此在 win32 汇编中,使用间接寻址之前一定要对变址寄存器初始化。
- PTR:与间接操作数的联合使用。有时候在一条指令的上下文中,操作数大小并不明确,在编译时编译器就会报错。如:
1 | INC [esi] |
正确格式应写为:
1 | INC BYTE PTR [esi] |
案例:32位整数相加
1 | ;--------------------------- |
变址操作数的比例因子
使用变址操作数时,在计算机偏移地址时必须考虑每个数组元素的大小。Intel CPU 的设计者们想让编译器编写者在处理数组操作时更加轻松,因此他们提供了一种使用比例因子(scale factor)计算偏移地址的寻址方式。比例因子通常是数组每个元素的大小。
例如:
指针
包含其他变量地址的变量称为指针变量(pointer variable)或指针(pointer),操纵数组和数据结构的指针是非常有用,使用指针进行动态内存分配成为可能。基于 Intel 的程序使用两种基本类型的指针:NEAR 和 FAR,它们的尺寸受当前处理器模式的影响,如下表所示:
TYPEDEF 操作符
TYPEDEF 操作符允许创建用户自定义的类型,在定义变量时,用户自定义类型与内建类型完全相同。TYPEDEF 非常适合创建指针变量。
例如:创建一个指向字节的指针 PBYTE。
1 | PBYTE TYPEDEF PTR BYTE |
该定义通常写在数据段前,之后就可以使用 PBYTE 来定义变量。
1 | .data |
案例:
1 | ;--------------------------- |
JMP和LOOP指令
整数数组求和
1 | ;--------------------------- |
复制字符串
1 | ;--------------------------- |
编程练习
写一个使用加法和减法指令来设置和清除进位标志的程序,在每条指令后插入 call DumpRegs 语句亿显示寄存器和标志的值。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21;---------------------------
;程序名:lx-1.asm
;功能:写一个使用加法和减法指令来设置和清除进位标志的程序,在每条指令后插入 call DumpRegs 语句亿显示寄存器和标志的值。
;作者:9unk
;编写时间:2022-11-11
;----------------------------
INCLUDE Irvine32.inc
.code
main PROC
;CF 置1
MOV ax,0ffffh
ADD ax,1
CALL DumpRegs
;CF 置0
MOV ax,0
ADD AX,1
CALL DumpRegs
exit
main ENDP
END main写一个小程序,说明 INC 和 DEC 指令不影响进位标志。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29;---------------------------
;程序名:lx4-2.asm
;功能:写一个小程序,说明 INC 和 DEC 指令不影响进位标志。
;作者:9unk
;编写时间:2022-11-11
;----------------------------
INCLUDE Irvine32.inc
.data
num1 db 0
.code
main PROC
XOR esi,esi
CALL DumpRegs
INC esi
INC num1
CALL DumpRegs
DEC esi
DEC num1
CALL DumpRegs
MOV ax,0fffh
INC ax
CALL DumpRegs
DEC ax
CALL DumpRegs
exit
main ENDP
END mainINC和DEC指令执行的结果影响标志 ZF、SF、OF、PF 和 AF,但不影响 CF。
写一个使用加法和减法指令来设置和清除零标志和符号标志的程序,在每条加法和减法指令后插入 call DumpRegs语句,以显示寄存器和标志。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26;---------------------------
;程序名:lx4-3.asm
;功能:写一个使用加法和减法指令来设置和清除零标志和符号标志的程序
;作者:9unk
;编写时间:2022-11-12
;----------------------------
INCLUDE Irvine32.inc
.code
main PROC
MOV ax,10
;ZF:零标志位。相关指令执行后结果为零那么ZF=1,结果非零则ZF=0
ADD ax,5
CALL DumpRegs
SUB ax,15
CALL DumpRegs
;SF:符号标志位。相关指令执行后结果为负那么SF=1,结果非负数则SF=0
MOV ax,1
SUB ax,2
CALL DumpRegs
ADD ax,3
CALL DumpRegs
exit
main ENDP
END main写一个使用加法和减法指令设置和清除溢出标志的程序,在每条加法和减法指令后插入 call DumpRegs 语句,以显示寄存器和标志。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25;---------------------------
;程序名:lx4-4.asm
;功能:写一个使用加法和减法指令设置和清除溢出标志的程序
;作者:9unk
;编写时间:2022-11-12
;----------------------------
INCLUDE Irvine32.inc
.code
main PROC
;ax寄存器对半分,正数:0~32767,负数:-1~-32768
XOR eax,eax
;32767+2=-32769,正1+正1=负,结果不符合计算规则,OF=1
MOV ax,32767
ADD ax,2
CALL DumpRegs
;-32769+1=-32768,负1+正1=负(负1>正1),结果符合计算规则,OF=0
ADD ax,1
CALL DumpRegs
;-32768+3=32767,负1+正1=正(负1>正1),结果不符合计算规则,OF=1
SUB ax,3
CALL DumpRegs
exit
main ENDP
END main直接偏移寻址
在寄存器中插入如下变量:
1 | ;--------------------------- |
写一个程序,使用循环来计算斐波那契数列的前 12 个值。在循环中把计算得到的数字送到 EAX 寄存器并调用 call DumpRegs 语句进行显示。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31;---------------------------
;程序名:lx4-6.asm
;功能:写一个程序,使用循环来计算斐波那契数列的前 12 个值。
;作者:9unk
;编写时间:2022-11-12
;----------------------------
INCLUDE Irvine32.inc
.data
F db 1
.code
main PROC
XOR eax,eax
XOR eax,eax
MOV ecx,12
XOR esi,esi
LEA ebx,F
MOV al,1
CALL DumpRegs
loop1:
INC ebx
MOV al,[ebx-1]
MOV dl,[ebx-2]
ADD al,dl
MOV [ebx],al
CALL DumpRegs
LOOP loop1
exit
main ENDP
END main写一个程序实现算数表达式:EAX = -val2 + 7 - val3 + val1
1 | ;--------------------------- |
- 写一个程序,使用 LOOP 指令和间接寻址方式把源字符串逆向复制到目的字符串变量中。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34;---------------------------
;程序名:lx4-8.asm
;功能:写一个程序,使用 LOOP 指令和间接寻址方式把源字符串逆向复制到目的字符串变量中。
;作者:9unk
;编写时间:2022-11-12
;----------------------------
INCLUDE Irvine32.inc
.data
source BYTE "This is the source string",0
target BYTE SIZEOF source DUP('#')
.code
main PROC
MOV ecx,SIZEOF source
DEC ecx ;SIZEOF 把末尾的 0 也算进去了。
;DEC ecx
LEA esi,source
ADD esi,ecx
DEC esi ;字符串偏移是从0开始算的,所以再减1
LEA edi,target
loop1:
MOV al,BYTE PTR [esi]
MOV BYTE PTR [edi],al
DEC esi
INC edi
LOOP loop1
MOV esi,OFFSET target
MOV ebx,1
MOV ecx,SIZEOF target-1 ;SIZEOF 把末尾的 0 也算进去了。
CALL DumpMem
exit
main ENDP
END main
- 本文标题:80386汇编-数据传送、寻址和算数运算
- 本文作者:9unk
- 创建时间:2022-11-08 23:53:00
- 本文链接:https://9unkk.github.io/2022/11/08/80386-hui-bian-shu-ju-chu-li/
- 版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!