第三章 程序设计的基本技术同高级语言一样,汇编语言程序的基本结构有:顺序程序,分支程序,循环程序。
解题的基本步骤也基本相同。
§ 3.1 顺序程序设计以直线方式一条指令接着一条指令顺序执行。常用的算术运算大多可用顺序程序来解决。
所以先介绍乘除指令与十进制运算指令。
一,乘除指令对加减运算,带符号数和不带符号数运算指令相同(只要字节够) ;对于无符号,关心
CF,可知结果正确与否;对带符号数,关心
OF,SF 可知结果正确与否,且知正负。但乘除运算则不行,为此 8088提供了带符号与不带符号的乘除指令。
1,无符号数乘法( MULtiply)
格式,MUL SRC
2,带符号数乘法( Signed Integer
MULtiply)
格式,IMUL SRC
操作,字节运算 AL * (SRC) → AX
字运算 AX * (SRC) → DX:AX
有一个操作数 隐含,即被乘数和乘积都在规定的寄存器中,源操作数只能是寄存器或存储器,且类型明确,不能为立即数,
当 CF=OF=1 时,不表示进位和溢出,而表示乘积已不是 8位或 16位,即高位为有效位,
例,IMUL BX ; AX* BX→ DX:AX
MUL 25 ╳
IMUL [SI] ╳
IMUL BYTE PTR [SI] ; AL* ([SI]) → AX
两指令的操作相同,只是 操作数是否带符号,
但对同一操作数进行操作,结果不一致 。 ∴ 应视操作对象,正确选择指令 。
例 1,MOV AL,0B4H ; 视做带符号数 → ﹣ 76
MOV BL,11H ; 视做带符号数 → 17
IMUL BL; AX=﹣ 76*17=﹣ 1292=0FAF4H?补码; CF=OF=1 表示 AH内容为有效位例 2,MOV AL,0B4H ; 视 B4H为无符号 → 180
MOV BL,11H ; 视 11H为无符号数 → 17
MUL BL ;; AX=180*17=3060=0BF4H
实际上,对带符号数进行运算,可理解
(内部过程),先对数求其绝对值,再进行二进制乘,再求补,得其补码结果。对无符号数乘则直接进行二进制运算。
无符号数的乘法指令可用于多倍精度的乘法操作(由乘移位,加实现)。多倍精度的负数乘,
则只能将它们的绝对值用无符号乘法实现后,再求补。
事实上,∵ 乘积的字长相对操作数来说总是加倍的
∴ 乘法不会产生溢出。
例、将 AX中的 3位 BCD数转换为二进制数?SB。
N2* 102+ N1* 10+ N0=( N2* 10+ N1)* 10+ N0
SB DB?
MOV AX,0125H
M0V CH,10
MOV CL,4
MOV SB,AL?暂存
MOV AL,AH
MUL CH
MOV AH,SB
SHR AH,CL
ADD AL,AH
MUL CH?( D2* 10+ D1)* 10?AL
AND SB,0FH
ADD SB,AL
D2* 10?AL
D2* 10+ D1?AL
( D2* 10+ D1)* 10+ D0?SB= 7DH
对同一操作数进行两种运算结果不同。
除法运算可能产生溢出,但 OF标志不能标志这种溢出 (除法对状态标志未定义),
而由溢出中断来指示,若溢出,则立即停止程序的执行。
MOV AX,0F252H
MOV BL,20H
DIV BL
MOV AX,0F252H
MOV DX,0 ; 无符号数扩展,高位直接送 0即可。
MOV BX,20H ;
DIV BX ; 应使用字运算才正确例,计算无符号数 0F252H÷ 20H
产生溢出,
∵ 商 ﹥ 255,
AL存放不下例、将 AL中的 8位无符号数转换为压缩型 BCD数?AX。
除 10取余数便得 BCD数。
MOV AL,7DH
MOV CL,4
MOV BL,10
MOV AH,0?扩展被除数为字类型
DIV BL?商?AL,余数?AH(个位数)
MOV BH,AH ;个位暂存
MOV AH,0 ;再扩展被除数
DIV BL?商?AL(百位数),余数?AH(十位数)
SHL AH,CL
OR BH,AH
MOV AH,AL
MOV AL,BH ; AX= 0125H
合并十位与个位?BH
二,带符号数的符号扩展指令
1.将字节扩展成字格式,CBW
操作:将 AL中的符号扩展至 AH中。
2.将字转换成双字格式,CWD
操作:将 AX中符号扩展至 DX中。
两指令都是隐含寻址,即被扩展数在 AL或 AX→ AX 或
DX:AX
例,﹣ 4001H ÷ 4
MOV AX,﹣ 4001H;对带符号数一定使用符号扩展指令
CWD
MOV CX,4
IDIV CX ; 若用字节除则产生溢出例,实现单精度的四则混合运算
[U- (X*Y+Z- 540)]/X→ AX…DX,
已知 U,X,Y,Z均为带符号数。
DATA SEGMENT
U DW 7FFFH
X DW 50
Y DW 1000
Z DW - 80
DATA ENDS
MOV AX,X
IMUL Y
MOV CX,AX X* Y?BX,CX
MOV BX,DX
MOV AX,Z Z扩展?DX,AX
CWD
ADD CX,AX X* Y+ Z?BX,CX
ADC BX,DX
SUB CX,540 X* Y+ Z- 540?BX,CX
SBB BX,0
MOV AX,U U扩展?DX,AX
CWD
SUB AX,CX
SBB DX,BX [ U-( …) ]/X?AX…DX
IDIV X
作业,P.164,1.( 2)(视为无符号数)
( 3)(视为带符号数)
三,BCD数调整指令
∵ BCD运算是逢十进一,而四位二进制数运算是逢 16进一,而计算机总是按二进制规律运算,所以对 BCD运算,要进行调整,由调整指令来实现。
( 一)、压缩型 BCD数运算(一个字节存放两位十进制数)
1,压缩型 BCD加法调整格式,DAA
操作,将 AL中的和调整为 BCD数 → AL
调整规则,(AL AND 0FH)>9或辅助进位 AF=1,则
AL加 6;( AL AND 0F0H)>90H或 CF=1,则 AL加
60H.
例,87+39=126→ AX
XOR AH,AH
MOV AL,87H ; AL= C0H
ADD AL,39H ; AF= 1,CF= 0
DAA ; CF= 1
RCL AH,1 ; AX= 0126H
该指令操作数 隐含为 AL,即只能对 AL调整,以 AL
为目的。
∵ 调整时要 用到 CF,AF标志,所以调整指令 应紧跟 ADD指令。
DAA指令会影响标志。
1000 0111
+ 0011 1001
1100 0000
+ 0110 0110
1 0010 0110
格式,DAS
操作,将 AL中的差调整为 BCD数 → AL
调整规则,(AL AND 0FH)>9或 AF=1,则 AL减 6
( AL AND 0F0H)>90H或 CF=1,则 AL减 60H
例,4位压缩型 BCD数加减运算(设不向更高位产生进位或借位)。
如,3754+5219-4981=3992
2、压缩型 BCD减法调整
DATA SEGMENT
BCD1 DB 54H,37H
BCD2 DB 19H,52H
BCD3 DB 81H,49H
BCD DB?,?
DATA ENDS
MOV AL,BCD1
ADD AL,BCD2
DAA
MOV BCD,AL
MOV AL,BCD1+1
ADC AL,BCD2+1
DAA
MOV BCD+ 1,AL
完成 54+ 19?BCD
单元完成 37+ 52?BCD+ 1单元
MOV AL,BCD
SUB AL,BCD3
DAS
MOV BCD,AL
MOV AL,BCD+1
SBB AL,BCD3+1
DAS
MOV BCD+1,AL
只能对 AL调整,∴ BCD运算只能是字节运算 。
注意 算法 —即运算步骤:一个数运算完成后,才能运算第二个数;先运算低字节,后运算高字节,高字节带进位加减。
完成减 81?BCD
完成减 49?BCD+ 1
(二)、非压缩型 BCD数运算指令(又称 ASCII码
BCD运算)
压缩型 BCD没有乘除调整,∵ 找不到一种简便算法,
∴ 要实现压缩型 BCD乘除运算时,往往是先转换成等效的二进制数,再进行二进制乘除运算,然后再转换成压缩型 BCD数。而非压缩型 BCD数一个数字占一个字节,∴ 可进行加、减、乘、除运算,
1.加法调整格式,AAA
操作;将 AL中的和调整为 BCD→ AL,
AH加调整时出现的一位 BCD进位 → AH
调整原则,(AL AND 0FH)>9或 AF=1,则
(AL+6) AND 0FH→ AL,
AH+1→ AH,且 CF=1,AF=1.
此进位同时体现在 CF
调整后的存放规律可理解为调整后的十位数,即进位 → 加到 AH中,个位数 → AL
∴ 一般执行该指令前 AH应为 0(若原不为 0,也是加上调整的进位)。
调整时要用到标志,∴ 应紧跟 ADD指令,
例,非压缩 BCD运算,如,89+67=156
BCD1 DW 0809H
BCD2 DW 0607H
BCD3 DB 3 DUP(?)
MOV AL,BYTE PTR BCD1 ; AL= 09H
ADD AL,BYTE PTR BCD2 ; AL= 10H,AF=1
AAA ; AL= 06H,CF=1
MOV BCD3,AL ; 存个位
MOV AL,BYTE PTR BCD1+1 ; AL= 08H
ADC AL,BYTE PTR BCD2+1 ; AL= 0FH,加低位 CY
AAA ; AL= 05H,CF= 1
MOV BCD3+ 1,AL ;存十位
MOV BCD3+ 2,0
RCL BCD3+ 2,1 ;存百位,(BCD3)= 010506BCD
调用 AAA指令,因为进位既加到了 AH中,又体现在 CF
中,所以对进位(高位)的处理有多种方法。
MOV AH,0

