MASM32
Introduction to Assembler    2
MASM Reference    11
宏参考MACRO Reference    11
伪指令参考    16
语法参考    30
其它参考    35
寄存器大全    35
标志寄存器 Processor Flags    35
80位数据寄存器Stack of 80-bit Data Registers    36
Ascii 字符表Ascii Characters    37
奔腾指令优化参考Pentium Optimisation    37
调用格式Calling Conventions    40
命令行工具Command Line Tools    41

Introduction to Assembler
汇编概述
直线内存模式FLAT Memory Model
32位(二进制位,bit。下同)Windows程序使用“直线内存模式”。这样的程序实际只有一个段,包括程序的所有代码和数据,而且只能在386以上的INTEL处理器上运行。早期的16位程序使用段和偏移来代表地址,每个段的大小不能超过64KB,而“直线内存模式”只有偏移,它的大小是4G,即0XFFFFFFFF,这种地址表示方式使汇编程序更易于开发。
在FLAT模式下,所有的段寄存器都自动设置成为同一个值,32位Windows程序中段/偏移形式的地址在不是必须的。对于DOS程序员来说,32位Windows PE格式的应用程序好象是.COM程序一样,使用单一的段包括了代码和数据,只操作偏移地址,而不是用段/偏移地址。
直线内存模式时,应用程序在4G内存空间内所有的引用都是近程地址(NEAR code addressing)和近程数据(NEAR data addressing)。GS和FS段寄存器在普通的FLAT模式应用程序中并不使用,一般是操作系统用于操作实例(used in instances)。
保护模式内存Protected Mode Memory
DOS是真实地址内存模式,这样应用程序可能改写操作系统的代码,而引起系统瘫痪。例如在编写CGA(注:早期显示模式,显示缓存地址在B800处开始)程序时,使用B800内存区域,如果LOOP循环写的不对,就可能改写高位内存中的DOS代码或BIOS设置,造成死机。设计保护模式就是用来防止这样的事情发生,内存管理器控制并保护应用程序访问的地址,阻止应用程序访问没有权限的内存区域。16位Windows是模拟多任务的操作系统,它的应用程序也可能改写其它应用程序甚至是操作系统的内存。
改写了操作系统代码,应用程序调用某个系统函数时就会引起死机,最常见的就是“蓝屏”(注:Windows95/98常见故障)死机。如果开发的应用程序本身存在逻辑混乱,应该是一个“黑屏”死机。
更改硬件、在多任务基础上使用硬件的时候,保护模式会更加可靠。因为汇编语言编程时允许读写任何地址,所以要留心读写的句柄和地址。如果分配了10K内存来读而却试图读20K内容,就会引起页错误。也可能因为使用变量或寄存器指向的地址超出了应用程序权限范围,而引起内存读写失败。操作系统会将页异常传递给造成出错的应用程序,如果应用程序没有处理异常,操作系统就会关闭应用程序。这给应用程序提供一个在保护模式下,得到有读写权限的地址范围的一种方法。
操作数Instruction Operands
操作数被用来做汇编指令助记符(mnemonic)中的参数。一条指令可能有0到3个操作数,有两个操作数的逻辑或数学指令当中,位于右边的是源操作数,左边的是目的操作数。如mov eax,1;mov是汇编代码助记符,eax是目的操作数,1是源操作数。指令的操作结果是将1放入eax。在其它处理器的汇编语言中,操作数使用顺序不同的都会特别注明。
指令码和助记符Opcodes and Mnemonics
Intel极兼容处理器的硬件层面,内建指令(instructions)叫opcodes(注:二进制的指令码),以位(bit)表示,最小的处理单元是字节(BTYE)。这样的二进制的指令码程序可以用16进制的编辑器来编写,但它太繁杂,如果将32位值hex:56 a7 00 fe放入eax寄存器,那就要指令码A1(mov eax),后面跟上fe 00 a7 56。这样的复杂性谁也受不了,于是很早以前发明了助记符,一直用到现在的32位(已经64位了)汇编时代。注意:相同的助记符表示的指令码可能不同,如mov eax,Var1和mov Var1,eax两条指令的指令码分别是A1H和A3H。助记符让程序员编写汇编程序时简便易记。
寄存器Registers
处理器内部的高速数据处理单元是寄存器,也是处理器内部的存贮单元。执行指令时,寄存器比内存操作数快的多。INTEL处理器内部的寄存器数量是有限的,普通寄存器8个:EAX,EBX,ECX,EDX,ESI,EDI,ESP和EBP。编程时经常把ESP和EBP单独列出来,因为它们主要在子过程的进入和退出时用来指示堆栈和参数,实际上只剩6个寄存器可以使用。ESI和EDI也可以以字节访问,如用读SI的方式来访问ESI的低字。清楚寄存器本身可以使用什么
样宽度类型的数据非常重要。通常带有整数操作数的指令使用三种类型数据:BYTE:单字节8位;WORD:双字节16位;DWORD:四字节32位。例如EAX寄存器,AL或AH表示使用8位二进制数据,是AX的低字节或高字节,AX表示使用16位数据,它是EAX的低字,而EAX表示32位数据。EAX寄存器从右到左依次是0位到31位(下同)。访问0到7位用AL表示,8到15位用AH表示,0到15位用AX表示。想访问16到31位要通过ROL eax,16;或ror eax,16;等移位指令,再读AX寄存器。mov eax,cl;这条指令是非法的,两个操作数数据宽度不对。如果必须进行这样的操作,需要进行符号扩展,使用有符号扩展功能的指令:movzx eax,cl;扩展为无符号整型或:movsx eax,cl;扩展为有符号整型。旧指令集中的cbw或cwde指令可以对AL或AX数据进行符号扩展。
寄存器保护Register Preservation
在x86系列处理器中,普通目的寄存器共8个,除ESP和EBP用来管理函数调用和退出之外,还有6个普通目的寄存器。保护寄存器是指程序在使用Call调用子程序或者中断时,应该保护哪些寄存器的数据。编写32位Windows应用程序时,使用寄存器有一个约定,即访问操作系统函数WIN32API时的一个接口标准:6个自主使用的寄存器中,3个可以自由更改:EAX,
ECX,和EDX;另外3个必须保护:EBX,ESI和EDI。如果函数需要使用应该保护的3个寄存器,就必须在使用前将它们的值保存,用后再进行恢复,如:
TestProc proc var1:DWORD, var2:DWORD
    push ebx
    push esi
    push edi
