本文是对汇编语言基础知识点的总结,主要包括以下内容
进制转换
10进制->16进制:除16取余法.
16进制->10进制:按权展开相加法
10进制->2进制:除2取余法
2进制->10进制:按权相加展开法
2进制->16进制:每4位转换一位
16进制->2进制:每一位转换4位
补码
已知x的补码,要求-x的补码:x的补码取反加1
x可以为正,也可以为负
类型
类型 | 2进制位 | 字节 | 寄存器 |
---|---|---|---|
db | 8 | 1 | al |
dw | 16 | 2 | ax |
dd | 32 | 4 | eax |
0h(一个16进制位) | 4 | 0.5 |
地址转换
段地址*10+偏移地址=物理地址
1234:5678h=12340h+5678h=179B8h
寄存器
数据寄存器
名称 | 用途 |
---|---|
ax | 数据处理 |
bx | 基址寄存器,存放基址 |
cx | 用来循环中计数 |
dx | 辅助ax |
变址寄存器
名称 | 用途 |
---|---|
si | 源变址寄存器 |
di | 目的变址寄存器 |
地址指针寄存器
名称 | 用途 |
---|---|
sp | 栈顶的偏移地址 |
bp | 基址指针寄存器 |
段寄存器
名称 | 用途 |
---|---|
cs | 代码段的段地址 |
ss | 堆栈段的段地址 |
ds | 数据段的段地址 |
es | 附加数据段的段地址 |
控制寄存器
名称 | 用途 |
---|---|
ip | 指向下一条指令的首字节(相当于cs:ip作为下一条指令的地址)用户不能访问,对其更改 |
fl | 标志寄存器 |
fl 标志位
名称 | 用途 |
---|---|
cf(进位标志) | 非符号数是否溢出标志,最高位有向上进位或者借位,cf=1,否则cf=0 |
zf(零标志位) | 结果为0时zf=1,否则zf=0 |
of(溢出标志) | 符号数是否溢出的标志,正+正=负,正-负=负,负-正=正,溢出of=1,不溢出of=0 |
sf(符号标志) | 符号数,为正时sf=0,为负时sf=1(就是首位的值) |
df(方向标志) | df=1, si和di减1(高到低),df=0,si和di加1(低到高) |
if(中断标志) | if=1允许中断,if=0禁止中断 |
tf(跟踪标志) | tf=1每条指令结束后中断,tf=0,正常工作,不产生中断 |
af(辅助进位标志) | 记录第三位进位借位,af=1,否则af=0 |
pf(奇偶标志) | 低8位1的个数为偶数,pf=1,否则为pf=0 |
程序格式
;.386如果需要用到eax,必须加这一句
data segment
a db 100 dup(0) ;定义了一个名叫a的数组,共一百位,全部为0
b db 'ABC',0Dh,0Ah,'$' ;字符串用db格式,0Dh:回车,0Ah换行
;'$'结束符,如果用21h输出整个字符串,必须使用
data ends
code segment
assume ds:data, cs:code ;这句话必写,并且不能放在main之后
main:
....
code ends
end main ;程序从main开始
寻址
;引用12345h
mov dx, 1000h
mov ds, dx
mov al, ds:[2345h]
;段地址和偏移地址都是16位,可以表示20位物理地址
mov ax, 1000h:[bx] ;错,段地址必须是段寄存器
mov ds, 1000h ;错,段地址只能用寄存器赋值
mov al, [12h]
;直接寻址,偏移地址用常数表示
mov al, [si]
;间接寻址,偏移地址用寄存器表示
;基址寄存器:bp,bx,变址寄存器:si,di
;间接寻找的格式,基址+变址+8位或16位符号数
ds:[bx+si] ;ds可以省略,bx的段地址默认是ds,bp段地址默认是ss
ds:[bx+bp] ;错,bx和bp只能出现一个,si和di只能出现一个
ds:[bx-si] ;错,只能出现加法
ds:abc[2] ;对
ds:[abc+2] ;对
;间接寻址中,寄存器必须用括号括起来,常数可括可不括
;如果常数在寄存器之后且没有括号一定要带加号,若在寄存器之前则可以省加号
mov bx, offset abc
mov si, 1
mov al, [bx+si] ;引用abc[1]的另一种方式
指令
;xchg---------------------------------------------------------------
xchg ax, bx ;交换两个寄存器或变量的值
xchg ax, es:[bx] ;对
xchg ax, cs ;错,不能有段寄存器
xchg byte ptr es:[bx], byte ptr ds:[bp] ;错,不能交换两个变量
;push---------------------------------------------------------------
push ax ;把ax压入堆栈,导致sp=sp-2
push word ptr ds:s[bx] ;对
push ds:s[bx] ;对
push al ;错,必须是16位变量或寄存器
;pop----------------------------------------------------------------
pop ax ;弹出到ax中
pop [bx] ;对
pop byte ptr [bx] ;错,不是16位的
pop cs ;错,不能更该cs的值
;pushf--------------------------------------------------------------
pushf ;后面不跟操作数,表示标志寄存器fl入栈
;用来对fl的标志位赋值
push ax
popf ;把ax的值赋给fl
pushf
pop ax ;获取fl的值
;popf---------------------------------------------------------------
popf ;表示弹出一个字到标志寄存器fl
;cbw----------------------------------------------------------------
cbw ;convert byte to word,把al中8位符号数扩充为ax
;如果al中最高位是1(16进制最高位是F),则ah填充0FFh,否则填充00h
mov al, 7Fh
cbw ;结果是ax=00 7Fh
mov al, 0FCh
cbw ;结果是ax=FF FCh
;cwd----------------------------------------------------------------
cwd ;convert word to double word,把ax中16位符号数扩充为dx:ax
;如果ax最高位是1(16进制最高位是F),则dx为0FFFFh,否则为0000h
mov ax, 0FFFEh
cwd ;dx:ax=0FFFF FFFEh
mov ax, 7FFFh
cwd ;dx:ax=0000 7FFFh
;lea----------------------------------------------------------------
lea ax, es:[bx] ;结果是ax=bx
;lea表示把右侧的内存偏移地址放入左侧的寄存器中
;add----------------------------------------------------------------
add ax, bx
add ax, 1234h
add ax, ds:[si]
add word ptr ds:[si], 1234h
add s[bx], ax
;adc----------------------------------------------------------------
;用于非符号数加法,和add的用法相同,但是结果加上cf
;用在数据存放在多个寄存器里的情况,比如放在dx:ax和cx:bx中
add ax, bx
adc dx, cx ;最后结果dx:ax=dx:ax+cx:bx
;inc----------------------------------------------------------------
inc ax ;ax++
inc ds:[bx] ;ds:[bx]++
;inc不影响标志位
;sub----------------------------------------------------------------
sub ax, bx
sub ax, 12h
sub ax, s[si]
sub s[si], ax
sub word ptr s[si], 12h
;sbb----------------------------------------------------------------
;和sub相同,但是会减去cf
;比如数据存放在dx:ax和cx:bx中
sub ax, bx
sbb dx, cx ;注意加减都是从低位先算
;dec----------------------------------------------------------------
dec ax ;ax--
dec ds:[bx] ;ds:[bx]--
;dec不影响标志位
;mul----------------------------------------------------------------
mov al, 12h
mov bl, 0FCh
mul bl ;8位乘法(乘数为8位),al默认为一个乘数,ax = bl * al
mov ax, 1234h
mov bx, 5678h
mul bx ;16位乘法(乘数数为16位),ax默认为一个乘数,dx:ax = ax * bx
mov eax, 12345678h
mov ebx, 0FFFFFFFFh
mul ebx ;32位乘法(乘数为32位),eax默认为一个乘数,edx:eax = eax * ebx
mul 12h ;错,因为无法判断是几位乘法
mul word ptr [bx] ;对
;imul---------------------------------------------------------------
;看作是符号数进行乘法
;div----------------------------------------------------------------
mov ax, 32h
mov bl, 12h
div bl
;8位除法(除数是8位),ax默认是被除数,ax/bl = al....ah 商放在低位
mov dx, 1234h
mov ax, 3456h
mov bx, 5678h
div bx
;16位除法(除数是16位),dx:ax默认被除数,dx:ax/bx = ax...dx
mov eax, 12345678h
mov edx, 23456789h
mov ebx, 3456789Ah
div ebxh
;32位除法(被除数是32位),edx:eax默认为被除数,edx:eax/12h = eax...edx
div 12h ;错
div [bx] ;错
div byte ptr [bx] ;对
;idiv---------------------------------------------------------------
;符号数除法
;xlat---------------------------------------------------------------
xlat ;换码指令
;将ds:[bx]指向的表作为一个数组,把al替换为以al为下标的内容
;如果ds:[bx]指向"0123456789ABCDEF"
mov al, 10
xlat ;al='A'
;in-----------------------------------------------------------------
in al, 12h ;表示读端口为12h位置的内容到al
in ax, 12h ;表示读端口为12h位置到al,13h位置到ah
;只能读到al或ax中,后面跟dx或立即数。
;立即数00-FFh之间,dx在0000-FFFFh之间
mov dx, 1234h
in ax, dx ;表示al=[1234h], ah=[1235h]
;out----------------------------------------------------------------
out 12h, ax
out dx, al
;与in类似
;and----------------------------------------------------------------
and ax, bx
and ax, 01h
and ax, [bx]
and [bx], 01h
and [bx], ax
;有0为0,全1为1
;or-----------------------------------------------------------------
;与and类似,有1为1,全0为0
;xor----------------------------------------------------------------
;相同为0,不同为1
;not----------------------------------------------------------------
not ax
not ds:s[bx]
;取反
;neg----------------------------------------------------------------
;求补,即取反加1
;test---------------------------------------------------------------
test ax, bx
test ax, [bx]
test ax, 12h
test [bx], ax
test [bx], 12h
;test和and运算相同,但是丢弃结果,只影响zf标志位
;shl----------------------------------------------------------------
;逻辑左移 shift left
shl ax, 1
shl ax, cl
shl [bx], cl
;超过1位必须用Cl
;左移后右边补0,最后一次移出的数放入cf中
mov al, 1110h
shl al, 1 ;则cf=1,al=1100h
;shr----------------------------------------------------------------
;逻辑右移 shift right
;右移后左边补0,最后一次移出的数放入cf中
;sal----------------------------------------------------------------
;算术左移 shift arithmetic left
;与shl完全相同
;sar----------------------------------------------------------------
;算术右移 shift artithmetic right
;如果左侧最高位是1,则左侧补1,否则补0,其他和shr指令相同
;rol----------------------------------------------------------------
;循环左移 rotate left
;每一位向左移,移出的放入右边空位,移出的同时放入cf中,重复若干回
;注意循环左移指的是每一位数字先向左移动,移出的向右移动到最右边
;ror----------------------------------------------------------------
;循环右移 rotate right
;每一位向右移,移出的放入左边空位,移出的同时放入cf中,重复若干回
;rcl----------------------------------------------------------------
;带位循环左移,rotate through carry left
;最左侧的一位移动到cf中,同时cf移动到最右边一位
;用于两个寄存器表示的数据高位的移动
;注意移动后的结果一定也能用两个寄存器表示
;移动内存变量,需要写明类型
;左移dx:ax
rhl ax, 1
rcl dx, 1 ;此时cx本来是dx移出的那位
;但是循环后执行第一步,cf的值又被ax移出的覆盖了
;循环多次可以实现左移若干次
;rcr----------------------------------------------------------------
;带位循环右移,rotate through carry right
;与带位循环左移类似
;cmp----------------------------------------------------------------
;和sub相同,但是只影响标志位,丢弃结果
;如果符号数溢出,of=1,非符号数溢出,sf=1,如果是0,zf=1
;loop---------------------------------------------------------------
again:
....
loop again ;如果cx=0则跳出,否则跳到指定的位置
;属于短跳,必须在距离00~FFh中(-128~127)
;call(近调用)------------------------------------------------------
call 1000h ;1000h指的是本段的偏移地址为1000h的位置
call ax ;必须是16位寄存器,ax,bx,cx,dx,bp,bx,si,di
call word ptr [bp-2] ;跳到偏移地址放在ds:[bp-2]中的地方
;call(远调用)------------------------------------------------------
call 1234h:5678h ;跳到该位
call dword ptr ds:[bx] ;跳到ds:[bx]存放的内容所在的位置
;call 将会push ip,然后使ip变到操作数的位置
;ret----------------------------------------------------------------
;返回到上一次call的下一个位置
;pop ip
;或者返回到指定的位置
ret
ret 1000h
;retf---------------------------------------------------------------
;远调用的返回
;pop ip
;pop cs
retf
retf 1000h
;与ret最大的不同就是恢复了cs,可以用于不同段间的call返回
;int----------------------------------------------------------------
;请求中断
int 21h
;首先压入ip和cs,然后将cs:ip赋值为函数的地址
;中断向量(入口程序的地址)存放在中断表中。中断表的地址是0000h:n*4h
;比如21h的中断表地址为0000:0084h
;中断向量的值:dword ptr 0000h:[n*4h]
;iret---------------------------------------------------------------
;返回到int的下一条指令上,对应
;pop ip
;pop cs
;popf
;mov----------------------------------------------------------------
mov ax, bx
mov ax, 21h
mov ax, ds:t[si]
mov ds:t[si], ax
mov word ptr ds:t[si], 21h
;注意mov的左侧必须是可变的对象,即寄存器或者内存变量。一共3中类型,但是变量之间不能直接mov,故只有3+
;add,sub都一样
mov cs, ax ;错,不能对cs赋值
mov ax, ip ;错,不能访问ip
mov ax, fl ;错,不能访问fl
mov bx, al ;错,mov后面必须等宽
mov ds, es ;错,段地址不能赋给段地址
mov [bx], 12h ;错,mov变量,数字组合必须写出变量的类型,
mov [bx], ax ;对,mov寄存器或数字,变量都不需要
mov ds, 1200h ;错,段地址只能用寄存器赋值
;jmp----------------------------------------------------------------
jmp 0108h ;短跳,距离不能超过-128~127
jmp ax ;近跳,距离不能超过32768
jmp 0120h ;近跳
jmp word ptr [bx]
jmp 1234h:5678h ;远跳,跨段跳
jmp dword ptr ds:[10F0h]
;movsb--------------------------------------------------------------
;move string byte 以字节传送字符串
;byte ptr es:[di] = byete ptr ds:[si]
;df=0,si++,di++
;df=1,si--,di--
rep movsb
;若cx=0则停止,否则重复每一次cx--,一共重复cx次
mov ax, 1000h
mov es, ax
mov ax, 2000h
mov ds, ax
mov si, 10F0h
mov di, 20F0h
cld
mov cx, 5
rep movsb
;lodsb-------------------------------------------------------------
;从字符串中取字节
;al=ds:[si]
;用于和stosb一起,取字符串时只取符合要求的
;把ds:[si]指向源字符串,es:[di]指向目标内存
mov cx, 15h ;字符串长度
cld ;字符串的方向,存取方向是一致的
again:
lodsb ;取出字符到al
cmp al, ' '
je next
stosb ;如果不是空格则存入
next:
dec cx
jnz again
;stosb--------------------------------------------------------------
;存入字节到字符串,重复cx次
;es:[di] = al,df=0为正方向,df=1为负方向
mov cx, 100h
mov al, 0h
rep stosb
;scasb--------------------------------------------------------------
;在es:[di]中查找al中字符cx次
mov es, 1000h
mov ds, 10F0h
mov cx, 6
mov al, '0'
cld
repne scasb
jne not_find
dec di
;正方向查找'0'6次
;zf=0没找到,zf=1找到了
;找到后需要反向操作一次
;cmpsb--------------------------------------------------------------
;cmpare string by byte
;byte ds:[si]-byte es:[di],导致标志位的变化
;df=0, si++,di++
;df=1, si--,di--
;判断是否继续的时候会恢复做差时的fl,所以cx变为0时的zf=1被覆盖
repe cmpsb
je equal
;最多比较cx次,如果相等会继续比较
;循环结束后,如果不相等,zf=0,相等zf=1
unequal:
dec di
dec si
;不相等di,si会再重复一次才退出,所以正方向df=0,di,si减1,负方向di,si加1
repne cmpsb
jne totally_unequal
je equal
equal:
dec di
dec si
;最多比较cx次,如果不相同,继续比较
;退出循环是因为相同,zf=1,否则是因为比较完了,zf=0
;di,si会多做一次,为了找到相同的下标需要反向操作di,si
字符串操作指令 | 意义 |
---|---|
repne scasb | 在es:[di] 中查找al ,若找到就退出 |
repe scasb | 在es:[di] 中查找al ,若有不同就退出 |
repe cmpsb | 比较es:[di] 和ds:[si] 字节是否相同,若有不同就退出 |
repne cmpsb | 比较es:[di] 和ds:[si] 字节是否相同,若有相同就退出 |
rep movsb | 把字ds:[si] 赋给es:[di] |
rep lodsb | 把字ds:[si] 赋给al |
rep stosb | 把al 赋给es:[di] |
前面四个如果找到就退出,没找到最多重复cx次
后面三个重复cx次
方向均由df
控制
df 的值 | 指令 | 方向 | 下标 |
---|---|---|---|
0 | cld | 正(低地址到高地址) | si++,di++ |
1 | std | 负(高地址到低地址) | si--,di-- |
跳转指令 | 意义 | 判断 |
---|---|---|
ja | 非符号数大于 | cf=0且zf=0 |
jae | 非符号数大于等于 | cf=0 |
jb | 非符号数小于 | cf=1 |
jbe | 非符号数小于等于 | cf=1或zf=1 |
jg | 符号数大于 | of=sf且zf=0 |
jge | 符号数大于等于 | of=sf |
jl | 符号数小于 | of ≠ \ne =sf |
jle | 符号数小于等于 | of ≠ \ne =sf或zf=1 |
je | 等于 | zf=1 |
jne | 不等于 | zf=0 |
注意非符号数判断大小看有没有借位,即cf的值,符号数看of=sf为大于,然后判断zf
跳转指令 | 判断 |
---|---|
jc/jnc | cf=1/cf=0 |
jo/jno | oc=1/of=0 |
js/jns | sf=1/sf=0 |
jz/jnz | zf=1/zf=0 |
jcxz | cx=0跳 |
杂类指令 | 作用 |
---|---|
clc (clear cf) | cf=0 |
stc (set cf) | cf=1 |
cli | if=0(禁止中断) |
sti | if=1(允许中断) |
cld | df=0 |
std | df=1 |
小端规则
内存中按字节存放,如果有多个字节,会倒着存放。
比如1234h
偏移地址 | 内容 |
---|---|
1000h | 34h |
1001h | 12h |
堆栈对参数调用
_CDECL(c语言方式,堆栈由调用者清理,压入的顺序是从左往右)
f:
push bp
mov bp, sp
mov ax, [bp+6] ;参数1
mov bx, [bp+4] ;参数2
...
pop bp
ret
;程序从这里开始
mov ax, 3 ;参数1
push ax
mov ax, 4 ;参数2
push ax
call f
add sp, 4
堆栈结构(堆栈是从下面开始压入参数的)
地址 | 堆栈内容 | 压入顺序 |
---|---|---|
0FFFh:FFF8h | bp高8位<-sp | 4 |
0FFFh:0009h | bp低8位 | 4 |
1000h:0000h | ip高8位 | 3 |
1000h:0001h | ip低8位 | 3 |
1000h:0002h | 参数2高8位 | 2 |
1000h:0003h | 参数2低8位 | 2 |
1000h:0004h | 参数1高8位 | 1 |
1000h:0005h | 参数1低8位 | 1 |
只需要注意:
- 压入的参数放在堆顶
- 地址参数均为16位,一个内存单位只能放8位,所以要两个内存单位存放一个地址
- 结束调用:恢复bp时弹出bp->ret时弹出ip->参数由调用者清理
_PASCAL堆栈布局
唯一的不同是,由被清理者清理堆栈,ret n n是需要清理的大小,上面的例子是ret 4
_STDCALL
参数从右往左的顺序压入,f(a1,a2,…,an)则an首先压入
祝大家学习愉快!