mov ax,10h #(ax)=10h
mov ax,bx
mov ax,ds:[bx+si+5]
add ax,10h #(ax)=(ax)+10h
add ax,bx
add al,bl
sub ax,10h #(ax)=(ax)-10h
sub ax,bx
sub al,bl
push ax #sp=sp-2 (ss:sp)=(ax) 栈底数值最高,栈顶数值最低,栈的生长是从高位到低位
push ds
pop ds #(ds)=(ss:sp) sp=sp+2 先将值转给寄存器,在调整栈指针
pop ax
循环进行某操作
循环次数通常存放在cx寄存器中
例如,mov ax,0ffffh 其中必须在字母之前加零
assume cs:code,ds:data,ss:stack #定义代码段,数据段,栈段
data segment
dw 0123h,0456h,0789h,0abch,0defh,0fedh,0cbah,0987h
data ends
stack segment
dw 0,0,0,0,0,0,0,0
stack ends
code segment
start: #将CS:IP指向代码段,否则将从先定义的数据段执行
mov ax,stack
mov ss,ax
mov sp,16
mov ax,data
mov ds,ax
mov bx,0
mov cx,8
s: push [bx]
add bx,2
loop s
mov ax,4c00h
int 21h
code ends
end start
mov ax,[5+bx]等价于mov ax,5[bx]
si,di与bx功能相近,主要用于作为内存寻址的偏移地址
si、di不能拆分为两个8位寄存器
mov ax,[bx][si]
即为(ax)=((ds)*16+(bx)+(si))
偏移地址为(bx)+(si)+idata
常用格式如下:
mov ax,[bx+200+si]
mov ax,[200+bx+si]
mov ax,200[bx][si]
mov [bx].200[si]
mov [bx][si].200
and al,01010101b #按位与
or al,01010101b #按位或
又由于ASCII码中大小写字母的码值相差32,即00100000b
因此
and al,11011111b
#将小写字母转换为大写字母,若本来为大写,则保持不变
or al,00100000b
#将大写字母转换为小写字母,若本来为小写,则保持不变
由此可见,使用and和or指令,能够在大小写转换中不必判断字母原来的情况,能够在位的角度轻松完成,这也启发我们在做事的时候应该从多方面思考,也许从另一个角度来看,问题会明了许多。
寄存器bx,si,di,bp可以单独出现
或以特定的组合出现
mov ax,[bx]
mov ax,[si]
mov ax,[di]
mov ax,[bp]
mov ax,[bx+si]
mov ax,[bx+di]
mov ax,[bp+si]
mov ax,[bp+di]
mov ax,[bx+si+idata]
mov ax,[bx+di+idata]
mov ax,[bp+si+idata]
mov ax,[bp+di+idata]
即bx与bp不能同时出现,si与di不能同时出现
[bx]默认段寄存器为ds(数据段)
[bp]默认段寄存器为ss(栈段)
通常情况下,[bx+si+idata]的寻址方式主要用于访问结构体中的数据。bx用于定位结构体,idata用于定位结构体中的某一个数据项,si定位数组项中的某个元素可以用类c语言的书写方式,例如[bx].idata、[bx].idata[si]
#寄存器名
mov ax,1 #字操作
mov al,1 #字节操作
#操作符X ptr
mov word ptr ds:[0],1 #字操作
inc byte ptr ds:[0] #字节操作
除法指令,除数可以为16位或8位,可以在寄存器或内存单元中。
被除数在AX寄存器或DX和AX寄存器中。
注:在debug中插入汇编代码时,插入的数据默认是16进制的,比如mov bl,100就会提示error,这里的100指的是十进制的128
乘法指令,两个相乘的数要么均为8位、要么均为16位。
#例子:计算100x10
mov al,100
mov bl,10
mul bl
#例子:计算100x10000
mul ax,100
mov bx,10000
mul bx
db(define byte) 定义字节型数据
dw(define word) 定义字型数据
dd(define double word) 定义双字型(dword)数据
db 3 dup (0) #相当于db 0,0,0
db 3 dup (0,1,2) #相当于db 0,1,2,0,1,2,0,1,2
格式如下:
db 重复的次数 dup (重复的字节型数据)
dw 重复的次数 dup (重复的字型数据)
dd 重复的次数 dup (重复的双字型数据)
应用:
#定义一个容量200个字节的栈段
stack segment
db 200 dup(0)
stack ends
用于取得标号的偏移地址
assume cs:codesg
codesg segment
s: mov ax,bx
mov si,offset s #将mov ax,bx指令的偏移地址送入si中
mov di,offset s0 #将nop的偏移地址送入di中
mov ax,cs:[si]
mov cs:[di],ax
s0: nop
nop
codesg ends
end s
1.转移地址在目标指令中
assume cs:codesg
codesg segment
start: mov ax,0
jmp short s
add ax,1
s: inc ax
codesg ends
end start
2.转移地址在寄存器中
3.转移地址在内存中
mov ax,0123h
mov ds:[0],ax
jmp word ptr ds:[0]
#从内存单元地址处开始的两个字,高地址处为转移的目的段地址,低地址处为转移的目的偏移地址
#(CS)=(内存单元地址+2)
#(IP)=(内存单元地址)
#例如
mov ax,0123h
mov ds:[0],ax
mov word ptr ds:[2]:,0
jmp dword ptr ds:[0]
#执行后,(CS)=0,(IP)=0123h,CS:IP指向0000:0123
注:形如"jmp 2000:0100"的转移指令,是在Debug中使用的汇编指令,汇编编译器无法识别,在源程序中无法使用
有条件转移指令,所有有条件转移指令都是短转移
Jmp if CX == Zero
jcxz 标号 等价于 if((cx)==0) jmp short 标号;
assume cs:code
code segment
start: mov ax,2000h
mov ds,ax
mov bx,0
s:mov cl,[bx]
mov ch,0
jcxz ok
inc bx
jmp short s
ok:mov dx,bx
mov ax,4c00h
int 21h
code ends
end start
注:段内转移、jcxz、loop等转移指令对IP的修改是根据转移目的地址和转移起始地址之间的位移来进行的,这种设计,方便了程序段在内存中的浮动装配
由此可见,ret、retf指令与SS、SP寄存器有关,即与栈段相关联。
ret指令,相当于:
pop IP
retf指令,相当于:
pop IP
pop CS
CPU执行call指令时,进行两步操作
call指令实现转移的方法和jmp指令的原理相同
有如下应用形式
1.根据位移进行转移
形式:call 标号(将当前的IP压入栈,转到标号处执行指令)
相当于进行:
push IP
jmp near ptr 标号
注:call指令不能实现短转移
2.转移的目的地址在指令中
形式:call far ptr 标号(段间转移)
相当于进行:
push CS
push IP
jmp far ptr 标号
3.转移的目的地址在寄存器中
形式:call 16位寄存器
相当于进行:
push IP
jmp 16位寄存器
4.转移的目的地址在内存中
有两种形式
push IP
jmp word ptr 内存单元地址
push CS
push IP
jmp dword ptr 内存单元地址
由以上应用形式可知,call无法进行短转移,call标号、call16位寄存器、callword大小的内存单元地址时,仅将ip入栈,而call far ptr 标号、call dword ptr 内存单元地址时,先将CS入栈(为栈中高地址)再将IP入栈(为栈中低地址)。这一点无论是在retf时还是在call、jmp时,CS均在高地址,IP均在低地址,与数值在内存中高位存放在高地址、低位存放在低地址类似
assume cs:code
code segment
main: :
:
call sub1 ;调用子程序1
:
:
mov ax,4c00h
int 21h
sub1: : ;子程序1
:
call sub2 ;调用子程序2
:
:
ret ;子程序1返回
sub2: : ;子程序2
:
:
ret ;子程序2返回
code ends
end main
IP的栈操作可表示为
push IP ;保存主程序IP
push IP1 ;保存子程序1IP
pop IP1 ;恢复子程序1IP
pop IP ;恢复主程序IP
为了避免出现寄存器冲突的可能性,例如在主程序中使用了cx来控制循环,子程序中用cx控制循环时会出现修改cx值导致主程序循环无法按预期进行的问题。我们需要在编写子程序(即函数)时,在子程序的开始将子程序用到的寄存器中的内容压入栈中,在子程序返回前,从栈中恢复寄存器在主程序中的数值。
codesg segment
main:
mov cx,4
s: : #主函数中的循环
:
call sub
loop s
:
mov ax,4100h
int 21h
sub:
push cx #cx寄存器的值入栈
mov cx,3
l: : #子函数中的循环
:
loop l
pop cx #cx寄存器内容出栈
ret
codesg ends
flag寄存器:每一位都有专门的含义,为空的位没有任何含义
15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
OF | DF | IF | TF | SF | ZF | AF | PF | CF |
1.ZF标志位
记录相关指令执行后,其结果是否为零,如果为0,zf=1,反之,zf=0
2.PF标志位
记录相关指令执行后,其结果的所有位中1的个数是否为偶数,如果为偶数,pf=1,反之,pf=0
3.SF标志位
记录相关指令执行后,其结果是否为负,如果为负,sf=1,反之,sf=0
4.CF标志位
mov al,90h
add al,al ;执行后:(al)=20h,CF=1,记录了最高有效位向更高位的进位值
mov al,91h
sub al,92h ;执行后:(al)=ffh,CF=1
mov al,91h
sub al,al ;执行后:(al)=0,CF=0
将CF设置为0的方法:
sub ax,ax
5.OF标志位
记录有符号运算的结果是否发生了溢出,如果发生了溢出,OF=1,反之,OF=0
6.DF标志位
在串传送指令中,控制每次操作后si、di的增减
详情见串传送指令
带进位的加法指令,利用了cf位上记录的进位值
adc ax,bx #等价于(ax)=(ax)+(bx)+CF
#可以将add指令转换为add、adc指令的组合
add ax,bx
#即为
add al,bl
adc ah,bx
应用:
#计算1EF000H+201000H,结果存放在ax(高16位)、bx(低16位)中
mov ax,001EH
mov bx,0F000H
add bx,1000H ;低16位
adc ax,0020H ;高16位,加上进位值
带借位的减法指令,利用了cf位上记录的借位值
sbb ax,bx #等价于(ax)=(ax)-(bx)-CF
用法与adc类似
比较指令,cmp的功能相当于减法指令,不过不保存结果
根据相关标志位的值可以看出比较结果
对于无符号数
无符号ax与bx大小比较 | 标志位情况 |
---|---|
(ax)=(bx) | zf=1 |
(ax)!=(bx) | zf=0 |
(ax)<(bx) | cf=1 |
(ax)>=(bx) | cf=0 |
(ax)>(bx) | cf=0 && zf=0 |
(ax)<=(bx) | cf=1 || zf=1 |
总结:相等于否决定zf,相减为负与否决定cf
对于有符号数
标记位值 | 实际结果的正负 |
---|---|
sf=1,of=0 | 无溢出,结果为负 |
sf=1,of=1 | 有溢出,结果为非负 |
sf=0,of=0 | 无溢出,结果为非负 |
sf=0,of=1 | 有溢出,结果为负 |
条件转移指令通常配合cmp使用,通过检测标志寄存器,来决定是否修改IP
利用无符号数、有符号数(这里的结果为负或非负取决于上表中的sf、of值)比较结果的条件转移指令
指令 | 含义 | 无符号比较检测的相关标记位 | 有符号比较检测的相关标记位 |
---|---|---|---|
je | 等于则转移 | zf=1 | zf=1 |
jne | 不等于则转移 | zf=0 | zf=0 |
jb | 低于则转移 | cf=1 | 结果为负 |
jnb | 不低于则转移 | cf=0 | 结果为非负 |
ja | 高于则转移 | cf=0 && zf=0 | 结果为非负&&zf=0 |
jna | 不高于则转移 | cf=1 || zf=1 | 结果为负||zf=1 |
注:当条件转移指令不与cmp指令配合使用时,将根据标志寄存器当前的状态来决定是否转移
#将ds:si指向的内存单元中的字节送入es:di中,然后根据df的值将si和di递增或递减
movsb ;类似于mov es:[di],byte ptr ds:[si] 并无该指令!
#将ds:si指向的内存单元中的字送入es:di中,然后根据df的值将si和di递增2或递减2
movsw ;类似于mov es:[di],word ptr ds:[si] 并无该指令!
rep指令的作用是根据cx的值,重复执行后面的串传送指令
rep movsb
#等价于
s:movsb
loop s
rep movsw
#等价于
s:movsw
loop s
cld指令,将标志寄存器的df位置0
std指令,将标志寄存器的df位置1
| 结果为非负 |
| ja | 高于则转移 | cf=0 && zf=0 | 结果为非负&&zf=0 |
| jna | 不高于则转移 | cf=1 || zf=1 | 结果为负||zf=1 |
注:当条件转移指令不与cmp指令配合使用时,将根据标志寄存器当前的状态来决定是否转移
#将ds:si指向的内存单元中的字节送入es:di中,然后根据df的值将si和di递增或递减
movsb ;类似于mov es:[di],byte ptr ds:[si] 并无该指令!
#将ds:si指向的内存单元中的字送入es:di中,然后根据df的值将si和di递增2或递减2
movsw ;类似于mov es:[di],word ptr ds:[si] 并无该指令!
rep指令的作用是根据cx的值,重复执行后面的串传送指令
rep movsb
#等价于
s:movsb
loop s
rep movsw
#等价于
s:movsw
loop s