【汇编语言】标志寄存器(二) —— 标志位驱动的计算:ADC、SBB 和 CMP 的巧妙应用

前言

📌

汇编语言是很多相关课程(如数据结构、操作系统、微机原理)的重要基础。但仅仅从课程的角度出发就太片面了,其实学习汇编语言可以深入理解计算机底层工作原理,提升代码效率,尤其在嵌入式系统和性能优化方面有重要作用。此外,它在逆向工程和安全领域不可或缺,帮助分析软件运行机制并增强漏洞修复能力。

本专栏的汇编语言学习章节主要是依据王爽老师的《汇编语言》来写的,和书中一样为了使学习的过程容易展开,我们采用以8086CPU为中央处理器的PC机来进行学习。

文章目录

  • 前言
  • [1. abc 指令](#1. abc 指令)
    • [1.1 功能介绍](#1.1 功能介绍)
    • [1.2 举例说明](#1.2 举例说明)
    • [1.3 为什么要提供这样的指令?](#1.3 为什么要提供这样的指令?)
      • [1.3.1 CF值的含义](#1.3.1 CF值的含义)
      • [1.3.2 示例演示](#1.3.2 示例演示)
      • [1.3.3 得出结论](#1.3.3 得出结论)
    • [1.4 例1------对大的数据进行相加](#1.4 例1——对大的数据进行相加)
      • [1.4.1 问题描述](#1.4.1 问题描述)
      • [1.4.2 问题的分析与解答](#1.4.2 问题的分析与解答)
    • [1.5 例2------对更大的数据进行相加](#1.5 例2——对更大的数据进行相加)
      • [1.5.1 问题描述](#1.5.1 问题描述)
      • [1.5.2 问题的分析与解答](#1.5.2 问题的分析与解答)
    • [1.6 例3------对更大更大的数据进行相加](#1.6 例3——对更大更大的数据进行相加)
      • [1.6.1 题目描述](#1.6.1 题目描述)
      • [1.6.2 问题的分析与解决](#1.6.2 问题的分析与解决)
      • [1.6.3 思考一个问题](#1.6.3 思考一个问题)
  • [2. sbb 指令](#2. sbb 指令)
    • [2.1 功能介绍](#2.1 功能介绍)
    • [2.2 举例说明](#2.2 举例说明)
  • [3. cmp 指令](#3. cmp 指令)
    • [3.1 功能介绍](#3.1 功能介绍)
    • [3.2 举例说明](#3.2 举例说明)
    • [3.3 不同比较的结果](#3.3 不同比较的结果)
      • [3.3.1 正向来判断标志位](#3.3.1 正向来判断标志位)
      • [3.3.2 反向来判断两个值的大小](#3.3.2 反向来判断两个值的大小)
    • [3.4 cmp 进行有符号数比较](#3.4 cmp 进行有符号数比较)
      • [3.4.1 相等与否的情况](#3.4.1 相等与否的情况)
      • [3.4.2 探究小于的情况](#3.4.2 探究小于的情况)
        • [3.4.2.1 常规情况](#3.4.2.1 常规情况)
        • [3.4.2.2 溢出时的情况](#3.4.2.2 溢出时的情况)
        • [3.4.2.3 举例说明](#3.4.2.3 举例说明)
    • [3.5 举例与总结各种结果的判断](#3.5 举例与总结各种结果的判断)
  • 结语

1. abc 指令

1.1 功能介绍

adc是带进位加法指令 ,它利用了CF位上记录的进位值。

  • 格式:adc 操作对象1,操作对象2

  • 功能:操作对象1=操作对象1+操作对象2+CF

  • 比如指令:adc ax,bx 实现的功能是:(ax)=(ax)+(bx)+CF

1.2 举例说明

(1)

assembly 复制代码
mov ax,2 
mov bx,1 
sub bx,ax 		;发生了借位
adc ax,1

执行后,(ax)=4。

adc执行时,相当于计算: (ax)+1+CF=2+1+1=4。

(2)

assembly 复制代码
mov ax,1
add ax,ax
adc ax,3

执行后,(ax)=5。

adc执行时,相当于计算: (ax)+3+CF=2+3+0=5。

(3)

assembly 复制代码
mov al,98H 
add al,al
adc al,3

执行后,(ax)=34H。

adc执行时,相当于计算: (ax)+3+CF=30H+3+1=34H。

可以看出,adc指令比add指令多加了一个CF位的值。

1.3 为什么要提供这样的指令?

为什么要加上CF 的值呢?CPU为什么要提供这样一条指令呢?

1.3.1 CF值的含义

先来看一下CF的值的含义。

在执行 adc 指令的时候加上的 CF 的值的含义,由 adc 指令前面的指令决定的,也就是说,关键在于所加上的CF值是被什么指令设置的。

显然,如果CF 的值是被sub指令设置的,那么它的含义就是借位值 ;如果是被add指令设置的,那么它的含义就是进位值

1.3.2 示例演示

我们来看一下两个数据:0198H和0183H如何相加的:

可以看出,加法可以分两步来进行:

(1)低位相加;

(2)高位相加再加上低位相加产生的进位值。

下面的指令和add ax,bx具有相同的结果:

assembly 复制代码
add al,bl
adc ah,bh

1.3.3 得出结论

看来CPU提供 adc 指令的目的,就是来进行加法的第二步运算的。

adc指令和add指令相配合就可以对更大的数据进行加法运算。

1.4 例1------对大的数据进行相加

1.4.1 问题描述

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

1.4.2 问题的分析与解答

因为两个数据的位数都大于16,用add指令无法进行计算。

我们将计算分两步进行,先将低16位相加,然后将高16位和进位值相加。

程序如下。

assembly 复制代码
mov ax,001EH
mov bx,0F000H
add bx,1000H
adc ax,0020H

adc 指令执行后,也可能产生进位值,所以也会对CF位进行设置。由于有这样的功我们就可以对任意大的数据进行加法运算。

1.5 例2------对更大的数据进行相加

1.5.1 问题描述

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

1.5.2 问题的分析与解答

计算分3步进行:

(1)先将低16位相加,完成后,CF中记录本次相加的进位值;

(2)再将次高16位和CF(来自低16位的进位值)相加,完成后,CF中记录本次相加的进位值;

(3)最后高16位和CF(来自次高16位的进位值)相加,完成后,CF 中记录本次相加的进位值。

程序如下。

assembly 复制代码
mov ax,001EH
mov bx,0F000H
mov cx,1000H
add cx,1EF0H
adc bx,1000H
adc ax,0020H

1.6 例3------对更大更大的数据进行相加

1.6.1 题目描述

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

  • 名称:add128

  • 功能:两个128位数据进行相加

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

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

1.6.2 问题的分析与解决

程序如下。

assembly 复制代码
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

1.6.3 思考一个问题

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

assembly 复制代码
add si,2
add si,2

来取代?

这想都不用想,肯定是不能嘛。

如果用add来取代的画,改变了CF记录的进制位,会影响下一次更高位相加的结果。

2. sbb 指令

2.1 功能介绍

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

  • 格式:sbb 操作对象1,操作对象2

  • 功能:操作对象1=操作对象1--操作对象2--CF

  • 比如:sbb ax,bx 实现功能: (ax) = (ax) -- (bx) -- CF

sbb 指令执行后,将对CF进行设置。

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

2.2 举例说明

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

assembly 复制代码
mov bx,1000H
mov ax,003EH
sub bx,2000H
sub ax,0020H

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

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

3. cmp 指令

3.1 功能介绍

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

cmp 指令执行后,将对标志寄存器产生影响。其他相关指令通过识别这些被影响的标志寄存器位来得知比较结果。

  • 格式:cmp 操作对象1,操作对象2

  • 功能:计算操作对象1--操作对象2。但并不保存结果,仅仅根据计算结果对标志寄存器进行设置。

  • 比如:cmp ax,ax做(ax)--(ax)的运算,结果为0,但并不在ax中保存,仅影响flag的相关各位。指令执行后:ZF=1,PF=1,SF=0,CF=0,OF=0。

3.2 举例说明

下面的指令:

assembly 复制代码
mov ax,8
mov bx,3
cmp ax,bx

执行后: (ax) = 8,ZF=0,PF=1,SF=0,CF=0,OF=0。

3.3 不同比较的结果

3.3.1 正向来判断标志位

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

例如:cmp ax,bx

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

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

3.3.2 反向来判断两个值的大小

反过来看上面的例子。

指令 cmp ax,bx的逻辑含义是比较 ax和 bx 中的值,如果执行后:

3.4 cmp 进行有符号数比较

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

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

上面所讲的是用cmp进行无符号数比较时,相关标志位对比较结果的记录。

下面我们再来看一下如果用cmp来进行有符号数比较时,CPU用哪些标志位对比较结果进行记录。

3.4.1 相等与否的情况

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

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

3.4.2 探究小于的情况

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

3.4.2.1 常规情况

对于有符号数运算,在 (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?

当然不是。

3.4.2.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位二进制信息所表示的数据的正负。

cmp ah,bh 执行后,sf记录的是(ah)-(bh)所得到的8位结果数据的正负,虽然这个结果没有在我们能够使用的寄存器或内存单元中保存,但是在指令执行的过程中,它暂存在CPU内部的暂存器中。

所得到的相应结果的正负,并不能说明,运算所应该得到的结果的正负。

这是因为在运算的过程中可能发生溢出。如果有这样的情况发生,那么,SF的值就不能说明任何问题。

3.4.2.3 举例说明

比如:

assembly 复制代码
mov ah,22H
mov bh,0A0H
sub ah,bh

结果 sf=1,运算实际得到的结果是(ah)=82H,但是在逻辑上,运算所应该得到的结果是:34-(-96)=130。就是因为130这个结果作为一个有符号数超出了-128~127这个范围,在ah中不能表示,而ah中的结果被CPU当作有符号数解释为-126。

而sf被用来记录这个实际结果的正负,所以sf=1。但sf=1不能说明在逻辑上,运算所得的正确结果的正负。

又比如:

assembly 复制代码
mov ah,08AH
mov bh,070h
cmp ah,bh

结果 sf=0,运算(ah)-(bh)实际得到的结果是1AH,但是在逻辑上,运算所应该得到的结果是:(-118)-112=-230。sf记录实际结果的正负,所以sf=0。但sf=0不能说明在逻辑上,运算所得的正确结果。

但是逻辑上的结果的正负,才是cmp指令所求的真正结果,因为我们就是要靠它得到两个操作对象的比较信息。所以cmp指令所作的比较结果,不是仅仅靠sf就能记录的,因为它只能记录实际结果的正负。

我们考虑一下,两种结果之间的关系,实际结果的正负,和逻辑上真正结果的正负,它们之间有多大的距离呢?从上面的分析中,我们知道,实际结果的正负,之所以不能说明逻辑上真正结果的正负,关键的原因在于发生了溢出。

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

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

3.5 举例与总结各种结果的判断

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

(1)如果SF=1,而OF=0

OF=0,说明没有溢出,逻辑上真正结果的正负=实际结果的正负;

因SF=1,实际结果为负,所以逻辑上真正的结果为负,所以(ah)<(bh)。

(2)如果SF=1,而OF=1

OF=1,说明有溢出,逻辑上真正结果的正负≠实际结果的正负;

简单分析一下,就可以看出,如果因为溢出导致了实际结果为负,那么逻辑上真正的结果必然为正。这样,SF=1,OF = 1 ,说明了(ah)>(bh)。

(3)如果SF=0,而OF=1

OF=1 ,说明有溢出,逻辑上真正结果的正负≠实际结果的正负;

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

(4)如果SF=0,而OF=0

OF=0,说明没有溢出,逻辑上真正结果的正负=实际结果的正负;

因SF=0,实际结果非负,所以逻辑上真正的结果必然非负。所以(ah)≥(bh)。




上面,我们深入讨论了cmp指令在进行有符号数和无符号数比较时,对flag 相关标志位的影响,和CPU如何通过相关的标志位来表示比较的结果。在学习中,要注意领会8086CPU这种工作机制的设计思想。实际上,这种设计思想对于各种处理机来说是普遍的。

结语

今天的分享到这里就结束啦!如果觉得文章还不错的话,可以三连支持一下。

也可以点点关注,避免以后找不到我哦!

Crossoads主页还有很多有趣的文章,欢迎小伙伴们前去点评,您的支持就是作者前进的动力!

相关推荐
用户2018792831673 分钟前
如何利用AI工具快速学习Android源码
android
柠石榴9 分钟前
《机器学习》(周志华)第二章 模型评估与选择
人工智能·机器学习
小何~~27 分钟前
基于 STM32 和 MPU6050 的三轴倾斜角度传感器设计与实现
stm32·单片机·嵌入式硬件·mpu6050·倾斜角度传感器
Li-Yongjun35 分钟前
深度解析 Linux 内核参数 net.ipv4.tcp_rmem:优化网络性能的关键
linux·网络·tcp/ip
whltaoin44 分钟前
Redis专题-实战篇一-基于Session和Redis实现登录业务
redis·缓存·springboot
枷锁—sha1 小时前
【DVWA系列】——xss(Reflected)——Medium详细教程
前端·网络·web安全·网络安全·xss
试剂界的爱马仕1 小时前
TCA 循环中间体如何改写肝损伤命运【AbMole】
大数据·人工智能·科技·机器学习·ai写作
不像程序员的程序媛1 小时前
http接口莫名奇妙返回body空白
网络·网络协议·http
音视频牛哥1 小时前
Android 平台RTSP/RTMP播放器SDK接入说明
android·音视频·大牛直播sdk·rtsp播放器·rtmp播放器·rtmp低延迟播放·rtmpplayer
Y3174291 小时前
python Day46 学习(日志Day15复习)
python·学习·机器学习