汇编语言包含两种指令:
1、汇编指令:有对应机器码的指令,可编译为机器指令被CPU执行
2、伪指令:没有对应机器码,不被CPU执行
dw字型 ——2bytes
db字节型——1byte
dd双字型——4bytes
mov/add/sub:
mov 寄存器,数据
mov 寄存器,寄存器
mov 寄存器,内存单元
mov 内存单元,寄存器
在寄存器和内存单元间(双向)传递数据时,以寄存器的大小为标准传递数据,用8位寄存器就传8位,用116位寄存器就传16位。
mov 段寄存器,寄存器
mov 寄存器,段寄存器
add/sub指令除了不能对段寄存器操作外,其余用法与mov相同
push和pop:
栈知识补充:栈的生长方向是由高地址到低地址,栈初始为空时,SS:SP指向栈底即栈空间最高地址单元的再高一个地址单元,即空栈时SS:SP指向栈外push ax表示将ax中的内容入栈,由以下两步完成:
(1)sp=sp-2,sp向前两个Bytes,栈顶更新
(2)将ax中的内容送入SS:SP指向的内存单元可以看出,使用栈段时应从高地址开始使用,如:
stack segment
dw 0,0,0,0,0,0,0,0
stack end
scodesg segment
mov ax,stack
mov ss,ax
mov sp,16
.
.
.
codesg ends
push (段)寄存器
pop (段)寄存器
push 内存单元
pop 内存单元
segment······ends:成对出现,用于定义一个段,格式如下:
段名 segment
.
.
段名 ends
end
end是一个汇编程序结束的标记,在编译汇编程序过程中若遇到end就结束对源程序的编译。
可以在某条指令前加上标记如start,整个程序最后end start,此处end的作用是告知编译器程序的入口。
assume
假设某一个段与某一个段寄存器关联。在数据段中定义的每一个变量标志符其实都代表了一个偏移量(也称为有效地址),这个偏移量与数据段段值相结合就指向某个内存地址。在程序中使用 ASSUME伪指令是用来指明一个默认的段地址。一旦你把某个数据段的段名指定给某个段寄存器(比如ASSUME DS,DATA1),这之后当你使用这个数据段(DATA1)内定义的变量,编译程序就自动把它与这个段寄存器(DS)里的段值(表示一个段地址)结合起来使用。
即只有当段中定义了变量assume才发挥作用?此处按下不表
inc bx:bx中的内容加1
功能上与add bx,1等价
dec bx:bx中的内容减1
loop p
先对cx减1,然后判cx是否为0,不为0,转后面给出标号所指的入口p;
为0,顺序执行
and和or
and:按位逻辑与
or:按位逻辑或
【字符常量由db定义,可当成整数对待】
对任意一个字符串,每个英文字母分别和00100000B做逻辑或可转换成小写字母
和11011111做逻辑与可转换成全大写字母
div
除法指令,用法:
div reg
div 内存单元
(实际上是div 除数)
其中:除数放在一个寄存器或内存单元中
被除数放在ax,dx中:
如果除数是8位,则被除数16位,默认放在ax中
如果除数是16位,则被除数32位,dx中放高16位,ax中放低16位
如果除数是8位,则al储存除法的商,ah存储除法的余数
如果除数是16位,则ax存储除法的商,dx存储除法的余数
16进制数的低四位是二进制数的低八位;
16进制数的高四位是二进制数的高八位。
dup
操作符。和db、dw、dd等数据定义伪指令配合使用,用于数据的重复
eg.
db 3 dup(0,1,2)
定义了9个字节,相当于
db 0,1,2,0,1,2,0,1,2
db 3 dup('abc','ABC')
定义了18个字节,相当于
db 'abcABCabcABCabcABC'
能够修改IP或同时修改CS和IP的指令统称转移指令。
offset操作符,由编译器处理。用于取得标号的偏移地址。
start:mov ax,offset start
s:mov bx,offset s
codesg ends
end start
上面程序将start和s处的偏移地址分别保存到了ax和bx中
nop
空操作,机器码占一个字节
jmp
无条件转移指令,可以只修改IP,也可以同时修改CS和IP
jmp 段地址:偏移地址 可以通过这样的指令修改CS和IP
jmp 合法寄存器 可以仅将IP改为寄存器中的值
根据位移进行转移的jmp
jmp short 标号
段内短转移,转到标号处执行。对IP的修改范围为-128~127,即向前最多128bits
CPU在执行jmp指令时不需要转移的目标地址,jmp指令的实际功能为:修改IP=IP+8位位移
1、8位位移=标号处地址-jmp指令后的第一个字节的地址(编译时算出)
2、short指明此处的位移为8位位移
3、8位位移范围为-128~127,用补码表示
4、8位位移由编译程序在编译时算出
jmp near ptr 标号
段内近转移,转到标号处执行。对IP的修改范围为-32768~32767,即向前最多32768bits
CPU在执行jmp指令时不需要转移的目标地址,jmp指令的实际功能为:修改IP=IP+16位位移
1、16位位移=标号处地址-jmp指令后的第一个字节的地址(编译时算出)
2、near ptr指明此处的位移为16位位移
3、16位位移范围为-32768~32767,用补码表示
4、16位位移由编译程序在编译时算出
短转移和近转移都只修改了IP的值
转移目的地址在指令中的jmp指令
jmp far ptr 标号
段间转移,又称远转移。该指令可同时将CS和IP修改为标号处的段地址和偏移地址。
只有jmp far ptr会将目的地址写在其机器码中
转移目的地址在寄存器中的jmp指令
jmp 16位寄存器
功能:IP=寄存器中的数据
转移目的地址在内存中的jmp指令
1、jmp word ptr 内存单元地址(段内转移)
将内存单元中的一个字作为偏移地址进行转移
2、jmp dword ptr 内存单元地址(段间转移)
将从内存单元地址开始存放的
作为转移地址,高地址处的字作目的段地址,低地址处的字作目的偏移地址
CS=内存单元地址+2里的数据
IP=内存单元所放地址里的数据
jcxz
有条件转移指令,所有有条件转移指令都是短转移,对IP的修改范围为-128~127
jcxz 标号
当此时cx=0时跳转到标号处
当cx!=0时继续向下执行
loop
循环指令,所有循环指令都是短转移。
操作:
1.cx=cx-1
2.若cx != 0,IP=IP+8位位移(编译时算出)
jmp short、jmp near ptr、jcxz、loop对IP的修改都是根据转移目的地址和起始地址之间的位移进行修改的。它们的机器码中只包含到目的地址的位移
call和ret
ret和retf
ret:只修改IP所以只能实现短转移
1、IP=SS*16+SP
2、SP=SP+2
以上两步相当于
pop IP
retf:既可修改IP又可修改CS
1、IP=SS16+SP
2、SP=SP+2
3、CS=SS16+SP
4、SP=SP+2
以上四步相当于
pop IP
pop CS
(先IP后CS)
call:根据位移进行转移
call 标号
1、将当前IP入栈
2、修改IP:IP=IP+(标号处地址-call指令后第一个字节的地址)
相当于
push IP
jmp near ptr 标号
转移的目的地址在指令中
call far ptr 标号
段间转移
1、SP=SP-2
2、CS入栈
3、IP入栈
4、CS=标号所在段的段地址
5、IP=标号所在段的偏移地址
相当于
push CS
push IP
jmp far ptr 标号
转移的地址在寄存器中
call 16位寄存器
1、SP=SP-2
2、IP入栈
3、IP=16位寄存器中的值
相当于
push IP
jmp 16位寄存器
转移地址在内存中
转移地址在内存中时,要告知call指令到内存取多长的地址:取16位(1个word)就只能修改IP,实现段内转移;取32位(两个字)就既能修改CS又能修改IP,故需要ptr来指明内存大小,以控制是段内转移还是段间转移。
call word ptr 内存单元地址
相当于
push IP
jmp word ptr 内存单元地址
call dword ptr 内存单元地址
相当于
push CS
push IP
jmp dword ptr 内存单元地址
(内存单元中高位作CS,即所call的内存单元地址+2中的值;低位作IP,即所call的内存单元中的值)
可以看到,call和ret(return)或return far可实现函数调功能。先call子函数,再ret返回。用于在使用call时先将CS入栈,再将IP入栈,所以使用ret或retf时先出栈IP再出栈CS。
mul乘法指令
1、乘数
* 都是8位
* 一个在al中
* 另一个在8位寄存器reg或内存单元中
* 都是16位
* 一个在ax中
* 另一个在16位寄存器reg或内存单元中
2、运算格式
mul reg
mul 内存单元
3、运算结果
8位乘法:结果在ax中
16位乘法:高16位结果在dx中,低16位结果在ax中
adc
带进位加法
adc 操作对象1,操作对象2
用法与add相同,但是功能有差别:
操作对象1=操作对象1+操作对象2+CF
作带进位加法
sbb
带进位减法
sbb 操作对象1,操作对象2
操作对象1=操作对象1-操作对象2-CF
使用带进位的运算时要先使用不带进位的add和sub,否则在CF位情况未知时使用带进位的运算可能导致结果出错。
cmp
比较指令
相当于减法指令,只是不保存结果。
可进行有符号数比较和无符号数比较
cmp 操作对象1,操作对象2
操作对象可以是16位寄存器也可以是8位寄存器(两个对象位数相同)
执行cmp时会计算操作对象1-操作对象2,但不保存结果,只对标志寄存器产生影响。
cmp ax,bx
ax=bx:ax-bx=0->zf=1
ax!=bx:ax-bx!=0->zf=0
axcf=1
ax>=bx:cf=0
ax>bx:cf=0&&zf=0
ax<=bx:cf=1 || zf=1
借助其他标志位对有符号数进行比较:
cmp ax,bx
ax=bx:of=0&&sf=0&&zf=1
ax!=bx:zf=0
axbx:of=0&&sf=0
根据无符号数的比较结果进行转移的各种条件转移指令:
≤表示偏序关系,其中两个操作数可以结合cmp指令表示为:操作数1≤操作数2(cmp 操作数1,操作数2)
指令 含义 检测的相关标志位
je 等于则转移 zf=1
jne 不相等则转移 zf=0
jb 低于则转移 cf=1
jnb 不低于则转移 cf=0
ja 高于则转移 cf=0且zf=0
jna 不高于则转移 cf=1且zf=1
movsb
串传送指令。
b相当于byte
1、相当于
mov es:[di],byte ptr ds:[si]
(这里只是这样表示)
2、若df=0,则
si=si+1
di=di+1
若df=1,则
si=si-1
di=di-1
执行一次movsb语句就相当于执行了上面两步
movsw
串传送指令
w相当于word
1、相当于
mov es:[di],word ptr ds:[si]
(这里只是这样表示)
2、若df=0,则
si=si+2
di=di+2
若df=1,则
si=si-2
di=di-2
执行一次movsw语句就相当于执行了上面两步
rep
movsb和movsw一般配合rep指令使用,格式如下:
rep movsb
rep movsw
rep指令相当于:
s:movsb
loop s
rep也是根据cx的值重复执行后面的传送指令
df=0时从ds:[si]传到es:[di],且每次si和di递增,cld将df设为0
df=1时从ds:[si]传到es:[di],且每次si和di递减,std将df为设为1
cld和std
cld(clean df)指令:将标志寄存器的df位置0,movsb和movsw指令正向传送
std(set df)指令:将标志寄存器的df位置1,movsb和movsw指令反向传送
pushf和popf
pushf:将标志寄存器的值压栈
popf:从栈中弹出数据到标志寄存器
in和out
端口读写指令
只能读入ax或al中
对0~255端口进行读写时:
访问端口:
in al,6h ;从6h号端口读入一个字节
out 20h,al ;向20h端口写入一个字节
对256~65535端口进行读写时:端口号放在dx中
mov dx,3f8h ;将端口号3f8h送入dx
in al,dx ;从3f8h端口读入一个字节
out dx,al ;向3f8h端口写入一个字节
shl和shr
逻辑移位指令
将一个寄存器或内存单元中的数据(二进制形式)左移或右移并补0
shl ax,n ;ax里的数左移n位(2进制形式)即乘以2^n
shr ax,n ;ax里的数右移n位,即除以2^n
※当移位位数大于1时,必须先将位数放在cl中:
mov al,01010001b
mov cl,3
shl al,cl
shl执行过程:
1、左移
2、移出的一位存入CF中
3、最低位补0
shr执行过程:
1、右移
2、移出的一位存入CF中
3、最高位补0
iret
中断程序返回指令。执行过程为:
pop IP
pop CS
popf
由此可推出在执行中断前所执行的压栈操作为:
pushf
push CS
push IP
int
引发中断
int n相当于引发一个n号中断过程。执行过程为:
1、取中断类型码n
2、标志寄存器入栈,IF=0,TF=0
3、CS、IP入栈
4、IP=n*4,CS=n*4+2
cli和sti
设标志寄存器中IF位的值
cli设IF的值为0,CPU不响应外部的可屏蔽中断
sti设IF的值为1,CPU可相应外部的可屏蔽中断