MOV BCD3+2,AH ; 进位 AH即高位
MOV BCD3+2,0 ;进位 CF即高位
RCL BCD3+2,1
MOV BCD3+2,0 ;进位 CF即高位
ADC BCD3+2,0
2,减法调整格式,AAS
操作:将 AL中的差调整为 BCD→ AL,
AH减调整时产生的借位 → AH
调整原则:若 (AL AND 0FH)>9 或 AF=1
则 (AL-6) AND 0FH→ AL,AH-1→ AH,
CF=1,AF=1
同样应紧跟 SUB或 SBB指令,
此借位同时体现在 CF
3,乘法调整格式,AAM
操作:将 AL中的积调整为 BCD?AX
调整原则,AL/10?商 → AH ← 即十位
余数 → AL← 即个位
∵ 两位非压缩型 BCD数的 max=9*9=81,∴ 对于 AL中小于 100=64H的二进制数都能调整为 BCD,且与标志无关,
不一定紧跟 MUL指令使用。
例,9*8=72→ AX
MOV AL,9
MOV BL,8
MUL BL ; AX= 0048H
AAM ; AX=0702BCD
例,MOV AX,0FFH
AAM
MOV CL,AL
MOV AL,AH
AAM ; AH:AL:CL=020505BCD
AAM即等效于 ÷ 10,凡是将字节数变换成 BCD数均可用此指令来实现,
4,除法调整格式,AAD
操作:将 AX中的 BCD数变换成二进制数 → AL
调整原则,AH*10+AL→ AL,0→ AH
此指令实际上是对被除数进行预调整,即将两位非压缩
BCD数变换成二进制形式集中在 AL中,
例,77÷ 3=25… 2
BCDW DW 0707H
BCDB DB 03H
R DB?
Q DW?
MOV AX,BCDW ; AX=0707H
AAD ; AX=004DH
DIV BCDB ; AX=0219H
MOV R,AH ;存余数
AAM ; AX=0205H
MOV Q,AX ;存商四,顺序程序设计举例例 1,非压缩型 BCD乘法运算,如,29*9=261,并显示结果。 参见 P,82 例 3,3
算法,29
* 9
81
+ 18
261
W DW 0209H
B DB 9
JJ DB 3 DUP(?)
JJS DB 3 DUP(?),’ $ ’
MOV AL,BYTE PTR W
MUL B
AAM
MOV JJ,AL
MOV JJ+1,AH
MOV AL,BYTE PTR W+1
MUL B
AAM
ADD AL,JJ+1
AAA
MOV JJ+ 1,AL ;存十位数
MOV JJ+ 2,AH ;存百位数十位?AH?JJ+ 1
9* 9
个位?AL?JJ
百位?AH
2* 9
十位?AL
十位合并,调整时的进位自动加在 AH中
MOV AL,JJ
ADD AL,30H
MOV JJS+2,AL
MOV AL,JJ+1
ADD AL,30H
MOV JJS+1,AL
MOV AL,JJ+2
ADD AL,30H
MOV JJS,AL
LEA DX,JJS
MOV AH,9
INT 21H
RET
个位数?ASCII
存显示区的最低位十位数?ASCII
存显示区的次低位百位数?ASCII
存显示区的最高位显示结果:
261
注,∵ 光标只能从左到右移动,
∴ 数字字符应从高到低存放 。
教材中例题均可增加显示,在 DOS下运行直观显示结果,
如何将运算结果(数值),?ASCII字符显示的方法可参见实验教材第二章。
例 2,(教材 P85、例 3,6)从键盘键入 0~ 9的任一数,求其立方值并显示。
INPUT DB ’Please Input N( 0~ 9),$ ’
LFB DB ’ 0$ 1$ 8$ 27$ 64$ 125$ ’
DB ’ 216$ 343$ 512$ 729$ ’
N DB?
MOV DX,OFFSET INPUT
MOV AH,9
INT 21H
MOV AH,1
INT 21H
MOV N,AL
MOV AH,2
MOV DL,0AH
INT 21H
显示提示信息键入一个数?N
输出换行
MOV DL,N
AND DL,0FH
MOV CL,2
SHL DL,CL
MOV DH,0
ADD DX,OFFSET LFB
MOV AH,9
INT 21H
查表技术是最常用的算法,凡是难以找到简便的数学方法的时候,都可以用查表法来实现。
作业,P164,2.(2),(4)作业本
P165,▲ 3,*9.
N* 4?DX
显示( LFB+ 4* N)单元的内容,直到$结束
§ 3.2 分支程序设计顺序程序是按指令的书写(存放)顺序执行,
而实际情况需要根据不同的条件做不同的处理,
形成分支。汇编中的分支的条件往往是通过标志的不同状态而反映的。常用判断标志指令和转移指令实现分支。
一,条件转移指令通用格式,Jcond S_LAB
操作:若满足 cond条件,则将 S_LAB标号的偏移地址 → IP,即转移,否则顺序执行。
cond→ 条件是:判 5个条件标志 ZF,SF,OF,PF,CF
成立 /不成立,可以判单个标志,也可以判多个标志,因而有多条指令。
S_LAB→ 短距离标号,条件转移是相对转移指令,
即从当前地址到目标地址的偏移量为 -128~ +127
(从本指令则为 -126~ +129,因为条件转移指令均为双字节指令),
∴ 只能在段内转移 。
( 一),简单条件转移?仅判断一个标志 转移,5种条件标志,成立 /不成立,有 10种状态,∴ 有 10
种指令。
参见教材,P88、表 3- 1
例,JZ/JE S_LAB→ 为零 /相等转移操作,测试前面操作结果为 0则转移,即判 ZF=1
转移,即 A=B转移 。
JZ,JE为等价助记符,任写一种均可,可根据程序设计意图选择。
例,MOV BX,0FFFFH?0
INC BX ; ZF=1 ; ZF=0
JZ NEXT ; 为 0转移到 NEXT
… ; 不为 0顺序执行
NEXT,……
例,MOV BL,1 ; → 5
CMP BL,5 ; ZF=0 ; ZF=1
JE NEXT ; 比较结果相等则转移到 NEXT
… ;不相等顺序执行
NEXT,…
此类指令,均指测试前面的操作结果所设标志,本指令不影响标志 ∴ 在条件转移指令前,一定有一条能正确影响标志的指令。
(二 ),无符号数条件转移指令条件转移常依据两个数的关系来决定,
两个数的关系除相等与否,还有大小之分,比较大小时,要区分是无符号数还是带符号数,否则答案不明确。
如,0FFH>0FH?
视为无符号数 0FFH=255 > 0FH=15 成立;
视为带符号数 0FFH= -1 > 0FH=15 不成立。
∴ 应将两种类型的数分开比较,∴ 提供两类指令:
对无符号数使用术语,低于 /高于 Below/Above.判 CF,ZF
对带符号数使用术语:小于 /大于 Less/Greater,判
SF,OF,ZF
无符号数条件转移指令( P89,表 3- 2):
JB/JNAE S_LAB,判两个无符号数 A<B转移,CF=1且 ZF=0转
JBE/JNA S_LAB,判两个无符号数 A≦ B转移,CF=1或 ZF=1转
JA/JNBE S_LAB,判两个无符号数 A > B转移,CF=0且 ZF=0转
JAE/JNB S-LAB,判两个无符号数 A≧ B转移,CF=0或 ZF=1转
B (Below) A (Above)
(三),带符号数条件转移指令( P89,表 3- 3)
这类指令的判断依据是 SF,OF,ZF。
JL/JNGE S_LAB,判两个带符号数 A<B转移。
JLE/JNG S_LAB,判两个带符号数 A ≦ B转移。
JG/JNLE S_LAB,判两个带符号数 A>B转移。
JGE/JNL S_LAB,判两个带符号数 A ≧ B转移。
L(Little) G(Great)
注意:一定要 视操作对象,正确使用指令,比较指令本身并不区分对象是否带符号,它只设标志,要由条件转移指令来区分比较对象。
例,MOV AL,-12 ; AL= F4H
CMP AL,20H
JA NEXT ;

