340 likes | 499 Views
第四章 汇编语言程序设计. 锡山职教中心 徐辉. 4.1 汇编语言的基本语法 4.1.1 汇编语言程序的格式 4.1.2 常量、标识符和表达式 4.1.3 指示性语句 4.1.4 指令性语句 4.1.5 宏指令. 4.1.1 汇编语言程序的格式 例:功能:求 10 个字节数据 a1~a10 之和。 DATA SEGMENT AT 2000H ARRAY DB a1,a2,a3, …… ,a10
E N D
第四章 汇编语言程序设计 锡山职教中心 徐辉
4.1 汇编语言的基本语法 4.1.1 汇编语言程序的格式 4.1.2 常量、标识符和表达式 4.1.3 指示性语句 4.1.4 指令性语句 4.1.5 宏指令
4.1.1 汇编语言程序的格式 例:功能:求10个字节数据a1~a10之和。 DATA SEGMENT AT 2000H ARRAY DB a1,a2,a3,……,a10 Count EQU $-ARRAY SUM DW DATA ENDS STACK SEGMENT PARA STACK ‘STACK’ STAK DB 10 DUP(?) TOP EQU LENGTH STAK STACK ENDS CODE SEGMENT ASSUMENT CS:CODE,DS:DATA,SS:STACK START: MOV AX,DATA MOV DS,AX MOV AX,0 MOV DI,OFFSET SUM MOV BX,OFFSET ARRAY MOV CX,COUNT LOP: ADD AL,[BX] ADC AH,0 INC BX LOOP LOP MOV [DI],AX MOV AH,4CH INT 21H CODE ENDS END START
基本格式和特点 S_NAME1 SEGMENT 语句 … 语句 S_NAME1 ENDS S_NAME2 SEGMENT 语句 … 语句 S_NAME2 ENDS S_NAME3 SEGMENT 语句 … 语句 S_NAME3 ENDS END 标号 8088汇编语言的源程序是由多个段组成的, 一个可执行的汇编程序一般至少有一个代码段,其中包含可执行的语句。其基本格式如右所示。其特点如下: 1)分段结构 指令性语句 2)使用 语句行 指示性语句 3)必须使用ASSUME语句完成段的指认 4)必须完成段地址寄存器及相关寄存器的初始化。 a)用户初始化:DS、ES b)系统初始化:CS:IP、SS:SP 5)有保证正常返回DOS的处理。
每个段是由语句行构成的。一个语句行的基本格式为:每个段是由语句行构成的。一个语句行的基本格式为: a)指令性语句: [标号 :] 操作码 [ 操作数1 ,操作数2] [ ;注释] 例:lop: add al,[bx] b)指示性语句 [名字/变量] 命令 参数表 [;注释] 例:data segment at 2000h
1. 标识符 标识符由一串字符构成,用于一段程序、一组(或一个)数据或一个段的开头。 2. 保留字 保留字是汇编语言中预先保留的特殊字串,只能用于特殊用途,所有8086/8088的指令、伪指令和寄存器名都是保留字。如AX、MOV、DB、INT、EQU等等。 保留字不能用作标识符 3. 界符 界符是用于一个程序或一条指令中不同部分的分隔符,如MOV AX, 0010H中的“,”就是界符。其内容见下表: 8086/8088汇编语言的界符 ’ ; > < * , : [ ] + - = ( ) $ & ? . /
4. 常量 常量就是指令中的一些不变的数据。它可以用二进制、十六进制、十进制和八进制表示,也可以用引号引起来的字串。例如: 二进制:00011101B 十六进制:0C5AAH 十进制:9856D 八进制:237O 字串:’The 2X means 2 multiple X’ 其中的B、H、D和O为数制的结尾符。 5. 注释 注释是用于使程序易于理解的句子,用来表示某行或某段语句的作用或与其它部分的关系,一般写在某语句的后面或某段的开始处,注释的前面由“;”前导,表示后面的内容为注释。如果一行写不下,换行后也要使用“;”前导。
4.1.2 常量、标识符和表达式 • 1. 常量 • 数字常量 例:10100110B,166Q,6AH,0F3H • 字符常量 例:‘dhjkshd’ (带单引号的ASCII字符表示) • 符号常量 例:ONE=11111111B • MOV AL,ONE;等价于 MOV AL,11111111B • 2. 标识符 • 标识符由一串字符构成,用于一段程序、一组(或一个)数据或一个段的开头。使用时要注意以下3点: • •标识符可由数字、字母和下划线、?、@、$组成; • • 不能以数字开头,数字可用在非开头的其它位置; • ?不能单独作为标识符 • •其最大长度为31个字符。
3. 表达式 表达式由操作数和运算符组成,如3+2、77 AND 55、NOT 5AH等。 1. 操作数 操作数用于代表一个数据或一个地址。 2. 运算符 运算符包括算术、逻辑、关系、分析、综合五类运算符(p136见表4-1)。 ① 算术运算符包括 + 、-、*、/ 、MOD 参加运算的内容可以是数据,也可以是地址,但在一个算术运算符 的两边不能都是地址。 ② 逻辑运算符包括 AND、OR、NOT、XOR ③ 关系运算符包括 相等 :EQ大于:GT 不等:NE小于或等于:LE 小于:LT大于或等于:GE
关系运算结果只有两个——真、假。 如果关系是真,则结果为 0FFH(0FFFFH); 如果关系是假,则结果为0。 例如 MOV BX, DATA1 LT 0010H MOV BX, 0FFFFH MOV BX, DATA1 LT 0010H MOV BX, 0 当 DATA1< 0010H 时成立(真用全1表示) 当 DATA1 > 0010H 时
④分析运算符 分析运算在汇编语言程序设计中很重要。它包括 取存储单元偏移:OFFSET 取存储单元段:SEG 取类型:TYPE 取字节数:SIZE 按类型取长度:LENGTH
TYPE 变量/标号 变量/标号类型为: 字节(DB) 1 字 (DW) 2 双字(DD) 4 8字节(DQ) 8 10字节(DT) 10 NEAR -1 FAR -2 LENGTH 变量 只有当数据用DUP定义时,LENGTH才等于数组 的元素个数 SIZE 变量 SIZE=LENGTH*TYPE
⑤ 综合运算符 综合运算符包括两个地址操作符 PTR、THIS和LABEL。 格式: 类型 PTR exp;类型允许WORD、BYTE、DWORD、NEAR、FAR PTR——改变存储器地址操作数的操作类型,但其段地址和偏移地址不变。 例1:重新指定类型: DATA1 DW 1234H,5678H MOV AL, DATA1 MOV AL, BYTE PTR DATA1 ;(AL)=34H MOV AX,DATA1;(AX)=1234H 例2:指定操作数的类型 INC [BX] ;操作数类型不明确 改为: INC BYTE PTR [BX] INC WORD PTR [BX] 例3:重新定义一个新变量 DATA1 DW 1234H,5678H BDATA1 EQU BYTE PTR DATA1 √ BDATA1 DATA1 34 12 78 56 DS
THIS——用来建立新的存储器地址操作数,且不分配存储单元。新的操作THIS——用来建立新的存储器地址操作数,且不分配存储单元。新的操作 数类型在THIS中指定,而其段地址和偏移量就是汇编时的当前值。 格式: THIS 类型 DATA1 SEGMENT WBUFFER EQU THIS WORD 两语句必须相邻, BUFFER DB 12H, 34H, 00H, 91H 且THIS必须在前 DATA1 ENDS CODE1 SEGMENT ASSUME CS: CODE1, DS: DATA1 START: MOV AX, DATA1 MOV DS, AX MOV AL, BUFFER MOV BX, WBUFFER …… CODE1 ENDS END START BUFFER WBUFFER 12 34 00 91 85 DS AL=12H BX=3412H
LABEL——用来定义其语句中的变量(标号)的类型属性为语句中设定的LABEL——用来定义其语句中的变量(标号)的类型属性为语句中设定的 类型,此时变量(或标号)的段属性和偏移地址属性由该语句 的位置确定。 格式: 标号/变量 LABEL 类型 例:BUFB LABEL BYTE 两语句必须相邻, BUFW DW 1234H,5678H 且LABEL语句必须在前 MOV AX,BUFW;(AX)=1234H MOV AL,BUFB; (AL)=34H
小结 1、在使用或访问变量时,必须指定变量的类型属性,即源操作数与目的操作数的类型属性一致,操作合法。 2、可以有多种方法指定、改变变量的类型属性: PTR可在访问时指定变量属性 THIS,LABEL在变量定义时,指定变量属性
4.1.3 指示性语句(伪指令) 在8086/8088汇编语言中伪指令是用于诸如数据定义、存储区分配等功能。所谓伪指令是非机器指令,它是在汇编期间进行操作的。 一、程序开始和结束语句 在程序的开始可以用 NAME或 TITLE 为程序取名字。 格式为: NAME 程序名 TITLE 程序名 整个程序的结束使用 END标出,当汇编器读到 END时,它认为程序到此就结束了。在程序中,首条被执行的语句有一个标号,在程序的结尾使用 END后跟上这个标号,用以标出这个程序的开始执行处。 注: NAME 、TITLE 操作不是必须的。 END则必不可少。
二、段的定义 分段结构是8086/8088的特点,程序和存储器都是按段来组织的,语句有:SEGMENT、ENDS、ASSUME、ORG。 1. 段定义语句 在段定义中,SEGMENT和ENDS总是成对使用的,它们将数据和程序分为多个段,如数据段、栈段和代码段。 2. 命令ORG、ASSUME ASSUME语句紧跟在SEGMENT语句之后,是汇编时进行操作的,它用于在汇编时告诉汇编器,哪个是数据段,哪个是代码段,哪个是栈段。 ORG用于指定目标程序存放单元的起始偏移地址,通常写在第一条源程序的前面,用于指定这段程序的首地址。如使用了: ORG 1000H MOV AL,BL 则程序执行时就从CS:1000H处开始执行其后面的程序。(MOV指令所在的内存单元在本段的偏移地址为1000H。
Segment 的类型及属性说明——在需要用连接程序将本程序与其它模块相连接时,需要使用这些说明。 Segname segment [align_type] ;定位类型 [combine_type] ;组合类型 [‘class’] ;类别 定位类型: para:指定段的起始地址必须从小节边界开始,即段地址的最后1位(十六进制)必须为0 。 byre: 该段可以从任何地址开始。 word:该段必须从字的边界开始,即段地址必须是偶数。 page:该段必须从页的边界开始,即段地址的最后2位(十六进制)必须为0 。 组合类型: public:该段连接时将与有相同名字的其它段连接在一起。 common:该段在连接时与其它同名分段有相同的起始地址(会产生覆盖)。 at expression :使段的起始地址是表达式所指定的16位段地址,但不能指定代码段。 stack:指定该段在运行时为堆栈段的一部分。 类别: 连接时用于组成段组的名字。
注意: ① SEGMENT和ENDS前面必须有标号,而且在相互配对的段,它们前面要使用相同的标号。对于不同的段定义标号,尽管这些标号可以是任意字串,但为了程序的可读性,应使其有一定的意义。 ② ASSUME语句可使汇编器知道CS、DS、SS指向哪个段,但它只在汇编时起作用,在运行时CPU并不知道除CS外的其它段地址,所以必须在程序中用指令进行赋值。 ③ 首条被执行的语句有一个标号,在程序的结尾使用END后跟上这个标号,用以标出这个程序的开始执行处。(只需在主程序的结束语句中写出标号,其他主程序模块的结束语句只需要写出语句END即可。)
三、过程定义语句 和其它程序设计一样,8086/8088汇编程序设计有过程或子程序的设计方法。8086/8066汇编程序的过程从运行位置上分为近过程(NEAR)和远过程(FAR),缺省值为NEAR。其定义如下: PROC_NAME1 PROC NEAR …… RET PROC_NAME1 ENDP PROC_NAME2 PROC FAR …… RET PROC_NAME2 ENDP
过程和其它程序可定义在同一个段中,也可定义在不同的段中。对过程的调用要使用CALL语句,如:过程和其它程序可定义在同一个段中,也可定义在不同的段中。对过程的调用要使用CALL语句,如: CALL PROC_NAME1 CALL PROC_NAME2 如果在过程定义中没有写明是NEAR过程或FAR过程,则汇编器将这个过程默认为NEAR过程。
四、符号定义语句 EQU 为了使程序便于阅读和修改,我们有时使用一个符号来代表一个值,而符号代表了一定的意义,在程序中使用一个符号代表一个值进行操作,其格式为: 名字EQU 表达式 例如: PORT EQU 3F8H 在程序中可使用 MOV DX, PORT 对DX进行赋值,实际上PORT就是一个常量。 例:DATA SEGMENT ARRAY DB 10H,24H,5AH,0C7H,98H,’ABCD’ COUNT EQU $ - ARRAY ;$-ARRAY=000AH-0000H=10 MAX DB ? DATA ENDS
还可在语句中进行运算,如: DATA_PORT EQU 3F8H STAT_PORT EQU 3F8H+2 SEED EQU 10 FUNC EQU SEED*SEED+2*SEED+1 ‘ = ’号操作 对符号的赋值还可使用“=”操作,它与EQU的区别是“=”操作可以重复定义,而EQU则不能重复定义。如: X=3 Y=6 Y=Y*Y-X …… MOV AX, Y 通过上述操作,最后使Y=33,后面的MOV语句中,当生成目标代码时将用33取代Y。即 MOV AX, 21H (33D)
五、数据定义语句 数据定义语句用于为数据分配相应的存储单元。用一个符号名代表一个或一些单元,并可为这个或这些单元提供初始值。定义数据的操作符有: 字节定义——DB 字定义——DW 双字定义——DD 四字定义——DQ 十字节定义——DT 重复定义—— m DUP(n)_ (重复m次个数据 n ) 与数据相联系的符号名称为变量。 数据定义语句的格式为: 变量名 操作符 数据项
4.1.4 指令性语句 [标号 :] 操作码 [ 操作数1 ,操作数2] [;注释] 一、标号 段属性 偏移地址属性 类型属性(NEAR、FAR) SEG 标号 OFFSET 标号 TYPE 标号 例:程序中有标号ME(NEAR类型)。若想定义新变量KI,类型为FAR, 而两者的段地址、偏移地址属性相同。 这样段内转移用ME,段间转移用KI,两个标号表示的是同一地址。 1) 用PTR重新指定类型 段内调用:JMP ME段间调用:JMP FAR PTR ME 2)用EQU和PTR定义新标号 ME:MOV AX,BX KI EQUFAR PTR ME 3)用EQU和THIS定义新标号 KI EQU THIS FAR ME:MOV AX,BX 4)用LABEL定义新标号 KI LABEL FAR ME:MOV AX,BX
4.1.5 宏指令 宏指令:程序员用汇编语言编程是,对于程序中多次重复使用的指令序列可定义一条宏指令,编写程序时就用这条宏指令代替该指令序列,从而简化书写工作。 一、宏定义、宏名字、宏调用和宏展开 宏定义:宏名字 MACRO [形式参数] …… ENDM 宏调用:宏名字 [实参数]
二、宏指令与子程序的区别 1)子程序目标程序比宏调用短。在汇编时,汇编程序将宏指令的宏体代码展开嵌入到程序中的宏调用处,然后转换成机器码生成目标程序。因此,宏指令只简化了源程序,但并没有简化目标程序;而子程序(过程)通过CALL指令调用,执行子程序时处理器改变CS:IP使其转子程序处执行,通过RET指令返回主程序,子程序的调用可以简化目标程序. 2)宏指令的执行速度比子程序快.子程序每次调用和返回都要增加额外开销,而宏调用不会。
三、 循环程序 对于程序中多次、有规律执行的部分,我们通常使用循环结构要重复运行这些部分,使程序有较高的运行效率和可读性。循环结构主要由三部分组成,它们是: 1. 循环体:重复运行的部分,其中还包含了工作部分和循环控制部分。工作部分进行相应的操作,而循环控制部分则保证在不满足循环条件时,跳出循环。 2. 循环结束条件:在循环体中包括了循环结束条件运算部分。每循环一次除了工作部分进行相应的操作外,循环条件部分还要通过运算,得出当前循环的状态,以便在适当的条件下结束循环。 3. 循环初值:用于设置开始循环时,循环体所处的初始状态,如循环变量初值,循环体中用到的数据和地址指针等。
四、 子程序设计 子程序是提高程序设计效率的良好手段,也为模块化设计提供了很好的基础。 在子程序设计中 •要明确地定义出这个子程序的入口参数和出口参数,使调用者能方便地使 用子程序。 •在子程序中在合理地保存主程序和子程序都用到的寄存器和存储单元,以 使主程序能正确地运行。 •参数传送主要有两种方式:① 利用寄存器传送参数 ② 利用堆栈传送参数
mov ah, 0 mov al, num ;实参N存在AX中 call factor mov result, ax ret Main endp Factor proc ;求N!参数存在AX中 push ax sub ax, 1 jne f_cont pop ax jmp return f_cont: call factor pop cx mul cl return: ret Factor endp Code ends end begin ax =1 1 IP POP CX地址 IP3 Pop cx 地址 cx =2 al*cl=1*2 2 Pop cx 地址 IP POP CX地址 IP2 cx =3 al*cl =1*2*3 3 IP1 Mov result, ax 地址 IP Mov result, ax 地址
五、DOS系统功能调用和ROM BIOS中断调用 • DOS功能调用的方法: • 入口参数送入指定的寄存器; • 功能号送入AH; • INT 21H • 掌握:01H、02H、09H、0AH子功能。 • ROM BIOS 的中断类型号为8—1FH • 概念:中断向量表、中断类型码 • INT N 指令的功能 • 例1:人机对话程序的编制。(IO.ASM) 例2:将一个字节的二进制数转换成两位ASCII码表示的十进制数,并在屏幕上显示出来。(P175 ERASCII.ASM)