11.7 ~ 11.9

2020-11-11
2020-11-11
7 min read
Hits

  《汇编语言(第3版)》11.7 ~ 11.9、《零基础入门学习汇编语言》P56 ~ 57

  编程计算 1EF000H+201000H,结果放在 ax(高 16 位)和 bx(低 16 位)中。

mov ax,001EH
mov bx,0F000H
add bx,1000H
adc ax,0020H

  adc 指令执行后,也可能产生进位值,所以也会对 CF 位进行设置。

  由于有这样的功能,我们就可以对任意大的数据进行加法运算。

  例如,编程计算 1EF0001000H+2010001EF0H,结果放在 ax(高 16 位)bx(次高 16 位)cx(低 16 位)中。

mov ax,001EH
mov bx,0F000H
mov cx,1000H
add cx,1EF0H
adc bx,1000H
adc ax,0020H

  下面我们,编写一个子程序,对两个 128 位数据进行相加

  参数:ds:si 指向存储第一个数的内存空间,因数据为 128 位,所以需要 8 个字单元,由低地址单元到高地址单元依次存放 128 位数据由低到高的各个字。运算结果存储在第一个数的存储空间中。

  ds:di 指向存储第二个数的内存空间

assume cs:code,ds:data
data 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,
data ends

code segment
start:
	mov ax,data
	mov ds,ax
	mov si,0
	mov di,16

	mov cx,8

	call add128

	mov ax,4c00h
	int 21h
add128:
	push ax
	push cx
	push si
	push di

	sub ax,ax     # 将 CF 设置为 0
s:
	mov ax,[si]
	adc ax,[di]
	mov [si],ax
	inc si
	inc si
	inc di
	inc di
	loop s

	pop di
	pop si
	pop cx
	pop ax
	ret
code ends
end start

  思考:inc 和 loop 指令不影响 CF 位,上面的程序中,能不能将 4 个 inc 指令,用

add si,2
add di,2

  取代?答:不能,因为 add 会影响 CF 位。

11.7 sbb 指令

sbb 是带借位减法指令,它利用了 CF 位上记录的借位值

  1. 格式:sbb 操作对象 1,操作对象 2
  2. 功能:操作对象 1= 操作对象 1- 操作对象 2-CF
  3. 比如:sbb ax,bx
  4. 实现功能:(ax)=(ax)-(bx)-CF

  利用 sbb 指令我们可以对任意大的数据进行减法运算。

  计算 003E1000H-00202000H,结果放在 ax,bx 中,程序如下

mov bx,1000H
mov ax,003EH
sub bx,2000H
sbb ax,0020H

  sbb 和 adc 是基于同样的思想设计的两条指令,在应用思路上和 adc 类似。在这里,我们就不再进行过多的讨论。

  通过学习这两条指令,我们可以进一步领会一下标志寄存器 CF 位的作用和意义。

11.8 cmp 指令

  cmp 是比较指令,功能相当于减法指令,只是不保存结果。

  cmp 指令执行后,将对标志寄存器产生影响。

  其他相关指令通过识别这些被影响的标志寄存器位来得知比较结果

  1. 格式:cmp 操作对象 1,操作对象 2
  2. 功能:计算操作对象 1- 操作对象 2 但并不保存结果,仅仅根据计算结果对标志寄存器进行设置。
  3. 比如:cmp ax,ax
  4. 做 (ax)-(ax) 的运算,结果为 0,但并不在 ax 中保存,仅影响 flag 的相关各位。

  指令执行后

  1. ZF=1
  2. PF=1
  3. SF=0
  4. CF=0
  5. OF=0

  下面的指令

mov ax,8
mov bx,3
cmp ax,bx     # 执行后:(ax)=8
ZF=0
PF=1
SF=0
CF=0
OF=0

  其实,我们通过 cmp 指令执行后,相关标志位的值就可以看出比较的结果。

  例如:cmp ax,bx

如果所以
(ax)=(bx)(ax)-(bx)=0ZF=1
(ax)≠(bx)(ax)-(bx)≠0ZF=0
(ax)<(bx)(ax)-(bx) 将产生借位CF=1
(ax)≥(bx)(ax)-(bx) 不必借位CF=0
(ax)>(bx)(ax)-(bx) 既不必借位,结果又不为0CF=0 且 ZF=0
(ax)≤(bx)(ax)-(bx) 既可能借位,结果可能为 0CF=1 或 ZF=1

  现在我们可以看出比较指令的设计思路

  即:通过做减法运算,影响标志寄存器,标志寄存器的相关位记录了比较的结果。

  反过来看上面的例子 cmp ax,ax

如果说明
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(ax)≤(bx)

  同 add、sub 指令一样,CPU 在执行 cmp 指令的时候,也包含两种含义:进行无符号数运算和进行有符号数运算。

  所以利用 cmp 指令可以对无符号数进行比较,也可以对有符号数进行比较。

  下面我们再来看一下如果用 cmp 来进行有符号数比较时,我们要注意哪些标志位!

  我们以 cmp ah,bh 为例进行说明

如果所以
(ah)=(bh)(ah)-(bh)=0ZF=1
(ah)≠(bh)(ah)-(bh)≠0ZF=0

  所以我们根据指令执行后 ZF 的值,就可以知道两个数据是否相等。

  我们继续看,如果 (ah)<(bh) 则可能发生什么情况呢?

  对于有符号数运算,在 (ah)<(bh) 情况下,(ah)-(bh) 显然可能引起 SF=1,即结果为负。

  比如:(ah)=1,(bh)=2:则 (ah)-(bh)=0FFH,0FFH 为 -1 的补码,因为结果为负,所以 SF=1。

  (ah)=0FEH,(bx)=0FFH:则 (ah)-(bh)=(-2)-(-1)=0FFH,因为结果为负,所以 SF=1。

