700 likes | 917 Views
汇编语言. 数学科学学院计算机应用技术专业. 修兴强 北京师范大学数学科学学院 E-mail: xq_xiu@yahoo.com.cn. 第 3 章 汇编语言基础. 汇编语言. 2. 汇编语言的基本元素. 整数常量 整数常量由 符号 ( 可选 ) 开头,加上一个或多个 数字 以及一个 表示数制基数的字符后缀 ( 可选 ) 构成: [{+|-}]digits[radix] 在 [..] 中的参数是可选的,在 {..} 之内的参数要求从多个括起来的参数中选择一个 ( 由 | 字符分隔 ) 。 Radix( 基数后缀 ) 可以是以下之一 ( 大小写均可 ) :
E N D
汇编语言 数学科学学院计算机应用技术专业 修兴强 北京师范大学数学科学学院 E-mail: xq_xiu@yahoo.com.cn 汇编语言
第3章 汇编语言基础 汇编语言 汇编语言 2
汇编语言的基本元素 整数常量 整数常量由符号(可选)开头,加上一个或多个数字以及一个表示数制基数的字符后缀(可选)构成: [{+|-}]digits[radix] 在[..]中的参数是可选的,在{..}之内的参数要求从多个括起来的参数中选择一个(由|字符分隔)。 Radix(基数后缀)可以是以下之一(大小写均可): d 十进制 t 十进制(可选) b 二进制 y 二进制(可选) h 十六进制 q/o 八进制 r 编码实数 如果整数常量后面没有后缀,就被认为是十进制的。 以字母开头的十六进制常量前面必须加一个0,以防止汇编编译器将其解释为标识符。 汇编语言 汇编语言 3
整数表达式 整数表达式是包含整数值和算术运算符的数学表达式。整数表达式计算的结果必须是能够存储在32位整数内的(0--FFFFFFFFh)。 算数运算符的优先级 汇编语言 汇编语言 4
实数常量 有两种类型的实数常量:十进制实数和编码(十六进制)实数。 十进制实数常量由符号位、整数部分、小数点、表示小数的整数和指数部分组成: [sign]integer.[integer][exponent] 符号位和指数的描述如下所示: sign {+,-} exponent E[{+,-}]integer 实数常量中应该至少有一个数字和一个小数点,如果没有小数点,那它就是一个整数常量。 编码实数 参考:IEEE Standard for Binary Floating-Point Arithmetic(754-1985) 汇编语言 汇编语言 5
字符常量 字符常量是以单引号或双引号引起的单个字符。汇编编译器将其转换为与字符对应的二进制ASCII码。 例如:’A’,”d” 字符串常量 字符串常量是以单引号或双引号引起的一串字符。 例如:’ABC’,”It is OK” • 引号可以嵌套使用 例如:”It’s OK”,’Say ”Hello”,’ 汇编语言
保留字 汇编语言中有一些被称为保留字的单词,这些单词有特殊含义,只能用于正确的上下文环境中,保留字可以是下列之一: • 指令助记符:例如MOV,ADD和SUB等,这些是与Intel处理器的内建操作相对应的。 • 伪指令:用于告诉MASM如何编译程序,例如.DATA,PROC和EQU等。 • 属性:用于为变量和操作数提供有关尺寸以及使用方式的信息,例如BYTE和WORD等。 • 运算符:用在常量表达式中。 • 预定义符号:例如@data,在编译时返回整数常量值。 汇编语言
标识符 标识符是程序员选择的名字,用来识别变量、常量、过程和代码标号。创建标识符时要注意以下几点: • 标识符可包含1—247个字符 • 标识符默认是对大小写不敏感的。在运行汇编编译器时,通过在命令行加-Cp选项可以使关键字和标识符大小写敏感。 • 标识符的第一个字母必须是字母(A—Z和a--z)、下划线(_)、@或$,后续字符也可以是数字。 • 标识符不能与汇编编译器的保留字相同。 • 建议尽量使用具有描述性并且易于理解的标识符名。 汇编语言
伪指令 伪指令是程序源代码被编译时由编译器识别和执行的命令。伪指令用于定义逻辑段、选择内存模式、定义变量和创建过程等。 • 伪指令是汇编编译器语法格式的一部分,与Intel指令集无关。各不同的汇编编译器可以为Intel处理器生成相同的代码,但其伪指令集不必相同。例如 .DATA伪指令标识了程序中包含变量的区域: .data .CODE伪指令标识了程序中包含指令的区域: .code PROC标识了过程的开始,其中的name可以使任何标识符: name PROC 汇编语言
指令 指令是在程序被加载至内存并且开始运行后,在运行期间由处理器执行的语句。一条指令包括标号(可选)、指令助记符(必须)、操作数(通常需要)和注释(可选)4个基本部分。 • 源代码行可以只包含标号和注释。 汇编语言
标号 标号是充当指令或数据位置标记的标识符,表示指令或数据相对其所在段开始处的偏移。(汇编编译器在扫描源程序的过程中为每条程序语句分配一个数字地址。) • 为什么要使用标号? mov ax,[0020] mov ax,myVariable • 代码标号:放置在一条指令之前,表明了该指令的地址。程序代码区中的标号必须以冒号(:)结尾。代码标号通常用做跳转和循环指令的目标地址。例如 target: mov ax,bx jmp target • 数据标号:放置在变量之前,表明了变量的地址。程序数据区中的标号不必用冒号结尾。例如 first BYTE 10 汇编语言
指令助记符 指令助记符是一个简短的单词,用于标识某个指令要执行的操作。例如 mov将一个值移动(赋值)到另一个值中 add两个值相加 sub从一个值中减去另外一个值 mul两个值相乘 jmp跳转到另一个位置 call调用一个过程 汇编语言
操作数 一条汇编语言指令可以有0—3个操作数,每个操作数可以是寄存器、内存操作数、常量表达式或I/O端口。 • 下面是一些带有不同数目操作数的汇编语言指令: stc ;设置进位标志 inc ax ;将ax的值加1 mov count,bx ;将bx的值赋给count imul r32, m32,imm8 ;r32m32 * imm8 汇编语言
注释 注释可以用下面两种方法来指定: • 单行注释:以分号(;)字符开始,编译器忽略同一行上分号后面所有的字符,因此可用来注释程序。 • 块注释:以COMMENT伪指令以及一个用户定义的符号开始,编译器忽略后面所有的文本行,直到另一个用户定义的符号出现。例如 COMMENT ! This line is a comment. This line is also a comment. ! 汇编语言
AddSub.asm TITLE Add and Subtract (AddSub.asm) ; This program adds and subtracts 32-bit integers. ; Last update: 2/1/02 INCLUDE Irvine32.inc .code main PROC mov eax,10000h ; EAX = 10000h add eax,40000h ; EAX = 50000h sub eax,20000h ; EAX = 30000h call DumpRegs ; display registers exit main ENDP END main 汇编语言
TITLE Add and Subtract (AddSub.asm) TITLE伪指令将整行标记为注释,该行可放置任何东西。 ; This program adds and subtracts 32-bit integers. ; Last update: 2/1/02 注释行,编译器将忽视分号右边的所有文本。 INCLUDE Irvine32.inc INCLUDE伪指令从Irvine32.inc文件中拷贝必需的定义和设置信息,Irvine32.inc文件在\Masm615\INCLUDE目录中。 .code .code伪指令用来标记代码段的开始,程序中的所有可执行语句都放于此处。 汇编语言
main PROC PROC伪指令用来标记子程序的开始,子程序的名字是main。 mov eax,10000h ; EAX = 10000h MOV指令将整数10000h赋值给eax寄存器,第一个操作数(eax)称为目的操作数,第二个操作数称为源操作数。 add eax,40000h ; EAX = 50000h ADD指令将40000h加到eax寄存器上。 sub eax,20000h ; EAX = 30000h SUB指令从eax中减去20000h。 call DumpRegs ; display registers CALL指令调用一个显示CPU寄存器值的子程序。 汇编语言
exit exit表达式(间接)调用与定义的MS-Windows函数来终止程序。 main ENDP ENDP伪指令标记main程序的结束。 END main END伪指令表明该行是汇编程序的最后一行,编译器将忽略该行后面的所有内容。标识符main是程序启动过程(即程序启动时执行的子程序,或程序入口点)的名字。 汇编语言
段 程序是以段来组织的,常见的段有代码段、数据段和堆栈段等。 • 代码段包含程序的全部可执行指令,通常代码段中都有一个或一个以上的过程,其中一个是启动过程。 • 堆栈段存放着子程序的参数和局部变量。 • 数据段存放着变量。 汇编语言
编译、链接和运行程序 步骤2:编译器读取源程序并生成目标文件(object file),另外还可以选择生成列表文件(listing file)。如果发现错误,程序员必需回到步骤1以修正程序。 步骤3:链接器读取目标文件并检查程序是否调用了链接库中的函数,链接器从库中读取全部所需的函数并将其同目标文件合并在一起生成可执行文件(executable file)。在这同时还可以生成映像文件(map file)。 链接库 汇编编译器 链接器 源文件 目标文件 可执行文件 输出 系统 装载器 映像文件 列表文件 文本编辑器 步骤1:程序员使用文本编辑器创建ASCII文本类型的源文件(source file)。 步骤4:操作系统的装载器(loader)将可执行文件读入内存,并将CPU转移到程序的起始地址,这样程序就可以开始执行了。 汇编语言
make32.bat REM make32.bat - Batch file for assembling/linking 32-bit Assembly programs REM Revised: 11/15/01 @echo off cls REM The following three lines can be customized for your system: REM ********************************************BEGIN customize SET PATH=C:\Masm615 SET INCLUDE=C:\Masm615\INCLUDE SET LIB=C:\Masm615\LIB REM ********************************************END customize ML -Zi -c -Fl -coff %1.asm if errorlevel 1 goto terminate REM add the /MAP option for a map file in the link command. LINK32 %1.obj irvine32.lib kernel32.lib /SUBSYSTEM:CONSOLE /DEBUG if errorLevel 1 goto terminate dir %1.* :terminate pause 汇编语言
AddSubAlt.asm TITLE Add and Subtract (AddSubAlt.asm) ;This program adds and subtracts 32-bit integers. ; 32-bit Protected mode version ; Last update: 2/1/02 .386 .MODEL flat,stdcall ExitProcess PROTO,dwExitCode:DWORD DumpRegs PROTO .code main PROC mov eax,10000h ; EAX = 10000h add eax,40000h ; EAX = 50000h sub eax,20000h ; EAX = 30000h call DumpRegs INVOKE ExitProcess,0 main ENDP END main 汇编语言
.386 .386指出了该程序要求的最低CPU(Intel 386)。 .MODEL flat,stdcall .MODEL伪指令指示编译器为保护模式生成代码,STDCALL允许调用MS-Windows函数。 ExitProcess PROTO,dwExitCode:DWORD DumpRegs PROTO 两条PROTO伪指令声明了该程序使用的子程序:ExitProcess是一个终止当前程序(进程)的函数;DumpRegs是Irvine32链接库中一个显示寄存器的函数。 INVOKE ExitProcess,0 INVOKE伪指令用于调用过程或函数,此处调用ExitProcess来结束程序的执行,传递给该函数的参数是返回码,取值是0。 汇编语言
代码风格和程序模板 TITLE Program Template (template.asm) ; Program Description: ; Author: ; Date Created: ; Last Modification Date: INCLUDE Irvine32.inc ; (insert symbol definitions here) .data ; (insert variables here) .code main PROC ; (insert executable instructions here) exit ; exit to operating system main ENDP ; (insert additional procedures here) END main 汇编语言
定义数据 内部数据类型 汇编语言
数据定义语句 数据定义语句为变量在内存中保留存储空间并且可以为变量指定名称。数据定义语句的格式如下: [名称] 数据定义伪指令 初始值 [,初始值] … • 数据定义语句中至少要有一个初始值或者“?”符号。初始值可以是与数据类型隐含尺寸匹配的整数常量或表达式。“?”符号在不想给数据赋任何特定值的时候使用。 • 所有的初始值,不管其格式如何,均由编译器转换为二进制数据。 汇编语言
定义BYTE和SBYTE数据 在数据定义语句中使用BYTE和SBYTE伪指令,可以为一个多多个有符号及无符号字节分配存储空间,每个初始值必须是8位的整数表达式或字符常量。例如 value1 BYTE ‘A’ ;字符常量 value2 BYTE 0 ;最小的无符号字节常量 value3 BYTE 255 ;最大的无符号字节常量 value4 SBYTE -128 ;最小的有符号字节常量 value5 SBYTE 127 ;最大的有符号字节常量 value6 BYTE ? ;未初始化的字节常量 汇编语言
偏移 值 0000: 10 0001: 20 0002: 30 0003: 40 • 变量名是一个标号,表示变量相对其所在段开始的偏移。例如 .data value1 BYTE ‘A’ value2 BYTE 0 • 如果一条数据定义语句中有多个初始值,那么标号仅仅指向第一个字节的偏移。例如,假设下面的标号list的偏移是0 .data list BYTE 10,20,30,40 汇编语言
定义字符串可以将一组字符用引号引起,每个字符都占用一个字节。例如定义字符串可以将一组字符用引号引起,每个字符都占用一个字节。例如 Greeting BYTE “Good Morning”,0 • 最常用的字符串是以空字符(也称为NULL,即数值0)结尾的字符串,C/C++,Java和Microsoft Windows函数都使用这种类型的字符串。 • 字符串可以占用多行,而无需为每行都提供一个标号。例如 Greeting BYTE “Good Morning, ”,0dh,0ah BYTE “My Friends”,0 • 十六进制字节0dh和0ah为CR/LF(称为回车换行符,或行结束符)。 • MASM的行延续符(\)用来把两行连接成一条程序语句,行延续符(\)只能放在每行的最后。例如 Greeting \ BYTE “Good Morning, My Friends”,0 汇编语言
DUP操作符使用一个常量表达式作为计数器来重复分配存储空间。例如DUP操作符使用一个常量表达式作为计数器来重复分配存储空间。例如 BYTE 20 DUP(0) ;20字节,全部等于0 BYTE 20 DUP(?) ;20字节,未初始化 BYTE 4 DUP(“STACK”) ;20字节:”STACKSTACKSTACKSTACK” • 在使用数组和字符串的时候,我们通常需要知道其大小,手动的修改和计算长度太耗时。MASM用$运算符(当前地址计数器)返回当前程序语句的地址偏移,可以方便地计算数组和字符串的大小。例如 list BYTE 10,20,30,40 listSize = ($ - list) Greeting BYTE “Good Morning, My Friends”,0 stringSize = ($ - Greeting) 汇编语言
定义WORD和SWORD数据 • 定义DWORD和SDWORD数据 • 定义QWORD数据 • 定义TWORD数据 • 定义实数 每种实数类型的最少有效数字位数和大致的表示范围 汇编语言
0000: 78 0001: 56 0002: 34 0003: 12 小尾顺序 Intel处理器使用称为小尾顺序(little endian order)的方案存取内存数据,小尾的含义就是变量的最低有效字节存储在地址最小的单元中,其余字节在内存中按顺序存储。例如 假设双字存储在 偏移0处 双字 12345678h 汇编语言
AddSub2.asm TITLE Add and Subtract, Version 2 (AddSub2.asm) ; This program adds and subtracts 32-bit integers ; and stores the sum in a variable. ; Last update: 2/1/02 INCLUDE Irvine32.inc .data val1 dword 10000h val2 dword 40000h val3 dword 20000h finalVal dword ? .code main PROC mov eax,val1 ; start with 10000h add eax,val2 ; add 40000h sub eax,val3 ; subtract 20000h mov finalVal,eax ; store the result (30000h) call DumpRegs ; display the registers exit main ENDP END main 汇编语言
符号常量 符号常量(或符号定义)是通过将标识符(或符号)与整数表达式或文本联系起来而创建的。与保留存储空间的变量定义不同,符号常量并不占用任何实际的存储空间。符号仅在编译期间使用,因此是不能在运行期间改变的。 汇编语言
等号伪指令 等号(=)伪指令将符号名和整数表达式联系起来。格式如下: 名字 = 表达式 • 通常,表达式(expression)是32位的整数值,程序编译的时候,所有出现名字(name)的地方都由编译器在预处理阶段替换为对应表达式的值。例如 • 以”=”定义的符号可以重复定义任意多次。 COUNT = 500 moval,COUNT mov al,500 汇编语言
EQU伪指令 EQU伪指令将符号名和整数表达式或任意文本联系起来,以下是三种格式: name EQU expression name EQU symbol name EQU <text> • 在第一种格式中,expression必须是有效的整数表达式;在第二种格式中,symbol必须是已用”=”或EQU定义的符号名;在第三种格式中,text必须置于尖括号内。当编译器在后面遇到name时,就用expression代表的整数值或text来代替。例如 matrix1 EQU 10 * 10 matrix2 EQU <10 * 10> .data M1 WORD matrix1 ;M1 WORD 100 M2 WORD matrix2 ;M2 WORD 10 * 10 • 用EQU定义的符号不能在同一源代码文件中重复定义。 汇编语言
TEXTEQU伪指令 6.0版本的MASM新引进的TEXTEQU伪指令与EQU非常相似,可用来创建Microsoft所称的文本宏(text macro)。它有三种不同的使用格式: name TEXTEQU <text> name TEXTEQU textmacro name TEXTEQU %constExpr • 在第一种格式中,将文本赋给符号;在第二种格式中,将已定义的文本宏内容赋给符号;在第三种格式中,将整数表达式常量赋给符号。例如 rowSize = 5 Count TEXTEQU %(rowSize * 2) ;count TEXTEQU <10> move TEXTEQU <mov> setupAL TEXTEQU <move al,count> ;setupAL TEXTEQU <mov al,10> • 与EQU不同的是,TEXTEQU可以在程序中重复定义。 汇编语言
第4章 数据传送、寻址和算术运算 2007年09月 汇编语言 汇编语言 汇编语言 38 38
操作数类型 指令中的操作数表示法 汇编语言
直接内存操作数 .data val BYTE 10h . . .code mov al,val mov al,[00010400] 假设val1位于偏移10400h处 数字周围的方括号表示当指令执行的时候,CPU根据该地址来寻址以获取内存中的内容。 汇编语言
MOV指令 MOV指令从源操作数向目的操作数拷贝数据。指令运行后,目的操作数的内容被改变而源操作数的内容保持不变。其基本格式是: MOV destination,source • 使用MOV指令操作数要遵循的规则 • 两个操作数的尺寸必须一致 • 两个操作数不能同时是内存操作数 • 目的操作数不能是CS、EIP和IP • 立即数不能直接送至段寄存器 • 内存值间移动数据 .data val1 WORD ? val2 WORD ? .code mov ax,val1 mov val2,ax 汇编语言
整数的零/符号扩展 拷贝较小值到较大值中: .data signeVal SWORD -16 ;FFF0h .code mov eax,0 mov eax,signeVal ;EAX=0000FFF0h .data count WORD 1 ;0001h .code mov eax,0 mov eax,cout ;EAX=00000001h .data signeVal SWORD -16 ;FFF0h .code mov eax,0FFFFFFFFh mov eax,signeVal ;EAX=FFFFFFF0h 汇编语言
0 01000111 源操作数 00000000 01000111 目的操作数 MOVZX指令 MOVZX(move with zero-extend)指令将源操作数的内容拷贝到目的操作数中,并将该值零扩展(zero-extend)至16位或32位。该指令仅适用于无符号整数,它有如下三种格式: MOVZX r32,r/m8 MOVZX r32,r/m16 MOVZX r16,r/m8 mov bx,0A69Bh movzx eax,bx ;EAX = 0000A69Bh movzx edx,bl ;EDX = 0000009Bh movzx cx,bl ;CX = 009Bh 汇编语言
MOVSX指令 MOVSX(move with sign-extend)指令将源操作数的内容拷贝到目的操作数中,并将该值符号扩展至16位或32位。该指令仅适用于有符号整数,它有如下三种格式: MOVSX r32,r/m8 MOVSX r32,r/m16 MOVSX r16,r/m8 • 操作数的符号扩展是指用较小操作数的最高位填充目的操作数的其余空位。 10000111 源操作数 mov bx,0A69Bh movsx eax,bx ;EAX=FFFFA69Bh movsx edx,bl ;EDX=FFFFFF9Bh movsx cx,bl ;CX=FF9Bh 11111111 10000111 目的操作数 汇编语言
LAHF和SAHF指令 • LAHF(load status flags into AH)指令将EFLAGS寄存器的低字节拷贝至AH寄存器,被拷贝的标志包括:符号标志、零标志、辅助进位标志、奇偶标志和进位标志。使用该指令可以方便地将标志保存在变量中。例如 .data saveflags BYTE ? .code lahf mov saveflags,ah • SAHF(store AH into status flags)指令将AH寄存器的值拷贝至EFLAGS寄存器的低字节。例如 mov ah,saveflags sahf 汇编语言
XCHG指令 XCHG(exchange data)指令交换两个操作数的内容,它有下面三种格式: XCHG reg,reg XCHG reg,mem XCHG mem,reg • XCHG指令的操作数与MOV指令操作数遵循的规则相同。 • 交换两个内存操作数 mov ax,val1 xchg ax,val2 mov val1,ax 汇编语言
直接偏移操作数 在变量名称后加上一个偏移值,可以创建直接偏移(direct-offset)操作数,通过它来访问没有显式标号的内存地址。例如 .data arrayB BYTE 10h,20h,30h,40h,50h .code mov al,arrayB ;AL=10h mov al,[arrayB+1] ;AL=20h mov al,[arrayB+2] ;AL=30h .data arrayW WORD 100h,200h,300h .code mov ax,arrayW ;AX=100h mov ax,[arrayB+2] ;AL=200h mov ax,[arrayB+4] ;AL=300h 有效地址(effective address):变量偏移地址+常数。当将有效地址用方括号([])括起来的时候,表明我们要引用这个表达式以获取目标内存地址的内容。 汇编语言
加法和减法 INC和DEC指令 INC(increment)和DEC(decrement)指令从操作数中加1或减1,格式是: INC reg/mem DEC reg/mem ADD指令 ADD指令将同尺寸的源操作数和目的操作数相加,格式是: ADD 目的操作数,源操作数 加法操作并不改变源操作数,相加的结果在目的操作数中。其操作数与MOV指令操作数遵循的规则相同。 汇编语言
1 进位: 1 1 1 1 1 (4) 0 0 0 0 0 1 0 0 (-1) + 1 1 1 1 1 1 1 1 (3) 0 0 0 0 0 0 1 1 SUB指令 SUB指令将源操作数从目的操作数中减掉,其格式是: SUB 目的操作数,源操作数 • SUB指令的操作数与MOV指令操作数遵循的规则相同。 • CPU执行减法的时候,首先对减数求补然后再相加。例如4-1实际上是4+(-1) NEG指令 NEG(negate)指令通过将数字转换为对应的二进制补码而求得其相反数。该指令允许使用如下的操作数。 NEG reg/mem 汇编语言
算术运算影响的标志 无论何时,当程序执行算术运算时都有可能发生因溢出而导致的错误,高级语言通常并不侦测整数溢出错误,这将导致产生难于发现的bug。与此相反,在汇编语言中由于能够在每步操作后检查CPU的状态标志值,因此可以很好地控制溢出错误。 • 零标志和符号标志 当算术运算指令的目的操作数被赋以零值的时候,零标志就被相应设置。 在算术运算结果为负的时候,符号标志位被设置。 汇编语言