; -------------------------------------
; write code that uses EBX ESI and EDI
; -------------------------------------
    pop edi
    pop esi
    pop ebx
    ret
TestProc endp
调用WIN32API函数后,可以自由更改的3个寄存器的值可能被WIN32API更改了,所以这3个寄存器中的有用数据在调用之前要保存好。应用程序使用3个被保护的寄存器,即使调用WIN32API时,这3个寄存器的内容不会因为调用而受影响,如做计数器(或存贮其它有用数据)都是有效的。它们在WIN32API中被保护了,意图是减少在代码中重复进行存贮和恢复。
汇编程序中也可以手工编写函数的入口和出口,这是汇编的一个复杂之处,因为这里的错误非常容易使操作系统死机。所以使用ESP和EBP是非常麻烦的事。下面是保护ESP和EBP的例子:
    call procname
procname:
    push ebp        ; preserve base pointer
    mov ebp, esp    ; stack pointer into ebp
; write your assembler code here
    mov esp, ebp    ; restore stack pointer
    pop ebp        ; restore base pointer
    ret
label:
还有其它保护ESP和EBP寄存器的方式,这看个人喜好和使用约定。需要注意的是使用其它方式或约定时,一定要将这两个寄存器的内容同时保存和恢复。使用个人约定方式,需要手工计算堆栈中每个函数参数和使用局部变量的偏移量,传递的参数开始于[EBP+8],局部变量在堆栈中以EBP为基准按相反的顺序排列,如果想将传递过来的一个参数放入EAX寄存器如mov eax,var1 ,那么实际是这样的指令mov eax,[ebp+8](注:用椎栈传递参数时,第
一个参数位于最底端,相对于基指针的位置)。
使用汇编/API混合编程时,如果不知道API用了哪个寄存器,有一对非常有用的汇编指令将所有使用寄存器和标志寄存器保存起来:PUSHAD和POPAD。这对指令在编写应用程序时并不是最优化(指速度)的,但在开发过程当中相当方便。使用条件转移指令时,使用PUSHFD和POPFD指令对可以保护标志寄存器。
寄存器的数据类型Data Types In Register
有三种操作数可以放入寄存器:立即数,内存数据,其它寄存器数据。字符串长度规则
立即数指数字或ASCII字符。字符的数值是其ASCII码。如:mov al,"a";
内存操作数是某种形式的内存地址。一般习惯将内存操作数两边加[]号,用以区别其它操作数和地址。如:mov al,[esi];再如:mov edx,lpMemvar 将变量的地址放入edx(注:masm32\HELP\Introduction to Assembler。这几处例子指令相互矛盾,内存数据和内存地址没有分开)。
用其它寄存器作为操作数时简单拷贝其内容。如mov ecx,edx;

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。