汇编语言及编程实例(电子教案)
更新时间:2024-03-10 09:17:01 阅读量: 综合文库 文档下载
汇编语言程序设计 2005
第四章 汇编语言程序设计
回顾:8086的内部结构、寄存器功能和工作过程,指令格式、寻址方式和功能。
本讲重点:了解汇编的概念及其方法, 掌握汇编程序的基本格式,常用运算符的使用方法,汇编的步骤。
4.1汇编语言的基本元素
一、汇编语言的语句格式
由汇编语言编写的源程序是由许多语句(也可称为汇编指令)组成的。每个语句由1~4个部分组成,其格式是:
[标号] 指令助记符 [操作数] [;注解]
其中用方括号括起来的部分,可以有也可以没有。每部分之间用空格(至少一个)分开,一行最多可有132个字符。 1. 标识符
给指令或某一存储单元地址所起的名字。可由下列字符组成: 字母:A ~ z ; 数字:0 ~ 9 ; 特殊字符:?、· 、@、一、$ 。
数字不能作标识符的第一个字符,而圆点仅能用作第一个字符。标识符最长为31个字符。当标识符后跟冒号时,表示是标号。它代表该行指令的起始地址;当标识符后不带冒号时,表示变量;伪指令前的标识符不加冒号。 2. 指令助记符
表示不同操作的指令,可以是8086的指令助记符,也可以是伪指令。 3. 操作数
指令执行的对象。依指令的要求,可能有一个、两个或者没有, 例如: RET ;无操作数
COUNT: INC CX ;一个操作数 如果是伪指令,则可能有多个操作数,例如: COST DB 3,4,5,6,7 ;5个操作数 MOV AX,[BP+4] ;第二个操作数为表达式 4. 注解
该项可有可无,是为源程序所加的注解,用于提高程序的可读性。 二、汇编语言的运算符
1. 算术运算符、逻辑运算符和关系运算符
- 1 -
汇编语言程序设计 2005
算术运算符可以应用于数字操作数,结果也是数字。而应用于存储器操作数时,只有+、- 运算符有意义。
2. 取值运算符SEG、OFFSET、TYPE、SIZE和LENGTH · SEG和OFFSET分别给出一个变量或标号的段地址和偏移量。 例如,定义: SLOT DW 25
则: MOV AX,SLOT;从SLOT地址中取一个字送入AX
MOV AX,SEG SLOT;将SLOT所在段的段地址送入AX MOV AX,OFFSET SLOT;将SLOT所在段的段内偏移地址送AX
· TYPE操作符返回一个表示存储器操作数类型的数值。各种存储器地址操作数类型部分的值如表4-1所示。
· LENGTH和SIZE操作符只应用于数据存储器操作数。(用DB/DW/DD等定义的操作数) LENGTH返回一个与存储器地址操作数相联系的单元数, SIZE操作数返回一个为存储器操作数分配的字节数。 例如:若 MULT-WORD DW 50DUP(0) 则 LENGTH(MULT-WORD)=50
SIZE(MULT-WORD)=100
注意:SIZE(X)=(LENGTH X)* (TYPE X) 3. 属性运算符
属性运算符用来给指令中的操作数指定一个临时属性,而暂时忽略当前的属性。常用的有:
(1) 合成运算符PTR
它作用于操作数时,则忽略了操作数当前的类型(字节或字)及属性(NEAR或FAR),而给出一个临时的类型或属性,
一般格式:类型 PTR 表达式
功能:建立一个存储器地址操作数,它与其后的存储器地址操作数有相同的段地址偏移量,但有不同的类型。
例如:SLOT DW 25
此时SLOT已定义成字单元。若我们想取出它的第一个字节内容,则可用PTR对其作用,
表4-1存储器操作数的类型属性及返回值 字节 NEAR 1 -1 字 FAR 2 双字 4 -2 - 2 -
汇编语言程序设计 2005
使它暂时改变为字节单元,即 MOV AL,BYTE PTR SLOT 三、表达式
由运算符和操作数组成的序列,在汇编时产生一个确定的值。这个值可以仅表示一个常量,也可以表示一个存储单元的偏移地址,相应的表达式称为常量表达式和地址表达式。 1. 常数
汇编语言语句中出现的常数可以有7种: ① 二进制数 后跟字母B,如01000001B。 ② 八进制数 后跟字母Q或O,如202Q或202O。 ③ 十进制数 后跟D或不跟字母,如85D或85。
④ 十六进制数 后跟H,如56H,0FFH。注意,当数字的第一个字符是A~F时,在字符前应添加一个数字0,以示和变量的区别。
另有,十进制浮点数、十六进制实数、字符和字符串 2. 常量操作数
常量操作数是一个数值操作数,一般是常量或者是表示常量的标识符。可以为数字常量操作数或字符串常量操作数。前者可采用二进制、八进制、十进制或十六进制等计数形式;而后者则为相应字符的ASCII码。 3. 存储器操作数
存储器操作数是一个地址操作数,代表一个存储单元的地址,通常以标识符的形式出现。 存储器操作数可以分为变量及标号两种类型,如果存储器操作数所代表的是某个数据在数据段、附加段或堆栈段中的地址,那么这个存储器操作数就称为变量;如果存储器操作数所代表的是某条指令代码在代码段中的地址,那么这个存储器操作数称为标号。变量所对应的存储单元内容在程序的运行过程中是可以改变的,标号通常作为转移指令或调用指令的目标操作数,在程序运行过程中不能改变。 存储器操作数有三个方面的属性。
(1) 段地址:即存储器操作数所对应的存储单元所在段的段地址; (2) 偏移地址:即存储器操作数所对应的存储单元在所在段内的偏移地址;
(3) 类型:变量的类型是相应存储单元所存放的数据项的字节数;而标号的类型则反映了相应存储单元地址在作为转移或调用指令的目标操作数时的寻址方式,可有两种情况,即 NEAR和FAR。具体值可见表4-1 4. 常量表达式
由常量操作数及运算符构成,在汇编时产生一个常量。
- 3 -
汇编语言程序设计 2005
如PORT、VAL+1、 OFFSET SUM、SEG SUM、TYPE CYCLE等。 5. 地址表达式
由存储器操作数与运算符构成,必须有明确的物理意义。 例如 SUM+2、CYCLE-5
表达式SUM+2、CYCLE-5的值仍然是一个存储器操作数,该存储器操作数的段地址与类型属性分别与存储器操作数SUM及CYCLE相同,但偏移地址分别比SUM及CYCLE大2或小5。表达式是在汇编时计算的,而变量单元的内容在程序的运行过程中可以改变。 四、汇编语言程序汇编步骤
汇编语言程序要能在机器上运行,还必需将汇编源程序汇编成可执行程序。为此必须完成以下几个步骤。 1. 编辑源程序
2. 调用宏汇编对源程序进行汇编 3. 对目标程序进行连接 4. 运行可执行程序并调试
【习题与思考】
1.下列语句在存储器中分别为变量分配多少字节空间?并画出存储空间的分配图。 VAR1 DB VAR2 VAR3 VAR4
10,2
5DUP(?),0
‘HOW ARE YOU?’,‘$’ -1,1,0
DW DB DD
2. 假定VAR1和VAR2为字变量,LAB为标号,试指出下列指令的错误之处。 (1)ADD (3)JMP
VAR1,VAR2
(2)SUB AL,VAR1
LAB[SI] (4)JNZ VAR1
3. 对于下面的符号定义,指出下列指令的错误。
A1 DB ? A2 DB 10 K1 EQU
1024
- 4 -
汇编语言程序设计 2005
(1) MOV (3)CMP
K1,AX A1,A2
(2)MOV A1,AX
EQU
2048
(4)K1
4.2 伪指令
【回顾】汇编的概念及其方法, 掌握汇编程序的基本格式。8086/8088的指令格式。 【本讲重点】了解伪指令的功能,掌握定义数据、符号、段、过程等伪指令的使用方法,能编写格式正确的汇编程序。
4.2 伪指令
伪指令用来对汇编程序进行控制,对程序中的数据实现条件转移、列表、存储空间分配等处理,其格式和汇编指令一样,但一般不产生目的代码,即不直接命令CPU去执行什么操作。
一、定义数据伪指令
该类伪指令用来定义存储空间及其所存数据的长度。 · DB:定义字节,即每个数据是1个字节。 · DW:定义字,即每个数据占1个字(2个字节)。
· DD:定义双字,即每个数据占2个字。低字部分在低地址,高字部分在高地址。 · DQ:定义4字长,即每个数据占4个字。
· DT:定义10个字节长,用于压缩式十进制数, 例如:DATA1 DB 5,6,8,100 DATA2 DW 7,287
TABLE DB ? ;表示在TABLE单元中存放的内容是随机的;
当一个定义的存储区内的每个单元要放臵同样的数据时,可用DUP操作符。 一般格式:COUNT DUP(?),COUNT 为重复的次数,?( )?中为要重复的数据。 如:BUFFER DB 100 DUP(0) ;表示以BUFFER为首地址的100个字节中存放00H数据
BUFFER1 DB 100 DUP(3,5,2DUP(10),35),24,‘NUM’) 想一想存储区的情况?
二、符号定义伪指令EQU、=、及PURGE
· EQU 伪指令给符号定义一个值。在程序中,凡是出现该符号的地方,汇编时均用其值代替,
- 5 -
汇编语言程序设计 2005
如:TIMES EQU 50
DATA DB TIMES DUP(?)
上述两个语句实际等效于如下一条语句: DATA DB 50 DUP(?) · ?=?伪指令可给初始变量赋值。 如:COUNT=100 ;COUNT=100
TIME=50 ;TIME=50
· PURGE伪指令用于释放由EQU伪指令定义的变量,使这些变量可以被重新定义。 PURGE TIMES ; 释放TIMES变量 TIMES EQU 2 ; 重新定义
三、段定义伪指令SEGMENT和ENDS
一般来说,一个完整的汇编源程序由3个段组成,即堆栈段、数据段和代码段。段定义伪指令可将源程序划分成若干段,以便生成目的代码和连接时将各同名段进行组合。 段定义伪指令一般格式为 :
段名 SEGMENT [定位类型] [组合类型] [类别] 段名 ENDS
SEGMENT和ENDS应成对使用,缺—不可。其中段名是不可省略的。其它是可选项,是赋予段名的属性,可以省略。 例如: DATA SEGMENT DW 20DUP(?)
DATA ENDS
四、设定段寄存器伪指令ASSUME
一般格式: ASSUME 段寄存器:段名[,段寄存器:段名,……]
功能:通知汇编程序,哪一个段寄存器是该段的段寄存器,以便对使用变量或标号的指令汇
编出正确的目的代码。在段名中,CODE表示代码段,DATA表示数据段,STACK表示堆栈段。
由于ASSUME伪指令只指明某一个段地址应存于哪一个段寄存器中,并没有包含将段地址送入该寄存器的操作。因此要将真实段地址装入段寄存器还需用汇编指令来实现。这一步是不可缺少的。
例如,CODE SEGMENT
- 6 -
汇编语言程序设计 2005
ASSUME CS:CODE,DS:DATA,SS:STACK MOV AX,DATA ;DATA段值送AX
MOV DS,AX ;AX内容送DS,DS才有实际段值
CODE ENDS
当程序运行时,由于DOS的装入程序负责把CS初始化成正确的代码段地址,SS初始化为正确的堆栈段地址,因此用户在程序中就不必设臵。但是,在装入程序中DS寄存器由于被用作其它用途,因此,在用户程序中必须用两条指令对DS进行初始化,以装入用户的数据段地址。当使用附加段时,也要用MOV指令给ES赋段地址。
五、定义过程的伪指令PROC和ENDP
在程序设计中,可将具有一定功能的程序段看成为一个过程(相当于一个子程序),它可以被别的程序调用。
一个过程由伪指令PROC和ENDP来定义,其格式为: 过程名 PROC [类型] 过程体 RET 过程名 ENDP
其中过程名是为过程所起的名称,不能省略,过程的类型由FAR(远过程,为段间调用)和NEAR(近过程,在本段内调用)来确定,如果缺省类型,则该过程就默认为近过程。ENDP表示过程结束。过程体内至少应有一条RET指令,以便返回被调用处。过程可以嵌套,也可以递归使用。
例如一个延时100ms的子程序,其过程可定义如下, DELAY PROC PUSH BX PUSH CX MOV BL,10
;延时10ms,改变BL和CX中的值,即可改变延时时间。
AGAIN: MOV CX,2801 ; WAIT; LOOP WAIT DEC BL JNZ AGAIN POP CX
- 7 -
汇编语言程序设计 2005
POP BX RET
DELAY ENDP
CALL DELAY ;调用该过程 远过程调用时被调用过程必定不在本段内。 例如,有两个程序段,其结构如下: CODE1 SEGMENT ASSUME CS:CODE1 FARPROC PROC FAR RET FARPROC ENDP CODE1 ENDS CODE2 SEGMENT ASSUME CS:CODE2
CALL FARPROC …..
CODE2 ENDS
CODE1 段中的FARPROC 过程被另一段CODE2调用,故为远过程。
六、宏指令
在汇编语言书写的源程序中,若有的程序段要多次使用,为了简化程序书写,该程序段可以用一条宏指令来代替,而汇编程序汇编到该宏指令时,仍会产生源程序所需的代码。
宏指令的一般格式为:宏指令名 MACRO [形式参量表] 宏体 ENDM 例如:SHIFT MACRO MOV CL,4 SAL AL,Cl ENDM
这样定义以后,凡是要使AL中内容左移4位的操作都可用一条宏指令SHIFT来代替。
宏指令与子程序有许多类似之处。它们都是一段相对独立的、完成某种功能的、可供调用的程序模块,定义后可多次调用。但在形成目的代码时,子程序只形成一段目的代码,调
- 8 -
汇编语言程序设计 2005
用时转来执行。而宏指令是将形成的目的代码插到主程序调用的地方。因此,前者占内存少,但执行速度稍慢;后者刚好相反。
七、ORG 伪指令
ORG伪指令规定了在某一段内,程序或数据代码存放的起始偏移地址。 一般格式: ORG <表达式> 例如:DATA
BUFF1
SEGMENT
DB 23,56H,‘EOF’
ORG 2000H BUFF2 DATA
DB ‘STRING’
ENDS
上述变量定义中,BUFF1从DATA段偏移地址为0的单元开始存放,而BUFF2则从DATA段偏移为2000H的单元开始存放,两者不是连续存放。
八、汇编结束伪指令END
该伪指令表示源程序的结束.令汇编程序停止汇编。因此,任何一个完整的源程序均应有END指令。
一般格式: END [表达式]
其中表达式表示该汇编程序的启动地址。例如:
END START ;表明该程序的启动地址为START。
习题与思考:
1.数据定义语句如下所示:
FIRST DB 90H,5FH,6EH,69H SECOND DB 5 DUP(?) THIRD DB 5 DUP(?) FORTH DB 5 DUP(?)
自FIRST单元开始存放的是一个四字节的十六进制数(低位字节在前),要求:
(1)编一段程序将这个数左移两位、右移两位后存放到自SECOND开始的单元(注意保留
- 9 -
汇编语言程序设计 2005
移出部分。
(2)编一段程序将这个数求补以后存放到自FORTH开始的单元。
2.试编程序将内存从40000H到4BFFFH的每个单元中均写入55H,并再逐个单元读出比较,看写入的与读出的是否一致。若全对,则将AL臵7EH;只要有错,则将AL臵81H。 3.在当前数据段4000H开始的128个单元中存放一组数据,试编程序将它们顺序搬移到A000H开始的顺序128个单元中,并将两个数据块逐个单元进行此较;若有错将BL臵00H;全对则将BL臵FFH,试编程序。
3.3汇编程序设计
【回顾】8086的指令系统,汇编程序的基本格式,伪指令的功能,汇编程序的正确格式。 【本讲重点】掌握汇编程序的分析与设计方法(包括分支程序和循环程序)
一、简单程序设计
简单程序设计是没有分支,没有循环的直线运行程序,程序执行按照IP内容自动增加的顺序进行。
【例1】 利用查表法计算平方值。已知0 ~ 9的平方值连续存在以SQTAB开始的存储区域中,求SUR单元内容X的平方值,并放在DIS单元中。假定0≤X≤ 9且为整数。 分析:建立平方表,通过查表完成。 STACK SEGMENT
DB 100 DUP(?)
STACK ENDS DATA SEGMENT
SUR DB ? DIS DB ?
SQTAB DB 0,1,4,9,16,25,36,49,64,81 ; 0~9的平方表
DATA ENDS CODE SEGMENT
ASSUME CS:CODE,DS:DATA,SS:STACK,ES:DATA
BEGIN:PUSH DS
MOV AX,0
PUSH AX ;保证返回DOS, MOV AX,DATA
- 10 -
汇编语言程序设计 2005
MOV DS,AX ;为DS送初值
LEA BX,SQTAB ;以下程序部分完成查表求平方值 MOV AH,0 ;亦可用查表指令完成(如下程序段) MOV AL,SUR ;AL=X LEA BX, SQTAB ADD
BX,AX ; MOV AL, SUR
MOV AL,[BX] ; XLAT MOV DIS,AL ; MOV DIS, AL
CODE ENDS
END BEGIN
【例2】已知Z=(X+Y)-(W+Z),其中X,Y,Z,W均为用压缩BCD码表示的数,写出程序。 分析:这也是一种典型的直线程序,在这里要注意是BCD数相加,要进行十进制调整。具
体程序如下:
MOV AL,Z MOV BL,W ADD DAA
MOV BL,AL ; BL=(W+Z) MOV AL,X MOV DL,Y ADD
AL,DL ; AL=(X+Y) AL,BL
DAA ;十进制调整 SUB
AL,BL ; AL=(X+Y)-(Z+W)
DAS ;十进制调整
MOV Z,AL ;结果送Z
二、分支程序设计
分支程序的基本思想是根据逻辑判断的结果来形成程序的分支,如图,若A成立,则执行P1;否则执行P2。
- 11 -
汇编语言程序设计 2005
【例3】 试编写程序段,实现符号函数。
分析:变量X的符号函数可表示为: 1 X>0 Y= 0 X=0
-1 X<0
程序可通过对符号标志的判别来确定执行哪一分支。 START: MOV AX,BUFFER ;(BUFFER)=X
OR JE JNS
AX,AX
ZERO ;X=0,则转ZERO PLUS ;X为正数,则转PLUS
MOV BX,0FFFFH ;X为负数,则-1送BX JMP
CONT1
表4-2 子程序R1—R8的入口地址表 P1 P2 P3 子程序R1的入口偏移地址 子程序R2的入口偏移地址 子程序R3的入口偏移地址 ZERO:
MOV BX,0
CONT1
JMP
PLUS: MOV BX,1
CONT1: ……
【例4】 利用表实现分支
根据AL中各位被臵位情况,控制转移到8个子程序P1~P8之一中去。转移表的结构如表4-2所示。
分析:对于这种程序关键要找出每种情况的转移地址,从图中可见
…… ……. …… …… P7 P8 子程序R7的入口偏移地址 子程序R8的入口偏移地址 表地址=表基地址+偏移量, 而偏移量可由AL各位所在位臵*2求得。 DATA BASE
SEGMENT
DW SR0,SR1,SR2,SR3, SR4,SR5,SR6,SR7
DATA CODE
ENDS
SEGMENT
ASSUME CS:CODE,DS:DATA,ES:DATA BEGIN: PUSH DS
- 12 -
汇编语言程序设计 2005
XOR AX,AX
PUSH AX
MOV AX,DATA MOV DS,AX LEA IN
BX,BASE ;表头送BX AL,PORT
GETBIT: RCR AL,1 ;右移一位
JC GETAD ;移出位是1? INC
BX
INC BX ;修改指针
JMP
GETBI
GETAD: JMP WORD PTR[BX] ;实现散转 CODE
ENDS
END BEGIN
根据跳转表构成方法不同,实现分支的方法也有所改变,下面有三个问题希望大家思考: (1) 若跳转表地址由段值和偏移量四个字节构成,程序应如何实现?
(2) 若跳转表中的内容由JMP OPRD指令构成,表的结构应如何组织、程序如何实现? (3) 上述程序若不用间接跳转指令,而改为直接跳转,程序如何变动? 【例5】 将内存中某一区域的原数据块传送到另一区域中。
分析 这种程序若源数据块与目的数据块之间地址没有重叠,则可直接用传送或串操作实现;若地址重叠,则要先判断源地址+数据块长度是否小于目的地址,若是,则可按增量方式进行,否则要修改指针指向数据块底部,采用减量方式传送。程序如下: DATA STR
SEGMENT
DB 1000DUP(?)
STR+7 STR+25
50
STR1 STR2
EQU EQU
STRCOUNT DATA STACK STAPN STACK
EQU
ENDS
SEGMENT PARA STACK ‘STACK’ DB ENDS
100DUP(?)
- 13 -
汇编语言程序设计 2005
CODE
SEGMENT
ASSUME CS:CODE,DS:DATA,ES:DATA,SS:STACK PROC PUSH DS SUB
AX,AX
GOO
PUSH AX MOV AX,DATA MOV DS,AX MOV ES,AX MOV AX,STACK MOV SS,AX
MOV CX,STRCOUNT MOV SI,STR1 MOV DI,STR2 CLD PUSH SI ADD CMP POP JL STD ADD ADD REP RET ENDP ENDS END
GOO 图4-4 循环结构示意图 SI,STRCOUNT-1 DI,STRCOUNT-1
MOVSB SI,STRCOUNT-1 SI,DI OK
SI
OK:
GOO
CODE
三、循环程序设计
循环程序是经常遇到的程序结构,一个循环结构通常由以下几个部分组成。 1. 循环初始化部分 一般要进行地址指针、循环次数及某标志的设臵,相关寄存器的清零
- 14 -
汇编语言程序设计 2005
等操作。只有正确地进行了初始化设臵, 循环程序才能正确运行,及时停止。
2. 循环体 是要求重复执行的程序段部分,对应于要求重复执行的操作。 3. 循环控制部分 由该部分修改并判断控制循环的条件是否满足。以决定是否继续循环。 4. 循环结束部分 如保存循环运行结果等。
循环程序有两种结构形式,一种是DO—WHILE结构,另一种是DO—UNTIL结构。前者把循环控制部分放在循环体的前面,先判断执行循环体的条件,满足条件就执行循环体,否则就退出循环,如图3-4(1)所示。而后者则是在执行循环体之后,再判断循环控制条件是否满足,若满足条件,则继续执行循环操作,否则,则退出循环。如图4-4(2)所示。DO—WHILE结构的循环程序,其循环体有可能并不执行,而DO—UNTIL循环程序的循环体至少必须执行一次。
【例6】 设内存BUFF开始的单元中依次存放着30个8位无符号数,求它们的和并放在SUM单元中,试编写程序。
分析:这是一个求累加的程序。程序如下:
MOV SI,BUFF ;设地址指针 MOV CX,30 ;设计数初值 XOR
AGAIN: ADD
ADC INC DEC JNZ
AX,AX ;设累加器初值 AL,[SI] AH,0 SI CX
AGAIN ;循环累加
MOV SUM,AX
【例7】 在给定个数的16位数串中,找出大于零、等于零和小于零的个数,并紧跟着原串存放。
分析:这是一个统计问题,须设定三个计数器分别统计三种情况下的结果。程序如下: DATA SEGMENT
BUFF DW X1,X2,X3,……,Xn
COUNT EQU $-BUFF ;此时,COUNT的值为BUFF所占的字节数 PLUSE DB ? ZERO DB ?
- 15 -
汇编语言程序设计 2005
MINUS DB ? DATA ENDS CODE SEGMENT
ASSUME CS:CODE,DS:DATA ASSUME ES:DATA,SS:STACK BEGIN: MOV AX,DATA
AGAIN:
PLU:
ZER: NEXT:
CODE
【例8】 MOV DS,AX MOV CX,COUNT SHR
CX,1 ;相当于除2,正好为BUFF中的数据个数
MOV DX,0 ;设定计数器初值 MOV AX,0 ;设定计数器初值 LEA
BX,BUFF
CMP WORD PTR[BX],0 JAE PLU ;大于等于0,则转PIU INC AH ;<0,则统计 JMP
NEXT
JZ ZER ;=0,则转ZER INC DL ;>0,则统计 JMP
NEXT
INC DH ;=0,则统计
INC
BX
INC
BX
LOOP AGAIN MOV PLUS,DL MOV ZERO,DH MOV MINUS,AH MOV AX,4C00H INT 21H
ENDS END
BEGIN 在ADDR单元中存放着16位数Y的地址,试编写一程序,把Y中1的个数存入
- 16 -
汇编语言程序设计 2005
COUNT单元中。
分析:这是一个循环统计的工作。采用DO—WHILE结构,做16次循环,每次将最高位移入CF中进行测试,先判断结果是否为0,若为0,则结束;否则统计计数后循环重复。 程序如下: DATA SEGMENT ADDR DW
NUMBER
Y ?
NUMBER DW COUNT DATA
DW ENDS
PROGRAM SEGMENT MAIN
PROC FAR
ASSUME CS:PROGRAM,DS:DATA START: PUSH DS
MOV AX,0 PUSH AX MOV AX,DATA MOV DS,AX
MOV CX,0 ;计数器初值=0 MOV BX,ADDR
MOV AX,[BX] ;取Y送AX
REPEAT: TEST AX,0FFFFH ;检测是否为全0
JZ JNS INC
EXIT ;是,则转EXIT
SHIFT ;最高位是0,则转SHIFT CX ;最高位是1,则统计计数
AX,1 ;处理下一位
SHIFT:
SHL
JMP REPEAT
EXIT:
MOV COUNT,CX RET ENDP
MAIN
PROGRAM ENDS
END
START
- 17 -
汇编语言程序设计 2005
在实际应用中,有些问题较复杂,一重循环不够,必须使用多重循环实现,这些循环是一层套一层的,通常称为循环嵌套。
【例9】在DS所决定的数据段,从偏移地址BUFFER开始顺序存放100个无符号16位数,现要编写程序将这100个字数据从大到小排序。 分析:排序的方法有很多,在这里,我们采用冒泡法。 程序如下:
LEA DI,BUFFER ;DI作为指针,指向要排序的数据 MOV BL,99 ;循环控制初值
NEXT0:MOV SI,DI
MOV CL,BL
NEXT3:MOV AX,[SI] ;取一个数
ADD SI,2 CMP JNC
AX,[SI] ;与下一个数进行比较
NEXT5 ;大于等于时转移
MOV DX,[SI] ;否则,两数交换 MOV [SI-2],DX MOV [SI],AX
NEXT5:DEC CL ;控制进行交换的次数
习题与思考:
1.设变量单元A、B、C存放有三个数,若三个数都不为零,则求三个数的和,存放在D中;若有一个为零,则将其余两个也清零,试编写程序。
2.有一个100个字节的数据表,表内元素已按从大到小的顺序排列好,现给定一元素,试编程序在表内查找,若表内已有此元素,则结束;否则,按顺序将此元素插入表中适当的位臵,并修改表长。
3.内存中以FIRST和SECOND开始的单元中分别存放着两个16位组合的十进制(BCD码)
JNZ DEC JNZ HLT
NEXT3
BL ;修改交换的次数 NEXT0
- 18 -
汇编语言程序设计 2005
数,低位在前。编程序求这两个数的组合的十进制和,并存到以THIRD开始的单元。 4.编写一段程序,接收从键盘输入的10个数,输入回车符表示结束,然后将这些数加密后存于BUFF缓冲区中。加密表为:
输入数字:0,1,2,3,4,5,6,7,8,9;密码数字:7,5,9,1,3,6,8,0,2,4
四、子程序设计
子程序是程序设计中经常使用的程序结构,通过把一些固定的、经常使用的功能做成子程序的形式,可以使源程序及目标程序大大缩短,提高程序设计的效率和可靠性。
对于一个子程序,应该注意它的入口参数和出口参数。入口参数是由主程序传给子程序的参数,而出口参数是子程序运算完传给主程序的结果。另外,子程序所使用的寄存器和存储单元往往需要保护,以免影响返回后主程序的运行。
主程序在调用子程序时,一方面初始数据要传给子程序,另一方面子程序运行结果要传给主程序,因此,主子程序之间的参数传递是非常重要的。
参数传递一般有三种方法实现:
(1) 利用寄存器 这是一种最常见方法,把所需传递的参数直接放在主程序的寄存器中传递给子程序。
(2) 利用存储单元 这种参数传递方法,把所需传递的参数直接放在子程序调用指令代码之后。
(3) 利用堆栈 这种方法将参数压入堆栈,在子程序运行时从堆栈中取参数。
下面我们通过实例说明子程序设计及参数传递方法。 【例10】 两个6字节数相加。
分析:将一个字节相加的程序段设计为子程序。主程序分3次调用该子程序,但每次调用的参数不同。 程序如下: DATA ADD1 ADD2 SUM
SEGMENT DB DB
FEH,86H,7CH,35H,68H,77H 45H,BCH,7DH,6AH,87H,90H
DB 6DUP(0) DB ENDS SEGMENT
- 19 -
COUNT DATA STACK
6
汇编语言程序设计 2005
DB ENDS
100DUP(?)
STACK CODE
SEGMENT
ASSUME CS:CODE,DS:DATA,SS:STACK MADD: MOV AX,DATA
MOV DS,AX MOV AX,STACK MOV SS,AX MOV SI,OFFSET MOV DI,OFFSET
ADD1 ADD2
MOV BX,OFFSET SUM
MOV CX,COUNT ;循环初值为6 CLC
AGAIN:CALL SUBADD ;调用子程序
LOOP AGAIN ;循环调用6次 MOV AX,4C00H INT
21H
出口参数:SI,DI,BX
;子程序入口参数:SI,DI,BX
SUBADD PROC ;完成一个字节相加
PUSH AX ;保护AX的值
MOV AL,[SI] ;SI是一个源操作数指针 ADC
AL,[DI] ;DI是另一个源操作数指针
MOV [BX],AL ;BX是结果操作数指针 INC INC INC POP RET
SI DI BX
AX ;恢复AX的值
SUBADD ENDP CODE
ENDS END MADD
- 20 -
汇编语言程序设计 2005
【例11】 把内存中的字变量NUMBER的值,转换为4个ASCII码表示的十六进制数码串,串的起始地址为STRING。
分析:把内存中的字变量NUMBER的值,转换为4个ASCII码表示的十六进制数码串的工作设计成一个子程序,在这个子程序中再调用另一个子程序,由它完成从BCD码到ASCII码的转换。 程序如下: DATA
NUMBER STRING DATA CODE
ASSUME BEGIN:
BINHEX
SEGMENT
DW 25AFH
DB 4DUP(?),0DH,0AH,‘$’
ENDS SEGMENT
CS:CODE,DS:DATA MOV AX,DATA MOV DS,AX MOV ES,AX LEA
BX,STRING
PUSH BX ;将参数(结果地址指针)压入堆栈 PUSH NUMBER ;将源数据压入堆栈 CALL BINHEX ;调用子程序 LEA
DI,STRING
MOV AH,9 INT
21H
PROC PUSH BP MOV BP,SP PUSH AX PUSH DI PUSH CX PUSH DX
PUSHF ;以上为保护现场 MOV AX,[BP+4] ;取出NUMBER
- 21 -
汇编语言程序设计 2005
MOV DI,[BP+6] ;取出STRING的偏移地址 ADD
DI,LENGTH STRING-1 ;使DI指向转换数据
MOV DX,AX ;保护原始数据 MOV CX,4
AX,0FH ;取低4位
AGAIN: AND
BINHEX HEXD
ADDZ:
HEXD CODE
CALL HEXD ;调子程序 STD
STOSB ;保护转换数据 PUSH CX ;保护CX的值 MOV CL,4 SHR
DX,CL
MOV AX,DX POP
CX
LOOP AGAIN POPF POP DX POP CX POP DI POP AX POP BP RET
4
ENDP
PROC ;将AL中的BCD码转换成ASCII码 CMP AL,0AH JL ADDZ
ADD AL,’a’-‘0’-0AH ;小写字母转换成ASCII码,若为大写ADD AL,‘0’ ;字母,则再加ADD AL,7
RET ENDP ENDS EDN
BEGIN - 22 -
汇编语言程序设计 2005
(以下例子作为选讲)
【例12】 数的阶乘 1 按照阶乘的定义 n!= n*(n-1)!
这是一个递归定义式,可采用子程序的的递归调用形式。程序如下: DATA NUM FNUM DATA STACK
STACK CODE
ASSUME BEGIN:
FACTOR
SEGMENT DB 5 DW ? ENDS SEGMENT
DB 100DUP(?) ENDS SEGMENT
CS:CODE,DS:DATA,SS:STACK PUSH DS MOV AX,0 PUSH AX MOV CX,1 PUSH CX MOV AH,0 MOV AL,NUM CALL FACTOR MOV FNUM,AX POP
CX
MOV AX,4C00H INT 21H
PROC
CMP AX,0 JNZ
IIA
MOV DL,1 RET
- 23 -
汇编语言程序设计 2005
IIA:
PUSH AX DEC
AL
CALL FACT POP MUL
CX
IIA1:
CL ;CALL MULT
IIA2:
MOV DX,AX RET
ENDP
FACTOR CODE
ENDS END
BEGIN
习题与思考:
1.试编程序,统计由40000H开始的16K个单元中所存放的字符?A?的个数,并将结果存放在DX中。
2.在当前数据段(DS),偏移地址为DATAB开始的顺序80个单元中,存放着某班80个同学某门考试成绩。按要求编写程序:
①编写程序统计≥90分;80分~89分;70分~79分;60分~69分,<60分的人数各为多少,并将结果放在同一数据段、偏移地址为BTRX开始的顺序单元中。
②试编程序,求该班这门课的平均成绩为多少,并放在该数据段的AVER单元中。 3.编写一个子程序,对AL中的数据进行偶校验,并将经过校验的结果放回AL中。 4.利用上题的予程序,对80000H开始的256个单元的数据加上偶校验,试编程序。
- 24 -
正在阅读:
汇编语言及编程实例(电子教案)03-10
《福楼拜家的星期天》课后练习答案03-18
LED100W泛光灯技术参数大功率100W泛光灯规格书04-15
煤矸石空心砖砌筑方案04-26
13.1平方根(2)教案02-27
意外的发现作文优秀5篇03-25
公司建筑工程技术档案资料管理 - 图文10-14
固废处理成套设备项目财务分析表07-27
- 天大砼方案 - 图文
- 农业科技网络书屋能力提升_玉米错题选
- DNS习题
- 浅议检察官对罪犯谈话的技巧与效果
- 高考语文文言文翻译专题训练
- AB类学科竞赛目录(2015)
- 建筑面积计算新规定(2015最新)
- Revit2012初级工程师题集一
- 十三五项目米线可行性报告
- 2013体育学院党组织建设工作总结
- 2014Revit工程师题库
- 高中数学如何实施研究性学习
- 茶艺表演 中英互译
- 小学音乐湘文艺版 四年级下册 第十一课《(歌表演)脚印》优质课公
- 山西省农村合作经济承包合同管理条例
- 2015年镇江市中考化学一模试题参考答案及评分标准(定稿)
- 统计 题集
- 批评意见清单
- 8潞安集团蒲县黑龙关煤矿矿业公司2
- 鄂教版四年级语文上册复习精要(光谷四小)
- 汇编语言
- 教案
- 实例
- 编程
- 电子
- 环境因素清单及评价
- The Gerund(尉岐讲语法)
- 山东省铸造机械生产企业名录2018版2036家 - 图文
- Excel-12数据处理题库题目(20150330)
- jsp试题
- 2013年湖南对口升学高考语文试题
- 国奖个人事迹第三人称
- 工程施工组织设计(总)方案
- 水泥砼路面冬季施工措施
- 餐饮管理-工资
- 青年创业大赛总结
- 四川省雅安市2011-2012学年度第二学期期末检测八年级
- 2014年6月英语六级选词填空习题及答案(2)
- 推进和谐计生 共筑乐居家园(定稿)20111125
- 检验科仪器设备维护保养记录
- 初中地理会考复习大纲(初一下册)
- 20090310局域网建设方案及配置报价
- 数学建模论文参考格式1
- 钢管超声波探头旋转探伤系统
- 电大程序设计作业册习题和答案1