第三章:汇编语言程序设计(二)
汇编语言程序格式
汇编语言数据的定义 (数据定义伪指令)
在程序中用到的所有数据必须指定其类型,数据分为常量和变量
常量
- 汇编源程序中具有固定值的数称为常量;
- 常量可以以多种数制及字符形式出现
- 汇编程序中常使用一下两种类型的常量
- 数字常量
- 二进制数:10100001B
- 八进制:76Q,335O
- 十进制: 98,4330
- 十六进制: 0FE65H
- 字符串常量
- 字符串常量表示为包含在两个单引号之间的字符,如:
’AB‘,‘123’
等 - 程序经编译后,单引号内的每个字符实际上是其ASCII码的形式,例如:
'AB'
,可以看成是4142H.
- 字符串常量表示为包含在两个单引号之间的字符,如:
- 数字常量
变量的定义
- 变量是用户为存放数据的内存单元所起的名字
- 变量名实际上是内存单元的符号地址
- 通常这些内存单元被分配在数据段、附加段或堆栈段,这些单元的数据在程序的运行期间可以随时修改。
- 在汇编程序中,用数据定义伪指令为变量分配内存单元,同时可以将这些内存单元预置初值。
- 字节定义语句 DB
- 定义字节存储单元,其后的每个操作数占有一个字节单元,可以连续存放。
- 格式:
[变量名] DB 操作数 [,....操作数]
-
举例:
//单字节数据 X DB 10 ;为变量X分配一个字节,初值是10 Y DB ? ;为变量Y分配一个字节,不设置初值,初值是随机值 //多字节数据 BUFFER DB 3,4,-5,-2 ;从地址BUFFER开始,连续分配4个字节,其内容分别是03H,04H,0FBH,0FEH(负数是以补码的形式在计算机中存在的) //定义字符串 STRING DB 'ABCD' ;从地址STRING开始,连续分配4个字节,其内容分别是41H,42H,43H,44H //定义重复数据 BUF DB 100 DUP(0) ;从地址BUF开始,连续分配100个字节,并将其全部赋值为0. 复制操作符DUP用来复制某个或某些连续的存储空间,并且可以嵌套使用。
- 自定义语句 DW
- 定义字存储单元,其后的每个操作数占据两个字节,地位字节在低地址,高位字节在高地址,可以连续存放。
- 自定义单元的定义格式与操作数的形式与字节定义语句DB类似。
-
注意: 如果DW后的操作数是地址(变量名),则存入对应字单元的是地址(变量名)的偏移地址部分。
DAT SEGMENT X DB 3,4 Y DW -2 ADDR DW Y DAT ENDS 其中,ADDR字单元内存储的事变量Y的偏移地址
- 其他数据定义语句DD/DQ/DT等
- DD定义双字存储单元,其后的每个操作数占据4个字节
- DQ定义的每个数据单元占8个字节
- DT定义的每个数据单元占10个字节
- 字节定义语句 DB
变量的属性
- 由于存储器是分段使用的,所以对源程序中定义的变量都有以下3种属性。
- 段属性: 变量所在段的段基址,为该段所对应的段寄存器的值。
- 偏移属性:变量距段起始地址的字节数,为16位二进制无符号数。
- 类型属性: 变量在存储单元所占的字节长度,为整数数值,主要有BYTE(1),WORD(2),DWORD(4),DQ(8),DT(10)等。
汇编语言的标号
- 标号的概念
- 标号是用户为程序中某条指令所起的名字,实质上是代码段中该指令所对应的目标代码在存储器内的存放地址。
- 标号经常在转移、调用指令中引用,作为程序转移的目标地址。
- 标号的属性
- 段属性: 标号所在段的段基址,为该段所对应的段寄存器的值,即CS的值。
- 偏移属性: 标号所代表的指令相对于代码段首地址的字节数,为16位二进制无符号数
- 类型属性: 若标号所代表的指令与引用标号的指令在同一代码段内,则为NEAR类型,用0FFFFH(1)表示;否则为FAR类型,用0FFFEH(-2)表示。
- 可见:变量代表的事数据存储单元的地址,而标号代表的是指令存储单元的地址。
汇编语言的运算符和表达式
- 汇编语言的数据或者操作数除了表示为常量、变量和标号的形式外,还可以表示成表达式的形式。
- 注意: 表达式并不是命令,其计算或操作都是在汇编时由汇编程序完成的,在程序运行时表达式已经被他的运算结果取代了
- 表达式根据其运算结果的类型分为数值表达式和地址表达式。
- 数值表达式的运算结果是数值,没有别的属性;
- 地址表达式的运算结果是地址,具有段属性、偏移属性和类型属性
- 构成表达式的运算符有5种,分别是算术运算符、逻辑运算符、关系运算符、分析运算符合合成运算符。
- 所有运算符如课本表:4.1 p119
- 这积累运算符的优先级: 4.2 p119
算术运算符
- 算术运算符共包括:加(+)、减(-)、乘(*)、除(/)、取余(MOD)、左移(SHL)、右移(SHR)共7种
- 说明:
- 参加运算的数值和运算结果均是整数
- “/”运算的结果取两个运算数商的整数部分,“MOD”是取两个运算数商的余数部分;
- SHL/SHR表示的是在汇编阶段对一个具体的整数值进行左移、右移运算
-
注意:左移算术运算符SHL与逻辑左移指令SHL是完全不同的两种操作,左移运算符是在汇编程序时完成的操作; 逻辑左移指令是在程序运行时进行的操作,如下指令:
SHL BL, 1 ; 执行到该指令时,寄存器BL的内容左移一位。
- 算术运算符都可以用于数值表达式,其中的“+”、“-” 还可以用于地址表达式,有以下3种形式
- 地址表达式 + 常数
- 运算结果为偏移地址,与地址表达式的类型和段基址相同,但偏移地址是地址表达式的偏移地址加上常数个字节。
- 地址表达式 - 常数
- 运算结果为偏移地址,与地址表达式的类型和段基址相同,但偏移地址是地址表达式的偏移地址减去常数个字节。
- 地址表达式1 - 地址表达式2
- 结果是一个数值,表示两偏移地址间距的字节数。 需要注意的是,地址表达式1和地址表达式2必须在同一个逻辑段中,即这两个地址必须有同一个段基址。
- 地址表达式 + 常数
逻辑运算符
- 逻辑运算符包括与(AND)、或(OR)、非(NOT)、异或(XOR)4种
- 逻辑运算符所处理的操作数均是数值型整数,在两个整数之间按位进行二进制运算,结果也是整数。
关系运算符
- 关系运算符包括等于(EQ)、不等于(NE)、小于(LT)、小于等于(LE)、大于(GT)大于等于(GE)6种
- 关系运算符用于比较两个表达式,这两个表达式可以是常数表达式或同一段内的地址(变量)表达式。
- 若为常数表达式,按照无符号数的规则进行比较,若为你同一段内的地址(变量)表达式,则比较他们的偏移地址。
- 关系运算符的结果有两种,若关系成立,结果为逻辑真值,用0FFFH表示;否则,为逻辑假值,用0表示。
分析运算符
分析运算符用来分析一个操作数(变量或标号)的属性,即将其不同的属性(段地址、偏移地址、类型、字节总数、数据项总数)用数值表示出来
- SEG 运算符
- 格式 : SEG 变量或标号
- 功能: 返回变量或标号所在段的段基址,为16位二进制数。
- OFFSET 运算符
- 格式: OFFSET 变量或标号
- 功能: 返回变量或标号的段内偏移地址,为16位二进制数
- TYPE 运算符
- 格式: TYPE 变量或标号
- 功能: 返回变量或标号的类型值
- 对变量而言,类型返回值分别为: 1(DB),2(DW),4(DD),8(DQ),10(DT)等,
- 对标号而言,类型返回值分别为: -1(NEAR)和 -2(FAR)
- LENGTH 运算符
- 格式: LENGTH 变量
- 返回所占内存储单元内变量的个数
- LENGTH 操作符只与定义变量的伪指令后的第一个参数有关,若第一个参数的形式为“n DUP(表达式)”,则LENGTH运算的结果为n,否则为1
- SIZE 运算符
- 格式 SIZE 变量
- 功能: 返回所占存储单元内变量的总字节数,即: SIZE = LENGTH * TYPE
- HIGH 和 LOW 运算符
- 格式: HIGH 表达式 LOW 表达式
- 功能: 分离运算符,取字操作数的高位字节和低位字节
- 注意: 表达式必须具有常量值,如常数,地址表达式的偏移量等。HIGH 和 LOW 运算符不能对存储器或寄存器操作数进行分离。
合成运算符
- PTR 运算符
- 格式:新类型 PTR 变量 或 标号
- 功能: 将原有变量或标号的类型改变成新类型,这种改变是临时的,仅在有此操作符的语句内有效。
-
举例:
DAT DD 12345678H ; 定义DAT 为双字类型的变量 MOV AX WORD PTR DAT ; 从DAT中去一个字放置AX,(AX) = 5678H
- THIS 运算符
- 格式: 变量或标号 EQU THIS 类型
- 功能: 定义变量或标号的类型为指定的类型。
- 如果指定的事变量,该变量的段地址和偏移地址与下一个定义的存储单元相同;
- 如果指定的事标号,则标号表示的是下一条指令的地址。
-
举例:
NEXT1 EQU THIS FAR ; 标号 NEXT1 被定义成FAR 类型,地址与NEXT相同 NEXT: XOR AX, AX ; 标号NEXT默认为NEAR 类型 ··· JMP NEXT ; 段内转移 ··· JMP NEXT1 ; 段间转移
- SHORT 运算符
- 格式: SHORT 标号
- 功能: 说明其后的标号在距当前指令一个字节的范围内(-128 ~ 127),常用于转移指令,限制转移的目标地址的范围。
汇编语言的其他常用伪指令
- 符号定义语句
- 如果汇编程序多次使用某个表达式、常数或指令,就可以把它定义成一个易于记忆的符号
- 凡是在后面程序中用到这些表达式、常数或指令的地方都用这个符号来代替。
- 这样既便于修改,有提高了程序的可读性,汇编后这个符号是代表确定的内容。
- 这样的符号定义语句有以下2种:
- 等值伪指令 EQU
- 格式: 符号 EQU 表达式
- 功能: 将表达式或表达式的值赋值给符号,在后续程序中用符号代替表达式或表达式的值。 表达式可以是常数、数值表达式、地址表达式、变量名、标号、寄存器或指令助记符等。
-
举例:
COUNT EQU 10 ; CALUE EQU 78+89 ; ADR EQU [BX][SI] ; 符号ADR 代表地址表达式 [BX][SI] NUM EQU VAL ; 为变量VAL 另定义一个别名NUM LOD EQU MOV ; 用LOD代表指令助记符 MOV
-
注意:在同一个程序中不能用EQU对同一个符号名重名定义,如:
NUM EQU 10 ; ··· ··· NUM EQU 20 ; 错误,不同在同一程序中对NUM 重复定义
- 等号伪指令
- 格式: 符号 = 表达式
- 功能: 与EQU伪指令相同,但是等号伪指令可以多次重复定义同一符号(覆盖旧值)。
- 等值伪指令 EQU
- 类型定义语句 LABEL
- 格式: 名字 LABEL 类型
- 功能: 与THIS运算符类似,定义变量或标号的类型为指定的类型。 如果指定的事变量,改变量的段地址和偏移地址与下一个定义的存储单元相同;如果指定的事标号,则标号表示下一条指令的地址。
- 定位伪指令 ORG
- 格式: ORG 常数表达式
- 功能: 指定下一个可用内存单元的偏移地址为常数表达式。 可用于数据段或代码段。
-
举例:
DATA SEGMENT ORG 0100H BUF DB 20 DUP('A') DATA ENDS // 由于ORG伪指令,因此变量BUF的偏移地址为0100H。 如果没有ORG,则数据段的数据从偏移地址0000H开始一次存放
- 地址计数器 $
- 汇编程序在对源程序汇编时,为每个逻辑段设置一个地址计数器,用来记录正在汇编的数据或指令的目标代码在当前段的偏移量。
- 在程序中,地址计数器的值用 “$”表示。
- 即地址计数器“$”表示下一个可用单元的偏移地址。
宏指令
宏与子程序的区别这里就不在赘述
- 不带参数的宏定义语句
-
格式:
宏名 MACRO ··· ENDM
-
- 带参数的宏定义语句
-
格式:
宏名 MACRO 参数1[,参数2,...] ··· ENDM
-
- 取消宏名伪指令 PURGE
-
格式:
PURGE 宏名1 [,宏名2,...]
-
- 宏标号定义语句 LOCAL
- 宏定义中如果包含语句标号,且在同一源程序中被多次调用,则在宏展开时,就要产生多个相同的语句标号。
- 而语句标号代表了改语句所在的内存地址,是不能重复的,这样就会使得程序出错。
-
为了避免这类错误,可以在宏定义中使用LOCAL伪指令,格式如下:
LOCAL 标号1 [,标号2,...]
- LOCAL 伪指令仅在宏定义中使用,且必须是宏体中的第一条语句。
- 实际上LOCAL伪指令是将标号变为形式参数,在宏展开时用实际参数“??0000,??0001,??0002…”等进行替换,这样就避免了标号的重复定义
-
举例:
SHIFTDW MACRO X, Y, n LOCAL L1 ;说明标号L1 MOV CX, n L1 : SAL X, 1 ;前面声明了,在同一段程序中调用多次这个宏,L1标号都不会报错了。 RCL Y, 1 DEC CX JNZ LE ENDM
DOS 系统功能调用
- 什么是BIOS?
- 基本输入输出系统的英文缩写。
- 驻留在我们计算机ROM中的一段软件
- 计算机出厂的时候,整个设备唯一带的一个软件。
- 与芯片完全成为一体的,因此也成为固件。
- 早期存储系统BIOS的芯片完全是用光技术刻写在硅片上的一个芯片,即里面的BIOS系统是完全通过硬件写在里面的,不可以改动的,所以叫固件。
- 现在都是存储在可以修改的存储器中了。
- 即BIOS就是驻留在ROM中的基本输入、输出系统
- BIOS 的作用:
- 加电自检,装入引导,主要I/O设备处理程序即接口控制
- 一开机,系统出现黑乎乎的字符界面,不停地出现,这个就是上电自检
- 操作系统引导:引导操作系统从硬盘引导到内存中
- 对输入输出接口进行控制。
- 加电自检,装入引导,主要I/O设备处理程序即接口控制
- DOS 是什么?
- 磁盘操作系统
- windows操作系统之前的操作系统,即今天操作系统的前身。
- 在Windows开发出来之后,DOS系统被嵌入到Windows中
- 因此在Windows开启后在程序-附件中可以看到命令提示符(cmd.exe,就是Windows的终端),这个就是DOS系统。
- DOS功能、BIOS功能调用是调用系统的内核子程序
- DOS功能、BIOS功能均通过中断方式调用
- DOS软中断
- DOS中断包括:设备管理、目录管理、文件管理、其他
- 这些所有的功能都是一个一个的软件。
- 这些软件的调用都是通过中断来调用的
- 用中断类型码来区分这些一个个软件
- DOS中软件功能包非常多,这里只介绍一个类型码(功能包)21H,也叫DOS软中断
- 下面介绍类型码为21H的这个功能包。
- 这个功能包中包含了很多子程序
- 这些子程序都有同一个类型码21H
- 每个子程序用功能号来区分
- 这个软中断中包含了几十个子程序,这里只介绍5个。
- 这5个子程序每个子程序都有一个功能号。
- DOS 功能调用的基本步骤
- 将调用参数装入指定的寄存器
- 将功能号装入AH
- 按中断类型号调用DOS中断
- 检查返回参数是否正确。
- 注意:由于DOS系统以及他的子程序都没有开源,所以我们只能通过入口参数、出口参数来调用。
-
调用格式:
MOV AH, 功能号 <置响应参数> INT 21H
常用的DOS 系统功能调用
- DOS的2号功能调用 - 显示单个字符
- 功能: 在屏幕的光标处显示单个字符
- 入口参数: 要显示字符的ASCII码放在DL中。
- 出口参数: 无
-
举例:
MOV DL, 'AL' ; 将待显示字符的ASCII码放在寄存器DL中 MOV AH, 2 ; 设置功能调用号 INT 21H ; 执行2号功能调用,运行后,在屏幕的当前光标出显示字符‘A’
- DOS的9号功能调用 - 在屏幕上显示字符串
- 功能: 在屏幕上当前光标处输出存储在内存数据段的一串字符串,该字符串以‘$’结束。
- 入口参数: DS : DX指向欲显示字符串的首地址
- 出口参数: 无
-
举例:
DATA SEGMENT STARING DB 'I am a student. $' DATA ENDS ... MOV DX, OFFSET STRING ;入口参数DX指向字符串首地址 MOV AH, 9 ;设置功能调用号 INT 21H ; 执行9号功能调用 执行后,在屏幕的当前光标处显示字符串 “I am a sutdent.”
- DOS的1号功能调用- 接收键盘输入的一个字符并回显
- 功能: 等待键盘输入,直到按下一个键。
- 入口参数:无
- 出口参数: 从键盘输入的字符的ASCII码放在AL中,并在屏幕上显示该字符。
-
举例:
MOV AH, 01H ;设置功能调用号 INT 21H ; 程序暂停运行,等待用户从键盘输入一个字符串后继续向下运行 MOV [SI], AL ; AL 存放着输入字符的ASCII码,同时输入的字符在屏幕上显示
- DOS的7号功能调用- 接收键盘输入的一个字符不回显
- 功能: 等待键盘输入,直到按下一个键。
- 入口参数:无
- 出口参数: 从键盘输入的字符的ASCII码放在AL中,但在屏幕上没有显示该字符,常用于输入密码
-
举例:
MOV AH, 07H ;设置功能调用号 INT 21H ; 程序暂停运行,等待用户从键盘输入一个字符串后继续向下运行 MOV [SI], AL ; AL 存放着输入字符的ASCII码,屏幕上并没有显示
- DOS的10号功能调用- 接收键盘输入字符串
- 功能: 将从键盘输入的以回车结束的一串字符存放到数据段的指定存储区。
- 入口参数:DS:DX 指向接收字符串的存储区的首地址,该地址的第一个字节是由用户设置的可输入字符串的最大字符数(含回车)
- 出口参数:将实际输入的字符数(不含回车)存放到存储区的第二个字节处,实际输入的字符串从该存储区的第三个字节处开始存放。
-
举例:
DATA SEGMENT BUF DB 10 ; 最大输入字符数 DB ? ; 保留一个字节返回实际输入的字符数 DB 10 DUP(?) ; 预留最大字符数的空间 DATA ENDS ... LEA DX, BUF ; 设置入口参数 MOV AH, 0AH ;设置功能调用号 INT 21H ; 程序暂停运行,等待用户从发键盘输入一串字符并按回车建后继续向下运行
- DOS的4CH号功能调用 – 结束用户程序返回DOS
- 功能: 将计算机控制权移交给DOS
- 入口参数: 无
- 出口参数: 无
-
举例:
MOV AH, 4CH INT 21H 该功能用于用户程序的非过程返回。