github.io 有点慢,font-awesome 有点大,
客官麻烦搬条板凳坐一下下,我马上就到!

汇编指令简析

本文是上学期学习汇编语言时候的课堂笔记。

分类索引(单击命令直接索引)

分类 命令
符号数相关 JGJLIMUL
通用数据传送指令 MOVPOPPUSHXCHGINOUT
地址传送指令 LEALDSLESPUSHFPOPF
转换指令 CBWCWDXLAT
加减乘除 ADDINCADCSUBDECNEGMULDIV
逻辑运算 ANDORXORNOTTEST
移位指令 SHLSHRSALSARROLRORRCLRCR
字符串操作 MOVCMPSBSCASBSTOSBLODSB
控制转移指令 JMPLOOPCALLRETNRETF




符号数相关

jg/jl大小比较

jg 符号数大于
jl 符号数小于

跳转依据:

符号 依据
a < b SF!=OF
a==b ZF=0
a > b SF=OF且ZF=0
1
2
3
4
ah=0FFh, bh=1
cmp ah, bh
jg ah_is_larger;
//跳转不发生(0FF作为符号数为-1)

回到目录

imul 符号数乘法

  • imul eax只寄存器, ebx寄存器或变量, 1234h只常数
    eax = ebx * 1234h
  • imul eax, ebx
    eax自乘ebx

回到目录


通用数据传送指令

MOV 指令

1
2
[√] mov byte ptr ds:[bx], ah
[×] mov ax , bh ;左右操作数宽度必须相等
  • DS / CS / IP 不能被mov改变

mov 的变体

  • movzx : move by zero extention
  • movsx : move by sign extention
  • [rep] movsb ; 以字节为单位移动CX个字符

DS : SI 指向源字符串
ES : DI 指向目标字符串
若 DF=0 即正方向则SI++,DI++,否则反之。

回到目录

POP / PUSH 指令

  • 不能对8位值进行push/pop:
1
[×] push ah

回到目录

XCHG 指令

(略)

回到目录

IN / OUT 指令

基本格式:

1
in al, 21h

从21h号端口读取一个字节存放到AL中

  • 端口地址>=100h,必须存放到DX
  • 端口地址的范围是:[0000h, 0FFFFh],共65536个端口。

回到目录


地址传送指令

LEA `取变量的偏移地址`

1
lea eax, [ebp-64]

EAX = EBP-64

  • lea dx, ds:[1000h] ; DX=1000h
  • 设BX=1000h, SI=2
    lea ax, ds:[bx+si+3]; AX=bx+si+3=1005h

回到目录

LDS `把远指针装到DS:DEST`

1
lds dest, src

src 指向的地址,高位存放在DS中,低位存放在dest中。

16位时,
比如当前DS=1000H, BX=0100H.
当前内存:
1000:0100 01
1000:0101 02
1000:0102 03
1000:0103 04
而有一条指令:LDS BX,[BX]
[BX]指向1000:0100,执行后BX存低位的内容,也就是BX=0201H,
而DS则存高位的内容,也就是[BX+2]的内容,DS=0403H

回到目录

LES `把远指针装到ES:DEST`

1
les dest, src

回到目录

PUSHF,POPF `标志寄存器传送指令`

(把FL压入/弹出ax)

1
2
3
4
5
pushf
pop ax ; AX=FL
or ax, 1; 第0位变1,其它位不变
push ax
popf ; FL=AX

回到目录


转换指令

CBW,CWD `符号扩充指令`

CBW: convert byte to word
CWD: convert word to double word
CDQ: convert double word to quadruple word(32位扩充为64位)

1
2
mov al, 7Fh
cbw ; AX=007F
1
2
mov al, 0F9h
cbw ; AX=0FFF9h
1
2
mov ax, 8000h
cwd ; DX=0FFFFh, AX=8000h
1
2
3
mov eax, 0FFFFFFFFh
cdq ; EDX=0FFFFFFFFh, EAX=0FFFFFFFFh
32位 低32
  • movzx : move by zero extention
  • movsx : move by sign extention

回到目录

XLAT `换码指令`

1
2
3
4
5
; 设 char t[]="0123456789ABCDEF";
mov bx, offset t
mov al, 10
xlat; 结果AL='A'
实际AL=DS:[BX+AL]

回到目录


加减乘除

指令 用法
add
inc 不影响CF位
adc 带进位加法。当进行32位以上运算时,要求低位字节相加,而高位字节再相加时就要考虑低位相加的进位,即CF

回到目录

指令 用法
sub
dec 自减
neg 求相反数

回到目录

乘 `非符号`

1
mul src

src 不能是立即数,寄存器和地址均可

src字节数 操作对象 结果存放
8字节 al ax
16字节 ax dx : ax
32字节 eax edx : eax