cmp 操作对象 1, 操作对象 2 指令执行后,SF=1,不能说明 操作对象 1< 操作对象 2

  (ah)=22H,(bh)=0A0H:则 (ah)-(bh)=34-(-96)=130=82H,82H 是 -126 的补码,所以 SF=1。

  这里虽然 SF=1,但是并不能说明 (ah)<(bh),因为显然 34>-96。

  两个有符号数 A 和 B 相减,得到的是负数,那么可以肯定 A<B,这个思路没有错误;

  关键在于我们根据什么来断定得到的是一个负数。

  CPU 将 cmp 指令得到的结果记录在 flag 的相关标志位中。

  我们可以根据指令执行后,相关标志位的值来判断比较的结果。

  单纯的考察 SF 的值不可能知道结果的正负。因为 SF 记录的只是可以在计算机中存放的相应位数的结果的正负。

  比如 add ah,al 执行后,SF 记录的是 ah 中 8 位二进制信息所表示的数据的正负。

  如果没有发生溢出的话,那么实际结果的正负和逻辑上真正的结果正负就一致了。

  所以,我们应该在考察 SF(得知实际结果的正负)的同时考察 OF(得知有没有溢出)就可以得知逻辑上真正结果的正负,同时就可以知道比较的结果。

  下面,我们以 cmp ah,bh 为例,总结一下 CPU 执行 cmp 结果后,SF 和 OF 的值是如何来说明比较的结果的

  1. 如果 SF=1,而 OF=0
    1. OF=0,说明没有溢出,逻辑上真正结果的正负 = 实际结果的正负
    2. SF=1,实际结果为负,所以逻辑上真正的结果为负,所以 (ah)<(bh)
  2. 如果 SF=1,而 OF=1
    1. OF=1,说明有溢出,逻辑上真正结果的正负 ≠ 实际结果的正负
    2. 简单分析一下,就可以看出,如果因为溢出导致了实际结果为负,那么逻辑上真正的结果必然为正。这样,SF=1,OF=1,说明了 (ah)>(bh)
  3. 如果 SF=0,而 OF=1
    1. OF=1,说明有溢出,逻辑上真正结果的正负 ≠ 实际结果的正负
    2. 简单分析一下,就可以看出,如果因为溢出导致了实际结果为正,那么逻辑上真正的结果必然为负,这样,SF=0,OF=1,说明了 (ah)<(bh)
  4. 如果 SF=0,而 OF=0
    1. OF=0,说明没有溢出,逻辑上真正结果的正负 = 实际结果的正负
    2. 因 SF=0,实际结果非负,所以逻辑上真正的结果必然非负。所以 (ah)≥(bh)

  (博主将以上几点总结为表格形式如下表所示)

SF说明OF说明所以
0结果非负0没有溢出,逻辑正负 = 实际正负(ah)≥(bh)
0结果非负1有溢出,逻辑正负 ≠ 实际正负(ah)<(bh)
1实际为负0没有溢出,逻辑正负 = 实际正负(ah)<(bh)
1实际为负1有溢出,逻辑正负 ≠ 实际正负(ah)>(bh)

  上面,我们深入讨论了 cmp 指令在进行有符号数和无符号数比较时,对 flag 相关标志位的影响,和 CPU 如何通过相关的标志位来表示比较的结果。

  在学习中,要注意领会 8086 CPU 这种工作机制的设计思想。实际上,这种设计思想对于各种处理机来说是普遍的。

11.9 检测比较结果的条件转移指令

  下面的内容中我们将学习一些根据 cmp 指令的比较结果(即,cmp 指令执行后,相关标志位的值)进行工作的指令。

  它们检测的是哪些标志位呢?

  就是被 cmp 指令影响的那些,表示比较结果的标志位。

  这些条件转移指令通常都和 cmp 相配合使用,就好像 call 和 ret 指令通常相配合使用一样。

  因为 cmp 指令可以同时进行两种比较,无符号数和有符号数比较,所以根据 cmp 指令的比较结果进行转移的指令也分为两种,即

  1. 根据无符号数的比较结果进行转移的条件转移指令,它们检测 ZF、CF 的值
  2. 根据有符号数的比较结果进行转移的条件转移指令,它们检测 SF、OF 和 ZF 的值

  条件转移指令小结

指令含义检测的相关标志位
je等于则转移ZF=1
jne不等于则转移ZF=0
jb低于则转移CF=1
jnb不低于则转移CF=0
ja高于则转移CF=0,ZF=0
jna不高于则转移CF=1 或 ZF=1

  这些指令比较常年用,它们都很好记忆,它们的第一个字母都是 j,表示 jump;后面的

  1. e:表示 equal
  2. ne:表示 not equal
  3. b:表示 below
  4. nb:表示 not below
  5. a:表示 above
  6. na:表示 not above

  注意观察一下它们所检测的标志位,都是 cmp 指令进行无符号数比较时候,记录比较结果的标志位。

  比如 je,检测 ZF 位,当 ZF=1 的时候进行转移,如果在 je 前面使用了 cmp 指令,那么 je 对 ZF 的检测,实际上就是间接地检测 cmp 的比较结果是否为两数相等。

Avatar

Hui.Ke

❤ Cyber Security | Safety is a priority.