NEXT,…
一般由题意给定是否带符号数,如地址,ASCII码,循环次数,多倍精度数的低位等都被视为无符号数。
当用 JA时,视 AL=F4H为无符号数 >20H条件成立,转移;
当用 JG时,视 AL=F4H为带符号数 >20H不成立,顺序执行。
二,无条件转移指令?无条件地使 CPU转移到某目的地址处执行。它虽然不能构成分支,但在分支程序中往往需要它将各分支的出口重新汇集到一起时使用。
1,无条件直接转移格式,JMP LAB
操作:直接转到 LAB处:
LAB在段内,LAB的偏移地址 → IP
LAB在段外,LAB的偏移地址 → IP
LAB的段地址 → CS
2,无条件间接转移格式,JMP DSP?通过各种寻址方式获得 转 移地址。
操作,目的操作数为寄存器?寄存器内容?IP
目的操作数为字变量?字变量内容?IP
目的操作数为双字变量?双字变量内容?CS:IP
条件转移范围 -128~ +127,而 JMP可在 ± 32K或跨段转 移。
例,JMP NEXT ; 无 条件转移转到 NEXT

NEXT,…
例,JMP WORD PTR[BX] ;即( [ BX ] )?IP
三,分支程序设计举例
① 分支实现的基本方法有两种,一种是利用比较转移指令实现分支;一种是利用跳转表实现分支。
② 视 比较对象,正确选择合适的转移指令。
③ 要为每个分支安排正确出口。
④ 凡是可共用的部分,应尽量放在公共程序段中以使程序简短。
⑤ 在调试分支程序时,应使用多组数据,分别对各种分支进行反复测试。
例 1,2个字节变量相减,以 16进制形式显示其结果。
A DB 0C5H
B DB 37H
D DB?
MOV AL,A
SUB AL,B
MOV D,AL
MOV DL,D
AND DL,0F0H
MOV CL,4
SHR DL,CL
CMP DL,0AH
JB NAD7
ADD DL,7
NAD7,ADD DL,30H
MOV AH,2
INT 21H
MOV DL,D
AND DL,0FH
CMP DL,0AH
JB ND7
ADD DL,7
ND7,ADD DL,30H
MOV AH,2
INT 21H
MOV DL,’ H’
INT 21H
A-B?D
显示高位字
,8“
显示低位字
,E

显示
,H

例 2,显示,Continuance( Y/N)? $”,
回答 Y,则显示:,Continuance !,,
回答 N,则显示:,Stop !”
∵ 比较对象为字符,只比较相等否,∴ 选用简单条件转移指令即可。 此例 利用比较转移指令实现分支。
OBUF DB ’ Continuance( Y/N)? $’
CONT DB ’ Continuance ! $’
STOP DB ’ Stop !$ ’
AGAIN,LEA DX,OBUF
MOV AH,9
INT 21H
MOV AH,1
INT 21H
PUSH AX ;
MOV DL,0AH
MOV AH,2
INT 21H
MOV DL,0DH
MOV AH,2
INT 21H
POP AX ;
保护输入字符 AL,
以免后面 INT 21H
调用会破坏 AL
输入 Y/N
显示
‘ Continuance( Y/ N)?’
输出换行输出回车恢复输入的 AL值
CMP AL,’ y ’
JE YES
CMP AL,’ Y ’
JE YES
CMP AL,’ n ’
JE NO
CMP AL,’ N ’
JE NO
JMP AGAIN ;
YES,LEA DX,CONT
JMP EXIT
NO,LEA DX,STOP ;
EXIT,MOV AH,9
INT 21H
RET
判断是否 Y字符判断是否 N字符输入 Y或 N方能退出程序,否则一直循环输入 Y则显示
Continuance !
输入 N则显示 Stop !
例 3,( P91、例 3,9) 某工 厂的产品有 8种不同的加工处理程序 P0~ P7,根据键盘输入,做不同的处理,若是 0~ 7以外的键,则退出加工处理。
此例可以用两种方法实现,一种是用 逐一比较判断,逐次比较 转移实现二叉分支、整体上实现多分支 ; 另一种是 跳转表法,直接实现多分支。
方法一 ︰ 逐一比较法 。
INPUT DB ’ Input( 0~ 7),$’
LEA DX,INPUT
MOV AH,9
INT 21H
MOV AH,1
INT 21H
CMP AL,’ 0 ’
JE P0
CMP AL,’ 1 ’
JE P1
CMP AL,’ 2 ’
JE P2
CMP AL,’ 3 ’
JE P3
CMP AL,’ 4 ’
JE P4
CMP AL,’ 5 ’
JE P5
显示提示等待键入一个字符为 0字符则转 P0
为 1字符则转 P1
CMP AL,’ 6 ’
JE P6
CMP AL,’ 7 ’
JE P7
JMP DOWN ;不是 0~ 7则退出程序
P0,MOV DL,’ 0 ’
JMP EXIT
P1,MOV DL,’ 1 ’
JMP EXIT

P7,MOV DL,’ 7 ’
JMP EXIT
EXIT,MOV AH,2
INT 21H
DOWN,RET ;分支程序一定要注意汇合到结束处键入 0则显示 0以替代 P0程序键入 1则显示 1以替代 P1程序
方法一简单,条理清楚,易于实现,但转移范围只能是:- 128~+ 127。
方法二,跳转表法 。
利用无条件的间接转移指令可实现远距离的多分支 (间接跳转至不同分支的入口处 ) 。
在数据区造一地址表、存放不同的分支入口地址;
设表的首地址为 PTAB,每一个 PTAB P0
入口地址占一个字单元,+ 2 P1
∴P i的入口地址= PTAB+ 2* i + 4 P2
若将 2* i?BX,则 JMP PTAB[BX],
可转到 Pi入口处。
INPUT DB ’ INPUT( 0~ 7),$ ’
PTAB DW P0,P1,P2,P3,P4,P5,P6,P7 ;定义地址表
LEA DX,INPUT
MOV AH,9
INT 21H
MOV AH,1
INT 21H
CMP AL,’ 0 ’
JB EXIT
CMP AL,’ 7 ’
JA EXIT
AND AX,0FH
ADD AX,AX
MOV BX,AX
JMP PTAB[BX] ;
EXIT,RET
显示提示等待键入 0~ 7的数字检查输入数据,
不是 0~ 7则退出
i* 2?BX
( PTAB+ 2* i)= Pi?IP
P0,MOV DL,’ 0 ’
JMP DOWN
P1,MOV DL,’ 1’
JMP DOWN