即 AX = AL * src

回到目录

除 `非符号`

1
div op
op字节数 操作对象 结果存放
8字节 ax al(整除);ah(余数)
16字节 dx : ax ax(整除);dx(余数)
32字节 edx : eax eax(整除);edx(余数)

语句比较

  • test / andcmp / sub
    运算结果不影响操作数,只影响标志位
    影响标志: C,O,P,Z,S(其中C与O两个标志会被设为0)
  • neg / not
    neg 求相反数,not 逐位求反
  • inc / add
    是否影响flag
  • jcjb 指令完全等价。
    jzje 指令完全等价。

比较方法

  1. 非符号数:CF / ZF
  2. 符号数:SF / OF / ZF

    OF=1 即结果溢出(有错),否定SF得到的符号结果

回到目录


逻辑运算和移位


逻辑运算指令

ANDORXORNOTTEST

回到目录

移位指令

SHLSHRSALSAR
ROLRORRCLRCR

1
shl ah, 1

**单向**

符号 含义 作用
SHL 逻辑左移 低位补0, 高位进CF
SHR 逻辑右移 低位进CF, 高位补0
SAL 算术左移 (同SHL)
SAR 算术右移 每位右移, 低位进CF, 高位不变

它们的结果影响 OF、SF、ZF、PF、CF

**循环**

符号 含义 作用
ROL 循环左移 高位到低位并送CF
ROR 循环右移 低位到高位并送CF
RCL 带进位循环左移 进位值(原CF)到低位, 高位进CF
RCR 带进位循环右移 进位值(原CF)到高位, 低位进CF

它们的结果影响 OF、CF

8086中,当次数>=2时,移位次数应先赋值给 cl

回到目录


字符串操作

MOVSB `字符串传送`

1
[rep] movsb

以字节为单位,从 DS:SIES:DI 移动CX个字符

  • DF控制方向。若DF=0即正方向则SI++,DI++。

回到目录

CMPSB `字符串比较`

1
[repe | repne] cmpsb

若本次比较 相等 / 不等 则继续比较下一个

回到目录

SCASB `字符串查找`

1
[repe | repne] scasb

设ES:DI → 以’\0’结束的字符串,要求该字符串长度

1
2
3
4
5
6
7
8
9
mov ax, 1000h
mov es, ax
mov di, 1080h ; ES:DI → "ABC..."
mov cx, 0FFFFh ; ES:DI → "ABC",0,[]...
mov al, 0 ; AL=待查找的字符
cld
repne scasb ; 循环结束时,DI=1088h, CX=FFFF-8
inc cx
not cx ; CX=FFFF-CX=字符串长度(不含0)

回到目录

STOSB `存入字符串`

1
[repe] stosb

stosb把AL的值保存到ES:DI所指向的内存单元中

  • 当DF==0时DI++,当DF==1时DI—

设要把从地址1000:10A0开始共100h个字节内存单元全部填0

1
2
3
4
5
6
7
mov ax, 1000h
mov es, ax; ES=1000h
mov di, 10A0h
mov cx, 100h
cld
xor al, al
rep stosb

回到目录

LODSB `从字符串取字节或字`

1
lodsb

AL = DS : [SI], SI++

回到目录


控制转移指令

JMP

1
2
3
[√] jmp 0108h
[√] jmp 1234h:5678h
[√] jmp dword ptr ds:[10F0h]

回到目录

LOOP

CX-1

回到目录

CALL,RETN,RETF `子程序调用与返回`

retn表示近返回,可简写成ret;
retf表示远返回。
call既可以表示近调用,也可以表示远调用
retn [count] ; count多出步骤:SP = SP + count

堆栈传递

1
2
3
4
5
6
7
8
9
10
11
12
f:
push bp
mov bp, sp
mov ax, [bp+4]; 从堆栈中取得参数
add ax, ax
pop bp
ret
main:
mov ax, 3
push ax; 参数压入到堆栈
call f
add sp, 2

C语言函数调用y=f(2,3)求两数之和转化成汇编语言

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
f:
push bp;(4)
mov bp, sp
mov ax, [bp+4]
add ax, [bp+6]
pop bp; (5)
ret; (6)
main:
mov ax, 3
push ax; (1)
mov ax, 2
push ax; (2)
call f; (3)
here:
add sp, 4;(7)

上述程序运行过程中的堆栈布局如下:

ss:1FF8 old bp <- bp (4)
ss:1FFA here <- (3)(5)
ss:1FFC 02 <- (2)(6)
ss:1FFE 03 <- (1)
ss:2000 ?? <- (7)

cx/dx可以随便用
ax一般用作返回值

回到目录



————  EOF  ————