80386汇编-数据传送、寻址和算数运算
9unk Lv5

数据传送、寻址和算数运算

与 8086汇编重复且用法上没有变化的指令这里就不过多介绍。

操作数类型

操作数类型有:立即操作数(immediate)、寄存器操作数(register)和内存操作数(memory)。
1

整数的零/符号扩展

  • MOVZX 指令将源操作数的内容复制的目的操作数中,并将该值零扩展至 16 位或 32位。
  • MOVSX 指令将源操作数的内容复制到目的操作数中,并将该值符号位扩展至 16 位或 32 位。

MOVZX 和 MOVSX 常用在除法指令前

LAHF 和 SAHF 指令

与 8086汇编中的指令一样,这里复习一下

  • LAHF 指令将 EFLAGS 寄存器的低字节复制到 AH 寄存器
  • SAHF 指令复制 AH 寄存器的值到 EFLAGS 寄存器的低字节

    这两个指令主要用于修改标志寄存器,或判断标志寄存器中哪个位为 1

案例

例1:演示数据传送指令

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
35
36
37
38
39
40
41
42
43
;---------------------------
;程序名:Moves.asm
;功能:演示整数的零/符号扩展指令
;作者:9unk
;编写时间:2022-11-8
;----------------------------
INCLUDE Irvine32.inc

.data
num1 db 21
num2 dw 1234
num3 db -1
num4 dw -1234

.code
main PROC
;MOVZX 零扩展演示
MOV eax,0ffffffffh
MOVZX ax,num1
;
MOVZX eax,num2

;MOVSX 符号位扩展演示
MOV eax,12345678h
MOVSX ax,num3
;
MOVSX eax,num4

;内存到内存交换
MOV ax,num2
XCHG ax,num4
MOV num2,ax

;偏移寻址
MOV al,num1
MOV al,[num1+1]
MOV al,[num1+4/1]
MOV al,[num1+1*4]
MOV al,[num1+TYPE num4]

exit
main ENDP
END main

在 win32 汇编中寻址方式可以使用 “*” 和 “/“,对内存地址对进行乘法和除法运算。

加法和减法指令

INC和DEC指令

INC 和 DEC 指令从操作数中加1 或 减1,指令格式如下:

1
2
INC reg/mem
DEC reg/mem

NEG指令

NEG 指令通过将数字转换为对应的补码而求得其相反数,指令格式如下:

1
2
NEG reg
NEG mem

在 win32 汇编中,INC、DEC、NEG 指令可对内存操作数进行操作。

案例:(AddSub3)

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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
;---------------------------
;程序名:AddSub3.asm
;功能:演示整数的零/符号扩展指令
;作者:9unk
;编写时间:2022-11-9
;----------------------------
INCLUDE Irvine32.inc

.data
Rval SDWORD ?
Xval SDWORD 26
Yval SDWORD 30
Zval SDWORD 40

.code
main PROC
;MOVZX 零扩展演示
MOV ax,1000h
INC ax
DEC ax

;对内存操作数进行加、减、取反
INC Rval
NEG Rval
DEC Rval

;表达式:Rval = -Xval+(Yval-Zval)
MOV eax,Xval
NEG eax
MOV ebx,Yval
SUB ebx,Zval
ADD eax,ebx
MOV Rval,eax

;零标志位的例子
MOV cx,1
SUB cx,1
MOV ax,0ffffh
INC ax

;符号标志位的例子
MOV cx,0
SUB cx,1
MOV ax,7fffh
ADD ax,2

;进位标志位的例子
MOV al,0ffh
ADD al,1
;溢出标志位的例子
MOV al,+127
ADD al,1
MOV al,-128
SUB al,1

exit
main ENDP
END main

除 INC 和 DEC 指令外,其他加减指令都影响进位标志位。

和数据操作相关的伪指令

指令 功能
OFFSET 返回偏移地址
PTR 允许改变变量的默认大小
TYPE 返回变量类型的大小
LENGTHOF 返回数组内元素的数目
SIZEOF 返回数组初始化时占用的字节数
LABEL 对同一变量重新定义不同大小的方法
ALIGN 将变量的位置按字节、字、双字或段边界对齐

这里 8086 汇编中都已经学过,如果忘了再看一遍回顾一下。

间接寻址

这里主要把 win32汇编中需要编写代码不一样的地方看一下。

  1. 通用保护故障:在保护模式下,如果有效程序指向数据段以外的区域,CPU 就有可能会产生通用保护故障(GP,General Protection Fault)。即使指令不修改内存,这种情况也有可能会发生。例如,如果 ESI 未初始化,下面的指令就有可能产生通用保护故障:
1
MOV ax,[esi]

因此在 win32 汇编中,使用间接寻址之前一定要对变址寄存器初始化。

  1. PTR:与间接操作数的联合使用。有时候在一条指令的上下文中,操作数大小并不明确,在编译时编译器就会报错。如:
1
INC [esi]

正确格式应写为:

1
INC BYTE PTR [esi]

案例:32位整数相加

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
;---------------------------
;程序名:T4-3.asm
;功能:32位整数相加
;作者:9unk
;编写时间:2022-11-10
;----------------------------
.386
.model flat,stdcall

.data
arrayD DWORD 10000h,20000h,30000h

.code
main PROC
MOV esi,OFFSET arrayD
MOV eax,[esi]
ADD esi,4
ADD eax,[esi]
ADD esi,4
ADD eax,[esi]
main ENDP
END main

变址操作数的比例因子

使用变址操作数时,在计算机偏移地址时必须考虑每个数组元素的大小。Intel CPU 的设计者们想让编译器编写者在处理数组操作时更加轻松,因此他们提供了一种使用比例因子(scale factor)计算偏移地址的寻址方式。比例因子通常是数组每个元素的大小。
例如:
2

