IBM-PC 汇编语言程序设计 教师:袁南儿
目 录 第一章 基础知识简介 第二章 IBM PC 计算机组织 第三章 IBM PC机的指令系统和寻址方式 第四章 汇编语言程序格式 目 录 第一章 基础知识简介 第二章 IBM PC 计算机组织 第三章 IBM PC机的指令系统和寻址方式 第四章 汇编语言程序格式 第五章 循环与分支程序设计 第六章 子程序结构 第七章 高级汇编语言技术 第八章 输入/输出程序设计 第九章 BIOS和DOS中断 第十章 发声系统的程序设计
第四章 汇编语言程序格式 4.1汇编程序功能 汇编语言程序的建立及汇编过程: EXE程序还需经DEBUG的调试 编辑程序 PROGR 第四章 汇编语言程序格式 4.1汇编程序功能 汇编语言程序的建立及汇编过程: 编辑程序 PROGR .ASM文件 汇编程序 连接程序 .OBJ 文件 .EXE 文件 EDIT.exe LINK.exe MASM.exe EXE程序还需经DEBUG的调试
在计算机上运行汇编语言程序的步骤是: 1) 用编辑程序建立源文件; filename.ASM 用MASM程序把ASM文件转换成OBJ文件; 2) 3) 用LINK程序把OBJ文件转换成EXE文件; 4)经DEBUG的调试 5)用DOS命令直接键入文件名就可以执行该程序。
汇编程序 汇编程序作用 ① 检查源代码语句,语法的错误,给出指示 连接程序 源程序:由一系列指令系统所提供的基本语句构成,还有标号、表达式、宏指令组成 。 汇编程序作用 ① 检查源代码语句,语法的错误,给出指示 ② 确定变量、标号的段内地址,计算表达式的值,生成符号表 ③ 展开宏指令 ④ 生成目标程序,但段末定位,称可浮动的目标程序 连接程序 确定段地址,成为装入内存可执行的二进制文件(exe)。 以下先介绍构成源代码程序的伪操作。
伪操作 又称伪指令: 是在汇编期间由汇编程序处理的操作 不是机器指令,不是程序运行时执行的 主要完成数据定义、分配内存、程序框架定义、指示程序结束等功能 相似于高级语言的指示性语句
8086的伪指令 有60多条,常用的有: ① 与汇编程序格式有关(框架) 段定义 段假设 过程定义 程序结束 ② 数据定义 ③ 符号定义 ① 与汇编程序格式有关(框架) 段定义 段假设 过程定义 程序结束 ② 数据定义 ③ 符号定义 ④ 结构定义 ⑤ 常用操作符
伪指令的格式 [名字] [ 伪操作命令] [操作数] [;注释] ①名字后不能有冒号,这是与指令语句的突出区别 [名字] [ 伪操作命令] [操作数] [;注释] ①名字后不能有冒号,这是与指令语句的突出区别 ②名字可以是变量名,过程名、段名、组名、常量名、符号 ③名字是由程序员创造的一些名称,有一系列字母、数字、下划线 4)伪操作命令不会产生或机器码,不是指令运行时执行。是在汇编过程中由汇编程序使用执行。
(一) 完整段定义伪操作(程序格式) … 段定义伪操作的格式如下: segment_ name SEGMENT segment _ name ENDS 段名 SEGMENT : 段名 ENDS SEGMENT ENDS总是成对使用,它将源程序分成几个段 ,段名是由程序员定。
… … 段寄存器:是CS,DS,ES、SS之一; 段名:是由SEGMENT定义 程序格式举例: data_seg1 segment ;define data segment … data_seg1 ends data_seg2 segment ;define extra segment … data_seg2 ends
明确段和段寄存器的关系 在代码段中表明段名和段寄存器的对应关系 ASSUME assignment,……, assignment 段寄存器名:段名,段寄存器名:段名 分配后,还要将段名送入相应的段寄存器: 注意:段名是段地址,是一个数据,不能直接送入段寄存器。
… code_seg segment; define code assume cs:code_seg,ds:data_seg1,es:data_seg2 start: mov ax,data_seg1 ;data segment addr mov ds,ax ;into DS register mov ax,data_seg2 ;extra segment addr mov es,ax ;into ES register … code_seg ends ;end of segment end start
代码段的程序结构(一) : cods segment main proc far assume cs:cods,ds:data1 es:data2 Start: PUSH DS MOV AX,0 PUSH AX MOV AX, DATA1 MOV DS, AX MOV AX, DATA2 MOV ES, AX * RET ; main endp cods ends end start :
代码段的程序结构(二) : cods segment main proc far assume cs:cods,ds:data1 MOV AX, DATA2 : MOV AH,4CH INT 21H main endp cods ends end start cods segment main proc far assume cs:cods,ds:data1 es:data2 start: MOV AX, DATA1 MOV DS, AX
段定义加类型和属性说明 Segname SEGMENT [定位类型] alin-type;段起始地址边界值的规定 [组合类型]combine-type 程序连接时段合并方法 [类别] ‘class’,给出连接时段组的类型名。相同的类别装入时位置靠在一起。 ;一般可不用,在有多个模块时,各个程序模块连接时需要的。 Segname ENDS
定位类型 Alin-type : PARA 表示段起始于小段,即段地址在 最后0H处。这为 默认设置。 BYTE 段起始于任何地址 WORD 段起始于偶地址 PAGE 段起始于页的边界。最后00H
组合类型Combine-type PUBLIC 相同名字分段连接一起,次序由连接命令指定。 COMMON 同名分段有相同起始地址 即相互交盖。连接长度是各个段中最长的。 AT EXP 分段起始地址是exp计算出来的16位值。注意:不能用来指定代码段 PRIVATE 私有段,不与其它同名段合并。 STACK 指明为堆栈段,长度为各段之和 MEMORY 指定该段分配和其他段连在一起
程序开始和结束伪操作 在程序的开始可以用NAME或TITLE为模块取名字: 表示源程序结束的伪操作的格式为: NAME module_name TITLE text 表示源程序结束的伪操作的格式为: END [start] 如没有模块名,就以文件名为模块名。 汇编程序在遇END时结束汇编, 而程序将从START开始执行
80386以上CPU的伪指令 一、处理器选择伪操作 。8086 选择8086指令系统 。286 选择80286指令系统 。8086 选择8086指令系统 。286 选择80286指令系统 。286P 选择保护方式下80286指令系统 。386 选择80386指令系统 。386P 选择保护方式下80386指令系统 。486 选择80486指令系统 。486P 选择保护方式下80486指令系统 。586 选择80586指令系统 。586P 选择保护方式下80586指令系统 选择放在整个程序的最前面
80386以上CPU的伪指令 二、存储模型 MODEL伪操作 格式:。MODEL MEMORY_MODEL 1) Tiny 数据和代码放在一个段内 2)Small 数据在一个64K数据段内,代码放在另一个64K段内。最常用的模型 3)Medium 代码放在多个段内,一个模块为一个段,数据合在一个64K段内 4)Compact 代码放在一个段内,数据放多个段 5)Large 代码和数据放在多个64K段内。 6)Huge 代码和数据放在多个段内,可超过64K。 7)Falt 允许用户用32位偏移量。但DOS下不允许。
80386以上CPU的伪指令 三、简化段定义伪操作 分段更细:常数段与数据段分开;初始化数据段和未初始化分开;近和远数据段分开,便于与高级语言兼容。 .CODE[name] 代码段,段名可选 .DATA 数据段 .DATA? 数据段未初始化 .FARDATA[name] 远数据段,可指段名 .FARDATA?[name] 远数据段未初始化,可指段名 .CONST 常数段 .STACK[size] 堆栈段,可指定大小,默认1KB
80386以上CPU的伪指令 简化段定义时,数据段只用.data定义,没有命名 mov ax, @data mov ds, ax 与简化段定义有关的预定义符号 完整段定义中的段名装入寄存器 mov ax,data_seg1 mov ds,ax mov ax,data_seg2 mov es,ax 简化段定义时,数据段只用.data定义,没有命名 mov ax, @data mov ds, ax
简化段定义例 .MODEL SMALL .DATA .FARDATA .CODE ASSUME ES:@FARDATA START: MOV AX, @DATA MOV DS, AX MOV AX, @FARDATA MOV ES, AX : : MOV AX, 4C00H INT 21H END START
(二)数据定义及存储器分配伪操作 格式:[变量名] 助记符 操作数1,… ,操作数n [;注释] 助记符 说明数据类型,常用的有以下几种: 助记符 说明数据类型,常用的有以下几种: DB 字节(8位) DW 字(16位) DD 双字(32位) DQ 四字(64位) ,存放双精度浮点数。 DT 十个字节,形成压缩的BCD码。 操作数 把数据放入指定的单元,可是常数、表达式、字符串、地址等。 下例:
1)数据定义伪操作:把数据存入存储单元;分配空间; DW和DD可存储偏移地址或完整的地址。 举例: 0 A 0 4 1 0 6 4 0 0 0 1 F B F F 3 C F D 10d 例: 操作数可以是常数,或者 表达式(可以求得一个常数), 如 4 10h 100d 100h DATA_BYTE DB 10,4,10H DATA_WORD DW 100,100H,-5 DATA_DW DD 3*20,0FFFDH -5 60d 汇编程序在汇编期间在存储器中 存入数据,如图所示 0FFFDh
注意:1、不能写为 DW ’ABCD‘ 2、用DB与DW来定义字符串的区别 例 :操作数也可以是字符串,如: 例 :操作数也可以是字符串,如: MESSAGE DB ‘HELLO’ 例:DATA DW ‘AB’,’CD’ 注意:1、不能写为 DW ’ABCD‘ 2、用DB与DW来定义字符串的区别 2) 操作数“?”用于保留存储空间,但不存入数据。 如: ABC DB 0,?,?,?,0 DFF DW ?,52,?
例:ARY1 DB 100 DUP(?) 3)操作数可以使用复制操作符来复制某个操作数。 格式: ARY3 FF 1 2 3 3)操作数可以使用复制操作符来复制某个操作数。 格式: repeat_count DUP(operand,……operand) repeat_count:表达式,正整数,用来指定括号中的 重复次数。 ARY3 例:ARY1 DB 100 DUP(?) DUP操作可以嵌套, ARY3 DB 10 DUP (0,2 DUP(1,2),0,3)
(DW)或整个地址(DD)存入存储器。 用DD操作存入地址时: 第一个字为偏移地址,第二个字为段地址。 例 : PARA_TAB DW PAR1 DW PAR2 DW PAR3 INTER_DATA DD DATA1 DD DATA2 则汇编后的存储情况如图所示
PARA_TABLE PAR1的偏移地址 PAR2的偏移地址 PAR3的偏移地址 INTER_DATA DATA1的偏移地址 DATA1的段地址 DATA2的偏移地址 DATA2的段地址 例 的汇编结果
则第一条指令应为字节指令,而第二条应为字指令。 例 注意:变量的类型 OPER1 DB ?,? OPER2 DW ?,? … MOV OPER1,0 MOV OPER2,0 则第一条指令应为字节指令,而第二条应为字指令。 OPER1 DB 1,2 OPER2 DW 1234H,5678H … MOV AX,OPER1+1 MOV AL,OPER2 例 程序将指示出错: 两条MOV指令的两个操作数的类型不匹配。
作业 4.5
5)变量在存储器中偏移地址值如何计算 汇编时,内部有一个地址计数器(位置计数器) 汇编指令时保存指令的偏移地址; 汇编指令时保存指令的偏移地址; 汇编数据段时,计算变量的偏移地址。 ① 段定义开始,位置计数器(LC)置0 ② 根据数据定义语句,按字节数,使位置计数器计数 ③变量偏移地址值就是位置计数器的计数值,也即该变量与段首的距离(字节数) LC中当前值用$表示
5)地址计数器与对准: Data segment Data ends JC $+6 例: array1 DW 1,2,$+4,4,$+4 2 ? 4 5 0a ff 例: Data segment array1 DW 1,2,$+4,4,$+4 Array2 DB 5, 0ah, -1, $ Data ends 例: JNE $ JC $+6 ORG伪操作:用来设置地址计数器的值。 ORG EXP 例:ORG 100H DATA1 DW 10 DUP ($), 1000H,$+200H
(三)表达式赋值伪操作EQU 程序中常有一些常数,是数据,数据地址或程序地址,多次出现. 直接用数字表示,1,可读性差 2, 修改麻烦 为方便,可用赋值伪操作给它赋予一个名字。 格式如下: Exp_name EQU Expression 表达式:有效的常数表达式,有效的助记符 举例如下: LF EQU 10 ;换行 CR EQU 13 ; 回车 port1 EQU 3F8H ; 端口地址 COM1 W-P EQU WORD PTR B EQU [BP+8] P8 EQU DS:[BP+8]
(1)EQU表达式中:有变量或标号的表达式,则应先定义如: AB EQU DATA__ONE+2 则必须放在DATA_ONE的定义之后。 应用时必须注意的几点: (1)EQU表达式中:有变量或标号的表达式,则应先定义如: AB EQU DATA__ONE+2 则必须放在DATA_ONE的定义之后。 (2)与EQU 相类似, = 伪操作,作为赋值使用。 区别是:EQU 中的表达式名是不允许重复定义的, = 则允许重复定义。 Var1 db 2,5,7,9 Var2 dw 0,10h,-8 Cont EQU $-var1
变量、标号的属性 名字项可以是标号或变量名。 标号: 标号在代码段中定义,后面跟冒号:。标号有三种属性:段,偏移及类型。 段属性: 偏移属性: 类型属性: 标号的段起始位置。 标号的偏移地址是16/32位无符号数。 指出该标号是在本段中引用还是在其他段中引用的。 段内称NEAR,对16位段 指针长2字节,32位4字节; 段外称FAR,对16位段 指针长4字节,32位6字节。
变量所占用的字节数。 变量: 代码段外的其他段中定义,不跟冒号。 常在操作数出现。有段,偏移及类型三种属性 段属性: 偏移属性: 类型属性: 代码段外的其他段中定义,不跟冒号。 常在操作数出现。有段,偏移及类型三种属性 段属性: 偏移属性: 类型属性: 变量的段起始地址,在一个段寄存器中 16/32位无符号数,从段的起始地址到变量地址之间 的字节数。偏移值等于当前地址计数器的值, 可以用$来表示。 变量所占用的字节数。 属性可用: SEG, OFFSET, TYPE来获取
常用操作符 ① 段属性 操作符 SEG 包含在可执行指令中的伪操作呌操作符 一,变量或标号属性操作符:将存储器地址分解。 作用 取段地址 MOV CX, SEG VAR1 ② 偏移属性操作符 offset 格式 offset<变量名>或<标号名> 作用 取其偏移地址 MOV BX, offset Var (LEA BX, Var 等效)
常用操作符 3 类型操作符 格式 type<变量名>或<标号名> 作用 取出类型(值) 3 类型操作符 格式 type<变量名>或<标号名> 作用 取出类型(值) 类型:该数据项长度,用以字节为单位表示。 var db 234 var1 dw 1 MOV AX, type Var ;(AX)=1 MOV AX, type Var1; (AX)=2 注:变量表达式的类型属性与变量同 如:mov ax, type var+4 若是标号类型的数值:Far 为 –2, Near 为 -1。
常用操作符 ① 格式:length Var 得到分配给Var变量的单元数。对使用DUP定义的变量有不同的值。 其他情况则是1。 ② 格式:size Var 得到Var变量所分配的字节总数 例:multi DW 50 DUP (?) length multi 就是50 Type multi 是 2 Size multi 是2*50=100 length和size 操作符只能和数据存储器地址操作数一起使用。
二,类型重指定操作符 (一)格式 : 类型 PTR <地址表达式> 类型: byte , word, Dword 功能:对内存操作数类型进行说明或重定义。 PTR的类型优先级高于变量定义伪指令 PTR是有临时性,PTR不分配存储空间 例:INC word PTR [DI] MOV byte PTR [SI] , 0 若无类型说明,汇编程序无法确定为字操作还是字节操作
类型重指定操作符 34 12 78 56 90 9a 00 VAR 例2,VAR DW 1234H, 5678H ,90 H TAB DB 12H,34H,56H,78H,9ah, MOV AL, VAR+2;(类型不匹配) MOV AL, byte PTR VAR+2 (AL)=78H MOV AX, word PTR TAB+2 (AL)=56H TAB 9a
类型重指定操作符 12 34 (二)格式:<变量名或标号>LABEL<类型> var2 为当前存储单元定义一个指定新类型 变量类型可以是byte,word,dword,struct, 标号类型可以是near和far 例1:var1 label byte var2 dw 50 DUP (3412H) 这样在100个数组字节中,第一个字节赋予二个不同类型 var1变量为字节类型 var2变量是字类型的 mov ax, var2; ax? mov ah, var1; ah? var2 12 34
<变量名或标号>LABEL<类型> SS <变量名或标号>LABEL<类型> 11 22 33 44 55 66 例2:stack segment dw 200 DUP(?) tos label word stack ends 目的设置堆栈 mov sp, tos 400 aa bb cc dd ee Sp tos
三,分离操作符HIGH 和 LOW 接收一个数或地址表达式,HIGH取高字符,low取低字符 例:Var dw 1234H MOV AL, HIGH Var ; (AL)=?H
变量使用的注意 12 34 56 78 90 00 BB CC DD EE ARRY ① 变量类型必须与指令要求相符 ① 变量类型必须与指令要求相符 例 WTABLE DW 20 DUP(0) MOV AX , WTABLE MOV AL , WTABLE ② 变量仅对应数据区中第一个数据项, 需对其它数据项进行操作时,必须 用地址表达式写出。 如:mov ax, arry mov cx, arry+4 mov arry+8, cx
操作数表达式 操作数项:一个或多个表达式。可以是常数、寄存器、 标号、变量或表达式组成。 汇编程序按优先规则进行计算后可得到一个数值或一个地址。 算术操作符 +、-、*、/ 、 MOD 逻辑操作符 AND、OR、XOR、NOT 关系操作符 EQ(相等)、NE(不等)、LT(小于)、GT(大于) LE(小于或等于)、GE(大于或等于)6种。
算术操作符的使用 算术操作符用于地址表达式时,注意: 只有当其结果有明确的物理意义时才是有效的结果。 两个地址相乘是没有意义的 不同段的地址相加是没有意义的 SUM+4,SUM-6表示变量SUM的前移和后移的地址单元
高 优 先 级 低 1 圆括号、方括号中的项 2 LENGTH,SIZE、WIDTH、MASK 3 PTR,OFFSET,SEG,TYPE,THIS 4 HIGH、LOW 5 *,/,MOD,SHL,SHR 6 +,- 7 EQ,NE,LT,LE,GT,GE 8 NOT 9 AND 10 OR,XOR
作业 4.2 4.3 4.9 4.14 自学 4.4 汇编语言程序的上机过程 要求:读懂程序 掌握上机操作步骤
程序的调试 -u 先进入DEBUG并装入我们要调试的程序如: EX_MOVS.EXE 键入: C>debug ex_movs.exe 再键入U后如图所示: -u 18F4: 0000 1E PUSH DS 18F4: 0001 2BC0 SUB AX,AX 18F4: 0003 50 PUSH AX 18F4: 0004 B8F618 MOV AX,18F6 18F4: 0007 8ED8 MOV DS,AX 18F4: 0009 B8F918 MOV AX,18F9 18F4: 000C 8EC3 MOV ES,AX 18F4: 000E 8D360000 LEA SI,[0000] 18F4: 0012 26 ES: 18F4: 0013 8D3E0000 LEA DI,[0000] 18F4: 0017 FC CLD 18F4: 0018 B92800 MOV CX,0028 18F4: 001B F3 REPZ 18F4: 001C A4 MOVSB 18F4: 001D CB RETF 18F4: 001E 0000 ADD [BX+SI],AL
的当前值,最后一行给出下一条将要执行指令的地址、 机器语言及汇编语言 在确定断点后,就可以用G命令使程序启动运行,同时设定 断点001D如下: -g ld AX=18F9 BX=0000 CX=0000 DX=0000 SP=FFFC BP=0000 SI=0028 DI=0028 DS=18F6 ES=18F9 SS=18F4 CS=18F4 IP=001D NV UP DI PL 2R NA PE NC 18F4:001D CB RETF 程序停在断点处,并显示出所有寄存器以及各标志位 的当前值,最后一行给出下一条将要执行指令的地址、 机器语言及汇编语言
我们还可以用D命令分别察看数据段和附加段的有关区域,如 下图所示: -d18F6:0 18F6:0000 61 61 61 61 61 61 61 — 61 61 61 61 61 61 61 61 a……a 16位