3.1 汇编语言的基本元素
有人说汇编难,有人说汇编简单,我个人不做评价,下面是一个简单的实例(部分代码):
main PROC
mov eax,5 ;5送EAX寄存器
add eax,6 ;EAX寄存器加6
call WriteInt ;显示EAX中的值
exit
main ENDP
这里通过调用 writeInt 库例程使情况稍微简化了一些,WriteInt本身也包含了相当数量的代码。通常来说,如果你乐于编程写实际上并不做什么的的小程序的话,汇编语言并不难学(额...那请问作者,我学汇编干啥)。还有就是,汇编一定要注意细节。细节。
3.1.1 整数常量
整数常量由符号(可选)开头,后跟一个活多个数字(digit)以及一个表示
基数(radix)的字符后缀。
[{+|-}]数字[基数]
Radix(基数后缀)可以是一下之一(大小写均可):
h 十六进制 r 编码实数
q/o 八进制 t 十进制(可选)
d 十进制 y 二进制 (可选)
b 二进制
如果整数常量后面没有后缀,就默认是十进制数。下面是一些例子:
26 十进制数 42o 八进制数
26d 十进制数 1Ah 十六进制数
1010011b 二进制数 0A3h 十六进制数
42q 八进制数
以字母开头的十六进制数常量前面必须加一个0,以防止汇编器将其解释为标示符。
3.1.2 整数表达式
整数表达式是包含整数值和算术运算的数学表达式。整数表达式计算的结果是能够以32个数据位存储的整数。下图是优先级:
有两种类型的实数常量:十进制实数和编码(十六进制)实数。十进制实数常量由符号(sign)、整数(integer)部分、小数点、表示小数的整数和指数(exponent)部分组成。
{sign} integer.[integert][exponent]
Sign {+,-}
Exponent E[{+,-}]integer
下面是例子:2. +3.0 -44.2E+05 26.E5
3.1.4 字符常量
字符常量是以单引号或者双引号括起来的单个字符。汇编器将其转换为与字符对应的二进制数ASCII码,例如:
‘A’ “d”
3.1.5 字符串常量
字符串常量是以单引号或者双引号括起来的一串字符:
‘ABC’
‘X’
“Goodnight, Asd”
‘4096’
按下面例子的方式使用嵌套的引号也是可以的:
“This isn’t a test”
‘Say “Goodnight,” Asd’
3.1.6 保留字
MASM中有一些有特殊含义的保留字,保留字只能用于合适的上下文环境中,有如下不同的类别的保留字:
1.指令助记符,如MOV、ADD和MUL等。
2.伪指令,用于告诉MASM如何编译程序。
3.属性,用于为变量和操作数提供有关尺寸以及使用方式的信息,如BYTE和WORD。
4.运算符,用在常量表达式中。
5.预定义符号,如@data,在编译时返回整数常量值。
3.1.7 标示符
标示符是程序员选择的名字,用来标示变量、常量、过程或代码标号。创建标示符时要注意一下几点:
1.标示符可以包含1-247个字符。
2.标示符大小写不敏感(MASM默认)。
3.标示符第一个字符必须是字母下划线或者@、?或$,后续字符可以是数字。
4.标示符不能与汇编器的保留字符相同。
运行汇编器时,在命令行上使用-Cp选项可以使所有的关键字和标示符大小写敏感。
汇编器大量使用@符号作为预定义符号的前缀,因此应尽量避免在自己等一的标示符中使用@符号作为数字符。尽量使用标示符的名字局域描述性并且易于理解,下面是一些有效的标示符:
var1 Conut &first
_main MAX open_file
@@myfile xVal _12345
3.1.8 伪指令
伪指令是内嵌在程序源码中,由汇编器识别并执行响应动作的命令。与真正的指令不同,伪指令在程序运行时并不执行。伪指令可以用于定义变量、宏以及过程,可用于命名段以及执行许多其他与汇编器相关的簿记任务。MASM中伪指令大小写不敏感,如.data,.DATA和。Data是等价的。
下面的例子有助于说明伪指令在运行时并不执行。DWORD伪指令告知汇编器要在程序中给一个双字节变量保留空间。MOV指令在运行时真正执行,把myVar的内容复制到EAX寄存器:
myVar DWORD 26 ;DWORD 伪指令
Mov eax,myVar ;MOV指令
每个汇编器都有一套不同的伪指令。例如,TASM(Borland)以及NASM和MASM的伪指令有一个公共的交集子集,而GNU汇编器与MASM的伪指令几乎完全不同。
定义段:汇编伪指令的一个重要功能就是定义程序的节(section)或者段(segment)。
.DATA伪指令标识了程序中包含变量的区域: .data
.CODE伪指令标识了程序中包含质量你开个的区域 .code
.STACK伪指令标识了程序中包含运行时栈的区域,并设定了运行时栈的大小:
.stack 100h
3.1.9 指令
汇编语言中的指令是一条汇编语句,在程序被汇编后就变成可执行的机器指令了。汇编器把汇编指令翻译成机器语言字节码,在运行时可以加载至内存由处理器执行。一条汇编指令包含4个基本部分:
1.标号(可选)
2.指令助记符(必须)
3.操作数(通常是必须的)
4.注释(可选)
基本格式如下:
标号:
数据标号:数据标号标识了变量的地址,为在代码中应用盖变量提供了方便。例如下例就定义了一个名为countde 变量:
Count DWORD 100
汇编为每个标号分配一个数字地址。在一个标号后定义多个数据项是可以的。在下面的例子中,array标示了第一个数字(1024)的位置,其他在内存中相邻数字紧接其后:
Array DWORD 1024 ,2048
DWORD 4096,8192
代码标号:程序代码区(存放指令的地方)中的标号必须以冒号(:)结尾。代码标号通常用做跳转和循环指令的目标地址。例如,下面的JMP(跳转)指令将控制权转到标号target标示的位置,从而构成了一个循环:
Target :
Mov ax,bx
...
Jmp target
代码标号可以和指令在同一行,也可以独立成行:
L1: mov ax,bx
L2:
数据标号不能以冒号结尾,标号命名遵循3.1.7节中讨论的标示符名的规则。
指令助记符:
指令助记符(instruction mnemonic)是一个简单的单词,用于表示一条指令。在英文中,mnemonic是辅助记忆的方法的意思。与此非常相似,汇编语言指令助记符如mov,add何sub等给出了关于指令要执行何种类型操作的提示:
mov 将一个值移动(赋值)到另外一个中
add 两个值相加
sub 从一个值中减去另外一个值
mul 两个值相乘
Jmp 跳转到一个新位置
call 调用一个过程
操作数:
一条汇编语言指令可以有0~3个操作数,每个操作数都可能是寄存器、内存操作数、常量表达式或I/O端口。在第2章中讨论过寄存器的名字;在3.1.2节中,讨论了常量表达式。内存操作数由变量的名字或包含变量地址的一个活多个寄存器制定,变量名字表明了变量的地址,并且指示计算机引用给定内存地址的内容。下表包含了几个实例操作数。
下面是一些带不同数目操作数的汇编语言指令的例子。例如,STC指令没有操作数:
STC ;设置进位标志
INC指令有一个操作数:
Inc eax ; eax 加1
MOV指令有两个操作数:
mov count,ebx ;EBX送变量count
在有两个操作数的指令中,第一个操作数称为目的(标)操作数,第二个操作数称为源操作数。通常,指令会修改目的操作数的内容。例如,在mov指令中,源操作数中的数据被复制到目标操作数中。
注释:
注释是程序作者同程序源代码的阅读者交流有关程序如何工作的信息的一条重要途径,程序清单顶部通常包含如下典型的信息:
1.程序功能的描述。
2.程序创建者/修改者的名字。
3.程序创建/修改的日期。
4.程序实现的技术注解。
注释可以用下面两种方法制定:
单行注释: 以分号(;)字符开始
块注释:以COMMENT伪指令以及一个用户定义的符号开始,编译器忽略后面所有的文本行,直到另一个相同的用户定义符号出现。例如:
COMMENT !
Dasdads
Asdasd asdas das asd
!
也可使用任何其他符号:
COMMENT $
Dasdads
Asdasd asdas das asd
$
3.1.10 NOP(空操作)指令
最安全的指令时NOP(no operation),一条NOP指令占用一个字节的存储,什么也不做。有事编译器或汇编器使用NOP指令把代码对齐到偶数地址边界。在下面的例子中,第一个MOV指令生成三个机器字节码,NOP指令将第三条指令的地址对齐到双字节(4的倍数)边界上。
00000000 mov ax,bx
00000003 nop ;对齐下一条指令
00000004 mov edx,ecx