一. cmp 指令
1. cmp 指令功能
cmp (compare) 是比较指令,cmp 的功能相当于减法指令,只是不保存结果,但会根据结果对标志寄存器进行设置,其它相关指令就可以通过识别这些被影响的标志寄存器的位来得知比较结果。
2. cmp指令格式
cmp 操作对象1, 操作对象2。
3. cmp 指令的 CPU 工作原理
对于指令:cmp ax, bx
CPU 执行该指令,会计算 (ax) - (bx) ,然后根据结果对 CPU 的标志寄存器进行设置,那么,我们可以做以下分析:
- 1. 如果 ax = bx,那么 ax - bx = 0,所以,ZF = 1
- 2. 如果 ax != bx,那么 ax - bx != 0,所以,ZF = 0
- 3. 如果 ax < bx,那么 ax - bx < 0,ax 的最高有效位将向假想的更高位借位,所以,CF = 1
- 4. 如果 ax >= bx,那么 ax - bx >= 0,不用借位,所以,CF = 0
- 5. 如果 ax > bx,那么 ax - bx > 0,所以,ZF = 0,不用借位,所以,CF = 0
- 6. 如果 ax <= bx,那么 ax - bx <= 0,这时可能结果为 0 或可能要借位,所以 ZF = 1 或 CF = 1
tips:
关于标志寄存器相关位表示的含义,可以看链接里博文的介绍汇编语言:标志寄存器ZF、PF、SF、CF、OF、DF、IF、AF_汇编pf标志位-CSDN博客
那么对于CPU来说,执行 cmp ax bx 指令后,CPU 就可以通过标志寄存器对应位的值,从而知道比较 ax,bx 的大小的结果。
- 1. 如果 ZF = 1,那么 ax = bx
- 2. 如果 ZF = 0,那么 ax != bx
- 3. 如果 CF = 1,那么 ax < bx
- 4. 如果 CF = 0,那么 ax >= bx
- 5. 如果 CF = 0 && ZF = 0,那么 ax > bx
- 6. 如果 ZF = 1 || CF = 1,那么 ax <= bx
二. je 指令
je(jump equal),等于则转移,检测的相关的标志:ZF = 1。
指令格式:je 标号
示例:
je s ;等于则跳转到 s 标号处执行指令,CPU实际是检测 ZF = 1
assume cs:code, ds:data
data segment
db 'ax = bx',0
db 'ax != bx',0
data ends
code segment
start:
mov ax, data
mov ds, ax
mov ax, 3 ;ax = 3
mov bx, 3 ;bx = 3
cmp ax, bx ;计算 ax - bx,并根据结果设置标志寄存器
je ax_e_bx ;等于则跳转,CPU实际是检测 ZF = 1
mov cl, 4 ;红色
call size_str ;计算第一个显示的字符串占用的字节数,结果放在 ax 中
mov si, ax ;si = ax,指向第二个字符串的首地址
jmp short ok ;jmp短转移指令,相当于当前 IP,能转移的范围在:-128 ~ 127,即:IP - 128 ~ IP + 127 这个偏移地址
ax_e_bx:
mov cl, 2 ;绿色
mov si, 0 ;指向第一个字符串的首地址
ok:
mov dh, 14 ;14行
mov dl, 32 ;32列
call show_str ;显示字符串
mov ax, 4c00h
int 21h
;名称:size_str
;功能:求一个以0结尾的字符串的长度
;参数:ds:si指向字符串的首地址
;返回:求的字符串长度存储在 ax 中
size_str:
push cx
mov ch, 0
mov ax, 1
loop_size_str:
mov cl, ds:[si]
jcxz ok_size_str
inc ax
inc si
loop loop_size_str
ok_size_str:
pop cx
ret
;名称:show_str
;功能:在指定的位置,用指定的颜色,显示一个以0结尾的字符串
;参数:dh=行号(取值范围:0-24),dl=列号(取值范围:0-79)
; cl=颜色,ds:si指向字符串的首地址
;返回值:无
show_str:
;B8000h-BFFFFh共32kb,是80 * 25彩色字符模式的显示缓冲区,每页4kb(4000字节),显示器可以显示任意一页的内容。
;一般情况下,显示第0页的内容, 也就是说,B8000h ~ B8F9Fh 这4000个字节的内容会显示在显示器上。每行80个字,
;每个字占2个字节,低字节存放字符的assii码,高字节为属性字节。所以每行占160个字节
mov ax, 0B800h ;显示缓冲区段地址
mov es, ax
;行
mov al, 160 ;每行160个字节
mul dh ;8位乘法,一个数默认在al中,另一个在dh中,结果放在ax中
mov bx, ax ;行偏移字节
;列
mov al, 2 ;一个字占2个字节,低字节存放字符的assii码,高字节为属性字节
mul dl ;8位,乘法,一个数默认在al中,另一个在dl中,结果放在ax中
add bx, ax ;行偏移字节 + 列偏移字节 = 相对于显示缓冲区段地址总的偏移地址
mov ch, 0
mov dl, cl ;循环中需要用到cx寄存器,所以把存放字体属性的字节存放在dl中
loop_show_str:
mov cl, ds:[si]
jcxz ok_show_str ;cx = 0,跳转到标号处执行,否则,继续顺序往下执行
mov es:[bx], cl
mov es:[bx+1], dl
inc si
add bx, 2
loop loop_show_str
ok_show_str:
ret
code ends
end start
三. jne 指令
jne(jump not equal),不等于则转移,检测的相关的标志:ZF = 0。
指令格式:jne 标号
示例:
jne s ;不等于则跳转到 s 标号处执行指令,CPU实际是检测 ZF = 0
assume cs:code, ds:data
data segment
db 'ax = bx',0
db 'ax != bx',0
data ends
code segment
start:
mov ax, data
mov ds, ax
mov ax, 3 ;ax = 3
mov bx, 4 ;bx = 4
cmp ax, bx ;计算 ax - bx,并根据结果设置标志寄存器
jne ax_ne_bx ;不等于则跳转,CPU实际是检测 ZF = 0
mov cl, 2 ;绿色
mov si, 0 ;指向第一个字符串的首地址
jmp short ok ;jmp短转移指令,相当于当前 IP,能转移的范围在:-128 ~ 127,即:IP - 128 ~ IP + 127 这个偏移地址
ax_ne_bx:
mov cl, 4 ;红色
call size_str ;计算第一个显示的字符串占用的字节数,结果放在 ax 中
mov si, ax ;si = ax,指向第二个字符串的首地址
ok:
mov dh, 14 ;14行
mov dl, 32 ;32列
call show_str ;显示字符串
mov ax, 4c00h
int 21h
;名称:size_str
;功能:求一个以0结尾的字符串的长度
;参数:ds:si指向字符串的首地址
;返回:求的字符串长度存储在 ax 中
size_str:
push cx
mov ch, 0
mov ax, 1
loop_size_str:
mov cl, ds:[si]
jcxz ok_size_str
inc ax
inc si
loop loop_size_str
ok_size_str:
pop cx
ret
;名称:show_str
;功能:在指定的位置,用指定的颜色,显示一个以0结尾的字符串
;参数:dh=行号(取值范围:0-24),dl=列号(取值范围:0-79)
; cl=颜色,ds:si指向字符串的首地址
;返回值:无
show_str:
;B8000h-BFFFFh共32kb,是80 * 25彩色字符模式的显示缓冲区,每页4kb(4000字节),显示器可以显示任意一页的内容。
;一般情况下,显示第0页的内容, 也就是说,B8000h ~ B8F9Fh 这4000个字节的内容会显示在显示器上。每行80个字,
;每个字占2个字节,低字节存放字符的assii码,高字节为属性字节。所以每行占160个字节
mov ax, 0B800h ;显示缓冲区段地址
mov es, ax
;行
mov al, 160 ;每行160个字节
mul dh ;8位乘法,一个数默认在al中,另一个在dh中,结果放在ax中
mov bx, ax ;行偏移字节
;列
mov al, 2 ;一个字占2个字节,低字节存放字符的assii码,高字节为属性字节
mul dl ;8位,乘法,一个数默认在al中,另一个在dl中,结果放在ax中
add bx, ax ;行偏移字节 + 列偏移字节 = 相对于显示缓冲区段地址总的偏移地址
mov ch, 0
mov dl, cl ;循环中需要用到cx寄存器,所以把存放字体属性的字节存放在dl中
loop_show_str:
mov cl, ds:[si]
jcxz ok_show_str ;cx = 0,跳转到标号处执行,否则,继续顺序往下执行
mov es:[bx], cl
mov es:[bx+1], dl
inc si
add bx, 2
loop loop_show_str
ok_show_str:
ret
code ends
end start
四. jb 指令
jb(jump below),小于则转移,检测的相关的标志:CF = 1。
指令格式:jb 标号
示例:
jb s ;小于则跳转到 s 标号处执行指令,CPU实际是检测 CF = 1
assume cs:code, ds:data
data segment
db 'ax < bx',0
db 'ax >= bx',0
data ends
code segment
start:
mov ax, data
mov ds, ax
mov ax, 3 ;ax = 3
mov bx, 4 ;bx = 4
cmp ax, bx ;计算 ax - bx,并根据结果设置标志寄存器
jb ax_b_bx ;小于则跳转,CPU实际是检测 CF = 1
mov cl, 4 ;红色
call size_str ;计算第一个显示的字符串占用的字节数,结果放在 ax 中
mov si, ax ;si = ax,指向第二个字符串的首地址
jmp short ok ;jmp短转移指令,相当于当前 IP,能转移的范围在:-128 ~ 127,即:IP - 128 ~ IP + 127 这个偏移地址
ax_b_bx:
mov cl, 2 ;绿色
mov si, 0 ;指向第一个字符串的首地址
ok:
mov dh, 14 ;14行
mov dl, 32 ;32列
call show_str ;显示字符串
mov ax, 4c00h
int 21h
;名称:size_str
;功能:求一个以0结尾的字符串的长度
;参数:ds:si指向字符串的首地址
;返回:求的字符串长度存储在 ax 中
size_str:
push cx
mov ch, 0
mov ax, 1
loop_size_str:
mov cl, ds:[si]
jcxz ok_size_str
inc ax
inc si
loop loop_size_str
ok_size_str:
pop cx
ret
;名称:show_str
;功能:在指定的位置,用指定的颜色,显示一个以0结尾的字符串
;参数:dh=行号(取值范围:0-24),dl=列号(取值范围:0-79)
; cl=颜色,ds:si指向字符串的首地址
;返回值:无
show_str:
;B8000h-BFFFFh共32kb,是80 * 25彩色字符模式的显示缓冲区,每页4kb(4000字节),显示器可以显示任意一页的内容。
;一般情况下,显示第0页的内容, 也就是说,B8000h ~ B8F9Fh 这4000个字节的内容会显示在显示器上。每行80个字,
;每个字占2个字节,低字节存放字符的assii码,高字节为属性字节。所以每行占160个字节
mov ax, 0B800h ;显示缓冲区段地址
mov es, ax
;行
mov al, 160 ;每行160个字节
mul dh ;8位乘法,一个数默认在al中,另一个在dh中,结果放在ax中
mov bx, ax ;行偏移字节
;列
mov al, 2 ;一个字占2个字节,低字节存放字符的assii码,高字节为属性字节
mul dl ;8位,乘法,一个数默认在al中,另一个在dl中,结果放在ax中
add bx, ax ;行偏移字节 + 列偏移字节 = 相对于显示缓冲区段地址总的偏移地址
mov ch, 0
mov dl, cl ;循环中需要用到cx寄存器,所以把存放字体属性的字节存放在dl中
loop_show_str:
mov cl, ds:[si]
jcxz ok_show_str ;cx = 0,跳转到标号处执行,否则,继续顺序往下执行
mov es:[bx], cl
mov es:[bx+1], dl
inc si
add bx, 2
loop loop_show_str
ok_show_str:
ret
code ends
end start
五. jnb 指令
jnb(jump not below),不小于则转移,检测的相关的标志:CF = 0。
指令格式:jnb 标号
示例:
jnb s ;不小于则跳转到 s 标号处执行指令,CPU实际是检测 CF = 0
assume cs:code, ds:data
data segment
db 'ax < bx',0
db 'ax >= bx',0
data ends
code segment
start:
mov ax, data
mov ds, ax
mov ax, 3 ;ax = 3
mov bx, 3 ;bx = 3
cmp ax, bx ;计算 ax - bx,并根据结果设置标志寄存器
jnb ax_nb_bx ;不小于则跳转,CPU实际是检测 CF = 0
mov cl, 2 ;绿色
mov si, 0 ;指向第一个字符串的首地址
jmp short ok ;jmp短转移指令,相当于当前 IP,能转移的范围在:-128 ~ 127,即:IP - 128 ~ IP + 127 这个偏移地址
ax_nb_bx:
mov cl, 4 ;红色
call size_str ;计算第一个显示的字符串占用的字节数,结果放在 ax 中
mov si, ax ;si = ax,指向第二个字符串的首地址
ok:
mov dh, 14 ;14行
mov dl, 32 ;32列
call show_str ;显示字符串
mov ax, 4c00h
int 21h
;名称:size_str
;功能:求一个以0结尾的字符串的长度
;参数:ds:si指向字符串的首地址
;返回:求的字符串长度存储在 ax 中
size_str:
push cx
mov ch, 0
mov ax, 1
loop_size_str:
mov cl, ds:[si]
jcxz ok_size_str
inc ax
inc si
loop loop_size_str
ok_size_str:
pop cx
ret
;名称:show_str
;功能:在指定的位置,用指定的颜色,显示一个以0结尾的字符串
;参数:dh=行号(取值范围:0-24),dl=列号(取值范围:0-79)
; cl=颜色,ds:si指向字符串的首地址
;返回值:无
show_str:
;B8000h-BFFFFh共32kb,是80 * 25彩色字符模式的显示缓冲区,每页4kb(4000字节),显示器可以显示任意一页的内容。
;一般情况下,显示第0页的内容, 也就是说,B8000h ~ B8F9Fh 这4000个字节的内容会显示在显示器上。每行80个字,
;每个字占2个字节,低字节存放字符的assii码,高字节为属性字节。所以每行占160个字节
mov ax, 0B800h ;显示缓冲区段地址
mov es, ax
;行
mov al, 160 ;每行160个字节
mul dh ;8位乘法,一个数默认在al中,另一个在dh中,结果放在ax中
mov bx, ax ;行偏移字节
;列
mov al, 2 ;一个字占2个字节,低字节存放字符的assii码,高字节为属性字节
mul dl ;8位,乘法,一个数默认在al中,另一个在dl中,结果放在ax中
add bx, ax ;行偏移字节 + 列偏移字节 = 相对于显示缓冲区段地址总的偏移地址
mov ch, 0
mov dl, cl ;循环中需要用到cx寄存器,所以把存放字体属性的字节存放在dl中
loop_show_str:
mov cl, ds:[si]
jcxz ok_show_str ;cx = 0,跳转到标号处执行,否则,继续顺序往下执行
mov es:[bx], cl
mov es:[bx+1], dl
inc si
add bx, 2
loop loop_show_str
ok_show_str:
ret
code ends
end start
六. ja 指令
ja(jump above),高于则转移,检测的相关的标志:CF = 0 && ZF = 0。
指令格式:ja 标号
示例:
ja s ;高于则跳转到 s 标号处执行指令,CPU实际是检测 CF = 0 && ZF = 0
assume cs:code, ds:data
data segment
db 'ax > bx',0
db 'ax <= bx',0
data ends
code segment
start:
mov ax, data
mov ds, ax
mov ax, 3 ;ax = 3
mov bx, 2 ;bx = 2
cmp ax, bx ;计算 ax - bx,并根据结果设置标志寄存器
ja ax_a_bx ;高于则跳转,CPU实际是检测 CF = 0 && ZF = 0
mov cl, 4 ;红色
call size_str ;计算第一个显示的字符串占用的字节数,结果放在 ax 中
mov si, ax ;si = ax,指向第二个字符串的首地址
jmp short ok ;jmp短转移指令,相当于当前 IP,能转移的范围在:-128 ~ 127,即:IP - 128 ~ IP + 127 这个偏移地址
ax_a_bx:
mov cl, 2 ;绿色
mov si, 0 ;指向第一个字符串的首地址
ok:
mov dh, 14 ;14行
mov dl, 32 ;32列
call show_str ;显示字符串
mov ax, 4c00h
int 21h
;名称:size_str
;功能:求一个以0结尾的字符串的长度
;参数:ds:si指向字符串的首地址
;返回:求的字符串长度存储在 ax 中
size_str:
push cx
mov ch, 0
mov ax, 1
loop_size_str:
mov cl, ds:[si]
jcxz ok_size_str
inc ax
inc si
loop loop_size_str
ok_size_str:
pop cx
ret
;名称:show_str
;功能:在指定的位置,用指定的颜色,显示一个以0结尾的字符串
;参数:dh=行号(取值范围:0-24),dl=列号(取值范围:0-79)
; cl=颜色,ds:si指向字符串的首地址
;返回值:无
show_str:
;B8000h-BFFFFh共32kb,是80 * 25彩色字符模式的显示缓冲区,每页4kb(4000字节),显示器可以显示任意一页的内容。
;一般情况下,显示第0页的内容, 也就是说,B8000h ~ B8F9Fh 这4000个字节的内容会显示在显示器上。每行80个字,
;每个字占2个字节,低字节存放字符的assii码,高字节为属性字节。所以每行占160个字节
mov ax, 0B800h ;显示缓冲区段地址
mov es, ax
;行
mov al, 160 ;每行160个字节
mul dh ;8位乘法,一个数默认在al中,另一个在dh中,结果放在ax中
mov bx, ax ;行偏移字节
;列
mov al, 2 ;一个字占2个字节,低字节存放字符的assii码,高字节为属性字节
mul dl ;8位,乘法,一个数默认在al中,另一个在dl中,结果放在ax中
add bx, ax ;行偏移字节 + 列偏移字节 = 相对于显示缓冲区段地址总的偏移地址
mov ch, 0
mov dl, cl ;循环中需要用到cx寄存器,所以把存放字体属性的字节存放在dl中
loop_show_str:
mov cl, ds:[si]
jcxz ok_show_str ;cx = 0,跳转到标号处执行,否则,继续顺序往下执行
mov es:[bx], cl
mov es:[bx+1], dl
inc si
add bx, 2
loop loop_show_str
ok_show_str:
ret
code ends
end start
七. jna 指令
jna(jump not above),不高于则转移,检测的相关的标志:CF = 1 || ZF = 1。
指令格式:jna 标号
示例:
jna s ;不高于则跳转到 s 标号处执行指令,CPU实际是检测 CF = 1 || ZF = 1
assume cs:code, ds:data
data segment
db 'ax > bx',0
db 'ax <= bx',0
data ends
code segment
start:
mov ax, data
mov ds, ax
mov ax, 3 ;ax = 3
mov bx, 3 ;bx = 3
cmp ax, bx ;计算 ax - bx,并根据结果设置标志寄存器
jna ax_na_bx ;不高于则跳转,CPU实际是检测 CF = 1 || ZF = 1
mov cl, 2 ;绿色
mov si, 0 ;指向第一个字符串的首地址
jmp short ok ;jmp短转移指令,相当于当前 IP,能转移的范围在:-128 ~ 127,即:IP - 128 ~ IP + 127 这个偏移地址
ax_na_bx:
mov cl, 4 ;红色
call size_str ;计算第一个显示的字符串占用的字节数,结果放在 ax 中
mov si, ax ;si = ax,指向第二个字符串的首地址
ok:
mov dh, 14 ;14行
mov dl, 32 ;32列
call show_str ;显示字符串
mov ax, 4c00h
int 21h
;名称:size_str
;功能:求一个以0结尾的字符串的长度
;参数:ds:si指向字符串的首地址
;返回:求的字符串长度存储在 ax 中
size_str:
push cx
mov ch, 0
mov ax, 1
loop_size_str:
mov cl, ds:[si]
jcxz ok_size_str
inc ax
inc si
loop loop_size_str
ok_size_str:
pop cx
ret
;名称:show_str
;功能:在指定的位置,用指定的颜色,显示一个以0结尾的字符串
;参数:dh=行号(取值范围:0-24),dl=列号(取值范围:0-79)
; cl=颜色,ds:si指向字符串的首地址
;返回值:无
show_str:
;B8000h-BFFFFh共32kb,是80 * 25彩色字符模式的显示缓冲区,每页4kb(4000字节),显示器可以显示任意一页的内容。
;一般情况下,显示第0页的内容, 也就是说,B8000h ~ B8F9Fh 这4000个字节的内容会显示在显示器上。每行80个字,
;每个字占2个字节,低字节存放字符的assii码,高字节为属性字节。所以每行占160个字节
mov ax, 0B800h ;显示缓冲区段地址
mov es, ax
;行
mov al, 160 ;每行160个字节
mul dh ;8位乘法,一个数默认在al中,另一个在dh中,结果放在ax中
mov bx, ax ;行偏移字节
;列
mov al, 2 ;一个字占2个字节,低字节存放字符的assii码,高字节为属性字节
mul dl ;8位,乘法,一个数默认在al中,另一个在dl中,结果放在ax中
add bx, ax ;行偏移字节 + 列偏移字节 = 相对于显示缓冲区段地址总的偏移地址
mov ch, 0
mov dl, cl ;循环中需要用到cx寄存器,所以把存放字体属性的字节存放在dl中
loop_show_str:
mov cl, ds:[si]
jcxz ok_show_str ;cx = 0,跳转到标号处执行,否则,继续顺序往下执行
mov es:[bx], cl
mov es:[bx+1], dl
inc si
add bx, 2
loop loop_show_str
ok_show_str:
ret
code ends
end start
参考
《汇编语言(第4版)》- 王爽