300 likes | 476 Views
第四章 汇编程序设计. 4.1 常量、变量和标号 4.2 汇编语言源程序格式 4.3 伪指令 4.4 顺序程序设计 4.5 分支程序设计 4.6 循环程序设计 4.7 子程序. 4 . 1 常量、变量和标号. 汇编语言的数据可以分为常量和变量。常量可以作为指令的立即数或者伪指令的参数,变量作为存储器的操作数。汇编语言中的标号、名字,具有逻辑地址和类型属性,主要用于作地址操作数。 4.1.1 常量表示一个固定的值 ,有 4 种形式; 1. 常量 由二进制、八进制、十进制和十六进制形式表达的数值。
E N D
第四章 汇编程序设计 4.1 常量、变量和标号 4.2 汇编语言源程序格式 4.3 伪指令 4.4 顺序程序设计 4.5 分支程序设计 4.6 循环程序设计 4.7 子程序
4.1 常量、变量和标号 汇编语言的数据可以分为常量和变量。常量可以作为指令的立即数或者伪指令的参数,变量作为存储器的操作数。汇编语言中的标号、名字,具有逻辑地址和类型属性,主要用于作地址操作数。 • 4.1.1常量表示一个固定的值,有4种形式; • 1.常量 由二进制、八进制、十进制和十六进制形式表达的数值。 (1)二进制由0、1两个数字组成,以字母B(或b)结尾;例如101101011B。 (2)八进制由0—7这8个数字组成,以字母Q(或q)结尾;例如456701Q。 (3)十进制由0—9这10个数字组成,以字母D(或d)结尾;例如126789D。但十进制可以省略后缀字母。因此,默认的不加后缀字母的数就是十进制数。 (4)十六进制由0—9、A—F组成,以字母H(或h)结尾;例如456ABH。以字母A—F开头时,数的前面要加0,以示与标识符加以区别。 2.字符串 字符串常以单引号或双引号括起来的单个字符或者多个字符,例如:‘fg1256K^&*KK’;其数值是每个字符对应的ASC码;ASC码表见附录。
3.符号常量 符号常量使用标识符来表达一个数值;符号定义伪指令有 “EQU” 和 “=” 格式为: 符号名 EQU 数值表达式 符号名 EQU <字符串> ;或者用引号 符号名 = 表达式 例如: X EQU 56 ;含义为 MOV X, 56 Y EQU “ hello” ;含义为 MOV Y,“hello” Z = 13 ;含义为 MOV Z, 13 4.数字表达式 数字表达式由操作符连接的各种常量所构成表达式;汇编语言进行汇编过程中,最终得到一个确定的数值,因此,也称为常量。 例如: MOV AL,1*2+3 ;(AL)= 1*2+3 = 5 MOV AH,13H OR 45H MOV BH,110110B SHL 2 汇编所用的运算符,见表4.1。
4.1.2 变量 变量就是内存单元;变量先定义后使用。 格式: 变量名 伪指令 初值 (1)变量名是用户自己定义的标识符,这个符号表示内存地址,常称为 符号地址。当然,变量名可以没有,初值分配的地址称为无符号地址。 (2)初值是用逗号分开的参数,主要由常量、表达式、?、DUP 组成 其中:?表示初值不确定或未赋初值。 DUP(5)表示重复初值 5 次。 (3)变量定义的伪指令有 DB、DW、DD、DF、DQ、DT 例如: DATA SEGMENT ;数据段 X DB -56H ;X的值=-56H Y DB 80H,?,“W”,3 DUP(0) ; Z DW ? ;Z无初值 DATA ENDS 变量定义在数据段 DATA SEGMENT内;X 为字节型数据,值为-56H; Y为字节型;Z单元为字型数据,没有初值。 本例在内存的分配方法见图4.1 。
以一个完整的汇编语言程序来说明; DATA SEGMENT ;定义数据段,段名为DATA X DB 34H ;定义各种数据 Y DW “HH” …….. DATA ENDS ;数据段定义结束 STACK SEGMENT ;定义堆栈段,段名为STACK DB 100 DUP(0) ;开辟堆栈区域100,初值为0 STACK ENDS ;堆栈段定义结束 CODE SEGMENT ;定义代码段,段名为CODE ASSUME CS:CODE,DS:DATA,SS:STACK START:MOV AX,DATA MOV DS,AX ;这两条给DS赋初值 ……;为源程序指令序列 MOV AH,4CH ;DOS调用INT 21H,功能号4CH INT 21H ;程序终止 CODE ENDS ;代码段结束 END START ;汇编结束 返回本章目录
4.2 汇编语言的源程序格式 以一个完整的汇编语言程序来说明; DATA SEGMENT ;定义数据段,段名为DATA X DB 34H ;定义各种数据 Y DW “HH” …….. DATA ENDS ;数据段定义结束 STACK SEGMENT ;定义堆栈段,段名为STACK DB 100 DUP(0) ;开辟堆栈区域100,初值为0 STACK ENDS ;堆栈段定义结束 CODE SEGMENT ;定义代码段,段名为CODE ASSUME CS:CODE,DS:DATA,SS:STACK START:MOV AX,DATA MOV DS,AX ;这两条给DS赋初值 ……;为源程序指令序列 MOV AH,4CH ;DOS调用INT 21H,功能号4CH INT 21H ;程序终止 CODE ENDS ;代码段结束 END START ;汇编结束
汇编语言源程序应包括: (1)汇编语言源程序存入内存时,分四个段进行存放;程序中一般有二个段:数据段、代码段;而附加数据段用于存放字符串数据,当程序没有用到字符串数据时可省略,堆栈段在内存中建立堆栈区,用于中断、子程序的调用,当程序没有用到中断、子程序时可省略;数据段在内存中建立工作区,存放程序需要进行操作的数据、变量等;代码段存放程序执行的指令集合,完成程序的操作。段与段之间随意存放。 (2)每个段以 “段标识符 SEGMENT” 开始,以 “段标识符 END” 结束START:MOV AX,DATA 表示程序操作指令开始,整个程序以 “END START” 结束,两个 START 相对应,当然可用你感兴趣的标识符。 (3)伪指令 ASSUME 告诉汇编程序,段地址与段寄存器之间的关系,各段在内存的起始地址。源程序要对数据段寄存器 DS 进行初始化,采用两条指令MOV AX,DATA 和MOV DS,AX实现。 (4)如果要对指令进行说明,在一条指令后加 “;” 接着写说明部分;汇编语言在编译时,对此不作处理。操作数之间、参数之间用 “,” 号分隔;其它部分一般用多个空格分隔符,标号后加 “:” 号,表示逻辑地址;原则上一条指令书写一行。 返回本章目录
4.3 伪指令 伪指令是用来对相关指令进行说明;主要有段定义、过程定义,数据定义、符号定义,模块定义、程序结构定义等,由于不产生代码,因此称为伪指令。常用的有下几种: 1.变量定义伪指令:为变量申请固定长度的内存空间; (1)定义字节变量:用于分配一个或多个字节单元 例如: X DB ‘a’;分配一个字节单元 Y DB 10 DUP(0) 连续分配11个字节单元 (2) 定义字变量:用于分配一个或多个字单元 例如: Z DW 8080H ;分配1个字单元,一个字2个字节 W DW ?,‘ABC’;分配多个字单元 (3)DD 定义双字变量,DQ 定义 4 字变量,DT 定义 10字变量 2.符号定义伪指令:为程序中的表达式赋予一个名字 (1)EQU 伪指令:将表达式的值赋给其前面的名字,以名字来代替表达式;程序中不允许对已经定义过的名字重新赋新的值。 例如: X EQU 100 ;将 100 赋给 X (2)= 伪指令:将表达式的值赋给其前面的名字,与 EQU 区别在于:= 伪指令定义后,可以多次改变,重新给变量赋新的值。
3.ASSUME段分配伪指令:设定四个段与对应的寄存器之间的关系3.ASSUME段分配伪指令:设定四个段与对应的寄存器之间的关系 例如: ASSUME CS:CODE1,DS:DATA1,SS:STACK1 4.SEGMENT/ENDS段定义伪指令:对数据段、代码段、堆栈段、附加数据段进行定义和赋予一个名字,指明类型、类别等; 例如: AA SEGMENT …….. AA ENDS 这里SEGMENT表示定义的一个逻辑段开始,ENDS 表示所定义的逻辑段结束,逻辑段的名称为 AA。类型、类别等不作介绍。 5.ORG定位伪指令:ORG 是起始位置设定; 6.程序计数器伪指令$:表示当前偏移地址的值。 例如:DATA SEGMENT Y DW 8899H BUF DB 10,-45H,66H,“A” N = $ - BUF ;当前地址$-BUF的偏移地址,BUF数据的个数 X DB 12 ORG $ + 10 ;设置Z的地址偏移量为当前位置后移10个单元 Z DW “ABCDEF” DATA ENDS 7.PROC过程定义伪指令:PROC定义一个过程。 返回本章目录
4.4 顺序程序设计 顺序程序结构是最基本、最常见的结构,完全按照指令书写的 顺序执行每条指令。顺序程序结构在程序中都会出现,是复杂结构 的一部分;是分支程序的其中一个分支;循环程序的循环体内就是 顺序程序构成。 例如:有三个变量 A、B、C,其值分别为 3、4、5,求出 A、B、 C 的和,将和存入变量 SUM中。 DATA SEGMENT ;定义数据段 A DB 3 B DB 4 C DB 5 ;给 A,B,C赋初值 SUM DB ? ;存放和,无初值 DATA ENDD CODE SEGMENT ASSUME CS:CODE,DS:DATA ;本程序只需两个段 START:MOV AX,DATA ;初始化 DS
START:MOV AX,DATA ;初始化DS MOV DS,AX MOV BH,A ;将 A的值送到 BH寄存器 ADD BH,B ;进行求和 ADD BH,C MOV SUM,AH ;存入运算结果 MOV AH,4CH ;返回 DOS INT 21H CODE ENDS ;代码段结束 END START ;汇编结束 本例子,源程序仅由代码段和数据段两部分组成。在数据段,定义了变量 A、B、C,即内存中数据段的三个连续的单元,并赋给了初值,接着定义了存储和的变量 SUM,即数据段中的第四个单元,由于没有初值,用?表示,当然也可赋初值 0。 执行代码段中的指令,对于顺序程序结构,从第一条指令开始执行,依次执行第二条,……直至最后一条指令。 返回本章目录
4.5 分支程序设计 汇编语言中,使用无条件指令 JMP 和条件转移指令 JCC来实现 分支程序设计。条件转移指令的条件指的是:FLAG 的标志位。影响 FLAG、设置 FLAG 标志位的指令主要有算术运算指令、比较指令 CMP、测试指令 TEST;因此,JCC 前面一般有影响标志位的指令。 分支程序结构有单分支和多分支两种。 1.单分支程序设计 例如:内存单元X中存放一个数据,求出X的绝对值,存入内存 单元 Y中。见示意图4.2 。 DATA SEGMENT X DB —23D ;请随意存入一个数据 Y DB ? ;存放 X的绝对值 DATA ENDS CODE SEGMENT ASSUME DS:DATA,CS:CODE
见示意图4.2 START:MOV AX,DATA MOV DS,AX MOV AH,X ;取出X单元的值 CMP AH,0 ;判断X值 JGE LP1 ;X为正数,直接存放 NEG AH ;X为负数,求绝对值(补码) LP1: MOV Y,AH ;存入X的绝对值 MOV AH,4CH ;返回DOS INT 21H CODE ENDS ;代码段结束 END START ;汇编结束 单分支程序设计,当条件满足(成立)时,发生转移,跳过分支体;当条件不满足(不成立)时,按程序书写的顺序向下执行分支体。
2.多分支程序设计 实际问题存在多个条件,进行不同的操作时,用到多分支程序。 例如:键盘上输入一个字符,如果是数字字符,将之对应的十进 制数据, 存入内在单元 D,否则,将 ‘X’字符存入单元 D,流程见图4.3 。 D DB ? ;根据输入的结果存放DX的值 START:MOV AX,DATA MOV DS,AX MOV AH,01H ;从键盘输入一个字符 INT 21H CMP AL,‘0’;输入的字符与‘0’比较 JB LP1 ;小于‘0’,不是数字字符 CMP AL,‘9’;与‘9’比较 JA LP1 ;大于‘9’,不是数字字符 SUB AL,30H ;将输入的数字字符转为对应的数据 MOV D,AL JMP EXIT LP1:MOV D,‘X’;输入的不是数字字符,将‘X’存入D单元 EXIT:MOV AH,4CH ;返回DOS INT 21H CODE ENDS ;代码段结束 END START ;汇编结束 返回本章目录
4.6 循环结构程序设计 循环结构程序是指满足条件时,重复执行一段指令,称为循环 体;不满足条件时,绕过循环体。循环结构一般由三个部分组成: (1)循环初始化部分:为循环开始作准备。赋循环次数、处理 多个数据时的初地址、所用内存数据单元的初始化、所用寄存器的 初始化等; (2)循环体:重复执行的指令序列;还包括修改控制循环所用 的参数,如计数器、操作时所用地址的指针等。 (3)循环控制部分:判断循环条件是否成立,确定循环是否继 续。循环的控制方法有两种; 先执行循环体,然后判断循环条件是否成立;循环体一定要执 行一次。这点设计程序时要留意,否则,程序执行结果出错。 先判断循环条件是否成立,如果成立,进入循环体,不成立, 不进入循环体;循环体可能一次都不执行。 循环结构程序设计的几种常用的控制循环方法:
1.计数控制循环 计数控制循环这种方式,事先就知道要循环的次数,将要循环的次数先存入 CX中,每执行一次循环,CX 的值减1,当(CX)= 0时,结束循环。次数来作为控制循环,最好的方法是利用 8086 汇编系统提供的循环指令LOOP 和 JCXZ 来实现。 例如:内存 BUF 区域,存放有10个带符号的字节数据,将它们求和,存入字节单元 SUM 中(和不超过字节)。 BUF DB 23,-45,-56,0,21,34,78,9,8,-32 SUM DB 0 ;不考虑和超过字节 START:MOV AX,DATA MOV DS,AX MOV CX,10 ;循环次数赋初值 MOV SI,OFFSET BUF ;第一个数据单元的地址初值 MOV AH,0 ;累加寄存清0
LP1:ADD AH,[ SI ] ;求和 INC SI ;指向下个单元 DEC CX ;循环次数修改 CMP CX,0 ;循环条件判断 JNZ LP1 ;次数未到,循环继续 MOV SUM,AH ;循环结束,存放结果 EXIT:MOV AH,4CH ;返回DOS INT 21H CODE ENDS ;代码段结束 END START ;汇编结束
2.条件控制循环 实际应用中,循环次数是无法事先知道的,计数控制循环受到很大的局限性;循环次数是实际问题中的某个条件来控制,以结束循环,控制循环的。 例如:内存BUF区域有多个带符号的数据,以回车结束,统计正数的个数,并将统计的个数存入内存SUM单元。 工作流程见图4.4。 BUF DB 23,-45,-56,0,21,34,78,9,8,-32….,0DH SUM DW 0 START:MOV AX,DATA MOV DS,AX MOV SI,OFFSET BUF ;第一个数据单元的地址初值
工作流程见图4.4。 LP1:MOV AH,[ SI ] ;取出当前单元的数据 CMP AH,0DH ;当前单元的数据是回车符吗 JZ EXIT ;是回车符,循环结束 CMP AH ,0 JS LP2 ;是负数,不统计,进入下个单元 INC SUM ;是正数,统计 LP2 INC SI ;进入下一个单元 JMP LP1 ;继续循环 EXIT: MOV AH,4CH ;返回DOS INT 21H CODE ENDS ;代码段结束 END START ;汇编结束 返回本章目录
4.7 子程序 程序中一段指令是实现固定的功能,而每次调用的参数不同,这些功能经常用到,这时采用子程序。这样设计结构清楚,程序的维护方便。当主程序需要执行这个子程序功能时,通过调用该子程序,执行子程序,子程序完成后返回主程序调用处,继续主程序后面的指令的执行。与子程序有关的指令有子程序的调用CALL、子程序返回RET两条指令。 4.6.1 子程序的调用指令 子程序的调用有4种情况,类似于无条件的转移JMP的情况。 (1) 段内调用,直接寻址 格式: CALL label ; 当前IP入栈,实现转移,转到偏移地址为label处执行 (2) 段内调用,间接寻址 格式: CALL r16/m16 ; 当前 IP入栈,转到偏移地址为 r16/m16 的内容指示的位置执行 (3) 段间调用,直接寻址 格式: CALL far ptr label ; 当前IP入栈,实现转移,转到 label所在的段,偏移地址为label处执行 (4) 段间调用,间接寻址 格式: CALL far ptr mem ; 当前 IP入栈,实现转移,转到 mem 所在的段,偏移地址为mem处执行
4.7.3子程序定义伪指令 子程序在汇编语言中称为过程,每个子程序过程有一个唯一的过程名; 由一对伪指令 PROC和ENDP定义。格式为: 过程名 PROC(属性) 指令系列 ;过程体 过程名 ENDP 例如:编写将寄存器AL的低4位的一个16进制数,转换成相应的ASCII码 的子程序 HEXASC PROC ;定义一个过程,名为HEXASC AND AL,0FH ;截取AL的低4位 OR AL,30H ;0-9的ASC码为30-39H CMP AL,39H ;判断是0-9,还是A-F JBE LP1 ;AL内容<39H,AL为0-9 ADD AL,7 ;A-F的ASC码,比9D大7 LP1:RET HEXASC ENDP ;名为HEXASC的过程结束
通过上面的子程序的例子,看出过程编写的规则: 通过上面的子程序的例子,看出过程编写的规则: (1)以 “过程名 PROC” 开始,以 “过程名 ENDP” 结束;子程序过程体内包含有 RET指令,遇到此指令,即返回主程序,从而结束了子程序的执行。 (2)子程序所用到的寄存器,子程序开始时,都应该利用堆栈来保护,防止子程序使用时破坏其原来的数据;子程序返回调用者前进行恢复原来的数据。 (3)子程序安排在主程序的代码段之外,最好放在主程序终止执行的位置,也可放在主程序开始执行之前。 (4)子程序允许与主程序共用一个数据段;也可以使用不同的数据段。 (5)子程序可以有多个出口(即内有多个 RET 指令);多个入口,即主程序与子程序多个参数传递。
4.7.4 子程序参数的传递 主程序与子程序的参数传递,指主程序要子程序处理的数据通过什 么途径、方式、方法传送给子程序。方法有:用寄存器、用变量、用堆 栈。 1.寄存器法 主程序需要子程序处理的数据,存放在一个事先约定的寄存器中, 子程序到这个寄存器中取出数据进行处理,结果传给调用者。 2.内存单元变量法 主程序需要子程序处理的数据,存放在一个事先约定的内存单元变 量中。 3.堆栈法 主程序将要转换的数据压入堆栈内,再调用子程序,子程序从堆栈 弹出数据,处理的结果也可压入堆栈返回给调用者。 举一个完整的包括主程序、子程序及调用的例子,来进行整体说明。 例如:将内存单元 X 的一个一位十进制数据显示出来;将一位十进制数 据转为对应的 ASC 码用子程序,主程序决定X单元的数据及显示这个数。
DATA SEGMENT X DB ? ;存放被显示的数 Y DB 0 ;存放此数转换好的ASC码 DATA ENDS CODE SEGMENT ASSUME DS:DATA,CS:CODE START:MOV AX,DATA MOV DS,AX MOV X,9D ;十进制数存入X单元 CALL DTOA ;调用子程序DTOA进行转换 MOV AH,02H ;显示一个字符用02H号功能调用 MOV DL,Y ;Y为要显示的数的ASC码 INT 21H EXIT: MOV AH,4CH ;返回DOS INT 21H CODE ENDS ;代码段结束
DTOA PROC ;定义一个子程序,名为 DTOA PUSH AX ; 子程序要用AX,保护AX原来的数 MOV AH,X ;从 X取出参数 ADD AH,30H ;转换 9D的 ASC为 39H MOV Y,AH ;ASC 码存入 Y单元 POP AX ;恢复 AX寄存器 RET ;返回调用者 DTOA ENDP ;子程序 DTOA结束 END START ;汇编结束 返回本章目录
图4.1 数据变量定义的存储格式 返回4
图4.2 单项分支程序结构图 返回12 返回13
图4.3 多分支程序结构示意图 返回14
图4.4 条件控制控制循环示意图 返回18 返回19
表4.1 8086汇编运算符 返回3