P7,MOV DL,’ 7’
DOWN,MOV AH,2
INT 21H
JMP EXIT
键入 0则显示 0以替代 P0程序键入 1则显示 1以替代 P1程序
∵ 键入的 ASCⅡ 码为无符号数;
∴ 用 JB,JA等指令而不能用 JG,JL等。
请思考,若为跨段标号,应修改哪些地方?
注:只需修改 DW?DD定义、(存 IP,CS)、
i* 2?i* 4 即可。
作业,P166,10.(作业本) *13,14.
§ 3,3 循环程序设计循环? 按照一定规律,多次重复执行一串指令。
一、循环程序的结构和高级语言一样,循环程序一般由 四部分组成:
⒈ 循环准备? 初始化,为保证循环能正常进行而做的必要的准备。
主要包括,建立地址指针,置计数初值,设置某些必要的常数,对工作寄存器及工作单元置初值或清 0等。
⒉ 循环体? 重复 执行的部分,循环的核心。
⒊ 循环的修改? 为下一轮循环作准备,包括 修改 计数器、寄存器、地址指针、恢复某些参数。
⒋ 循环控制? 控制循环正常结束,判断循环是否结束,
或继续。
任何循环都包括上述四部分,但各部分界线并不很清楚。
具体结构流程和高级言一样有两种,直到型、当型。
N
Y
N
Y
初始化修改初始化循环体修改循环体循环结束吗?
循环继续?
二,循环控制方法、最常见的有两种,
⒈ 计数控制?循环次数已知,故可用某个寄存器或单元作为计数器,用计数器的值来控制循环的结束与否。
⒉ 条件控制?循环次数未知,即循环次数与循环体的执行 情况有关,通过条件测试指令来测试是否满足循环条件,以控制循环是否结束。
三、重复控制指令 ( 循环控制 指令)
计数型控制是最常见,为此提供了专用指令。此类指令的特点是,循环次数由 CX
计数器控制。循环指令本身不影响标志。
⒈ 循环 指令格式,LOOP S_ LAB
操作,CX- 1→CX,当 CX≠0 则循环转移,
否则顺序执行。
注:本指令不影响标志,CX- 1操作不影响 ZF
标志,不判 ZF标志。
⒉ 为零 /等于循环格式,LOOPZ/LOOPE S_ LAB
操作,CX- 1→ CX,当 CX≠0,
且 ZF=1则循环转移,否则顺序执行。
注:本指令不影响 ZF,ZF由前面指令设定。
即前面比较结果相等。
⒊ 非零 /不等循环格式,LOOPNZ/LOOPNE S_ LAB
操作,CX- 1→ CX,当 CX≠0,且 ZF=0则循环转移;否则顺序执行。
⒋ CX为零转移格式,JCXZ S_ LAB?(特殊的转移指令)
操作,CX=0转移,否则顺序执行。
注,指令本身不作减操作,本指令一般用在循环的开始处,当 CX=0,则跳过循环。
注,以上指令的转移范围 ﹣128 ~ ﹢ 127。
四、单重循环程序设计举例
(注意循环结构,防止死循环)
下面通过几个例题介绍循环控制。
计数控制
条件控制
计数与条件控制
对于某些问题,还有一些特殊控制,如,
设开关变量等,可参其他教材。
例⒈ N个字节无符号字节数求和(设和为字数据)。
X DB 22H,9FH,0F4H,55H
N EQU $-X ;统计数据个数
SUM DW?
MOV CX,N
MOV AX,O 初始化
LEA SI,X
ADLOP,ADD AL,[SI]
ADC AH,0 循环体
INC SI
LOOP ADLOP 修改,控制
MOV SUM,AX ;循环结束处理例 2,2字节数相减 (A- B→D)( 设不产生借位),
以二进形式显示结果。
如,0C5H - 37H = 8EH,显示,1000 1110B
A DB 0C5H
B DB 37H
D DB?
N EQU 8
MOV AL,A
SUB AL,B
MOV D,AL
MOV CX,N
MOV BL,D
若修改为字相减,则计数改为 16
NEXT,MOV DL,0
SHL BL,1
RCL DL,1
ADD DL,30H
MOV AH,2
INT 21H
LOOP NEXT
MOV DL,’B’
MOV AH,2
INT 21H
A- B?D
初始化逐位显示 0
或 1
显示
,B“
例 3、( P97、例 3,10)计算 X + Y→Z,
X,Y 为无符号双字变量。
结果和为 5字节数。
X DD 72345678H
Y DD 90ABCDEFH
Z DB 5 DUP(?)
N EQU 4
MOV CX,N
MOV SI,0 初始化
AND AX,AX
清 CF
AGAIN,MOV AL,BYTE PTR X[SI]
ADC AL,BYTE PTR Y[SI]
MOV Z[SI],AL
INC SI
LOOP AGAIN
MOV Z[SI],0
RCL Z[SI],1
X+Y?Z
指向下一个字节处理更高位进位作业,P165,▲ 6,
例 4、( P98.例 3.11)加密程序。为保密,对数据加密,对方再解密。
设加密数关系如下,
十六数,0 1 2 3 4 5 6 7 8 9 A B C D E F
加密数,A 9 8 E F 1 0 B 2 5 D 3 7 4 6 C
解密数,6 5 8 B D 9 E C 2 1 0 7 F A 3 4
意即发 0,则发 A;发 3,则发 E;发 A,则发 D。
解密规律 →∵ 解密数的位移量 =加密数表的数值。
如:解密数 0的位移量是 = 0AH
如:解密数 4的位移量是 = 0FH
HEXS DB 4,3,2,1,0 ; 待发数
N EQU $ - HEXS ; 统计待发数个数
JMH DB N DUP(?) ; 存加密数
JMB DB 0AH,9,8,0EH,0FH,1,0,0BH,2,5,0DH,3,7,4,6,0CH
MOV CX,N
MOV BX,OFFSET JMB 初始化
MOV SI,0
AGAIN,MOV AL,HEXS[SI] ; 取待发数
XLAT JMB ; ([BX+AL]) →AL
MOV JMH[SI],AL ; 存加密数
INC SI ; 指向下一数
LOOP AGAIN
或等效
MOV AH,0
ADD BX,AX
MOV AL,[BX]
加密表同样可以编写 解密程序 如下,
JMH DB 0FH,0EH,8,9,0AH ; (设加密数)
N EQU $- JMH ;统计加密数个数
KMB DB
6,5,8,0BH,0DH,9,0EH,0CH,2,1,0,7,0FH,0AH,3,4
KMH DB N DUP(?) ;(存解密数)
MOV CX,N
MOV BX,OFFSET KMB
MOV SI,0
NEXT,MOV AL,JMH[SI]
XLAT KMB
MOV KMH[SI],AL
INC SI
LOOP NEXT
解密表初始化查表解密例 5,( P100、例 3,13)将键入的十进制数
( ﹣32768 ~ 32767)转换为二进数,并以十六进制形式显示结果 。
算法,①,十进制数?二进制数:
反复* 10+Di→ 循环体
D4D3D2D1D0=D4* 104+ D3* 103+ … + D0
= ((((0* 10+D4)* 10+D3)* 10+D2)* 10+D1)* 10+D0
输入负数 → 输入字符个数- 1;
循环次数 输入正数(不带符号) → 输入字符个数。
循环体中对其绝对值转换为二进制数,若为负数还应求补。
② ·欲显示、可将结果的 16进制数逐位转换成 ASCⅡ 字符,用 2号功能逐位显示、先高位后低位。
16进制变换成 ASCⅡ 字符:
0 ~ 9则 +30H
A ~ F则 +37H( 41H~ 46H)
键入十进制数转换初始化跳过,﹣,
AA转换
AX* 10+ Di?AX
转换结束吗?
为负数吗?
1
为负数吗?
1
求补显示回车换行及=
显示初如化取一位十六进制数?ASCII
显示一位十六制数四位显示完吗?
RET
2
2
N
Y
N
Y
N
Y
Y
N
BINARY DW?
OBUF DB ’INPUT A DECIMAL(﹣32768 ~ 32767):$’
IBUF DB 7,?,7 DUP(?)
LEA DX,OBUF
MOV AH,9 提示输入
INT 21H
MOV DX,OFFSET IBUF
MOV AH,10 键入十进制数
INT 21H
MOV CL,IBUF+1
MOV CH,0 计数 → CX
LEA SI,IBUF+2 ; → 设地址指针
CMP BYTE PTR[SI],’ - ’
PUSHF
JNE SININC
INC SI
DEC CX
SININC,MOV AX,0
AGAIN,MOV BX,10
MUL BX
MOV DL,[SI]
AND DL,0FH
ADD AL,DL
ADC AH,0
INC SI ;指向下一位
LOOP AGAIN ;计数循环为负数则跳过“-“
AX* 10+ Di?AX
POPF
JNZ AXNNEG
NEG AX 为负数,则求补,使结果
AXNNEG,MOV BINARY,AX 为补码表示
MOV AH,2
MOV DL,0DH
INT 21H 换行,回车
MOV DL,0AH
INT 21H
MOV DL,’ = ’
INT 21H 输出,=”
MOV BX,BINARY
MOV BP,4 循环初始化
MOV CL,4
NEXT,ROL BX,CL
MOV DL,BL 将 BX高四位移至底四位
AND DL,0FH
CMP DL,0AH
JC NOADD7 取一位十六进制数 → 字符
ADD DL,7
NOADD7,ADD DL,30H
MOV AH,2
INT 21H 显示一位十六进制数
DEC BP 计数循环控制,4位显示完吗?
JNZ NEXT 计数循环可用其他寄存器或单元
MOV DL,’ H ’
MOV AH,2 显示十六进制数标志
INT 21H
ret
运行,100↙
=0064H
-32768↙ IBUF
=8000H + 1
32767↙ + 2
=7FFFH
如输入:- 125↙
= FF83H
内存分配?
07
04
‘-’
‘1’
‘2’
‘5’
0D
作业,P165,▲ 7.
P166,▲ 15,
* 16.(= 6FF1H= 28657)
▲ 17.
例 6.( P108、例 3,18)将存储器中的 16位无符号二进制数转换成十进制数,并显示结果。
算法,N/ 10取其余数,则为十进制数,重复直到商为 0.它是次数未知的循环。为条件控制循环。
将余数 → 字符,送显示缓冲区,用 9号功能显示即可。
BINARY DW 0FFFFH
OBUF DB 5 DUP(?),’ $ ’
MOV BX,OFFSET OBUF+5 初始化,BX指
MOV AX,BINARY 向低位字符地
MOV CX,10 址
AGAIN,MOV DX,0 → 被除数扩展(无号符数扩展) →
一定放在循环体中
DIV CX → 余数 → DX,商 → AX
ADD DL,30H
DEC BX → 修改地址指针,指向更高位,
MOV [BX],DL 存十进制数字串
OR AX,AX 商为 0?
JNZ AGAIN
MOV DX,BX
MOV AH,9 显示结果
INT 21H
ret
思考,
若改为键入( 0~ FFFFH) → 即 0~ 65535 无符号数,应如何修改程序?
键入( 0~ FFFFH) → 将字符串变换为 真值 存入某单元?转换?输出。
若改为键入补码( 8000H~ 7FFFH) → 即
﹣32768 ~ 32767的带符号数,应如何修改程序?
键入( 8000H~ 7FFFH) → 将字符串变换为 真值 存入某单元 → 判符号(正、负) → 对绝对值进行转换 → 判正负?输出。
作业,P166,▲ 18,▲ 19,20,*23.
例 7,寻找缓冲区中字符串是否有空格 ;若字符串中有空格则显示 ‘ Find !’;
若无,则显 ‘ Not Find! ’ 。
算法:从头到尾搜索缓冲区。
在此不能用 LOOP循环,∵ 循环次数 ≦ 串长
→ LOOPNE;属于 计数与条件双重控制型循环 。
BUF DB ’ ABC 123DEF’
L EQU $-BUF ;统计串长
FIN DB ’ FIND!$’
N_ FIN DB ’ NOT FIND !$ ’
MOV CX,L → 计数
MOV SI,-1 → 变址初值 初始化
MOV AL,20H → 空格
NEXT,INC SI ; 指向下一个字符
CMP AL,BUF[SI] ;与空格比较设标志
LOOPNE NEXT→CX -1→CX 若 CX≠0,且 ZF=0(不等)
不为空格则循环
JNZ NOT_FIND→ 结束时有两种可能,CX=0或
ZF=1(相等),再判 ZF
LEA DX,FIN 相等,则显示,FIND!,
JMP EXIT
NOT_FIND,LEA DX,N_FIND→ 不等则显示,NOT FIND!”
EXIT,MOV AH,9
INT 21H
ret
INC,CMP位置不能互换 ∵ INC指令不影响 CF,但影响 ZF
五、多重循环程序设计 —
即循环体内再套有循环。
例、( P111、例 3,21) 对 N个字节 符号数 排序。 降序 排列
(大 → 小)。
N-1轮 2 3 –1 4
↓ 4 2 –1 3→3 次外计数 4 3 –1 2→2 次
DX 4 3 2 -1→1 次内循环 CX
采用逐一比较法,即将第一个数与其后的 N-1个数逐一比较,
将最大数放在第一个单元;
第二轮从第二个数开始比较,
将次大数放在第二个单元;
经 N-1轮比较,便已排序。
在每一轮的比较中总是将
max→AL 。
内循环次数依赖于外循环。
BUF DB 2,3,-1,4,0,5
COUNT EQU $-BUF
MOV SI,OFFSET BUF 外循环初始化
MOV DX,COUNT-1
OUTSID,MOV CX,DX → 内循环次数 内循环
PUSH SI → 保存第一个数地址 初始化
MOV AL,[SI] → 取第一个数
INSIDE,INC SI 前一个数 AL≧ 后一个数,则
CMP AL,[SI] 不交换;否则将 max→AL
JGE NEXCHG
XCHG [SI],AL
NEXCHG,LOOP INSIDE → 内循环修改控制
POP SI
MOV [SI],AL 存 max至第一单元
INC SI
DEC DX 外循环 修改控制
JNZ OUTSID
注意 PUSH,POP配对使用,注意位置。
注意内外循环的初始化的位置正确。
所介绍的计数型循环常规使用 -1计数;实际上也可用 +1
计数;先设计数器为 0,+ 1后比较是否达到每一特定计数值,小于等于则循环,大于则结束。
作业,P166,▲ 21,▲ 26.
§ 3.4 字符串处理程序设计字符以 ASCII码存贮于存贮器中,一个单元一个字符,
字符串总是存贮在 一片连续的存贮区,成为一个 数据块,
经常要对字符进行各种操作,如查找、传送、删除、插入等处理,为此提供了 5条串操作指令。
串操作指令的共同 约定:
①源串地址? DS:SI
② 目的串地址? ES:DI
③ 源串、目的串的偏移地址指针的移动方向由方向标志确定,DF=0,SI,DI自动增址,﹢ 1或 ﹢ 2
DF=1,SI,DI自动减址,﹣ 1或 ﹣ 2
字节?在助记符后面加 B,或 DST,SRC类型属为
BYTE.
字?助记符后面加 W,或 DST,SRC类型属为
WORD.
⑤ 带重复前缀符时的重复计数器? CX。
④ 字节串 ﹑ 还是字串操作,由符号指令确定:
—﹑ 方向标志设置指令
1﹑ 清方向格式,CLD
操作,0? DF,即清除 DF,使串操作,地址 增址 。
2﹑ 置方向格式,STD
操作,1? DF,使 DF置 1,使串操作,地址 减址 。
二 ﹑ 串操作指令
1﹑ 串传送指令格式:① MOVS DST,SRC? 字节串或字串传送
② MOVSB? 字节串传送
③ MOVSW? 字串传送操作:
传送( DS:[SI])? ES:[DI]
修改指针:当 DF=0,SI﹑ DI增 1(字节串 )或增 2(字串 )
当 DF=1,SI﹑ DI减 1(字节串 )或减 2(字串 )
即将 DS:[SI]为地址指针的源串中的一个字节或字存贮单元中的数据传送到 ES:[DI]为指针的目的单元中去,
并自动修该地址指针,使之指向下一个字节或字单元。
此指令解决了 MOV指令不能直接由存贮单元到存贮单元传送的问题。
例 ﹑ 将 STR1字节单元内容? STR2单元中。
LEA SI,STR1
LEA DI,STR2
CLD
MOVSB ; (STR1)?STR2,STR1+1? SI,STR2+1?DI
串传送指令 不影响标志。
按约定初始化
MOV AL,[SI]
MOV [DI],AL
INC SI
INC DI
格式:① LODS SRC?从源串中取一个字节或一个字
② LODSB? 从源串中取一个字节
③ LODSW? 从源串中取一个字操作:
字节( DS,[SI])?AL,字( DS,[SI] )?AX
修改 SI指针,指向下一个元素:
DF=0,SI+1或 +2,
DF=1,SI﹣ 1或 ﹣ 2。
该指令 不影响标志。
2﹑ 从源串中取数指令
3.往目的串中存数指令格式:① STOS DST? 往字节或字串中存一个数
② STOSB? 往字节串中存一个数
③ STOSW? 往字串中存一个数操作:
字节 AL? ES,[DI],字 AX? ES,[DI]
修改目的指针,指向下一个串单元:
DF=0,DI+1或 +2,
DF=1,DI-1或 -2。
该指令 不影响标志。
4.串比较指令格式:① CMPS DST,SRC? 字节或字串比较
② CMPSB? 字节串比较
③ CMPSW? 字串比较操作:
比较( DS,[SI]) -( ES,[DI]) ;源串 -目的串,设标志,
修改指针,DF=0,SI﹑ DI+1或 +2,
DF=1,SI﹑ DI-1或 -2。
该指令 影响标志,影响规律同减指令。
注意与 CMP指令 区别,CMP? 目的操作数 -源操作数
CMPS? 源串 -目的串
5.串搜索指令格式:① SCAS DST? 字节串或字串搜索
② SCASB? 字节串搜索
③ SCASW? 字串搜索操作:
字节 AL-( ES,[DI]),字串 AX -( ES,[DI]) ;设标志,
修改指针指向下一个目的单元:
DF=0,DI+1或 +2; DF=1,DI-1或 -2。
该指令 影响标志,按减法规律设标志。
该指令主要用来检索某个串中是否存在 AL或 AX的特定关键字。
三 ﹑ 重复前缀?只能用于串操作指令之前
循环程序中,常利用基址或变址寄存器建立地址指针,设置循环计数器,执行循环体后修改地址指针和计数器,判断循环是否结束。为方便这类循环程序设计,提供了重复前缀,即可在串操作指令前加入重复前缀符。
1.重复?REP,无条件重复 CX中指定次数。
重复次数由 CX值决定,重复一次,CX-1? CX,
直到 CX为 0。
重复操作是否完成的检测是在操作前进行的,若初始 CX=0,则一次也不执行。
REP可用于 MOVS,STOS的前缀,应用类同 LOOP指令 。
例 ﹑ 对 ES段的 2000H起始的 50个单元置 FFH;
即将 ( 2000H)~( 2031H) =FFH
LEA DI,ES,2000H
CLD
MOV CX,25
MOV AX,0FFFFH
REP STOSW
按约定初始化
NEXT,STOSW
LOOP NEXT
NEXT,MOV [DI],AX
INC DI
INC DI
LOOP NEXT
2.(结果)相等 /为 0重复?REPE/REPZ,使比较或搜索操作 重复执行,直到 CX=0或 ZF=0。
可用与串比较或串搜索指令的前面(因为要判标志)。
即源串与目的串未比较完( CX≠0),且两串元素相等
( ZF=1),则继续比较,所以常用来寻找不相等的数。
应用类同 LOOPZ/LOOPE指令,结束时有两种可能:
CX=0或 ZF=0。
3.(结果)不相等 /不为 0重复?REPNE/REPNZ,使比较或搜索操作 重复执行,直到 CX=0或 ZF=1
可用于串比较或串搜索指令(因为要判标志)。
即源串与目的串未比较完( CX≠0),且两串元素不等( ZF=0),则继续比较,所以常用来寻找相等的数。
应用类同 LOOPNZ/LOOPNE指令,结束时有两种可能,CX=0或 ZF=1。
四 ﹑ 串操作程序设计举例
因为串操作有基本约定,有的源或目的隐含寻址,
所以按规定初始化:
因为源串地址? DS:SI,目的地址? ES:DI
段约定有两种方法实现:
源 ﹑ 目的定义在同一个数据段,既与 DS联系,又与 ES联系,
定义两个不同的段,一个与 DS联系,一个与 ES联系;
程序中一定要 注意给 ES赋值,否则死机。
STACK SEGMENT STACK
DW 32 DUP(?)
STACK ENDS
DATA SEGMENT
STR1 DB ’ AB CDE FG ’
L EQU $-STR1
COUNT DB?
DATA ENDS
CODE SEGMENT
START PROC FAR
ASSUME CS:CODE,SS:STACK,DS:DATA,ES:DATA
例 1﹑ 统计串长为 L的字符串中有多少个空格(即搜索目的串中的空格)。
PUSH AX
MOV AX,DATA
MOV DS,AX
MOV ES,AX?设目的串段地址
LEA DI,STR1? 设目的串地址
MOV CX,L? 设计数
MOV AL,20H? 送关键字空格 初始化
CLD? 增址方向
MOV BL,0? 统计单元清 0
PUSH DS
SUB AX,AX
CMP CX,0?相等时亦可能 CX= 0
JNE NEXT? CX≠0,则继续搜索
DOWN,MOV COUNT,BL?保存统计结果
RET
START ENDP
CODE ENDS
END START
NEXT,REPNE SCASB? 不等则重复搜索
JNE DOWN? 不等,即 CX=0而结束
INC BL? 相等则为空格例 2﹑ 用串操作指令实现将一组字类型的带符号数,
分别送正数或负数缓冲区。
算法:从源串中取数( LODS),判正负后,再存到正负缓冲区( STOS),注意正确使用三个数据块指针。
DATA SEGMENT
BLOCK DW 1,-2,-1,2,-3,8
COUNT EQU($ —BLOCK) /2
DATA ENDS
DATAE SEGMENT
PLUS DW COUNT DUP(?)
MINUS DW COUNT DUP(?)
DATAE ENDS
CODE SEGMENT
START PROC FAR
ASSUME CS:CODE,SS:STACK,DS:DATA,ES:DATAE
STACK SEGMENT STACK
DW 32 DUP(?)
STACK ENDS
MOV AX,DATA
MOV DS,AX
MOV AX,DETAE?设目的串段地址
MOV ES,AX
LEA SI,BLOCK? 设源串地址
LEA DI,PLUS? 设正数地址
LEA BX,MINUS? 设负数地址 初始化
MOV CX,COUNT? 设计数
CLD? 设增址方向
PUSH DS
SUB AX,AX
PUSH AX
CMP AX,0
JS MIN
STOS PLUS? 为正则存入正数缓冲区
JMP DECX
MIN,XCHG BX,DI
STOS MINUS 为负则存入负数缓冲区
XCHG BX,DI
DECX,LOOP GOON
RET
START ENDP
CODE ENDS
END START
GOON,LODS BLOCK?从源串中 取一个字? AX
为负数则转注意:使用串操作指令,可自动修改地址,循环中无须修改地址指针。
作业,P166,22,▲ 24,
▲ 25.
§ 3.5﹑ 子程序设计对于大型的复杂的程序,往往是根据程序要实现的若干主要功能将程序化分为若干个相对独立的模块。 确定各模块的入口及出口参数,为各模块分配不同的名字
(入口地址),然后对每一模块编制 独立的程序段?
子程序,最后将这些子程序根据调用关系连成一个整体。
A
A1 A2 A3
A11 A12 A13 A31 A32
成 可供反复调用的独立程序段。
主程序?调用子程序的程序。
子程序调用?进入子程序的操作,子程序运行结束又回到主程序?即子程序返回。
2﹑ 主程序与子程序间的转返子程序的调用与返回,实质上就是控制程序的转移,
但不能使用前面的转移指令,转移指令转走后,不再回到原来的地方,但子程序必须回到原来地方( 断点 )继续执行。为此提供专用的 调用指令和返回指令。
一 ﹑ 子程序的概念
1﹑ 子程序?将某些重复的或经常要使用的程序段设计格式,CALL〈 子程序名 〉
功能:将返回地址( CALL指令的下一条指令地址 )自动 进栈,将程序控制 转移 到子程序(即转移到目标处)。
因为子程序名有类型属性,NEAR? 主 ﹑ 子在同一段内,
FAR? 主 ﹑ 子在不同段。段内只改变 IP,段间改变 CS:
IP,所以具体操作:
段内调用,IP? [SP-2],SP-2? SP,
OFFSET〈 子程序名 〉? IP
段间调用,CS? [SP-2],IP? [SP-4],SP-4?
SP
SEG〈 子程序名 〉? CS,OFFSET 〈 子程序名 〉? IP
① 直接调用指令格式,CALL DST
功能:将返回地址 进栈,将目的操作数的内容送 IP或
CS:IP( 转移 )
段内操作,IP? [SP-2],SP-2? SP,
(REG16/MEM16)? IP
段间操作,CS? [SP-2],IP? [SP-4],SP-4? SP
(MEM32)? IP,(MEM32+2)? CS
② 间接调用指令
不管是直接或间接调用 ﹑ 执行 CALL指令后,堆栈内容发生变化:
段内调用 段间调用
SP- 2? 原 IPL SP- 4? 原 IPL
原 IPH 原 IPH
原 SP? SP- 2? 原 CSL
原 CSH
原 SP?
格式,RET [N]
功能:将程序控制 返回 到子程序。
N一般可省,有 N表示返回后,再将 SP+N? SP,即废除 N/2
个元素。
段内返回,[SP]? IP,SP+2? SP
段间返回,[SP]? IP,[SP+2]? CS,SP+4? SP
③ 返回指令
参数?主 ﹑ 子程序互相 传递的信息 (可以是信息本身或信息的地址 ),分两类,
入口参数?主程序提供给子程序以便加工处理的信息
原始数据 。
出口参数?经子程序加工处理后送回给主程序的信息
处理结果。
传递方式?为了传递参数,约定一种主子双方都能接受的参数传递方式,
3.主程序与子程序间的 参数传递
① 约定寄存器法?
子程序的 入口参数或出口参数都在主子双方事先约定的寄存器中?传递 单元在 CPU内部。 在调用子程序前主程序将入口参数送约定寄存器中,
子程序直接从这些寄存器中取得参数进行计算处理,经加工处理后得到的结果 (出口参数 )也放在约定寄存器中,返回主程序后,主程序直接到该寄存器中取得结果。该法简单 ﹑ 直观,信息传递快,但寄存器个数有限,所以适用于参数较少的情况,
③ 堆栈法?入口和出口 参数都放在堆栈中 —传递 单元在 SS
段。
调用前,将入口参数进栈,子程序从堆栈中取得这些参数进行处理,处理后的结果又送到堆栈中。此法不占用公共寄存器,也无需另外开辟单元,但因为子程序的返回地址也在堆栈中,所以一定要小心计算参数与地址,否则出错。
② 约定单元法?入口和出口 参数放在事先约定好的存贮单元中?传递 单元在 DS段。
调用前预置好入口参数至约定单元,子程序到约定单元取数据进行处理,结果放在约定单元中。该法可将入口 ﹑ 出口参数放在独立的单元,编程不易出错,
所以常用,
④ 参数赋值法?入口 ﹑ 出口 参数存放在主程序的调用指令之后,即在 CS段,书写指令时,用伪指令定义参数,—
传递 单元在 CS段。
此时一定要注意区分参数与代码。
参数?可以是信息本身?直接参数 ;若信息较多,可以是信息的地址? 间接参数,
无参数子程序?有时主 ﹑ 子之间无参数传递,子程序只是完成一个特定动作,
以上参数的传递方法各有优缺点,采用哪种方法,具体问题具体分析,有时,几种方法混合使用,
子程序要进行计算处理,自然要使用一些寄存器。子程序执行后,某些寄存器的内容会发生变化。如果主程序在这些寄存器中已经存放了有用信息,则子程序返回后,
主程序的运行势必因原存放信息被破坏而出错,所以要保护寄存器中内容不被破坏?保护现场 ;在子程序处理后,恢复寄存器的内容?恢复现场 。
保护现场 ﹑ 恢复现场的工作可以在主程序中完成,也可以在子程序中完成:
①子程序中保护 (一般用此法)?在子程序开始,将子程序用到的所有寄存器的内容保护起来,在返回之前恢复这些寄存器的内容。
4﹑ 主程序和子程序公用寄存器的问题:
ABC PROC
PUSH AX
PUSH BX 保护现场
PUSH CX
﹕ 子程序工作部分
POP CX
POP BX 恢复现场(注:先进后出)
POP AX
RET
ABC ENDP
例,某子程序要用到 AX ﹑ BX ﹑ CX,可用进栈 ﹑ 出栈实现保护与恢复现场。
区分入口参数与出口参数,保护现场和恢复现场。一般入口参数可以不保护或就具体情况而定,而出口参数不应保护 。
例 ﹑ 调用 DEF子程序,主、子程序中均要使用 AX,可用以下方法保护:
PUSH AX ;保护现场
CALL DEF
POP AX ;恢复现场
子程序结构:
保护现场?取入口参数进行加工处理?存结果至指定单元(作出口准备)?恢 复现场? 返回
② 在主程序中保护与恢复?在调用前保护某些寄存器的内容,返回后再恢复。
二 ﹑ 子程序及其调用程序设计举例
在进行子程序设计时,一般应对子程序加以说明,
一般说明以下内容:;子程序名:;子程序功能:;入口参数:;出口参数:
例 1,慢速显示缓冲区的字符?延时子程序。;子程序名,DELAY;子程序功能,延时;入口参数:无;出口参数:无
STACK SEGMENT STACK
DW 40 DUP(?)
STACK ENDS
DATA SEGMENT
BUF DB ’ ABCDEFGH’
N EQU $- BUF
DATA ENDS
CODE SEGMENT
BEGIN PROC FAR
ASSUME CS,CODE,SS,STACK,DS,DATA
PUSH DS
SUB AX,AX
PUSH AX
MOV AX,DATA
MOV DS,AX
程序结构为方法一,P130。
主子程序同在一个模块,同在一个代码段。
为无参子程序。
MOV SI,OFFSET BUF
MOV CX,N
NXT,MOV DL,[SI]
MOV AH,2
INT 21H
CALL DELAY
INC SI
LOOP NXT
RET
DELAY PROC
PUSH BX
PUSH CX
MOV CX,0
MOV BX,500
LOP,LOOP LOP
DEC BX
JNZ LOP
POP CX
POP BX
RET
DELAY ENDP
BEGIN ENDP
CODE ENDS
END BEGIN
例 2,显示字缓冲区中的十六进制数?
显示子程序。;子程序名,DISP;子程序功能:显示 BX中的 4位十六制数。;入口参数:待显示数?BX;出口参数:无
STACK SEGMENT STACK
DW 32 DUP(?)
STACK ENDS
DATA SEGMENT
BUFW DW 1234H,5678H,0ABCDH,0EF00H
N EQU ($-BUFW)/2
DATA ENDS
程序结构为方法二,P130。
主子程序在同一个模块,在不同的代码段。
寄存器传递参数。
CODE SEGMENT
BEGIN PROC FAR
ASSUME CS:CODE,SS:STACK,DS:DATA
PUSH DS
SUB AX,AX
PUSH AX
MOV AX,DATA
MOV DS,AX
MOV SI,OFFSET BUFW
MOV CX,N
NXT,MOV BX,[SI]
CALL DISP
MOV DL,’H’
MOV AH,2
INT 21H
设置入口参数
MOV DL,’,’
MOV AH,2
INT 21H
INC SI
INC SI
LOOP NXT
RET
BEGIN ENDP
CODE ENDS
PCODE SEGMENT
ASSUME CS:PCODE
DISP PROC FAR
PUSH AX
PUSH CX
PUSH DX
PUSH BP
MOV BP,4
MOV CL,4
DIS1:ROL BX,CL
MOV DL,BL
AND DL,0FH
CMP DL,0AH
JC NAD7
ADD DL,7
NAD7:ADD DL,30H
MOV AH,2
INT 21H
DEC BP
JNZ DIS1
POP BP
POP DX
POP CX
POP AX
RET
DISP ENDP
PCODE ENDS
END BEGIN
作业,P168,▲ 27,
28,29,30.
补例,用子程序显示回车换行,实现在不同行显示不同的提示信息。;子程序名,CRLF;功能:输出回车换行;;入口参数:无;;出口参数:无
STACK SEGMENT STACK
DW 40 DUP(?)
STACK ENDS
DATA SEGMENT
OBUF1 DB ’ INPUT X:$’
OBUF2 DB ’ INPUT Y:$’
DATA ENDS
CODE SEGMENT
BEGIN PROC FAR
ASSUME CS,CODE,SS,STACK,DS,DATA
0C63,0000 PUSH DS
0C63,0001 SUB AX,AX
0C63,0003 PUSH AX
0004 MOV AX,DATA
0007 MOV DS,AX
0009 LEA DX,OBUF1
000C MOV AH,9
000E INT 21H
0010 CALL CRLF
0013 LEA DX,OBUF2
0016 MOV AH,9
0018 INT 21H
001A CALL CRLF
001D RET
BEGIN ENDP
DEBUG XXX.EXE 跟踪 IP,SP:
-R↙ IP= 0000 SP= 0050
DS= 0C4C ES= 0C4C
SS= 0C5C CS= 0C63
-T7 ↙ IP= 000E SP= 004C
-P ↙ IP=0010 SP=004C
-T ↙ IP=001E SP=004A
-T2 ↙ IP=0020 SP=0046
-P5 ↙ IP=002A SP=0046
-T ↙ IP=002B SP=0048
-T ↙ IP=002C SP=004A
-T ↙ IP=0013 SP=004C
-T5 ↙ IP=0000 SP=0050
CRLF PROC
001E PUSH AX
001F PUSH DX
0020 MOV AH,2
0022 MOV DL,ODH
0024 INT 21H
0026 MOV DL,OAH
0028 INT 21H
002A POP DX
002B POP AX
002C RET
CRLF ENDP
CODE ENDS
END BEGIN
主 ﹑ 子程序在同一模块同一代码段,兰字为固定结构。
P130、方法一。
无参子程序。
执行示意:
现场保护在子程序中进行。
常将某些具有特定功能的程序段定义成子程序,实现资源共享。如显示 4位十六进制数;十进制转换为二进制;二进制转换为十进制等。
堆栈数据
EXE文件的内存映像图
0C4C0H+ 100H=
0C5C0H
0C5C0H+ 50H=
0C610H
0C610H+ 20H=
0C622H
例 3.将键入的无符号的十进制数( 0~ 65535)转换为二进制数。 转换由子程序完成。
子程说明:;子程序名,BCDB。;功能:将十进制数转换为二进数 。;入口参数:待转换的 BCD字符串首地址 IBUF+2?SI;
待转换的 BCD字符串串长 ( IBUF+1) → CX。;出口参数:转换结果 → SW字单元 。
PUBLIC SW
EXTRN BCDB,FAR
stack segment stack
dw 32 dup(?)
stack ends
data segment
SW DW?
OBUF DB ’INPUT A( 0~ 65535) BCD:’,ODH,OAH,’ $ ’
IBUF DB 6,?,6 DUP(?)
data ends
code segment
start proc far
assume ss,stack,cs,code,ds,data
push ds
sub ax,ax
push ax
程序结构?主、子各独立构成模块。 P131、方法 3,由于主、子在不同模块,要用到模块通信伪指令,
①说明公共符号伪指令:
PUBLEIC 符号,……
② 说明外部符号伪指令:
EXTRN 符号:类型,……
两指令成对互相呼应的说明
mov ax,data
mov ds,ax
LEA DX,OBUF
MOV AH,9 键入十进制数
INT 21H
LEA DX,IBUF
MOV AH,10
INT 21H
MOV CL,IBUF+1
MOV CH,0 入口准备
LEA SI,IBUF+2
CALL BCDB
ret
start endp
code ends
end start
PUBLIC BCDB
EXTRN SW,WORD
PCODE SEGMENT
BCDB PROC FAR
ASSUME CS,PCODE
PUSH AX
PUSH BX
PUSH DX
PUSHF
XOR AX,AX
MOV BX,10
AGA,MUL BX
MOV DL,[SI]
AND DL,0FH
ADD AL,DL
ADC AH,0
INC SI
LOOP AGA
现场保护初始化
MOV SW,AX
POPF
POP DX
POP BX
POP AX
RET
BCDB ENDP
PCODE ENDS
END* 10+ Di
十 → 二进制数算法:
反复 *10+Di,循环次数 由输入的位数确定,
入口参数 → 约定寄存器 法,
出口参数 → 约定单元法。
∵ 多模块,
∴ 另加公共 符号与外部符号的说明。
现场保护?子程中使用了 AX,BX,CX,DX,SI.
∵ CX,SI为入口参数,可不保护,其余应保护。
前面程序属于内部子程,此例为外部子程序,其上机步骤:
① 编辑主模块,×××,ASM;
② 汇编主模块,Tasm ××× ;
③ 编辑子模块,××× 1,ASM;
④ 汇编子模块,Tasm ××× 1;
⑤ 连接主子模块,TLINK ××× +××× 1;
⑥ 运行主程序,××× ↓
主,子程序有三种结构:一般小型程序使用方法一较好,方法三适用模块化编程 。
各种完整程序结构可参考实验教材 。
例 4,求某数据区中无符号字数据的最大值。
子程序完成求最大值( P136.例 3.32) 。
子程序说明:;子程序名,MAX。;功能:求最大值。;入口参数:数据区首地址?堆栈数据,个数,?堆栈;出口参数:最大值?堆栈
参数传递?堆栈法传递参数,调用前将首地址、个数进栈,子程序中由 BP间址存取参数,结果亦可放入堆栈。
程序结构为方法 1。
SMAX DW?
BUF DW 63H,76H,857H,323H,66H,888H
COUNT=($ -BUF)/2
PUSH DS
SUB AX,AX
PUSH AX
MOV AX,DATA
MOV DS,AX
LEA AX,BUF
PUSH AX
MOV AX,COUNT
PUSH AX
CALL MAX ; → IP进栈 ( 段内 )
POP SMAX ; → 出口处理,从堆栈中得 MAX
ret
COUNT进栈
BUF进栈入口准备
PUSH BP
MOV BP,SP
PUSH SI
PUSH AX
PUSH BX
PUSH CX
PUSHF
MOV SI,[BP+6] ; BUF →SI
MOV CX,[BP+4] ; COUNT →CX
MOV BX,[SI] ;取第一个数,假设为 MAX
DEC CX
MAX1,ADD SI,2
MOV AX,[SI]
CMP AX,BX
JNA NEXT
XCHG AX,BX
AX≦ BX则交换,MAX?BX
F
NEXT,LOOP MAX1 CX
MOV [BP+6],BX BX
POPF AX
POP CX SI
POP BX BP BP
POP AX IP BP+2
POP SI COUNT BP+4
POP BP BUF BP+6
RET 2
MAX?
堆栈
RET
ADD SP,2
堆栈中的数据
MAX
例 5,( P139.例,3.33)两个无符号的四字类型的数求知,和为十字节类型。子程序完成加运算。
子程序说明,;子程序名,ADDDQ。;功能:四字类型数求和。;入口参数:两数的首地址 → 代码段;出口参数:结果和的首地址 → 代码段
参数传递?参数赋值法传递?书写指令时,用伪指令在代码段定义加数和 结果的首地址。
NUM1 DQ 7654321089ABCDEFH
NUM2 DQ OFEDCBA9801234567H
RESULT DT?
CALL ADDDQ
DW NUM1,NUM2,RESULT
ret
ADDDQ PROC
PUSH BP
MOV BP,SP
PUSH AX
PUSH BX
PUSH CX
PUSH DX
PUSH SI
PUSH DI
PUSHF
代码段数据:
CALL
原 IP NUM1?BX
IP+ 2 NUM2?BX+ 2
IP+ 4 RESULT?BX+ 4
IP+ 6 RET
堆栈段的数据:
BP?BP
原 IP?BP+ 2
原 IP+ 6
MOV BX,[BP+2] ; 原 IP?BX
MOV SI,CS:[BX] ; NUM1? SI
MOV DX,CS[BX+2] ; NUM2? DX
MOV DI,CS:[BX+4] ; RESULT? DI
XCHG BX,DX ; BX?NUM2,DX?原 IP
MOV CX,4
CLC
AGAIN,MOV AX,[SI]
ADC AX,[BX]
MOV [DI],AX
INC SI
INC SI
INC BX
INC BX
INC DI
INC DI
LOOP AGAIN
若用 ADD BX,2
代替 INC BX则错。考虑对 CF标志位的影响
MOV WORD PTR[DI],0
RCL WORD PTR[DI],1
ADD DX,6
MOV [BP+2],DX
POPF
POP DI
POP SI
POP DX
POP CX
POP BX
POP AX
POP BP
RET
ADDDQ ENDP
进位处理修改返回地址,
跳过 3个参数,
指向 RET
修改原 IP
§ 3.6 宏功能程序设计对于程序中经常要使用的独立功能的程序段,可以将它设计成子程序的形式,供需要时调用 。 但使用子程序需要付出一些额外的开销 。 有些简单功能的重复,若用子程序则不合算,为此提供了宏功能 —可用宏指令 —为重复的语句序列定义一个名字,以代替一串指令序列简化书写 。
宏指令的使用要经过三个步骤:宏定义,宏调用,
宏扩展,前两步由用户完成,第三由宏汇编在汇编期间自动完成 。
一,宏定义 宏调用例,利用宏功能输入两个数
WRD MACRO A,B;用伪指令定义宏指令,WRD为宏指令
LEA DX,B; A,B为形式参数中间为宏体 ( 由一系
MOV AH,A;列指令或伪指令组成 )
INT 21H ;先定义,后调用,所以定义放在前
ENDM ;宏指令比指令和伪指令具更高的优先权
STACK……
DATA SEGMENT
BUF1 DB ’ Input X=,$ ’
IBUF1 DB 6,?,6 DUP(? )
BUF2 DB ‘Input Y=,$ ’
IBUF2 DB 6,?,6 DUP(,)
CRLF DB OAH,ODH,‘ $ ’
DATA EBDS
CODE SEGMENT
START PROC FAR
ASSUME SS,STACK,CS,CODE,DS,DATA
PUSH DS
SUB AX,AX
PUSH AX
MOV AX,DATA
MOV DS,AX
WRD 9,BUF1
WRD 10,IBUF1
WRD 9,CRLF
+ LEA DX,BUF1
+MOV AH,9
+INT 21H
+LEA DX,IBUF1
+MOV AH,10
+ INT 21H
WRD 9,BUF2
WRD 10,IBUF2
WRD 9,CRLF
RET
START ENDP
CODE ENDS
END START
宏调用时,实参在顺序,属性,类型上要同形参一一对应 。
宏扩展 ––实参代替形参,并将宏体插到宏调用处 。
宏调用相当于我们自行设计了一条新指令 –––宏指令,可出现在源程行中 。
§ 3.7 386/486汇编程序举例例,四字类型的数求和(参见实验教材 P68)
·486
stac segment use16 stack
dw 40 DUP(?)
stac ends
dat segment use16
F DQ 123456789ABCDEFOH
S DQ 1122334455667788H
T DQ?
dat ends
cod segment use16
begin proc far
assume cs:cod,ss:stac,ds:dat
push ds
sub ax,ax
push ax
mov ax,dat
mov ds,ax
MOV EAX,DWORD PTR F
ADD EAX,DWORD PTR S
MOV DWORD PTR T,EAX
MOV EAX,DWORD PTR F+4
ADC EAX,DWORD PTR S+4
MOV DWORD PTR T+4,EAX
ret
begin endp
code ends
end begin
§ 3.8 Turbo Assembler( TASM)程序设计简例例,显示字符串信息(参见实验教材 P95),
MODEL Small ;指定汇编模块的内存模式为 Small
STACK 80h ;定义堆栈段的长度为 80H
DATA ;定义数据段
Str1 DB ’ Hello World’,0AH,0DH,’$’
CODE ;定义代码段
START PROC FAR
PUSH DS
SUB AX,AX
PUSH AX
MOV AX,@DATA
MOV DS,AX
初始化 5条,
用户必须设置的数据段
LEA DX,STR1
MOV AH,9
INT 21H
RET
START ENDP
END START
一、上机步骤:
1、编辑源程序?XXX,ASM
2、汇编?TASM XXX
3、链接?TLINK XXX
4、运行 EXE? XXX 或进入 DEBUG调试或查看结果。
二,TASM和 MASM的区别,
TASM拥有 MASM的全部扩充功能。
1,TASM?由 Borland公司设计;
MASM?由 Microsoft公司设计。
2,TASM?一次性扫描汇编程序;
MASM?两次性扫描汇编程序。
3,TASM?简化段定义伪指令;
MASM?完整段定义。