指针

包含其他变量地址的变量称为指针变量(pointer variable)或指针(pointer),操纵数组和数据结构的指针是非常有用,使用指针进行动态内存分配成为可能。基于 Intel 的程序使用两种基本类型的指针:NEAR 和 FAR,它们的尺寸受当前处理器模式的影响,如下表所示:
3

TYPEDEF 操作符

TYPEDEF 操作符允许创建用户自定义的类型,在定义变量时,用户自定义类型与内建类型完全相同。TYPEDEF 非常适合创建指针变量。
例如:创建一个指向字节的指针 PBYTE。

1
PBYTE TYPEDEF PTR BYTE

该定义通常写在数据段前,之后就可以使用 PBYTE 来定义变量。

1
2
3
4
.data
arrayB BYTE 10h,20h,30h,40h
ptr1 PBYTE ? ;未初始化
ptr2 PBYTE arrayB ;指向数组

案例:

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
35
36
37
;---------------------------
;程序名:pointers.asm
;功能:使用 TYPEDEF 创建三种指针类型(PBYTE,PWORD和PDWORD)
;作者:9unk
;编写时间:2022-11-11
;----------------------------
TITLE Pointers

INCLUDE Irvine32.inc

;创建用户自定义类型
PBYTE TYPEDEF PTR BYTE ;字节指针
PWORD TYPEDEF PTR WORD ;字指针
PDWORD TYPEDEF PTR DWORD ;双字指针

.data
arrayB BYTE 10h,20h,30h
arrayW WORD 1,2,3
arrayD DWORD 4,5,6

;创建指针变量
ptr1 PBYTE arrayB
ptr2 PWORD arrayW
ptr3 PDWORD arrayD

.code
main PROC
;使用指针变量访问数据
MOV esi,ptr1
MOV al,[esi]
MOV esi,ptr2
MOV ax,[esi]
MOV esi,ptr3
MOV eax,[esi]
exit
main ENDP
END main

JMP和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
;---------------------------
;程序名:SumArray.asm
;功能:整数数组求和
;作者:9unk
;编写时间:2022-11-11
;----------------------------
TITLE Summing an Array

INCLUDE Irvine32.inc

.data
intarray WORD 100h,200h,300h,400h

.code
main PROC
XOR esi,esi
XOR eax,eax
MOV ecx,4
SumArray1:
ADD ax,intarray[TYPE intarray*esi]
ADC ax,0
INC esi
loop SumArray1
;
CALL DumpRegs
exit
main ENDP
END main

复制字符串

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
35
36
37
38
;---------------------------
;程序名:CopyStr.asm
;功能:复制字符串
;作者:9unk
;编写时间:2022-11-11
;----------------------------
TITLE Copying a String

.386
.model flat,stdcall
option casemap:none

include kernel32.inc
includelib kernel32.lib
include masm32.inc
includelib masm32.lib

.data
source BYTE "This is the source string",0
target BYTE SIZEOF source DUP(0),0

.code
main PROC
XOR esi,esi
XOR edi,edi
MOV ecx,SIZEOF source
;
CopyStr1:
MOV al,source[esi]
XCHG al,target[edi]
inc edi
inc esi
LOOP CopyStr1
;
INVOKE StdOut,offset target
INVOKE ExitProcess,0
main ENDP
END main

编程练习

  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
  2. 写一个小程序,说明 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 main

    INC和DEC指令执行的结果影响标志 ZF、SF、OF、PF 和 AF,但不影响 CF。

  3. 写一个使用加法和减法指令来设置和清除零标志和符号标志的程序,在每条加法和减法指令后插入 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
  4. 写一个使用加法和减法指令设置和清除溢出标志的程序,在每条加法和减法指令后插入 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
  5. 直接偏移寻址
    在寄存器中插入如下变量:
    4

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
;---------------------------
;程序名:lx4-5.asm
;功能:写一个分别将数据段变量的值,分别传送到EAX,EBX,ECX 和 EDX 寄存器中。
;作者:9unk
;编写时间:2022-11-12
;----------------------------
INCLUDE Irvine32.inc

.data
Uarray WORD 1000h,2000h,3000h,4000h
Sarray SWORD -1,-2,-3,-4

.code
main PROC
MOVZX eax,WORD PTR Uarray
MOVZX ebx,WORD PTR Uarray+TYPE Uarray
MOVZX ecx,WORD PTR Uarray+TYPE Uarray*2
MOVZX edx,WORD PTR Uarray+TYPE Uarray*3
CALL DumpRegs
MOVSX eax,SWORD PTR Sarray
MOVSX ebx,SWORD PTR Sarray[TYPE Sarray]
MOVSX ecx,SWORD PTR Sarray[TYPE Uarray*2]
MOVSX edx,SWORD PTR Sarray[TYPE Uarray*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
  2. 写一个程序实现算数表达式:EAX = -val2 + 7 - val3 + val1
    5

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-7.asm
;功能:写一个程序实现算数表达式:EAX = -val2 + 7 - val3 + val1
;作者:9unk
;编写时间:2022-11-12
;----------------------------
INCLUDE Irvine32.inc

.data
val1 SDWORD 8
val2 SDWORD -15
val3 SDWORD 20

.code
main PROC
MOV eax,val2 ;eax=FFFF FFF1h
NOT eax ;eax=0000 000Eh
INC eax ;eax=0000 000Fh
ADD eax,7 ;eax=0000 0016h
SUB eax,val3 ;eax=0000 0002h
ADD eax,val1 ;eax=0000 000Ah
CALL DumpRegs
exit
main ENDP
END main
  1. 写一个程序,使用 LOOP 指令和间接寻址方式把源字符串逆向复制到目的字符串变量中。
    6
    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 许可协议。转载请注明出处!