4.1 汇编语言程序格式 4.2 MASM中的表达式 4.3 伪指令语句 4.4 DOS系统功能调用和BIOS中断调用 第4章 宏汇编语言程序设计 4.1 汇编语言程序格式 4.2 MASM中的表达式 4.3 伪指令语句 4.4 DOS系统功能调用和BIOS中断调用
4.1 汇编语言程序格式 4.1.1 程序结构 程序由数条语句构成,每条语句占一行。语句分两类: 4.1 汇编语言程序格式 4.1.1 程序结构 程序由数条语句构成,每条语句占一行。语句分两类: 指令性语句(指令语句):能译成机器代码,完成一定的操作功能的语句。 指示性语句(伪指令语句):为汇编程序在翻译汇编语言源程序时提供有关控制信息,没有相应的机器代码。 指令性(指令)语句与指示性(伪指令)语句区别: 程序经汇编、连接和装入内存后,在执行程序之前 ◢指示性语句的功能已经完成,故又称伪操作。 ◢而指令性语句的功能尚未完成,需控制CPU去执行才能完成。
例 4-1 给出一个完整的汇编语言源程序 完成两个字节数据相加。 DATA SEGMENT ; 伪指令语句 BUF1 DB 34H 例 4-1 给出一个完整的汇编语言源程序 完成两个字节数据相加。 DATA SEGMENT ; 伪指令语句 BUF1 DB 34H BUF2 DB 2AH SUM DB ? DATA ENDS CODE SEGMENT ASSUME CS:CODE,DS:DATA START:MOV AX, DATA ; 指令语句 MOV DS, AX MOV AL, BUF1 ADD AL, BUF2 MOV SUM, AL MOV AH, 4CH INT 21H ;返回DOS CODE ENDS END START
分段结构 程序按段编写, 与8086内存分段编址相对应。 每段由伪操作SEGMENT开始、由ENDS结束。 程序可由多个段构成,至少有一个代码段。 程序最后为END结束语句,后跟一启动地址。 启动地址指示程序开始执行的第一条语句。 程序中设有返回DOS的功能。 使程序执行完后返回DOS系统的命令接受状态。 程序中用到内存操作数时, 应按操作数的寻址方式,给相应的段寄存器赋值
4.1.2 语句格式 指令性语句 格式: 标号: 指令助记符 操作数,操作数 ;注释 * 指令助记符(操作码)不可以省略 伪指令语句 格式: 4.1.2 语句格式 指令性语句 格式: 标号: 指令助记符 操作数,操作数 ;注释 * 指令助记符(操作码)不可以省略 伪指令语句 格式: 名字 伪指令指示符 操作数,操作数 ;注释
1、 名字项(提供符号地址) 根据语句功能的不同, 名字项可用来表示段名、变量名、 标号、过程名等。 名字项用一个符号表示。 对符号的规定: ① 由字符A~Z ,a~z ,0~9及符号@、$、下划线_ 等组成,最长31个字符,超出部分忽略。 ② 不能用数字打头,以免与十六进制数相混。 ③ 不使用汇编程序中的保留字。 (如指令的助记符等) ④ 对定义的符号不区分大小写。但为便于记忆,名字的定义应该做到见名知意。
名字的两种主要形式:标号和变量 标号: 在代码段中定义,后面跟着冒号“:”,标号作为目标操作数经常在转移指令或调用指令中出现,用以表示转向地址。 标号的三种属性: 段属性(SEGMENT):标号所在段的段基值。 偏移地址属性(OFFSET):标号所在地址与其所在段首址之间的偏移地址字节数。 类型属性(TYPE):反应转移过程中标号可转移的距离,类型NEAR为近标号,只能实现段内转移;类型FAR为远标号,可实现段间转移。
变量: 变量在数据段、附加段或堆栈段中定义,后面不跟冒号。变量通常指存放在存储单元中的值,在程序运行中是可以修改的。 变量的三种属性: 段属性(SEGMENT):变量所在段的段基值。 偏移地址属性(OFFSET):变量所在地址与其所在段首址之间的偏移地址字节数。 类型属性(TYPE):指变量中每个元素所包含的字节数,类型有:字节变量(BYTE)、字变量(WORD)及双字变量(DWORD)等。 * 在同一个程序中,同样的标号或变量的定义只允许出现一次,否则汇编程序会指示出错。
2、 操作码项(不可省略) 操作码可以是指令、伪指令中的助记符,用以指明操 作的性质或功能。 对于指令,汇编程序将其翻译成机器语言指令。 MOV AX, 100 → B8 00 01 对于伪指令,汇编程序据其要求的功能进行处理。 data SEGMENT → data与一段基值对应 string DB ‘Tsinghua’ → string与一内存地址对应
3、操作数项 操作数给出参与操作的数或数所在的地方。 操作数多于一个时,用逗号分开。 操作数可以是常数、寄存器、存储器操作数、变量、 标号、过程名或表达式(下一节介绍)等。
常数 例 : data1 DB 12, 34, 56 ;十进制 给出具体的数据。可以是数字常量或字符常量。 数字默认十进制,也可加D表示十进制数。如 1234D, 1234 数字后加B表示二进制数。 如 1010B 数字后加H表示十六进制数。 如 1234H 字符常量,用单引号表示。 如 ‘1234’ 汇编时,用字符对应的ASCII表示。如 31H, 32H, 33H, 34H 例 : data1 DB 12, 34, 56 ;十进制 data2 DB 12H, 34H, 56H ;十六进制 MOV AL, ‘G’ ;字符 string DB ‘1234’ ;字符串
例: mov AL, 0AH A、B、C、D、E、F开头的十六进制数前面加0, 与H结尾的标识符区别。 如 寄存器名AH、BH、CH、 DH 变量名 abcdH 等 例: mov AL, 0AH mov AL, AH mov BX, 0abcdH
4、注释项 例: 、、、 由分号引出,用来说明语句或程序的功能。 汇编程序对分号后的内容不做处理。 作用: ①注释程序,增强程序可读性。 作用: ①注释程序,增强程序可读性。 ②可放在语句最前,暂时注释某语句,调试程序用。 例: 、、、 ; MOV AH, 2 ;显示提示信息 ; MOV DL, ’A’ ; INT 21H 、、、
4.2 MASM中的表达式 表达式 例 MOV AX, 12 - 4 ;MOV AX, 8 ;数字表达式 表达式由运算对象(常量、标号、变量)及运算符组成。 汇编程序对表达式进行运算后,得到一个确定的数值,再把这个数值汇编到指令中。 运算结果可以是一个常数字,也可以是一个存储器的地址。故表达式分数字表达式、地址表达式。 表达式是指令或伪操作的操作数; 表达式中的操作符由汇编程序完成 例 MOV AX, 12 - 4 ;MOV AX, 8 ;数字表达式 LEA BX, [ string + 4 ] ;地址表达式
表达式中的操作符(参考P128 表4-1) 有以下几类: 1). 算术运算符 ( +、-、*、/、MOD ) 2). 逻辑运算符 ( AND、OR、NOT、XOR ) 3). 关系运算符 ( EQ、NE、GT、GE、LT、LE ) 真:FFFFH; 假: 0 4). 数值返回操作符 ( SEG、OFFSET、TYPE、LENGTH、SIZE ) 5). 修改属性操作 ( PTR ) 6). 地址记数器( $ )
例: 1). 算术运算符 ( +、-、*、/、MOD(取余数) ) MOV AX, 6 * 8 ← MOV AX, 48 2). 逻辑运算符 (AND、OR、NOT、XOR) MOV AX, 80h OR 70h ← MOV AX,0F0h 3). 关系运算符 ( EQ、NE、GT、GE、LT、LE ) 真:FFFFH; 假: 0 MOV AX, 1 GE 2 ← MOV AX, 0 * 表达式只是一个成份,不能单独构成语句。
4). 数值返回操作符 SEG 取符号地址(变量或标号)的段地址 例 MOV AX , SEG yy OFFSET 取符号地址(变量或标号)的偏移地址 例 MOV BX , OFFSET yy bb SEGMENT yy DB 6 dup (?) bb ENDS cc SEGMENT ASSUME CS:cc, DS:aa, ES:bb start : CLD MOV AX , SEG yy MOV ES , AX MOV DI , OFFSET yy MOV CX , 6 ……. cc ENDS END start
MOV AX, bb LEA DI, yy aa SEGMENT xx DB 'Hello!’ aa ENDS bb SEGMENT yy DB 6 dup (?) bb ENDS cc SEGMENT ASSUME CS:cc, DS:aa, ES:bb start : CLD MOV AX , aa MOV DS , AX LEA SI , xx MOV AX , SEG yy MOV ES , AX MOV DI , OFFSET yy MOV CX , 6 REP MOVSB MOV AH , 4CH INT 21H cc ENDS END start D:\>DEBUG hello1.exe -U ;查看程序代码 129F:0000 FC CLD 129F:0001 B89D12 MOV AX , 129D 129F:0004 8ED8 MOV DS , AX 129F:0006 8D360000 LEA SI , [ 0000 ] 129F:000A B89E12 MOV AX , 129E 129F:000D 8EC0 MOV ES , AX 129F:000F BF0000 MOV DI , 0000 129F:0012 B90600 MOV CX, 0006 129F:0015 F3 REP 129F:0016 A4 MOVSB 129F:0017 B44C MOV AH, 4C 129F:0019 CD21 INT 21 、、、、、、 MOV AX, bb LEA DI, yy
注意:SEG、OFFSET只能对符号地址操作 MOV AX, SEG [BX] MOV BX, OFFSET [SI]
TYPE 取符号地址(变量或标号) 的类型属性 例 A1 DB 20H,30H A2 DW 0438H MOV AL, TYPE A1 MOV AH, TYPE A2 汇编时形成指令 MOV AL, 1 MOV AH, 2 类型 返回值 变量 DB DW DD DQ 1 2 4 8 标号 NEAR FAR -1(FFH) -2(FEH)
LENGTH 取变量的长度属性 例 A1 DW 100 DUP (?) A2 DW 1,2,3 MOV CX, LENGTH A1 MOV AH, LENGTH A2 汇编时形成指令 MOV CX, 100;使用DUP时,返回此变量包 含的单元数为100个字。 MOV AH, 1;对其他情况则返回1。
SIZE 取变量的总字节数 SIZE=LENGTH*TYPE 例 A1 DW 100 DUP (?) A2 DW 1,2,3 MOV CX, SIZE A1 MOV AH, SIZE A2 汇编时形成指令 MOV CX, 200 MOV AH, 2
5). PTR 修改属性操作符 格式:类型/距离 PTR 变量或标号 功能:将PTR左边的类型属性赋给右边的变量或标号。 常用类型:BYTE、WORD、DWORD、NEAR、FAR 例: MOV BYTE PTR [BX],10; [BX] 10H MOV WORD PTR [BX],10; [BX],[BX+1] 0010H
4.3 伪指令语句 4.3.1 定义符号的伪指令 1、赋值伪指令EQU 作用: 用符号名等值指定的表达式 其中, 表达式可以是任何有效的操作数 汇编时用语句中的表达式代替程序中符号所在的地方。 应用: 1. 定义符号常量,方便修改程序。 2. 某表达式多次出现时,用等值伪操作可以方便编程。 例:COUNT EQU 5
举例 CONSTANT EQU 256 ;将数256赋以符号名CONSTANT DATA EQU HEIGHT+12 ;HEIGHT为一标号,地址表达式赋以符号名DATA ALPHA EQU 7 BETA EQU ALPHA-2 ;这是一组赋值伪操作,把7-2=5赋以符号名BETA ADDR EQU VAR + BETA ;将VAR+5赋以符号名ADDR B EQU [BP+8] ;变址引用赋以符号名B P8 EQU DS:[BP+8] ;加段前缀的变址引用赋以符号名P8
2、等号伪指令“=” 3、解除定义伪指令PURGE “=”与EQU 相类似,也可作为赋值操作使用。它们之间的区别: 格式:PURGE <符号1,符号2,…, 符号N> 功能:解除指定符号的定义。解除符号定义后,可用EQU重新进行定义。 例: Y1 EQU 7 ; 定义 PURGE Y1 ;解除 Y1 EQU 36 ;重新定义
4.3.2 定义数据的伪指令 用来定义程序中所用的内存操作数,并用变量与存储单元建立联系。 其中变量名:指示内存操作数的地址(符号地址) 4.3.2 定义数据的伪指令 格式 [变量名] 类型助记符 操作数 [ ,操作数 , ... ][;注释] 用来定义程序中所用的内存操作数,并用变量与存储单元建立联系。 其中变量名:指示内存操作数的地址(符号地址) 类型助记符:指示内存操作数的类型(字节、字、双字等) 常用的类型助记符有: DB 指示其后的操作数为字节类型 DW 指示其后的操作数为字类型 DD 指示其后的操作数为双字类型 汇编程序将定义的操作数,按其类型分配内存单元数。 *其它类型: DQ(4个字) DT(10个字节) 操作数:指示内存操作数的内容顺序存入变量名指向的内存单元中。
定义数据伪指令应用: 可以把其后跟着的数据存入指定的存储单元,形成初 始化数据。 或者只分配存储空间而并不存入确定的数值,形成未 初始化数据空间。 DW和DD伪指令还可存储地址。
例1: 定义赋初值的变量 data SEGMENT xx DB 1, -1, 0fcH yy DW 1, -1, 0fcH 例1: 定义赋初值的变量 data SEGMENT xx DB 1, -1, 0fcH yy DW 1, -1, 0fcH zz DD 1,- 1, 0fcH data ENDS * 操作数可以是常量,或者是表达式。
注意:3个及其以上的字符, 例2: 定义字符串变量( 只能用DB定义 ) data SEGMENT str1 DB ' TsingHua ' str2 DB 'INPUT:' , 0dH , 0aH ,'$' data ENDS str1 str2 54 73 69 6e 67 48 75 61 49 4e 50 55 3a 0d 0a 24 ‘T’ ‘s’ ‘i’ ‘n’ ‘g’ ‘H’ ‘u’ ‘a’ ‘I’ ‘N’ ‘P’ ‘U’ ‘:’ 0dH 0aH ‘$’ 注意:3个及其以上的字符, 只能用DB定义 str1 DW ‘abcd’ str2 DD ‘abcd’
例3:DB ‘AB’ 和 DW ‘AB’的存储有区别 41 42 A B (b) (c) 参看书 P136 图4.2 (b)、(c)
操作数可以是用常量、表达式和 ? 表示。 常量和表达式表示内存操作数的初始值,其值应在其定义的类型范围内,否则汇编出错。 例 aa DB 270;错误 bb DW 80000;错误 用 ? 表示只保留存储空间,不置初始值的内存操作数。 例 cc DB ? 可用 DUP 复制操作符定义相同的操作数,其格式为: 重复次数 DUP( 操作数) 例 ee DB 3 DUP ( 4 ) 等价于 ee DB 4, 4, 4
例4: 在DEBUG下查看变量存放情况。 data SEGMENT xx DB 1, -1, 0FCH yy DW 1,- 1, 0FCH zz DD 1, -1, 0FCH str DB 'TsingHua' buf DB 4, ?,4 DUP (?) DB ‘dataend’ data ENDS code SEGMENT ASSUME CS:code ASSUME DS:data start: MOV AX , data MOV DS , AX LEA BX , xx LEA SI , yy LEA DI , zz LEA BX, str LEA SI ,buf MOV AH , 4CH INT 21H code ENDS END start
4.3.3 定义程序开始和结束的伪指令 1 、 NAME 和 TITLE 伪操作 模块名的作用是指示给连接程序进行连接用。 源程序中可无模块定义,此时源文件名作为模块名。
2、 END伪操作 格式 END 启动地址标号 作用是指示源程序到此结束。 汇编程序对 END 之后的语句不进行处理。 多个模块连接时,则只有主模块要使用标号,程序 从主模块第一个标号处开始执行。 源程序中必须有 END 结束语句。 汇编程序对无END语句的源程序会给出错误信息。
启动地址可是一个标号或过程名, 指示程序的入口。 程序装入内存后,系统跳转到入口处, 开始执行程序。 aa SEGMENT ;数据段1 str1 DB 'Hello!’ aa ENDS bb SEGMENT ;数据段2 str2 DB 6 dup (?) bb ENDS cc SEGMENT ;代码段 ASSUME CS:cc ASSUME DS:aa, ES:bb start: CLD MOV AX , aa MOV DS , AX LEA SI , str1 MOV AX , SEG str2 MOV ES , AX MOV DI , OFFSET str2 MOV CX , 6 REP MOVSB MOV AH , 4CH INT 21H cc ENDS END start D:\masm>DEBUG hello.exe -U :查看代码段 129F:0000 FC CLD 129F:0001 B89D12 MOV AX , 129D 129F:0004 8ED8 MOV DS , AX 129F:0006 8D360000 LEA SI , [0000] 129F:000A B89E12 MOV AX , 129E 129F:000D 8EC0 MOV ES , AX 129F:000F BF0000 MOV DI , 0000 129F:0012 B90600 MOV CX , 0006 129F:0015 F3 REPZ 129F:0016 A4 MOVSB 129F:0017 B44C MOV AH , 4C 129F:0019 CD21 INT 21 、、、 -D 129d:0 l10 ;查看数据段1的内容 129D:0000 48 65 6C 6C 6F 21 00 00-00 00 00 00 00 00 00 00 Hello!.......... -D 129e:0 l10 ;查看数据段2的内容 129E:0000 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................ -
4.3.4 指令集选择伪指令(略) 汇编程序默认值为.8086指令系统 4.3.4 指令集选择伪指令(略) 汇编程序默认值为.8086指令系统
4.3.5 地址计数器与对准伪操作 1、地址计数器——$ 4.3.5 地址计数器与对准伪操作 1、地址计数器——$ 汇编过程中使用地址计数器来保存当前正在汇编的 指令的偏移地址,地址计数器的值可用$来表示。 $用在指令中时,表示当前地址,即本条指令的第一 个字节的地址。 例: JNE $
2、ORG伪操作 作用: 用来设置当前地址计数器的值。当用户要求指定某条指令 或数据为某个指定地址时,可用ORG语句来改变,ORG语句 可以放在程序的任何位置。 格式: ORG 表达式 例:VECT SEGMENT ORG 10 VECT1 DW 47A5H ORG 20 VECT2 DW 0C596H VECT ENDS
4.3.6 汇编语言源程序段结构定义 1、完整段定义的程序结构 4.3.6 汇编语言源程序段结构定义 1、完整段定义的程序结构 段定义伪操作 段名 SEGMENT 、、、 段名 ENDS 段定义由伪操作SEGMENT开始、ENDS结束。 其中: SEGMENT 和ENDS 必须成对出现,且语句前必须有段名,段名必须相同。 SEGMENT和ENDS语句之间可以有指令和其他伪操作指令。 程序中可以定义多个段。程序经汇编、连接及装入内存后,段名为 一具体的段值。
例:数据传送源程序 程序经汇编、连接后,装入内存的情况如下: aa SEGMENT ;数据段1 str1 DB 'Hello!’ aa ENDS bb SEGMENT ;数据段2 str2 DB 6 dup (?) bb ENDS cc SEGMENT ;代码段 ASSUME CS:cc,DS:aa, ES:bb start: CLD MOV AX , aa MOV DS , AX LEA SI , str1 MOV AX , bb MOV ES , AX LEA DI , str2 MOV CX , 6 REP MOVSB MOV AH , 4CH INT 21H cc ENDS END start D:\masm>DEBUG hello2.exe -U :查看代码段 12A0:0000 FC CLD 12A0:0001 B89E12 MOV AX , 129E 12A0:0004 8ED8 MOV DS , AX 12A0:0006 8D360000 LEA SI , [0000] 12A0:000A B89F12 MOV AX , 129F 12A0:000D 8EC0 MOV ES , AX 12A0:000F 8D3E0000 LEA DI , [0000] 12A0:0013 B90600 MOV CX , 0006 12A0:0016 F3 REP 12A0:0017 A4 MOVSB 12A0:0018 B44C MOV AH,4C 12A0:001A CD21 INT 21 、、、 -D 129E:0 L10 ;查看数据段1的内容 129E:0000 48 65 6C 6C 6F 21 00 00-00 00 00 00 00 00 00 00 Hello!.......... -D 129F:0 L10 ;查看数据段2的内容 129F:0000 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................ - 例:数据传送源程序 程序经汇编、连接后,装入内存的情况如下:
ASSUME伪操作 格式: ASSUME 段寄存器:段名 [, 段寄存器:段名, … ] 其中 段寄存器为CS、DS、ES、SS中的一个 段名为用伪操作SEGMENT定义过的段名 例 ASSUME CS: cc , DS:aa 作用:用来明确段和段寄存器之间的关系。 说明:ASSUME语句只起指示作用,没有赋值作用,并不能把段地址装入段寄存器中,因此在程序中需用指令(如MOV)给相应的段寄存器赋值。但是,代码段CS除外,CS的装入是在程序初始化时完成的。 ASSUME NOTHING则可取消前面由ASSUME所指定的段寄存器。
例 用程序实现 1234H + 5678H data SEGMENT value DW 1234H, 5678H result DW ? data ENDS code SEGMENT ASSUME CS:code, DS:data start: MOV AX, data ;给DS赋值 MOV DS, AX MOV AX, value ;取数 ADD AX, value+2 ;两数相加 MOV result, AX ;保存结果 MOV AH, 4CH ;返回DOS INT 21H code ENDS END start
注释掉ASSUME语句,其汇编结果: value DW 1234H, 5678H result DW ? data ENDS data SEGMENT value DW 1234H, 5678H result DW ? data ENDS code SEGMENT ; ASSUME CS:code, DS:data start: MOV AX, data MOV DS, AX MOV AX, value ADD AX, value+2 MOV result, AX MOV AH, 4CH INT 21H code ENDS END start D:\MASM>MASM assume; Microsoft (R) Macro Assembler Version 5.10 Copyright (C) Microsoft Corp 1981, 1988. All rights reserved. assume.ASM(8): Missing or unreachable CS assume.ASM(10): Cannot address with segment register assume.ASM(11): Cannot address with segment register assume.ASM(12): Cannot address with segment register 49872 + 421341 Bytes symbol space free 0 Warning Errors 4 Severe Errors D:\MASM>
程序中有ASSUME语句,经汇编,连接和装入内存后的情况: data SEGMENT value DW 1234H, 5678H result DW ? data ENDS code SEGMENT ASSUME CS:code, DS:data start: MOV AX, data ;给DS赋值 MOV DS, AX MOV AX, value ;取数 ADD AX, value+2 ;两数相加 MOV result, AX ;保存结果 MOV AH, 4CH ;返回DOS INT 21H code ENDS D:\MASM>DEBUG assume.exe -R ;查看程序执行前各寄存器 AX=0000 BX=0000 CX=0023 DX=0000 SP=0000 BP=0000 SI=0000 DI=0000 DS=1295 ES=1295 SS=12A5 CS=12A6 IP=0000 NV UP EI PL NZ NA PO NC 12A6:0000 B8A512 MOV AX,12A5 -U ;查看在内存的程序 12A6:0000 B8A512 MOV AX, 12A5 12A6:0003 8ED8 MOV DS, AX 12A6:0005 A10000 MOV AX, [ 0000 ] 12A6:0008 03060200 ADD AX, [ 0002 ] 12A6:000C A30400 MOV [ 0004 ], AX 12A6:000F B44C MOV AH, 4C 12A6:0011 CD21 INT 21 、、、、、 - 注意: 程序装入内存后,执行程序前, 当前的DS值并非程序定义的data段值, 1295 不等于 12A5
思考: 如果上例中没有 MOV DS , AX 程序执行的结果会怎样? D:\MASM>DEBUG assume.exe -R ;查看程序执行前各寄存器 AX=0000 BX=0000 CX=0023 DX=0000 SP=0000 BP=0000 SI=0000 DI=0000 DS=1295 ES=1295 SS=12A5 CS=12A6 IP=0000 NV UP EI PL NZ NA PO NC 12A6:0000 B8A512 MOV AX,12A5 -U ;查看在内存的程序 12A6:0000 B8A512 MOV AX, 12A5 12A6:0003 A10000 MOV AX, [ 0000 ] 12A6:0006 03060200 ADD AX, [ 0002 ] 12A6:000A A30400 MOV [ 0004 ], AX 12A6:000D B44C MOV AH, 4C 12A6:000F CD21 INT 21 、、、、、 data SEGMENT value DW 1234H, 5678H result DW ? data ENDS code SEGMENT ASSUME CS:code, DS:data start: MOV AX, data ;给DS赋值 ; MOV DS, AX MOV AX, value ;取数 ADD AX, value+2 ;两数相加 MOV result, AX ;保存结果 MOV AH, 4CH ;返回DOS INT 21H code ENDS
2、伪指令SEGMENT的完整格式 格式:段名 SEGMENT [定位类型] [组合类型] [‘类别’] 、、、 段名 ENDS 定位类型: PARA(缺省值):指定该段起始地址定位在节(16字节)的整数边界(末 4位地址为0)。 BYTE:指定该段起始地址定位在任何字节地址。 WORD:指定该段起始地址定位在字边界(末位地址为0 )。 DWORD:指定该段起始地址定位在双字边界(末2位地址为0)。 PAGE:指定该段起始地址定位在页(256字节)边界(末8位地址为0)。
组合类型: 类别名(‘CLASS ’ ): 用于说明程序连接时段的合并方法。 PRIVATE(缺省值): 连接时该段不与其它模块中的同名段合并。 PUBLIC: 连接时把不同模块中的同名段顺序连接合并为一个段。 COMMON: 连接时把不同模块中的同名段重叠而形成一个段。 AT 表达式: 定位该段的起始地址在表达式所指定的节边界上。 MEMORY: (略) STACK: 指定该段为堆栈段,此参数在堆栈段中不可省略。多个模块只需 设置一个堆栈段,各个模块中的堆栈段采用覆盖方式组合,容量为各个模块 中所设置的最大堆栈容量。 类别名(‘CLASS ’ ): 在引号中给出连接时组成段组的类型名。汇编连接时类别 名相同的逻辑段组成一个段组(物理位置靠在一起)。
4.3.7 子程序结构形式与操作 1、子程序(过程)定义 4.3.7 子程序结构形式与操作 1、子程序(过程)定义 子程序(过程)定义伪操作 格式 过程名 PROC 类型 、、、 过程名 ENDP 子程序(过程)定义由伪操作PROC开始、ENDP结束。 其中: PROC 和ENDP 必须成对出现, 且语句前必须有过程名,过程名必须相同。
2、说明 PROC和ENDP语句之间为子程的指令序列。 程序中可以定义多个过程。 程序经汇编、连接及装入内存后, 过程名为一具体的内存地址,指示子程入口。 过程名常用作CALL调用指令的操作数, 子程的最后安排 RET返回指令,使执行完子程后能返回调用处。 过程有两种类型:NEAR和FAR ,无类型项时,默认为 NEAR类型。 当过程与调用指令不在同一段时,应将过程定 义为 FAR 类型。过程类型决定子程中RET的返回类型。 汇编程序用PROC伪操作的类型来确定CALL和RET指令 的属性。
例1 display PROC 、、 、、 C3H RET ;NEAR 属性的过程对应段内返回 display ENDP 例2 display PROC FAR 、、 CBH RET ;FAR属性的过程对应段间返回
DOS把主程序看作一个FAR类型子程加以调用,故应把主程序定义为FAR类型。 例3:调用程序和子程序在同一代码段中。 MAIN PROC FAR : CALL SUBR1 RET MAIN ENDP SUBR1 PROC NEAR SUBR1 ENDP DOS把主程序看作一个FAR类型子程加以调用,故应把主程序定义为FAR类型。
例4:调用程序和子程序不在同一代码段中。 SEGX SEGMENT : SUBT PROC FAR RET SUBT ENDP : CALL SUBT SEGX ENDS SEGY SEGMENT SEGY ENDS
4.3.8LABEL 伪指令 LABEL伪指令给已定义的变量或标号取另一个名字(别 名),并可重新定义它的类型属性,使同一变量或标号在不同 地方被引用时,可采用不同的名字,具有不同的类型属性,但 它们具有相同的地址属性。 格式:名称 LABEL 类型属性 例: DATB LABEL BYTE; DATB为DATW的别名 DATW DW 3031H,3233H; MOV AL,DATB[0]; (AL)=31H MOV BX,DATW[1]; (BX)=3330H 例: STACK SEGMENT STACK ‘STACK’ DW 100 DUP(?); 堆栈中经常使用LABEL。 TOP LABEL WORD; TOP为栈底名,类型为字。 STACK ENDS;如用指令MOV SP , OFFSET TOP
4.4 DOS系统功能调用和BIOS中断调用 4.4.1 DOS功能调用 DOS是PC/XT的操作系统,负责管理系统的所有资源,DOS中包括了许多可供用户调用的子程序。 用户调用DOS功能的途径: * 在DOS环境下,键入DOS命令; * 通过用户程序调用DOS和BIOS中的服务程序。
1、DOS功能调用概述 n= 0 ~ 4 ;8088/8086CPU占用 n=5~1FH; BIOS功能调用 利用软中断指令 INT n 调用相应的中断服务程序。 n= 0 ~ 4 ;8088/8086CPU占用 n=5~1FH; BIOS功能调用 n=20~3FH; DOS功能调用 其中INT 21是一个具有100多个子功能的中断服务程序,分别用于设备管理,目录管理,文件管理及其它功能。 2、 DOS功能调用方法 1) DOS软中断( INT 20H~ INT 27H )指令 参见教材 P153 表4-4
表4-4 DOS软中断 软中断 功能 入口参数 出口参数 INT 20H 程序正常退出 INT 21H 系统功能调用 AH=功能号 INT 21H 系统功能调用 AH=功能号 功能调用相应的入口参数 功能调用相应的出口参数 INT 22H 结束退出 INT 23H CTRL+BREAK退出 INT 24H 出错退出 INT 25H 读盘 CX=读出扇区数 DX=起始逻辑扇区 DS:BX=缓冲区地址 AL=盘号 CF=1出错 INT 26H 写盘 CX=写扇区数 INT 27H 驻留退出 INT 28H~INT 2FH DOS专用
2)DOS系统功能调用(INT 21H) 有上百个子功能中断服务程序,功能大致分设备管理、文 件管理等4个方面,每个子功能有一个功能号。 调用方法 设置入口参数 执行中断指令INT 21H 分析、应用出口参数 在AH设置功能号m
常用DOS系统功能调用 (21H类型中断调用) 3.单字符输入 ( 01H、07H、08H功能 ) 4.字符串输入 ( 0AH功能 ) 5.检测键盘状态 ( 0BH功能 ) 6. 返回操作系统( 4CH功能 ) 7.保存中断向量 ( 35H功能 ) (后面章节介绍) 8.设置中断向量 ( 25H功能 )
1. 显示单字符 ( 02H功能 ) 入口参数 DL = 要显示字符的ASCII 码 功能号 AH=02H 类型号 INT 21H 出口参数 无 实现功能 显示指定字符,光标随动。 例 显示字符A MOV DL, ‘A’ MOV AH, 02H INT 21H
~ 例 使光标回到下一行的行首。 MOV DL , 0DH ;显示回车符 MOV AH , 02H INT 21H 例 使光标回到下一行的行首。 MOV DL , 0DH ;显示回车符 MOV AH , 02H INT 21H MOV DL , 0AH ;显示换行符 ~ H e l l o W o r l d
2. 显示字符串 ( 09H功能 ) 入口参数 ①定义要显示的字符串, 字符串尾应为’$’,作为结束显示的标志。 ②DS : DX = 字符串的首地址 功能号 AH = 09 H 类型号 21H 出口参数 无 实现功能 显示字符串,遇 ’$’ 停止显示,光标随动。
例 编程显示字符串‘Wuhan University’ data SEGMENT ;定义显示的子符串 stri DB ‘Wuhan University’, ‘$’ data ENDS code SEGMENT ASSUME CS:code, DS:data start: MOV AX, data ;置缓冲区地址于DS:DX MOV DS, AX LEA DX, stri MOV AH, 09H ;调显示功能 INT 21H MOV AH, 4CH ;返回DOS code ENDS END start
学习输入功能前介绍有关键盘 的一些知识 ① 键盘通过键盘接口电路与计算机连接。 ② 键盘上的键有三种类型: 字符键 A~Z, 0~9等 功能键 F1~F12, BackSpace,Page Up等 组合控制键 Shift, Ctrl, Alt等 ③ 键盘上的每个键都有一个扫描码。 ◢ ◢ 据扫描码可确定操作的是哪个键、是按下键还是释放键; ◢ ◢ 扫描码用一个字节表示。 低7位是扫描码的数字编码, 与键盘上的键一一对应; 最高位D7位表示键的操作状态: 当按下键时, D7=0 ; 当释放键时, D7=1
④ 键盘接口对按下键和释放键均向计算机发出中断申请, 如果中断响应条件满足, CPU转去执行键盘中断子程。 键盘中断子程类型号为09H。 ⑤ 键盘中断子程功能(09H类型中断子程) ◢ 从键盘接口读取操作键的扫描码; ◢ 将扫描码转换成字符码; 大部分键的字符码为ASCII码, 无ASCII码键(如组合键Shift、Ctrl等)的字符码为0。 ◢ 将键的扫描码、字符码存放在键盘缓冲区, 供其它有关键盘的中断子程应用。
3. 单字符输入 ( 01H、07H、08H功能) ① 入口参数 无 功能号 AH = 01H 类型号 21H 出口参数 AL = 键入字符的ASCII码 实现功能 等待从键盘输入一个字符, 并在屏幕上回显,光标移动, 检测Ctrl-Break键
例 利用键盘输入的字符产生分支: MOV AH, 1 ;等待从键盘输入 INT 21H CMP AL, ‘Y’ ;是’Y’? JZ yes CMP AL, ‘y’ ;是’y’? no: . . . . . . JMP exit yes: . . . exit: . . .
并将其个数用二进制形式显示出来。( scanskey.asm) 例 在中断向量表中查找从键盘输入的字符, 并将其个数用二进制形式显示出来。( scanskey.asm) code SEGMENT ;代码段开始 ASSUME CS:code begin: MOV AX, 0000H ;设置查找首地址 MOV DS, AX MOV SI,0000H MOV CX,0400H ;设置查找长度 MOV BX,0 ;计算值初值 MOV AH,01H ;从键盘输入 INT 21H ;输入关键字 MOV DL,AL ;保存在DL中 next: LODSB ;从串取 CMP AL, DL ;与关键字比较 JNZ point INC BX ;计算值加1 point: LOOP next ;查找结束? CALL display ;调用显示子程 MOV AH,4CH ;调用4CH功能 INT 21H ;返回DOS ;用二进制显示BX内容子程 display PROC MOV CX, 16 ;显示位数 rotate: ROL BX, 1 ;循环左移 MOV DL, BL AND DL, 01H ADD DL, 30H MOV AH, 2H INT 21H LOOP rotate RET ;子程返回 display ENDP code ENDS ;代码段结束 END begin
② 入口参数 无 功能号 AH = 07H 类型号 21H 出口参数 AL = 键入字符的ASCII码 实现功能 等待从键盘输入一个字符, 不在屏幕上回显, 不检测Ctrl-Break键 *功能号 AH = 08H (无回显,但检测Ctrl-Break键)
例 用不带回显功能输入密码时: input: MOV AH, 07H ;等待从键盘输入 INT 21H MOV [DI], AL ;存入缓冲区 CMP AL, 0DH ;是回车符? JNZ input ;不是,继续输入 check: 、、、 、、、
4. 字符串输入 ( 0AH 功能 ) 入口参数 DS : DX = 缓冲区的首地址 ( DS :DX ) = 用户定义的最大输入的字符数 功能号 AH = 0AH 类型号 21H 出口参数 ( DS : DX+1 ) = 实际键入的字符(不含回车符) 从( DS : DX+2 )开始顺序存放键入的字符串, 回车符0DH为串尾最后一字符。 实现功能 等待从键盘输入字符串,并存入设定的缓冲区内, 同时回显字符串,光标随着移动, 回车符使光标回到行首。 注意事项:应按要求先定义缓冲区,再调用。
例 应用0AH功能输入字符串。 data SEGMENT ;定义缓冲区 max DB 11 ;定义限制最多输入个数 lenth DB ? ;用于存放实际输入个数 stri DB 11 DUP(?) ;用于存放输入的字符串 data ENDS code SEGMENT ASSUME CS:code, DS:data start: MOV AX , data ;置缓冲区地址于DS:DX MOV DS , AX LEA DX , max MOV AH , 0AH INT 21H ;调0A输入功能 MOV CH , 0 MOV CL , lenth ;取字符串长度放CX中 LEA BX , stri ;取字符串首址于BX中 MOV AL , [BX] ;应用输入字符 、、、、、 code ENDS
data SEGMENT ;定义缓冲区 max DB 11 ;定义限制最多输入个数 lenth DB ? ;用于存放实际输入个数 stri DB 11 DUP(?) ;用于存放输入的字符串 data ENDS 从键盘输入‘ABCD’, 回车,内存的存放结果: 0B 04 41 42 43 44 0D 00 max 7 8 0a 0b 0f 1 2 3 4 5 6 9 0c 0d 0e lenth stri 地址 内容 DS:DX
0AH功能执行过程: ① 若(DS:DX)字节单元的值为0,则不等待从键盘输入,结束调用。 ② 若(DS:DX) 字节单元的内容大于0,则等待从键盘输入, 并把输入键的ASCII码顺序存放在DS : DX+2开始的单元, 按回车键表示结束输入。 当按下键的个数超过( DS : DX )中值,发出警告声’ 嘟嘟 ’, 不再接收输入的数据, 直到输入回车键。 ③ 将实际输入的字符个数(不包括回车键)填入(DS:DX+1), 结束调用。 DS:DX 00
0AH功能注意事项: ◢ 输入的字符均带回显,且光标随字符移动。 当输入回车符结束时,也回显回车符。 表现为功能调用结束后,光标回到了行首。 ◢ 回车符0DH作为一个输入的字符存放在字符串尾, 但计数输入个数时,不包括回车键。 实际最多能输入的字符数 = 限制的最多数-1 (回车符占一个) ◢ 执行完0AH功能后,DS和DX的值不变, DS:DX仍指向缓冲区的首地址。 ◢ 整个缓冲区的大小应为:限制的最多数 +2 max DB 11, ? , 11 dup (?)
例 利用DEBUG查看0A功能输入结果。(与上例功能相同) data SEGMENT max DB 11, ?, 11 DUP(?) data ENDS code SEGMENT ASSUME CS:code, DS:data start: MOV AX, data ;置缓冲区地址于DS:DX MOV DS, AX LEA DX, max MOV AH, 0AH ;调输入功能 INT 21H MOV CH, 0 MOV CL, max+1 ;取输入长度到CX中 MOV AH, 4CH ;返回DOS INT 21H code ENDS END start
D:\MASM>DEBUG TEST0a.exe ;装入执行文件 -U ;反汇编程序 129E:0000 B89D12 MOV AX , 129D 129E:0003 8ED8 MOV DS , AX 129E:0005 8D160000 LEA DX , [0000] 129E:0009 B40A MOV AH , 0A 129E:000B CD21 INT 21 129E:000D B500 MOV CH , 00 129E:000F 8A0E0100 MOV CL , [0001] 129E:0013 B44C MOV AH , 4C 129E:0015 CD21 INT 21 、、、 -D 129D:0 L10 ;查看程序执行前缓冲区内容 129D:0000 0B 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................ -G 000D ;执行程序 Hello ;从键盘输入字符串 Program terminated normally -D 129D:0 L10 ;查看程序执行后缓冲区内容 129D:0000 0B 05 48 65 6C 6C 6F 0D-00 00 00 00 00 00 00 00 ..Hello......... -
1. 若将程序改为如下形式, 设想程序执行的结果。 思考: 1. 若将程序改为如下形式, 设想程序执行的结果。 data SEGMENT max DB 100H DUP ( ? ) data ENDS code SEGMENT ASSUME CS:code, DS:data start: MOV AX, data ;置缓冲区地址于DS:DX MOV DS, AX LEA DX, max MOV AH, 0AH ;调输入功能 INT 21H MOV AH, 4CH ;返回DOS INT 21H code ENDS END start
结果是:不等待输入,结束0AH功能调用。 00 max 7 8 0a 0b 0f 1 2 3 4 5 6 9 0c 0d 0e 地址 内容 DS:DX 注意:由于DS:DX限制输入最大字符数为0, 所以不等待输入, 结束0AH功能调用。 原因是:
2. 若将程序改为如下形式, 设想程序执行的结果。 data SEGMENT max DW 100H,? ,100H DUP ( ? ) ;定义为字类型 data ENDS code SEGMENT ASSUME CS:code, DS:data start: MOV AX, data ;置缓冲区地址于DS:DX MOV DS, AX LEA DX, max MOV AH, 0AH ;调输入功能 INT 21H MOV AH, 4CH ;返回DOS INT 21H code ENDS END start
结果是:不等待输入,结束0AH功能调用。 00 01 max 7 8 0a 0b 0f 1 2 3 4 5 6 9 0c 0d 0e 地址 内容 DS:DX 注意:0AH功能以字节类型应用DS:DX指向的缓冲区。 并不随缓冲区类型定义的变化而变化。 原因是:
例 编程从键盘输入字符串,给出显示提示’Please input:’ DOS 功能调用应用举例 data SEGMENT stri DB ‘Please Input :’,’$’ max DB 11, ?, 11 DUP(?) data ENDS code SEGMENT ASSUME CS:code, DS:data start: MOV AX, data ;置缓冲区地址于DS:DX MOV DS, AX LEA DX, stri MOV AH, 09H ;显示提示 INT 21H LEA DX, max ;等待从键盘输入 MOV AH, 0AH MOV AH, 4CH code ENDS END start DOS 功能调用应用举例
D:\MASM>DEBUG e0a09.exe -U ;查看程序 129F:0000 B89D12 MOV AX,129D 129F:0003 8ED8 MOV DS,AX 129F:0005 8D160000 LEA DX ,[0000] 129F:0009 B409 MOV AH ,09 129F:000B CD21 INT 21 129F:000D 8D160F00 LEA DX ,[000F] 129F:0011 B40A MOV AH , 0A 129F:0013 CD21 INT 21 129F:0015 B44C MOV AH , 4C 129F:0017 CD21 INT 21 -D 129D:0 L20 ;执行程序前查看 data段内容 129D:0000 50 6C 65 61 73 65 20 49-6E 70 75 74 20 3A 24 0B Please Input :$. 129D:0010 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................ -G ;执行程序 Please Input :Hello! Program terminated normally -D 129 D:0 L20 ;执行程序后查看 data段内容 129D:0000 50 6C 65 61 73 65 20 49-6E 70 75 74 20 3A 24 0B Please Input :$. 129D:0010 06 48 65 6C 6C 6F 21 0D-00 00 00 00 00 00 00 00 .Hello!......... -
data段内存情况: stri P l e a s I n p u t : $ 0B 06 H o ! 0D 00 max
思考:程序中少了一个 ’$’ 符的后果。 data SEGMENT stri DB ‘Please Input :’ max DB 11, ?, 11 DUP(?) data ENDS code SEGMENT ASSUME CS:code, DS:data start: MOV AX, data ;置缓冲区地址于DS:DX MOV DS, AX LEA DX, stri MOV AH, 09H ;显示提示 INT 21H LEA DX, max ;等待从键盘输入 MOV AH, 0AH MOV AH, 4CH code ENDS END start
data SEGMENT stri DB ‘Please Input :’ max DB 11, ?, 11 DUP(?) data ENDS stri P l e a s I n p u t : 0B 00 max B8 A8 12 8E D8 8D 16 B4 09 CD 21 0E start 注意:09H功能只在遇到’$’符才结束显示。
5.检测键盘状态 ( 0BH功能 ) 入口参数 无 功能号 AH= 0BH 类型号 21H 出口参数 AL= FFH , 表示有键按下。 AL= 0 , 表示无键按下。 实现功能 检测键盘状态(通过检测键盘缓冲区实现)
例 利用0B功能实现按任意键退出循环。 、、 continue: 、、 MOV AH, 0BH ;检测键盘缓冲区是否有键按下 INT 21H CMP AL, 0FFH JNZ continue ;无键按下继续循环 MOV AH, 4CH
6. 返回操作系统( 4CH功能 ) 入口参数 无 功能号 AH= 4CH 类型号 21H 实现功能 结束当前正在执行的程序,返回操作系统,屏 幕显示操作提示符。 例 在要返回DOS 处,安排指令: MOV AH, 4CH INT 21H 执行完4CH的功能调用,即返回DOS 。
4.4.2 BIOS中断调用(略) BIOS(Basic Input/Output System)固化在主机的 ROM中,含有I/O设备的基本驱动程序,以中断服务程序的 形势存在,处于DOS调用和硬件环境之间,和DOS功能调用 相比,其优点是效率高,缺点是编程相对复杂。 用户编程原则 ①尽可能使用DOS的系统功能调用,提高程序可移植性。 ②在DOS功能不能实现情况下, 考虑用BIOS功能调用。 ③在DOS和BIOS的中断子程不能解决问题时,使用IN/OUT指令直接控制硬件。
用户程序和操作系统关系示意图
作业: 教材:P198 习题4 2,4,5,6,7