条件汇编
条件汇编是汇编器在汇编阶段,依据所设定的条件,使汇编器汇编某⼀段程序,或不汇编某⼀段程序。⼀般⽽⾔,条件汇编常配合宏使⽤,使得撰写汇编语⾔源⽂件能有初步的结构化 (注⼀ )。底下⼩⽊偶使⽤条件汇编与宏配合⽽写成的⼀个简单程序:EXAM05.ASM。
这个例⼦是先在屏幕上显⽰『计算:2593 8888 = 』字串,然后再显⽰结果来。这是⼀个是很简单的例⼦,但是⼩⽊偶要⽤⼀个宏来解决显⽰字串及数字这两种不同的数据形态。⽅法就是在宏中加⼊能⾃动判断输⼊的引数是字串或是数字的伪指令。
这两者的分别在⾼级语⾔是泾渭分明的,在汇编语⾔中,这两者都是以⼆进制数字表⽰,分别并不是那么明显。EXAM05.ASM 在处理⽂字与数字的程序码当然也是不⼀样的。假如数据是⽂字的话,只需打印即可,如果是数字的话,就得换成⼗进制再显⽰出来。但是在汇编语⾔⾥很难真的判断数据是⽂字抑或数字。
TYPE 运算⼦
⼩⽊偶的想法很简单,⽤ TYPE 运算⼦这个伪指令来判断该数据是字串或是数字。TYPE 伪指令的⽤法是:
TYPE    变量
TYPE 会根据变量的定义决定运算结果,假如变量是以『DB』定义,则结果为 1;以『DW』定义,结果为 2;以『DD』定义,结果为4;以『DQ』定义,结果为 8;以『DT』定义,结果为 10。在⼀般⾼级语⾔⾥,很少变量只占⼀个字节,⽽汇编语⾔则不⼀定,但是⼩⽊偶在想不出其他更好的⽅法,只有⽤ TYPE 来判断数据形态。汇编语⾔⾥,定义⼀个字串,常⽤『DB』,定义变量,常⽤『DW』或『DQ』,因此如果 TYPE 传回来的值为 1,则引数应该是字串,反之则否。
IF - ELSE - ENDIF 条件汇编伪指令
解决了判断数据的形态之后,接下来就是依据数据形态决定汇编那⼀段源程序,也就是这⼀章的主⾓,IF - ELSE - ENDIF 伪指令,它的格式是
IF 判断式
叙述⼀
[ELSE
叙述⼆]
ENDIF
假如判断式为真,汇编器就会汇编『叙述或指令⼀』内的指令;假如判断式为伪,汇编器会汇编『叙述⼆』内的指令。假如判断式为伪时,并没有指令需要执⾏,那也可以省略叙述⼆,省略时,必须由 ELSE 到叙述⼆为⽌的部分省略,ENDIF 是⽤来表⽰ IF 叙述结束的,是不可省略的。叙述⼀或叙述⼆可以是由很多指令或是叙述组成。
⼀般⽽⾔,判断式⼤部分是两个数值之⽐较,⽐较结果为真,则传回 0FFFFH,⽐较结果为否,则传回 0,汇编器依据 0FFFFH 或 0 来汇编那⼀个程序⽚段。⽽⽐较的两个数值必须是在汇编阶段就能够确定⼤⼩的数值,因此不可以使⽤寄存器或变量,⽽像数据长度,或是地址都是可以拿来作为⽐较的数值。下表表⽰能⽤在判断式的关系运算⼦:
运算⼦实例说明
EQ var1 EQ var2若 var1 等于 var2 时,为真
NE var1 NE var2若 var1 不等于 var2 时,为真
LT var1 LT var2若 var1 ⼩于 var2 时,为真
LE var1 LE var2若 var1 ⼩于或等于 var2 时,为真
GT var1 GT var2若 var1 ⼤于 var2 时,为真
GE var1 GE var2若 var1 ⼤于或等于 var2 时,为真
NOT NOT var若 var 为伪时,为真
AND var1 AND var2若 var1、var2 皆为真时,为真
OR var1 OR var2若 var1、var2 中有⼀为真时,为真
XOR var1 XOR var2若 var1 为真且 var2 为伪,或 var1 为伪且 var2 为真时,为真
数值var若 var 不为零时,为真
源程序
底下就是 EXAM05.ASM 程序列表:
include mymac.inc    ;01 载⼊ MYMAC.INC 宏程序库
purge  display      ;02 除去 DISPLAY 宏
.
286          ;03 使⽤ 80286 指令集
print_number    macro      ;;05 此宏⽤来把 DL ⾥的数值以 ASCII 字符
add    dl,'0'      ;;06 ⽅式显⽰于萤光幕,显⽰前 DL 应该在 0
mov    ah,2        ;;07 到 9 之间。
int    21h
endm                        ;;09 结束 print_number 宏
print  macro  var        ;;11 print 宏开始
local  tmp_var,nxt
if      (type var) eq 1    ;;13
code    segment para    public  'code'
mov    dx,offset var
mov    ah,9
int    21h
code    ends
exitm
endif                      ;;20
if      (type var) eq 2    ;;21
data    segment para    public  'data'
tmp_var dt      ?
data    ends
code    segment para    public  'code'
fild    var
fbstp  tmp_var
mov    si,offset tmp_var 2
mov    dl,[si]
print_number        ;;30 调⽤ print_number 宏
mov    cx,2
nxt:    dec    si
mov    dl,[si]
shr    dl,4
print_number
mov    dl,[si]
and    dl,0fh
print_number
loop    nxt
code    ends
exitm
endif                          ;;42
endm                            ;;43 print 宏结束
;***************************************
data    segment para    public  'data'
string  db      '计算: 2593  8888 =
print 宏在逻辑上可分成两部分,判断数据形态及依据数据形态如何处理这两部分。就前者⽽⾔,第 13 ⾏和第 21 ⾏这两⾏就是判断引数之数据形态是字串抑或字整数;就后者⽽⾔,假如是字串的话,汇编器将汇编第 14 ⾏到第 20 ⾏,假如是字整数的话,汇编器将汇编第22 到第 41 ⾏。
在 MASM 5.0 及其以后的版本,⼀个宏⾥⾯是可以再使⽤另⼀个宏,像这种,宏⾥⾯⼜有宏的情形称为『巢状』,像第 30 ⾏、第 35 ⾏及第 38 ⾏都是在 print 宏⾥再使⽤⼀个宏,这是可以被允许的。MASM 并没有限制巢状宏的层数,只要存储器及堆栈不被使⽤完即可。
其他条件汇编指令
IFE 伪指令
MASM 所提供的条件汇编叙述,除了 IF - ELSE - ENDIF 之外,还有好⼏个,它们都可以配合 ELSE 使⽤,并且似乎『成双成对』。⼩⽊偶的意思是,IF 是当条件为真时,汇编 IF 之后的叙述或指令,⽽还有⼀个 IFE 伪指令与之配对,IFE 是指当条件为伪时,汇编 IFE 之后的叙述或指令。
IF1 与 IF2 伪指令
IF1、IF2 是测试⽬前的汇编步骤。MASM 是两阶段 ( 注⼆ )的汇编器,IF1 与 IF2 就是分别只在第⼀阶段汇编或第⼆阶段汇编才汇编的条件汇编伪指令。⼀般⽽⾔,宏只需汇编⼀次,所以可以⽤ IF1 来增快汇编速度。这两个伪指令的语法是:
IF1
叙述1
[ELSE
叙述2]
ENDIF
IF2 也是⼀样,都不需要测试条件,因为都已经写在 IF 之后了。
IFDEF 与 IFNDEF 伪指令
IFDEF 伪指令是⽤来测试其后的变量或标号等符号是否经过定义,如果是的话才汇编;⽽ IFNDEF 则是未定义才汇编。其语法是:
IFDEF  符号名
叙述1
[ELSE
叙述2]
ENDIF
但这个指令却有令⼈不解的地⽅。假如某个符号在 IFDEF 之后才定义,在第⼀阶段汇编 ( 注⼆ ) 时,当然是还未定义,但第⼆阶段汇编时就是已定义了;⼜如果该符号在 IFDEF 之前就已经定义了,不管第⼀阶段或第⼆阶段汇编都是已定义,所以照这样看来,似乎都得汇编IFDEF 之后的叙述了,这样 IFDEF、IFNDEF 岂不是根本就毫⽆⽤处?
原来要使⽤ IFDEF 或 IFNDEF 有两个⽅法可供使⽤,⼀是配合前⾯的 IF1 或 IF2 使⽤,另⼀种⽅法是根本就不要在源程序中定义该符号,等到要使⽤时,再于 DOS 命令提⽰下输⼊ MASM 的参数『/D』来定义该变量,例如底下这个程序,SUM02.ASM,是计算由⼀开始,公差为⼀的等差数列之和,⾄于最末⼀项是什么,则是由『/D』参数后⾯的定义来决定,如果没以『/D』参数定义最末项,则设为 100。
last_number    macro  ;;01 是否定义最后⼀数
ifdef  number
n      dw      number  ;;02 是,则以选项定义为准
else
n      dw      100    ;;03 否,则加到 100
endif
endm
;***************************************
code    segment
assume  cs:code,ds:code
org    100h
;---------------------------------------
start:  jmp    short begin
last_number            ;15 定义最后⼀项之值
counter dw      1              ;16 计数器
string  db      "1 2 ... ___FCKpd___5quot;    ;17 显⽰的字串
begin:  sub    bx,bx          ;18 BX 为和
mov    cx,n            ;19 CX 为项数
next:  add    bx,counter      ;20 相加循环
inc    counter
loop    next
mov    dx,offset string
mov    dx,offset string
mov    ah,9
int    21h
push    bx              ;27 保存和
mov    ax,n            ;28 显⽰最后⼀项
call    display_ax
mov    dl,'='          ;30 显⽰等号
mov    ah,2
int    21h
pop    ax              ;34 显⽰和
call    display_ax
mov    ax,4c00h        ;36 结束
int    21h
;---------------------------------------
;39 AL 之数值为⼗进制之个位数,此⼦程序将加上 30h
;40 使之成为 ASCII 字符,显⽰屏幕上
display_decimal proc    near
cmp    n_zero,0        ;42 检查
jnz    dply
or      al,al          ;44 检查最⾼位数是否为零
jz      exit            ;45 若是,则不显⽰出来
or      n_zero,1        ;46 若否,则显⽰出来并且使 n_zero 设为⼀dply:  push    ax
push    dx
mov    dl,al
add    dl,'0'
mov    ah,2
int    21h
pop    dx
pop    ax
exit:  ret
n_zero  db      0              ;56 n_zero 为⼀个标志,若是最⾼位数为display_decimal endp            ;57 零则为⼀,依次递减直到最⾼位数不为;-------------------------------;58 零时,n_zero 才设为⼀
;59 把 AX 内的⼗六进制数值,以⼗进制⽅式显⽰屏幕上
display_ax      proc    near
sub    dx,dx
mov    bx,10000
mov    n_zero,dl
div    bx
call    display_decimal
mov    ax,dx
mov    bx,1000
sub    dx,dx
div    bx
call    display_decimal
mov    ax,dx
mov    bl,100
div    bl
mov    dl,al
call    display_decimal
mov    al,ah
cbw
mov    bl,10
div    bl
mov    dl,al
call    display_decimal
mov    dl,ah
add    dl,'0'
mov    ah,2
int    21h
ret
display_ax      endp
;
---------------------------------------
code    ends
code    ends
;***************************************
end    start
汇编 SUM02.ASM 可以像以前⼀样直接于 DOS 提⽰号下『MASM SUM02;』即可,这样的话执⾏结果会是『1 2 ... 100=5050』。但您也可以指定末项为其他数,执⾏结果会不同喔。例如:
H:/HomePage/SOURCE>masm /Dnumber=200 sum02; [Enter]
Microsoft (R) Macro Assembler Version 5.00
Copyright (C) Microsoft Corp 1981-1985, 1987.  All rights reserved.
51502  418690 Bytes symbol space free
0 Warning Errors
0 Severe  Errors
H:/HomePage/SOURCE>link sum02; [Enter]
Microsoft (R) Personal Computer Linker  Version 2.40
汇编语言大小写字母转换
Copyright (C) Microsoft Corp 1983, 1984, 1985.  All rights reserved.
Warning: no stack segment
H:/HomePage/SOURCE>exe2bin sum02 sum02 [Enter]
H:/HomePage/SOURCE>sum02 [Enter]
1 2 ... 200=20100
H:/HomePage/SOURCE>
注意到 MASM 汇编器⽩⾊部分的参数选项,改变其值就会造出不同的执⾏⽂件来。
IFB 与 IFNB 伪指令
这两个伪指令的语法是:
IFB    <;引数>
IFNB    <;引数>
IFB 是⽤来测试是否有引数传到宏中,如果没有引数的话 ( B 是指空格,blank,的意思,即没有引数 ),则汇编。⽽ IFNB 则是有引数 ( NB 是指不空格,即有引数 ),则汇编。这样说,您可能还是不懂,待⼩⽊偶举个例⼦吧,底下这个宏,push_reg,可以把好⼏个寄存器推⼊堆栈,直到没有指定的寄存器可推⼊,⽽推⼊堆栈的寄存器数⽬可以不固定且可以是任意⼗六位的寄存器。
push_reg        MACRO  reg_string
IRP    reg,<reg_string>
IFNB    <reg>
push    reg
ENDIF
ENDM
ENDM
使⽤这个宏时,输⼊之参数必须以⾓括号包围起来,例如在程序中⽤
push_reg        <ax,bx,si>
来使⽤此宏,因为⾓括号的关系,MASM 会把输⼊的 ax,bx,si 当做⼀个字串传⼊ push_reg 宏,宏的主要内容是⼀个不定重复块,该不定重复块的引数就是刚刚传⼊宏的字串,⽽后依次取出⼀个寄存器推⼊堆栈,直到寄存器都被提出为⽌。如何检查寄存器全都被提出了呢?就是⽤ IFNB 来检查,当还有寄存器未被提出时,IFNB 为真,汇编 push reg 这⼀⾏,若为伪时,则汇编 exitm,就跳出宏了。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。