汇编中的循环和分支结构

虽然我们没有花里胡哨的函数和对象等等,但是我们也可以实现循环和分支结构(其实就是if_else)

我们要知道一个问题,就是代码是一行行执行的
(其实这个是说给我这样的被Verilog降维打击的人听的)

如果是循环,我们就要想办法在执行完一次循环就跳转到循环开始,同时改变一些条件,方便进行跳出循环。
再加上初始值,循环三要素就出来了。

如果是if_else,那么我们就需要进行判断,然后选择我们要执行的代码,直接将ip指针(控制代码执行到的位置的寄存器)切到指定位置,就能实现功能了。

两个问题,一个是判断,一个是跳转。
判断都好说,我们有现成的cmp,还有一大堆的标志位,主要还是跳转。
所以我们要给出跳转指令。

先给一下我们常见的几种循环和分支的结构吧,然后配上指令基本上就不需要我讲了。
(其实汇编中的for和while分的不是很清楚,感觉其实在其他高级语言中其实也差不多)

跳转指令

先声明一下,虽然我们是可以直接跳转到地址的,但是我们一般都是利用标号进行跳转。
比如:

loop1:       inc bxmov ax,2jmp loop1

jmp指令就是一个跳转的,这样我们就能在jmp之后直接到inc那步。(好叭这是一个死循环)
注意标号不可以重复就行了。

无条件跳转指令jmp

格式 jmp opr,opr为一个地址/标号。
这里有几个问题,就是我们一般来说跳转是有距离差值的,而要知道虽然我们这里看起来只是一个标号,计算机还是要算出来转移的地址大小,这里就有一个跳转距离的问题,我们需要找到大小合适的空间来存储这个距离。

jmp short/near ptr opr,short为8位位移量(短跳转),而near ptr为16位(近跳转)。
正常不说就是默认为8位的。

段内间接转移:
在之前的寄存器寻址中我们得出一个结论,间接就是绕了一下,就是需要一个物理地址上的值

格式:jmp word ptr opr,此时的opr为一个物理地址,我们将物理地址的值赋值给IP寄存器。
(如果我们是基于16位寄存器的寻址方式,比如寄存器寻址、基址变址,那么word ptr省略)

如果是多个代码段之间的跳转呢?
和上面差不多,分为直接和间接。

jmp far ptr opr为段间直接跳转,这里的opr也是一个物理地址,我们将其偏移地址赋值给IP,段地址给CS(cs寄存器是ip的默认段寄存器,负责判断是哪一个段。)

jmp dword ptr opr为段间间接跳转,opr为物理地址,我们将该物理地址的一个字内容拿出来赋值给IP,然后将下一个字的内容给CS寄存器。

比如地址为31220H:

上面这些,说实话我做了几次实验就没用过一次……,也就偶尔jmp一下还是不写near的那种。

条件转移指令

这个比较常见,是根据标志位决定是否进行跳转。

先祭出一张表,然后我们再说。

多不多,没事我们一会再补两个。

相等跳转

使用ZF判断跳转的指令有四个,其实原理上只有两个。
ZF位的判断本质上就是cmp进行相减,所以对于equal(JE)和zero(JZ)也没什么区别。
至于JNZ和JNE就是加一个not,相反的意思。

使用:

cmp bx,ax
JZ loop1
jmp loop2

这就是一个简单的跳转,当结果相等时跳转到loop1,否则按照汇编的执行原则,下一条语句跳转到loop2。

JS、JO、JP

这三个和ZP差不多,都是通过cmp影响标志位,然后看标志位状态(分别为SF、OF和PF)进行跳转的,都有否定形式。
只不过JP的命名方式多了一对。

这个感觉不是很常用,也可能是自己写的代码少?
反正一般上面的相等和我们下面的比较跳转比较常用。

比较跳转

两个数比较的结果有四种,大于 小于 大于等于 小于等于。
所以我们上面的还要补充一下:
格式都是先cmp a,b。(大于等于表示 a => b)

