16位汇编笔记7-11
7.定位内存地址
7.1 and & or
用and
将操作对象相应位设为0;
用or
将操作对象相应位设为1;
将al的第0位设为0
and al,11111110B
将al的第0位设为1
or al,00000001B
大写变小写
and al,11011111B
小写变大写
or al,00100000B
7.3 字符数据
assume cs:code, ds:data
data segment
db 'unix'
data ends
code segment
start:
mov al,'a'
mov ax,4c00h
int 21h
code ends
end start
mov al,'a'
相当于mov al,61H
7.5 [bx+idata]
[bx+idata]
表示一个内存单元,偏移地址为(bx)+idata
.
也可以写为:
[idata+bx]
idata[bx]
[bx].idata
2000:1000 BE 00 06 00 00 ...
mov ax,2000H
mov ds,ax
mov bx,1000H
mov ax,[bx] ; (ax)=00BEH
mov cx,[bx+1] ;(cx)=0600H
add cx,[bx+2] ;(cx)=0606H
将第一个字符串转为大写,第二个转为小写。
assume cs:code, ds:data
data segment
db "Basic"
db "MiNix"
data ends
code segment
start:
mov ax,data
mov ds,ax
mov bx,0
mov cx,5
s: mov al,[bx]
and al,11011111B
mov [bx],al
inc bx
loop s
mov bx,5
mov cx,5
s0: mov al,[bx]
or al,00100000B
mov [bx],al
inc bx
loop s0
code ends
end start
使用[bx+idata]
mov ax,data
mov ds,ax
mov bx,0
mov cx,5
s: mov al,[bx]
and al,11011111B
mov [bx],al
mov al,[bx+5]
or al,00100000B
mov [bx+5],al
inc bx
loop s
或者下面这样
mov ax,data
mov ds,ax
mov bx,0
mov cx,5
s: mov al,0[bx]
and al,11011111B
mov 0[bx],al
mov al,5[bx]
or al,00100000B
mov 5[bx],al
inc bx
loop s
c语言:a[i],b[i]
汇编: 0[bx],5[bx]
[bx+idata]
为高级语言实现数组提供了便利机制。
7.8 [bx+si]和[bx+di]
si,di
与bx
功能相近,但si,di
不能分成两个8位寄存器使用。以si
为例。
[bx+si]
[si+5]
5[si]
7.9 [bx+si+idata]和[bx+di+idata]
以mov ax,[bx+si+5]
为例,可以写成
mov ax,[bx+5+si]
mov ax,[5+bx+si]
mov ax,5[bx][si]
mov ax,[bx].5[si]
mov ax,[bx][si].5
struct Person{
int age
char name[10];
}
如果有一个Person类型位于ds:0,name偏移为04h
mov bx,04h
[bx].0[si]则可以访问name的每个字节
7.10 应用
从[idata]
到[bx+si+idata]
的演化:
[idata]
–>[bx]
–>[bx+idata]
–>[bx+si]
–>[bx+si+idata]
把每个单词的首字母改为大写
assume cs:code,ds:data
data segment
db "1. file "
db "2. edit "
db "3. search "
db "4. view "
db "5. options "
db "6. help "
data ends
code segment
start:
mov ax,data
mov ds,ax
mov bx,0
mov cx,6
s:
mov al,[bx+3]
and al,11011111B
mov [bx+3],al
add bx,16
loop s
mov ax,4c00h
int 21h`
code ends
end start
把每个单词改为大写
assume cs:code,ds:data
data segment
db "mac "
db "ibm "
db "dos "
db "win "
data ends
code segment
start:
mov ax,data
mov ds,ax
mov ax,0020h
mov ss,ax
mov sp,10h
;如果cx不利用栈存放当前值,外层loop指令cx为0-1==fffff,进入死循环。
mov bx,0
mov cx,4 ; 4 rows
s0:
mov si,0
push cx
mov cx,3 ;3 columns/letters
s1:
mov al,[bx+si]
and al,11011111B
mov [bx+si],al
inc si
loop s1
add bx,10h
pop cx
loop s0
mov ax,4c00h
int 21h
code ends
end start
上面的程序如果是修改这样的数据:
data segment
db "1. mac "
db "2. ibm "
db "3. dos "
db "4. win "
data ends
则要使用相对基址加变址寻址方式:
mov al,[bx+3+si]
and al,11011111B
mov [bx+3+si],al
8 数据处理的两个基本问题
8.1 bx,bp,si,di
在[]
中,只能有这四个寄存器,而且有四种组合:[bx/bp+si/di]
。
不存在[ax],[bx+bp],[si+di]
这种用法。
在[]
中使用bp
,若指令没有显性给出段地址,则段地址默认在ss
中,不使用bp
则默认ds
。
8.2 数据位置
数据处理有3类:
- 读取
- 写入
- 运算
数据可以出现在3个地方:
- cpu内部
- 内存
- 端口
指令 | 执行前数据位置 |
---|---|
mov bx,[0] | 内存,ds:0 |
mov bx,ax | cpu内部,ax |
mov ax,1 | cpu内部,指令缓冲器 |
汇编有3个概念表达数据位置:
- 立即数/常数,
idata
,执行前在指令缓冲器中 - 寄存器
- 段地址
SA
和偏移地址EA
,段地址可默认,可显性
8.3寻址方式
再次强调,有效地址存在si,di,bx,bp
中,前三个的段地址默认为ds,bp的段地址默认为ss。
寻址方式 | 名称 | 常用格式 |
---|---|---|
mov ax, idata | 立即寻址 | |
mov ax,[0] | 直接寻址 | |
mov ds,ax | 寄存器寻址 | |
mov ax,[si] | 寄存器间接寻址 | |
mov ax, [di + idata] | 寄存器相对寻址 | 结构体:[bx].idata 数组: idata[si] 二维数组: [bx][idata] |
mov ax,[bx+si] | 基址变址寻址 | 二维数组:[bx][si] |
mov ax,[bx+si+idata] | 相对基址变址寻址 | 结构中的数组项:[bx].idata[si] 二维数组: idata[bx][si] |
8.4 处理数据长度
寄存器使用al,ah
等,则是字节操作,否则是字操作。
没有寄存器名时,用操作符[byte|word] ptr
指明内存单元长度
eg.
mov word ptr ds:[0],1
mov byte ptr ds:[0],1
特殊的,push
只进行字操作。
assume cs:codesg
codesg segment
db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
start:
mov ax,codesg
mov ds,ax
mov byte ptr ds:[0],0fh
mov word ptr ds:[2],0fh
mov ax,4c00H
int 21H
codesg ends
end start
8.5 除法指令div
指令格式:
- `div reg``
- ``div 内存单元`
需要注意:
- 除数:有8位和16位,在
reg
或内存单元
中。 - 被除数:默认在
ax
,或,ax和dx
中。若除数为8位,则被除数位16位,默认在ax中;若除数为16位,则被除数为32位(dd
类型),在ax
中放低16位,dx
中放高16位. - 结果:若除数为8位,则
al
存储商,ah
存储余数;若除数为16位,则ax
存放商,dx
存储余数。
除数(小) | 被除数(大) | 结果(低商高余) |
---|---|---|
8位byte | 16位word (ax ) | ax 中 |
16位word | 32位dword (dx,ax ) | dx,ax |
div byte ptr ds:[0]
含义
(al) = (ax) / ( (ds)*16 + 0 )的商
(ah) = (ax) / ( (ds)*16 + 0 )的余数
div word ptr es:[0]
含义
(ax) = [(dx)*10000H + (ax)] / ((es)*16 + 0)的商
(dx) = [(dx)*10000H + (ax)] / ((es)*16 + 0)的余数
8.6 伪指令dd
dd
:define double word
data segment
db 1 ;01H @ data:0,1 byte
dw 1 ;0001H @ data:1,1 word
dd 1 ;0000 0001H @ data:3,2 word
data ends
8.7 dup
和db,dw,dd
一样,由编译器识别处理。并与这些数据定义伪指令配合使用,进行数据的重复。
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 2 dup('ab','AB') ;db,'abABabAB'
dw,dd
用法相同。
9 转移指令
分类:
- 无条件转移指令,如
jmp
- 条件转移指令
- 循环指令,如
loop
- 过程,相当于函数
- 中断
9.1 操作符offset
是由编译器处理的伪指令,功能是取得标号的偏移地址。
start:
mov ax,offset start ; mov ax,0 occupy 3 bytes
s:
mov ax,offset s ;mov ax,3
把s的一条指令复制到s0
s:
mov ax,bx ; occupy 2 bytes
mov si,offset s
mov di,offset s0
mov ax,cs:[si]
mov cs:[di],ax
s0:
nop ;occupy 1 byte
nop
9.2 jmp
jmp
:无条件转移指令,可修改ip
或同时修改cs
和ip
的指令。
需要两种信息:
- 转移的目的地址
- 转移的距离(段间转移,段内短转移,段内近转移)
9.2.1 依据位移转移的jmp
jmp short 标号
,段内短转移,可越过-128~127
个字节。它的功能是实现(ip)=(ip)+8位位移
mov ax,0
jmp short s
inc ax
s:inc ax
立即数操作,机器码中会包含这个数。但jmp 标号
机器码中不会出现标号偏移地址,而是转移的距离。
jmp
的机器码为EB转移距离:标号地址-
jmp
下一指令地址
jmp near ptr 标号
,段内近转移,它的功能是实现(ip)=(ip)+16位位移
,可越过-32768~32767
个字节
9.2.2 目的地址在指令中的jmp
jmp far ptr 标号
,实现段间转移,又称远转移。
(cs)
=标号所在段的段地址,(ip)
=标号所在段中的偏移地址。
mov ax,0
jmp far ptr s ; EA 0B01 BD0B
db 256 dup(0)
s:add ax,1 ;0BBD:010B
远转移的
jmp
机器码为EA。
9.2.3 转移地址在寄存器中的jmp
jmp reg
,即(ip)=(reg)
,功能是实现ip = (16位寄存器)
9.2.4 转移地址在内存中的jmp
jmp word ptr 内存单元地址
,段内转移,内存单元中的字存放目的偏移地址。
内存单元地址可用任一寻址方式给出。
jmp dword ptr 内存单元地址
,段间转移,(cs)=(内存单元地址+2)
即高地址处的字;(ip)=(内存单元地址)
mov ax,0123h
mov ds:[0],ax
mov word ptr ds:[2],0
jmp dword ptr ds:[0]
执行后,(cs)=0000,(ip)=0123h
9.7 jcxz
jcxz(jump if cx zero):有条件转移指令,格式:jcxz 标号
所有有条件转移指令都是短转移,也包含转移位移,ip
修改范围为-128~127个字节。
if((cx)==0) jmp short 标号
;
利用jcxz,在内存2000h段查找第一个值为0的字节,找到后把它的偏移地址存到dx中。
assume cs:codesg
codesg segment
start:
mov ax,2000h
mov ds,ax
mov bx,0
s:
______
______
______
______
jmp short s
ok:
mov dx,bx
mov ax,4c00h
int 21h
codeseg ends
end start
答:
mov cl,[bx]
mov ch,0
jcxz ok
inc bx
9.8 loop
循环指令:loop 标号
所有循环指令只进行短转移,含转移位移,ip
修改范围为-128~127个字节。
(cx)--;
if ((cx)!=0) jmp short 标号;
还是上一题
assume cs:codesg
codesg segment
start:
mov ax,2000h
mov ds,ax
mov bx,0
s:
mov cl,[bx]
mov ch,0
______
inc bx
jmp short s
ok:
dec bx
mov dx,bx
mov ax,4c00h
int 21h
codeseg ends
end start
答:
inc cx
9.9 根据位移进行转移的意义
jmp short 标号
jmp near ptr 标号
jcxz 标号
loop 标号
根据位移进行转移,方便程序不断封装和调用;如果机器码为目的地址,而目标指令不在该处,程序就会出错。
9.10 编译器对转移位移超界的检测
位移太大,编译时会显示jump destination too far:by X bytes(s)
。x
为超界字节数。
assume cs:codesg
codesg segment
mov ax,4c00h
int 21h
start:
mov ax,0
s:
nop ;will become EBF6 , which means jmp 0000 here
nop
mov di,offset s
mov si,offset s2
mov ax,cs:[si]
mov cs:[di],ax
s0:
jmp short s ;EBF6
s1:
mov ax,0
int 21h
mov ax,0
s2:
jmp short s1 ;EBF6,which means jmp short s1 here
nop
codesg ends
end start
颜色
B8000H-BFFFFH
共32kB的空间为80*25彩色字符模式的显示缓冲区。这里的数据会显示在屏幕上,可显示25行,每行80个字。显示缓冲区分为8页,每页4kB。
一个字符占2个字节,分别存放ascii码和属性。
属性字节格式:
7 | 654 | 3 | 210 |
---|---|---|---|
闪烁 | 背景(RGB) | 高亮 | 前景(RGB) |
一屏4000个字节,通常b8000h-b8f9fh的4000个字节会显示出来。
assume cs:codesg,ds:datasg,ss:stacksg
datasg segment
db "welcome to masm!" ;index:si
db 00000010B,00100100B,01110001B ;index:bx
datasg ends
stacksg segment
db 16 dup(0)
stacksg ends
codesg segment
start:
mov ax,datasg
mov ds,ax
mov ax,stacksg
mov ss,ax
mov sp,10h
mov ax,0b872h ;screen buffer segment address
mov es,ax
xor bx,bx ;screen buffer offset address
mov cx,3 ;3 strings
s0:
push cx
push ax
push bx
mov si,0 ;datasg index
mov di,0 ;ax character index
mov cx,10h
s1:
mov al,ds:[si]
mov es:[di],al
inc si
add di,2
loop s1
mov di,1 ;ax attribute index
pop bx
mov al,ds:10h[bx]
inc bx
mov cx,10h
s2:
mov es:[di],al
add di,2
loop s2
pop ax
add ax,0ah ;the next string
pop cx
loop s0
mov ax,4c00h
int 21h
codesg ends
end start
10 call和ret
call
和ret
都是转移指令,都修改ip
,或同时修改cs
和ip
。
10.1 ret和retf
ret
用栈中的数据修改ip
,实现近转移。相当于pop ip
.
(ip) = ((ss)*16 + (sp))
(sp) = (sp) + 2
retf
用栈中的数据修改cs
和ip
,实现远转移。相当于pop ip ;pop cs
.
(ip) = ((ss)*16 + (sp))
(sp) = (sp) + 2
(cs) = ((ss)*16 + (sp))
(sp) = (sp) + 2
两个指令都没有参数.
10.2 call
call
不能实现短转移。转移原理和jmp
相同,
10.2.1 依据位移转移
call 标号
操作:
(sp)=(sp)-2 ((ss)*16+(sp))=(ip)
(ip)=(ip)+16位位移
相当于push ip; jmp near ptr 标号
。
10.2.2 目的地址在指令中
call far ptr 标号
,可实现段间转移。
操作:
(sp)=(sp)-2 ((ss)*16+(sp))=(cs)
(sp)=(sp)-2 ((ss)*16+(sp))=(ip)
(cs)=标号段地址 (ip)=标号在段中偏移地址
相当于push cs; push ip; jmp far ptr 标号
10.2.3 转移地址在寄存器中
call 16位reg
操作:
(sp)=(sp)-2 ((ss)*16+(sp))=(ip)
(ip)=(16位reg)
相当于push ip; jmp 16位reg
10.2.4 转移地址在内存中
有两种格式:
call word ptr 内存单元地址
相当于push ip; jmp word ptr 内存单元地址
call dword ptr 内存单元地址
相当于push cs; push ip; jmp dword ptr 内存单元地址
mov sp,10h
mov ax,0123h
mov [0],ax
mov word ptr [2],0
call dword ptr [0]
执行后,(cs)=0,(ip)=0123h,(sp)=0ch
所以,仍然是段地址在高位,偏移地址在地位。
10.7 call与ret配合
计算2的(cx)次方
assume cs:code
code segment
start: mov ax,1
mov cx,3
call s
mov bx,ax
mov ax,4c00h
int 21h
s: add ax,ax
loop s
ret
code ends
end start
10.8 mul
mul reg
或mul 内存单元
注意:
- 两个相乘的数,位数应相同(8或16)。若都是8位,一个默认在
al
中,另一个在8位reg或内存字节单元
中;如果是16位,一个默认在ax
中,另一个在16位reg或内存字单元
中。 - 结果:8位乘法,结果默认在
ax
中;16位乘法,默认高位在dx
中,低位在ax
中。
mul byte ptr ds:[0]
,即(ax)=(al)*((ds)*16+0)
mul word ptr [bx+si+8]
,即(ax)
存放(ax) * ((ds)*16 + (bx) + (si) + 8)
的低16位,(dx)
存放高16位。
10.9 模块化程序设计
10.10 参数结果传递
用寄存器存储参数和结果是最常用的方法。写子程序时要有详细说明。
; calc n*n*n, n is stored by bx
; cube(bx)
; return dx,ax
assume cs:codesg,ds:datasg
datasg segment
dw 1,2,3,4,5,6,7,8
dd 0,0,0,0,0,0,0,0
datasg ends
codesg segment
start:
mov ax,datasg
mov ds,ax
mov si,0
mov di,16
mov cx,8
s:
mov bx,[si]
call cube
mov [di],ax
mov [di].2,dx
add si,2
add di,4
loop s
mov ax,4c00h
int 21h
cube:
mov ax,bx
mul bx
mul bx
ret
codesg ends
end start
10.11 批量数据的传递
对于多个参数,我们可以将数据放到内存中,然后把这段内存的首地址存在寄存器中,传递给子程序。
返回结果同理。
将data段的字符串转为大写
assume cs:codesg,ds:datasg
datasg segment
db "conversation"
datasg ends
codesg segment
start:
mov ax,datasg
mov ds,ax
xor si,si
mov cx,12
call capital
mov ax,4c00h
int 21h
capital:
and byte ptr [si],11011111B
inc si
loop capital
ret
codesg ends
end start
传递参数还有一种通用方法是使用栈。
3种传参方法:
- 寄存器
- 内存
- 栈
将data段以0结尾的字符串转为大写
因为用0来检测结尾,所以不需要设置cx
assume cs:codesg,ds:datasg
datasg segment
db "linux",0
db "windows",0
datasg ends
codesg segment
start:
mov ax,datasg
mov ds,ax
xor si,si
mov cx,2
s:
push cx
call capital
inc si
pop cx
loop s
mov ax,4c00h
int 21h
capital:
mov cl,[si]
mov ch,0
jcxz ok
and byte ptr [si],11011111B
inc si
jmp short capital
ok: ret
codesg ends
end start
实验十
show_str子程序
功能:在指定位置用指定颜色显示一个用0结尾的字符串
参数:(dh)
为行号,取值范围0-24
,(dl)
为列号,取值范围0-79
。
返回:无
举例:在第8行第3列,绿色显示data段字符串
assume cs:codesg,ds:datasg
datasg segment
db "welcome to masm!",0
datasg ends
codesg segment
start:
mov ax,datasg
mov ds,ax
mov si,0
mov dh,8
mov dl,3
mov cl,00000010B
mov ch,0
call show_str
mov ax,4c00h
int 21h
show_str:
mov ax,0b800h
mov es,ax
dec dh
mov al,160 ;bytes per row
mul dh
mov di,ax
dec dl
mov al,2 ;bytes per character
mul dl
add di,ax ; es:di is the screen buffer
mov bl,cl ;mov the color to bx
mov ch,0
s:
mov cl,[si]
jcxz ok ; 0?
mov es:[di],cl ;copy the character
mov es:[di+1],bl ;copy the color
add di,2
inc si
jmp short s
ok: ret
codesg ends
end start
divdw除法溢出
做除法时,如果结果的商大于
al
或ax
能存储的最大值,会引发cpu的一个内部错误:除法溢出。
功能:进行不会溢出的除法运算,被除数为dword
型,除数为word
型,结果为dword
型。
参数:dx
存储dword
高16位,ax
存储低16位;cx
存储除数。
返回:dx
存储结果的高16位,ax
存储低16位;cx
存储余数。
公式:dx|ax / cx = int(dx / cx) * 10000H + [rem(dx / cx) * 10000H + ax] / cx
,可以自己写个十进制除法体会一下.
举例:
mov ax,4240H
mov dx,000fH
mov cx,0ah
call divdw
结果:(dx)=0001h,(ax)=86a0h,(cx)=0
assume cs:codesg,ss:stacksg
stacksg segment
dw 8 dup(0)
stacksg ends
codesg segment
start:
mov ax,stacksg
mov ss,ax
mov sp,10h
mov ax,4240H
mov dx,000fH
mov cx,0ah
call divdw
mov ax,4c00h
int 21h
divdw:
push ax ;push lower 16 bits
mov ax,dx
xor dx,dx
div cx ;handle higher 16 bits
mov bx,ax ;int(dx / cx)
pop ax
div cx ;handle lower 16 bits
mov cx,dx ;the final remainder
mov dx,bx
ret
codesg ends
end start
dtoc数值显示
功能:将word
型数据转变为十进制数的字符串,以0结尾。
参数:ax
存储word
数据,ds:si
指向字符串存放位置的首地址。
返回:无
思路是连续除以10d
取余,直到商为0。本程序会调用showstr
assume cs:codesg,ds:datasg,ss:stacksg
datasg segment
db 10 dup(0)
datasg ends
stacksg segment
dw 8 dup(0)
stacksg ends
codesg segment
start:
mov ax,stacksg
mov ss,ax
mov sp,10h
mov ax,datasg
mov ds,ax
xor di,di
mov ax,12666 ;31h,32h,36h,36h,36h,0
call dtoc
mov dh,8
mov dl,3
mov cl,00000010B
mov ch,0
call show_str
mov ax,4c00h
int 21h
dtoc:
push ax
push bx
push cx
push dx
mov bx,0 ; store how many digits
s0:
mov cx,10d
mov dx,0
div cx
add dx,30h
push dx
inc bx
mov cx,ax
jcxz s1 ; int(ax/10d)==0
jmp short s0
s1:
mov cx,bx
s2:
pop ax
mov [di],al
inc di
loop s2
pop dx
pop cx
pop bx
pop ax
ret
show_str:
mov ax,0b800h
mov es,ax
dec dh
mov al,160 ;bytes per row
mul dh
mov di,ax
dec dl
mov al,2 ;bytes per character
mul dl
add di,ax ; es:di is the screen buffer
mov bl,cl ;mov the color to bx
mov ch,0
s:
mov cl,[si]
jcxz ok ; 0?
mov es:[di],cl ;copy the character
mov es:[di+1],bl ;copy the color
add di,2
inc si
jmp short s
ok: ret
codesg ends
end start
11 标志寄存器flag
flag寄存器用来存储程序状态字psw
。它有3种作用:
- 存储相关指令的某些执行结果
- 为cpu执行相关指令提供行为依据
- 控制cpu的相关工作方式
flag按位起作用,每一位有专门的含义。
| f | e | d | c | b | a | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
| | | | | of | df | if | tf | sf | zf | | af | | pf | | cf |
1,3,5,12,13,14,15在8086中没有含义,0,2,4,6,7,8,9,10,11有特殊含义。
从低位到高位:cpazstido
在debug中,如下显示:
标志 | 1 | 0 |
---|---|---|
of | OV | NV |
df | DN | UP |
if | EI | DI |
sf | NG | PL |
zf | ZR | NZ |
af | AC | NA |
pf | PE | PO |
cf | CY | NC |
运行指令时,要注意指令会影响哪些位。
11.1 zf
zf(zero flag)在第6位,记录结果是否为0,是则zf=1,否则为0.
mov ax,1
and ax,0
则zf=1
运算指令会影响flag,传送指令没有影响。
11.2 pf
pf(parity flag)在第2位,是奇偶校验位,如果结果bit位中1的个数是偶数则pf=1,奇数则pf=0。可以联系单词0dd来记。
pf不常用。
11.3 sf
sf(sign flag)在第7位,符号标志位,结果为负则sf=1,非负则sf=0。
sf在无符号运算时没有意义。
11.4 cf
cf(carry flag)在第0位,进位标志位,记录了无符号数运算最高有效位向更高位的进位值,或从更高位的借位值。
cf是对无符号数运算有意义的标志位。例如,他可以充当8位数据运算的第9位。
11.5 of
of(overflow flag)在第11位,记录有符号数运算是否产生溢出。
of是对有符号数运算有意义的标志位,就像进位只针对无符号数一样。
11.6 adc指令
带进位加法指令,add carry。它用到了cf。
格式:adc ax,bx
功能:(ax) = (ax) + (bx) + cf
如果cf由sub
设置,则含义是借位值;如果由add
设置,则含义是进位值。
下面的指令和add ax,bx
有相同结果:
add al,bl
adc ah,bh
adc
和add
配合可以对更大的数进行加法运算。
计算1E F000H + 20 1000H
,结果高16位存在ax中,低16位存在bx中。
mov ax,001eh
mov bx,0f000h
sub cx,cx ;set cf 0
add bx,1000h
adc ax,0020h
大数运算,可以用内存来存储。运算前先将cf置0.
assume cs:codesg,ds:datasg
datasg segment
db 88h,88h,88h,88h,88h,88h,88h,88h,88h,88h,88h,88h,88h,88h,88h,88h
db 11h,11h,11h,11h,11h,11h,11h,11h,11h,11h,11h,11h,11h,11h,11h,11h
datasg ends
codesg segment
start:
mov ax,datasg
mov ds,ax
mov si,0
mov di,10h
call add128
mov ax,4c00h
int 21h
add128:
push si
push di
sub ax,ax ;set cf 0
mov cx,0fh
s:
mov ax,[si]
adc ax,[di]
mov [si],ax
inc si
inc si
inc di
inc di ; add will influence cf
loop s
pop di
pop si
ret
codesg ends
end start
11.7 sbb指令
带错位减法指令,subtract with borrow.
格式:sbb ax,bx
功能:(ax) = (ax) - (bx) - cf
利用sbb
可以对更大的数进行减法运算。
计算003E 1000H - 0020 2000H
mov bx,1000h
mov ax,003eh
sub bx,2000h
sbb ax,0020h
11.8 cmp指令
比较指令,相当于减法指令,但不保存结果,仅对flag进行设置。
cmp ax,ax
zf = 1
pf = 1
sf = 0
cf = 0
of = 0
执行后,主要观察zf
、cf
。
只由sf
并不能判断大小关系,因为有符号数可能会溢出。所以要根据sf
和of
判断结果的正负。of
为1时,结论相反。
对于cmp ah,bh
of | sf | 结论 |
---|---|---|
0 | 0 | (ah) > (bh) |
0 | 1 | (ah) < (bh) |
1 | 0 | (ah) < (bh) |
1 | 1 | (ah) > (bh) |
11.9 检测比较结果的条件转移指令
这些指令与cmp
配合,根据被cmp
指令影响的标志位跳转。
cmp
可以同时进行无符号数和有符号数的比较,只不过改变了不同的标志位。所以这些转移指令也分为两种:
- 根据无符号数比较结果转移,检测
zf
和cf
。 - 根据有符号数比较结果转移,检测
zf
、of
和sf
。
无符号数比较结果相关指令:
指令 | 含义 | 执行时的标志位 |
---|---|---|
je | jump if equal | zf=1 |
jne | … | zf=0 |
jb | jump if below | cf=1 |
jnb | … | cf=0 |
ja | jump if above | cf=0,zf=0 |
jna | … | cf=1 或zf=1 |
如果(ah)=(bh),则(ah)=(ah)+(bh),否则(ah)=(ah)+(bh)
cmp ah,bh
je f
add ah,bh
jmp short ok
f:
add ah,ah
ok:
ret
判断8的个数
assume cs:codesg,ds:datasg
datasg segment
db 8,1,1,8,2,2,8,8
datasg ends
codesg segment
start:
mov ax,datasg
mov ds,ax
mov si,0
xor ax,ax
mov cx,8
s:
cmp byte ptr [si],8
;or je ..
jne next
inc ax
next:
inc si
loop s
mov ax,4c00h
int 21h
codesg ends
end start
判断大于8的个数
assume cs:codesg,ds:datasg
datasg segment
db 8,1,1,8,2,2,9,8
datasg ends
codesg segment
start:
mov ax,datasg
mov ds,ax
mov si,0
xor ax,ax
mov cx,8
s:
cmp byte ptr [si],8
jna next
inc ax
next:
inc si
loop s
mov ax,4c00h
int 21h
codesg ends
end start
11.10 df标志和串传送指令
方向标志位direction flag在第10位,在串处理指令中控制每次操作后si,di
的增减。
df=0
,递增;df=1
,递减。
串传送指令movsb
(move string byte)功能:
mov es:[di],byte ptr ds:[si]
,即以字节为单位传送;8086不支持这种指令,只是描述,即((es)*16 + (di)) = ((ds)*16 + (si))
- if df=0,
inc si;inc di
;if df=1,dec si; dec di
movsw
则是传送一个字,每次si,di
加2或减2.
movsb
和movsw
一般和rep
配合使用。rep
是根据cx
重复执行后面的串指令,即repeat。
rep movsb
功能:传送(cx)
个字节。
s: movsb
loop s
cld
指令:将df
设为0
std
指令:将df
设为1
assume cs:codesg,ds:datasg
datasg segment
db 1,2,3,4,5,6,7,8
db 0,0,0,0,0,0,0,0
datasg ends
codesg segment
start:
mov ax,datasg
mov ds,ax
mov es,ax
mov si,0
mov di,8
mov cx,8
cld
rep movsb
mov ax,4c00h
int 21h
codesg ends
end start
11.11 pushf和popf
pushf
:将flag寄存器的值压栈
popf
:从栈中弹出数据送入flag寄存器