Intel汇编语言程序设计学习-第六章 条件处理-中

6.3  条件跳转

6.3.1  条件结构

    在IA-32指令集中没有高级的逻辑结构,但无论多么复杂的结构,都可以使用比较和跳转指令组合来实现。执行条件语句包括两个步骤:首先,使用CMPAND,SUB之类的指令修改CPU标志六七次,使用条件跳转指令测试并导致向新地址的分支转移。

    例子1:使用CMP指令比较AL0,如果CPU指令设置了零标志,那么JZ(为0则跳转)指令就跳转到标号L1处:

cmp al ,0

jz L1

.

.

L1:

 

6.3.2  条件跳转(Jcond)指令

    条件跳转指令在标志条件为真时分支跳转到新的目的标号处,如果条件标志为假,那么执行紧跟在跳转跳转指令之后的指令。格式如下:

        jcond 目标地址

    格式中的cond指的是一个标志条件,用来表示一个或多个标志的状态。例如:

      jc    如果进位则跳转

      jnc   如果无进位则跳转

      jz    如果为零则跳转

      jnz   如果不为零则跳转

    我们已经知道算数指令。比较指令和布尔指令几乎重视会设置标志位、条件跳转指令检查标志位的状态并且根据标志位的状态决定是否跳转。

    限制:MASM在默认情况下要求跳转的目的地址在当前的过程之内,为了突破这种限制,可以声明一个全局标号(标号后面跟”::”:

jc MyLabel

.

.

MyLabel::


通常,应尽量避免跳转到当前的过程之外,否则调试程序时会比较困难。

    在Intel386之前,跳转的目标地址被限制在跳转指令后的第一条指令+128~-127个字节范围之内。IA-32处理器可跳转到当前段内的任何地址。

    使用CMP指令:假设我们想在AX等于5时跳转到位置L1处。假设AX等于5CMP指令设置了零标志,由于零标志置位了,执行JE指令就会发生跳转:

    cmp ax,5

    je  L1       ;相等则跳转

    如果AX不等于5CMP就会清楚零标志,执行JE指令就不会发生跳转,在下面的例子中,由于AX小于6而发生了跳转:

    mov  ax ,5

    cmp  ax,6

    jl     L1    ;如果小于则跳转大于是(jg

6.3.3  条件跳转指令的类型

     IA-32指令集中跳转中的数目惊人地多,支持根据有符号、无符号整数的比较以及对CPU状态标志的检查进行跳转的一系列指令,跳转跳转指令可分成下面四类:

1.基于特定的标志值。

2.根据两个操作数是否相等,或根据(ECX的值的。

3.基于无符号操作数的比较结果的。

4.基于有符号操作数的比较结果的。

下表列出了基于特定CPU标志:零标志、进位标志、溢出标志、奇偶标志和符号标志的跳转指令。

基于恒等性比较的跳转指令

    下表列出了基于两个操作数是否相等或CX,EXC值是否为零的跳转指令。

CMP leftOp ,rightOp


JE指令和JZ指令是等价的,JNZ指令和JNE指令时等价的。

基于无符号数比较的跳转指令

 

基于有符号数比较的跳转指令

 

6.3.4  条件跳转的应用

测试状态位

    ANDORCMPNOTTEST指令后面常跟能够改变程序流程的条件跳转指令,条件跳转指令通常要测试CPU状态标志位的值。例如,假设8位的内存操作数status中存放着同计算机相连的外部设备信息,下面的指令在位5置位时跳转到某标号处,表示机器处于脱机状态:

mov  al ,status

test  al,00100000b  ;测试位5

jnz   EquipOffline

    下面的语句在位014中的任何一位置位时跳转到另一个标号处:

mov  al ,status

test  al ,00010011b   ;测试位0,1,4

jnz   InputDataByte

    如果想在2,3,7全部置位时跳转某标号处,需要使用ANDCMP两条指令:

mov   al ,status

and   al ,10001100b

cmp   al ,10001100b

je     ResetMachine

    取两个整数中的较大的值:下面的指令比较AXBX中的无符号整数并把其中的较大者送DX寄存器:

mov  dx ,ax    ;假设AX较大

cmp  ax ,bx

jae   L1       ;如果A>=B 就直接跳转到L1,否则就把较大的值B放到结果dx

mov  dx ,bx

L1:

    取三个整数中的较小值:下面的指令比较V1,V2V3三个无符号变量的值,并把其中的最小者送至AX寄存器:

.data

V1 WORD ?

V2 WORD ?

V3 WORD ?

.code

  mov  ax ,V1  ;假设V1是最小的

  cmp  ax ,V2

  jbe   L1     ;小于等于则跳转

  mov  ax ,V2

L1: cmp ax ,V3

  jbe   L2   

  mov  ax ,V3

L2:


    应用:数组的顺序查找

    在数组中找到第一个非零值。

 

应用:字符串加密(XOR

TITLE Ecnryption Program    (Encrypt.asm)

INCLUDE Irvine32.inc

KEY = 239

BUFMAX = 128

.data

sPrompt  BYTE  "Enter the plain text:" ,0

sEncrypt BYTE  "Cipher text:         " ,0

sDecrypt BYTE  "Decrypted:           " ,0

buffer   BYTE  BUFMAX+1 DUP(0)

bufSize  DWORD ?

.code

main PROC

    call  InputTheString

call  TranslateBuffer

mov   edx ,OFFSET sEncrypt

call  DisplayMessage

call  TranslateBuffer

mov   edx ,OFFSET sDecrypt

call  DisplayMessage

exit

main ENDP

 

;-----------------------------------

InputTheString PROC

;

;Prompts user for a plaintext string.Saves the string

;and its length

;Receives:nothing

;Returns: nothing

;-----------------------------------

    pushad

mov    edx ,OFFSET sPrompt

call   WriteString

mov    ecx ,BUFMAX

    mov    edx ,OFFSET buffer

call   ReadString

mov    bufSize ,eax

call   Crlf

popad

ret

InputTheString ENDP

 

;-----------------------------------

DisplayMessage PROC

;

;Display the ecnrypted or decrypted message.

;Receives :EDX points to the message

;Returns  :nothing

;-----------------------------------

    pushad

call  WriteString

mov   edx ,OFFSET buffer

call  WriteString

call  Crlf

call  Crlf

popad

ret

DisplayMessage ENDP

 

;-----------------------------------

TranslateBuffer  PROC

;

;Translates the string by exclusive-ORing each

;byte with the encryption key buye.

;Receives :nothing

;Returns  :nothing

    pushad

mov ecx ,bufSize

mov esi ,0

L1:

    xor  buffer[esi] ,KEY

inc  esi

loop L1

popad

ret

TranslateBuffer ENDP

END main


6.3.5  位测试指令

BT ,BTC ,BTRBTS指令统称为位测试(bit testing)指令,这些指令很重要,因为他们可以在单挑院子指令内执行很多个步骤。为测试指令对多线程程序非常有用,对多线程程序而言,在不冒被其他线程中断的危险的情况下对重要的标志(称为信号量)进行测试、清除、设置或求反是非常重要的。

BT指令

BT(位测试,bit test)指令选择第一个操作数位n并把它复制到进位标志中:

BT bitBase,n

第一个操作数成为位基(bitBase,它不会被指令所修改。BT指令允许以下类型操作数:

BT  r/m16 ,r16

BT  r/m32 ,r32

BT  r/m16 ,imm8

BT  r/m32 ,imm8

在下例中,进位标志等于变量semaphone第七位的值:

.data

semaphone WORD 10001000b

.code

BT  semaphone ,7      ;CF = 1

Intel指令集引入BT指令之前,我们不得不把变量复制到寄存器中,然后再通过移位把第7位送到进位标志中:

mov  ax ,semaphone

shr   ax ,8         ;CF = 1

BTC指令

BTC(位测试并取反,bit test and complement)指令选择第一个操作数的位n并把它复制到标志位中,同时对位n取反。

BTR指令

BTR(位测试并复位,bit test and reset)指令选择第一个操作数位n并把它复制到进位标志中,同时位n清零。

BTS指令

BTS指令(位测试并置位,bit test and set)指令选择第一个操作数的位n并把它复制到进位标志中,同时位n置位。

6.4  条件循环指令

    LOOPZLOOPE指令:ecx大于0,并且零标志位置位则循环。

    LOOPNZLOOPNE指令:ecx大于0,并且零标志位复位则循环。

6.5  条件结构

    条件结构可认为是在不同的逻辑分支之间引发选择的一个或多个条件表达式,每个分支都会执行不同的指令序列。

6.5.1  IF块结构语句

    这节不想细总结了,书上很大的篇幅就是为了解释用cmpj*来对应解释高级语言里那些IF语句什么的。没什么新东西。

6.5.2  复合表达式

    这节是说在高级语言中的类似if(A And B) [A&&B] if(A OR B) [A||B]等在汇编里怎么对应翻译,其实比较简单,比如A&&B 可以判断A满足然后跳转到B继续判断,有一个不满足那么直接jmp到其他地方,A||B的话就是先判断A,如果满足 那么jmp到满足,否则判断B如果满足那么jmp到满足,否则jmp到不满足,这样也就明白了为什么在高级语言中 a = 0 ,b = 0  if(++a || ++b)..之后b没有被自加的原因(a = 1 ,b = 0)。比较简单,这一节也不细总结了。

6.5.3  WHILE循环

    ...这节是说高级语言中的while怎么解析成汇编,比较简单,我们直接mark一个地方,然后满足条件就直接往上跳回去就行了,如果不满足那么就不跳,这样就自动往下执行,相当于while完了,当然也可以增加其他cmp,j*指令实现break等。

 

6.5.4  以表格驱动的分支选择

    原理是对于多层条件语句嵌套那种,翻译成汇编会很乱,很麻烦,同时如果我们用汇编开发过程中出现了太多层的条件嵌套写着也麻烦,有一种方法是我们把每个条件和执行地址看成一个”结构体”,然后在创建一个”结构体数组”,例如

 

    条件A执行00000120处的函数,或者跳转到那里,条件B则是00000130...这样我们每次只要线性扫描这个表就行了,这个姿势叫做以表格驱动的分支选择。

例子程序:用户从键盘输入一个字符,程序使用一个循环将该字符同表中每个项比较,对于找到的第一个匹配项,紧跟在待查找值其后存储的过程将被调用。每个过程使用EDX来装入不同的字符串偏移,然后在循环中显示该字符串:

TITLE Table of Procedure Offsets (ProcTable.asm)

;This program contains a table with offsets of procedures.

;It uses the table to execute indirect procedure calls.

INCLUDE Irvine32.inc

.data

CaseTable BYTE   'A'

          DWORD  Process_A

EntrySize = ($ - CaseTable)

  BYTE   'B'

          DWORD  Process_B

  BYTE   'C'

          DWORD  Process_C

  BYTE   'D'

          DWORD  Process_D

NumberOfEntries = ($ - CaseTable) / EntrySize

prompt    BYTE   "Press capital A,B,Cor D:" ,0

msgA      BYTE   "Process_A" ,0

msgB      BYTE   "Process_B" ,0

msgC      BYTE   "Process_C" ,0

msgD      BYTE   "Process_D" ,0

.code

main PROC

    mov   edx ,OFFSET prompt

call  WriteString

call  ReadChar

mov   ebx ,OFFSET CaseTable

mov   ecx ,NumberOfEntries

L1:

    cmp   al ,[ebx]

jne   L2

call  NEAR PTR [ebx + 1]

call  WriteString

call  Crlf

jmp   L3

L2:

    add   ebx ,EntrySize

loop  L1

L3:

     exit

main ENDP

 

Process_A PROC

    mov  edx ,OFFSET msgA

ret

Process_A ENDP

Process_B PROC

    mov  edx ,OFFSET msgB

ret

Process_B ENDP

Process_C PROC

    mov  edx ,OFFSET msgC

ret

Process_C ENDP

Process_D PROC

    mov  edx ,OFFSET msgD

ret

Process_D ENDP

END main

 

 

同时上面有几个个地方需要注意一下:

1.一个是函数名字直接可以当变量用了:

 

2.A=B不占用.data定义东西的时候的地址空间(不然的话,上面那么写代码就算错了)

 

3.CALL指令调用存储在EBX+1内存地质处的过程地址,这种间接调用格式要使用NEAR PTR运算符。

 

 

原文地址:https://www.cnblogs.com/csnd/p/12062276.html