上面的名字实在是烦人,我们看一下整理过的:
对于无符号数,我们管小于叫JB,大于叫JA。(我没骂人!)
那么小于等于是不是就应该是JNA,大于等于是JNB。(大于等于就是小于取反,取反加N

但是,我们还知道可以加E表示相等,所以:小于等于是JBE,大于等于是JAE,
我们再对这两个取反,得到小于和等于的另外表示方式:
小于是大于等于取反,JNAE;大于是小于等于取反,JNBE。

综上:
我们得到了这样的:

  • 小于:JB、JNAE
  • 大于:JA、JNBE
  • 大于等于:JAE、JNB
  • 小于等于:JBE、JNA

还没完,不要急。
那么我们接下来看一下实现,也就是计算机怎么判断结果。

我们知道cmp的实现其实就是将a-b,
那么如果无符号数a<b的结果溢出,CF = 1;
相反,大于等于 CF = 0。

大于则有一点烦,我们不但要看CF,还需要看ZF(大于等于 - 相等),所以这个逻辑是CF并ZF = 0,
也就是CF为0(大于等于)且ZF = 0(不相等),得到大于。
小于等于则是 CF并ZF = 1。

但是有符号数有所不同,此时相减的溢出要看OF,但是只看OF是不够的。

先给出有符号数的小于是jl(jmp less),大于是jg,相对应的也有大于等于和小于等于以及变式。

我们拿a<b说事吧,有以下几种情况:

  • a为负,b为正
  • a为负,且负的比b多
  • a为正,且正的比b少

OF的溢出标准

我们先提一下OF的溢出标准,有符号数的减法溢出其实就是正数减负数负数减正数两种情况,
将其减数求补,转化成加法,就是下面两种情况:

我们知道CF位只需要看有没有借位和进位,那么OF就是需要看数据类型了。
开始我们是两个负数,然后出现了正数;或者是两个正数,出现了负数,说明溢出。
但如果是正数和负数(也就是正数减正数,或者负数减正数),通过求补转化

很明显不管怎么做,都不会发生溢出。

通过求补将减法转化成加法的方式,我们发现如果两个加法的最高位相同,结果的最高位(不考虑进位)和其不同,那么发生溢出

再转化成减法,也就是再对第二个操作数求补,我们可以发现此时溢出的情况是两个最高位(标志位)不相同,但最高位(不考虑溢出)为1

回收问题,我们已经有了减法的溢出标准,那么我们就该看看a<b了。
第一种很明显只看OF解决不了问题,所以我们还需要看SF,SF本质上就是最高位的数值
对于第一种的a负b正,我们可以转化成负数加负数,如果溢出那么OF = 1,SF = 0:

如果没有溢出,OF = 0,所以最高位一定为1(不是1就溢出了),SF = 1。

综上,我们得到a<b 的有符号数判准为 OF异或SF = 1

对于剩下两种情况,我们知道一定不会溢出,但是因为结果小于零,SF = 1,仍成立。

知道了a<b是SF异或OF = 1,那么a =>b就是异或为0;
按照上面的定义,再加上ZF就能得到小于等于和大于的结果。

整理一下:
无符号数判定标准

有符号数判定标准

上述就是计算机判定两个数据大小的方式了,可以帮助我们更好的理解指令的含义。

不要走,补充一些。
对于无符号数来说,小于和大于等于是只看CF的,所以这才是JC和JNC的由来。

补充

可能还有一个比较常用的:JCXZ,当CX = 0跳转。
我们知道CX是负责计数器的,在循环过程中我们常常将循环个数记录在CX中,每一次减一,当CX为0说明循环结束,我们就需要跳转了。

循环指令

上面的各种跳转,看起来更像分支结构的,比如在进行了一串的判定之后,确定了一个输入是数字还是字母,然后跳转到对应的分支上进行打印。(比如0-9对应的ASCII需要加30h,而字母需要37h)

但是对于循环来说,这就很不好了,我们当然可以使用JCXZ然后每一次将CX自减,但是我们有更好的loop指令:

xor bx,bx
mov cx,10
loop1:
inc bx
loop loop1

上述代码段实现了将bx初始化为0(异或),以及自增十次。
在loop指令中,不但实现了跳转,还有CX的自减和判断,当CX为0跳出。

对应的我们还有高端的LOOPE和LOOPZ,当ZF = 0或 CX = 0 退出
相反还有LOOPNE和LOOPNZ。

举个例子

将一个字的数据转换成16进制输出。

字为16位,应当有4个十六进制数,我们采取移位的方式,一次移动四位,需要循环四次;
(也可以每一次移动四位,当寄存器内容为0则停止移动)
但是这样就会发现,最后的结果是反着的,所以我们需要先将数据转过来
先放代码:

;description
data SEGMENTtemp dw 0B2FAH
data ENDS
;description
stack SEGMENTdw 32 dup(?)
stack ENDS
;description
code SEGMENTASSUME CS:CODE, DS:DATA, SS:STACK
print PROC                                      ;内容在dx的低四位push   axpush   bxpush   cxpush   dxcmp    dl,10                      ;小于10就跳转JB     add2add    dl,7add2:     add    dl,30hmov    ah,2int    21hpop    dxpop    cxpop    bxpop    axRET
print ENDP
main PROCstart:    mov    ax,datamov    ds,axxor    ax,axmov    cx,4                        ;移位四次mov    bx,temploop_turn:mov    dx,bxand    dx,0fh                      ;dx的低四位需要放入ax中push   cxmov    cl,4shr    bx,clshl    ax,cladd    ax,dxpop    cxloop loop_turnmov    cx,4loop1:    mov    dx,axand    dx,0fhcall   printpush   cxmov    cl,4                         ;存储位移个数shr    ax,clpop    cxloop   loop1mov    ah,4chint    21hRET
main ENDPcode ENDS
END start

这里调用了一个子程序来解决问题。,子程序中有一个加7,还有一个加30h,这是因为我们要判断是否为字母,字母转换ASCII加37h,数字加30h,两者是不一样的。

开始进行了一个将原始数据进行调转的指令,先从temp取最低四位到dx,然后将ax左移四位,加上dx,然后将存储temp的bx右移四位,这样,我们就实现了将temp逆转到ax中。
因为我们在两个部分都需要使用cx寄存器,但是又不能乱用,所以就在移位的时候将cx压栈了一下,用完了就出栈。

运行截图:

这里我们还可以采用判断ax的值是否为0判断是否结束移位。

汇编——跳转指令与分支结构(包括OF和CF的溢出判断)相关推荐

  1. 【debug】汇编跳转指令: JMP、JECXZ、JA、JB、JG、JL、JE、JZ、JS、JC、JO、JP 等

    汇编跳转指令: JMP.JECXZ.JA.JB.JG.JL.JE.JZ.JS.JC.JO.JP 等 2017年11月12日 15:01:09 zmmycsdn 阅读数 19334 转自:http:// ...

  2. 关于汇编跳转指令的说明

    虽然jmp指令提供了控制转移,但是它不允许进行任何复杂的判断.80x86条件跳转指令提供了这种判断.条件跳转指令是创建循环和实现其他条件执行语句,如if-endif的基本要素. 条件跳转指令检查一个或 ...

  3. 汇编跳转指令: JMP、JECXZ、JA、JB、JG、JL、JE、JZ、JS、JC、JO、JP 等

    转自:http://www.cnblogs.com/del/archive/2010/04/16/1713886.html http://pan.baidu.com/s/1gVTSi 跳转指令分三类: ...

  4. 汇编jnl_汇编跳转指令: JMP、JECXZ、JA、JB、JG、JL、JE、JZ、JS、JC、JO、JP 等

    http://pan.baidu.com/s/1gVTSi 跳转指令分三类: 一.无条件跳转: JMP ;无条件跳转 二.根据CX.ECX寄存器的值跳转: JCXZ ;CX 为 0 则跳转 JECXZ ...

  5. python中分支结构包括哪些_Python分支结构(switch)操作简介

    Python当中并无switch语句,本文研究的主要是通过字典实现switch语句的功能,具体如下. switch语句用于编写多分支结构的程序,类似与if-.elif-.else语句. switch语 ...

  6. 汇编跳转指令B、BL、BX、BLX 和 BXJ的区别

    已针对原链接错误翻译并更正                                                    跳转指令用于实现程序流程的跳转,在 ARM 程序中有两种方法可以实现程 ...

  7. python中分支结构包括哪些_python分支结构

    if分支 一.单分支结构 # if 表达式: # 语句块 # 后续语句 # 执行流程:如果表达式结果为真,则执行语句块.否则,直接执行后续语句 二.双分支结构 # 双分支语句 # if 表达式: # ...

  8. 单片机汇编跳转指令延时一秒

    DELAY: MOV      R7,#10    ;延时1S子程序 DL1:   MOV      R6,#200-----1T DL2:   MOV      R5,#248------1T DJ ...

  9. python中分支结构包括哪些_python中的分支结构

    python不提供switch语句,但是python可以通过字典实现switch语句的功能 实现方法分两步: 首先:定义一个地点 其次:调用字典的get()获取相应的表达式 原始方法: from __ ...

最新文章

  1. Android开发技术周报176学习记录
  2. 【 FPGA 】门控时钟专题
  3. Eclipse IDE for Java EE Developers 与Eclipse Classic 区别
  4. 算法导论学习笔记 第6章 堆排序
  5. 2019全球开发者调查:仅2%的人996,Python并不是最受喜爱的语言
  6. python闭包详解函数_详解python函数的闭包问题(内部函数与外部函数详述)
  7. 两种参数类型_布尔参数这些缺点不能忍?不如试试枚举吧
  8. 【英语学习】【Level 07】U03 Amazing wonders L2 A global city
  9. 怎么修改x轴_Origin绘图:共X轴Stack绘制分波态密度与绘图中的科学构思
  10. iphone个系列尺寸_iPhone“立体边框”壁纸来了,拥有3D效果
  11. 过滤器-filter
  12. 图像平均池化 利用pytorch对图像进行池化
  13. 10.Doctrine2 (2)
  14. c语言教材系统管理课设
  15. 如何使用Bootbox?
  16. 实战第二步:如何做一份有针对性的竞品分析
  17. 10年日语营业转行IT从深圳到日本东京圈工作生活2019copy
  18. 电子课件html咋阅读,html教学课件
  19. ppoe拨号服务器没响应,pppoe拨号失败怎么办 pppoe拨号失败解决方法
  20. MRR@K P@K R@K意义阐述与对比

热门文章

  1. 移动安全工具-jarsigner
  2. 域控不停机升级,AD2008到AD2016
  3. echarts折线图横轴标签间隔
  4. 创世区块诞生 | 历史上的今天
  5. perl——当split函数用空格做分隔符时
  6. Java字符串截取冒号(:)之后的内容
  7. Struts2与Websocket配置UrlPattern冲突问题
  8. 记一次黑苹果更换硬盘之旅
  9. wordpress常用标签调用
  10. 办公软件套装 Office 2010 With SP1 专业增强版