一、不会转换成本地代码的伪指令
汇编代码看起来比较难,不过实际上其实比较简单,而且可能比C语言还要简单,为了便于阅读汇编代码的源代码,需要注意几个要点
汇编语言的源代码,是由转换成本地代码的指令(后面讲述的操作码)和针对汇编器的伪指令构成的。伪指令负责把程序的构造以及汇编的方法指示给汇编器(转换程序)。不过伪指令是无法汇编转换成为本地代码的。下面是上面程序截取的伪指令
_TEXT segment dword public use32 'CODE' _TEXT ends _DATA segment dword public use32 'DATA' _DATA ends _BSS segment dword public use32 'BSS' _BSS ends DGROUP group_BSS, _DATA _AddNum proc near _AddNum endp _MyFunc proc near _MyFunc endp _TEXT ends end
由伪指令segment 和 ends 围起来的部分,是给构成程序的命令和数据的集合体上加一个名字而得到的,称为 段定义。段定义的英文表达具有 区域的意思,在这个程序中,段定义指的是命令和数据等程序的集合体的意思,一个程序由多个段定义构成
上面代码的开始位置,定义了3个名称分别为_TEXT、_DATA、_BSS 的段定义,_TEXT 是指定的段定义,_DATA 是被初始化(有初始值)的数据的段定义,_BSS 是尚未初始化的数据的段定义。这种定义的名称是由Borland C++定义的,是由 Borland C++编译器自动分配的,所以程序段定义的顺序就成为了_TEXT、_DATA、_BSS,这样也确保了内存的连续性
_TEXT segment dword public use32 'CODE' _TEXT ends _DATA segment dword public use 'DATA' _DATA ends _BSS segment dword public use32 'BSS' _BSS ends
段定义(segment)是用来区分或者划分范围区域的意思。汇编语言的segment伪指令表示段定义的起始,ends 伪指令表示段定义的结束。段定义是一段连续的内存空间
而 group 这个伪指令表示的是将_BSS和_DATA 这两个段定义汇总名为 DGROUP 的组
DGROUP group_BSS, _DATA
围起_AddNum和 _MyFun 的 _TEXT segment 和_TEXT ends,表示_AddNum和_MyFun属于_TEXT 这一段定义的
_TEXT segment dword public use32 'CODE' _TEXT ends
因此,即使在源代码中指令和数据是混杂编写的,经过编译和汇编后,也会转换成为规整的本地代码
_AddNum proc 和 _AddNum endp 围起来的部分,以及 _MyFuncproc 和 _MyFunc endp 围起来的部分,分别表示AddNum函数和MyFunc函数的范围
_AddNum proc near _AddNum endp _MyFunc proc near _MyFunc endp
编译后在函数名前附带上下划线 _ ,是Borland C++ 的规定。在C语言中编写的AddNum函数,在内部是以 _AddNum 这个名称处理的。伪指令 proc 和 endp 围起来的部分,表示的是过程(procedure)的范围。在汇编语言中,这种相当于C语言的函数形式称为过程。末尾的end伪指令,表示的是源代码的结束
二、汇编语言的语法
在汇编语言中,一行表示一对CPU的一个指令。汇编语言指令的语法结构是操作码+操作数,也存在只有操作码没有操作数的指令。
操作码表示的是指令动作,操作数表示的是指令对象。操作码和操作数一起使用就是一个英文指令。比如从英语语法来分析的话,操作码是动词,操作数是宾语。比如这个句子 Give me money 这个英文指令的话,Give 就是操作码,me和money就是操作数。汇编语言中存在多个操作数的情况,要用逗号把它们分割,就像是 Give me,money 这样
能够使用何种形式的操作码,是由CPU的种类决定的,下面对操作码的功能进行了整理。
部分操作码及其功能:
操作码 | 操作数 | 功能数 |
mov | A,B | 把B的值赋给A |
and | A,B | 把A和B同时相加,并把结果赋给A |
push | A | 把A的值存储在栈中 |
pop | A | 从栈中读出值,并将其赋值给A |
call | A | 调用函数A |
ret | 无 | 处理返回给调用源函数 |
本地代码需要加载到内存后才能运行,内存中存储着构成本地代码的指令和数据。程序运行时,CPU会从内存中把数据和指令读出来,然后放在CPU内部的寄存器进行处理
CPU和内存的关系:
寄存器是CPU中的存储区域,寄存器除了有临时存储和计算的功能之外,还具有运算功能,x86系列的主要种类和角色如下图所示
x86系列CPU的主要寄存器:
寄存器名 | 名称 | 主要功能 |
eax | 累加寄存器 | 运算 |
ebc | 基址寄存器 | 存储内存地址 |
ecx | 计数寄存器 | 计算循环次数 |
edx | 数据寄存器 | 存储数据 |
esi | 源基址寄存器 | 存储数据发送源的内存地址 |
edi | 目的基址寄存器 | 存储数据发送目标的内存地址 |
ebp | 扩展基址指针寄存器 | 存储数据存储领域基点的内存地址 |
esp | 扩展栈指针寄存器 | 存储栈中最高位数据的内存地址 |