【转载】ASM编程环境的构建和MASM32宏(10年前的文章 经典之作)
ASM编程环境的构建和MASM32宏
作者:Hume/冷雨飘心·发布日期:2002-6-24·阅读次数:13620
Part I: ASM编程环境的构建
俗云:工欲善其事,必先利其器.
(一)编辑器的选择
编辑器是不可或缺的,而现在的编辑器也实在太多,在dos下你肯定用过经典的dos自带的edit,或者asmedit,wps等,然而现在平台已经转移到了Windows,我们
的选择就更加丰富了,替代edit的是notepad,甚至有word,wps2000这样强大的文字处理工具,然而选择他们并不是写asm的最佳选择,因为他们并不是为asm设
计所专门设计的,不能完全满足我们的需要...
现在有几种比较流行的集成asm环境,其一是asmStudio,国人开发,不过只是适合dos下的asm编写,支持win32ASM的还没有看到;其2是ASMedit,完全是用
asm写的,小巧支持语法加亮等功能,也是不错的选择;现在呼声最高的恐怕唯Ketlio正在开发的RadASM了,支持MASM,TASM和FASM的集成环境的构建,有自动
语法提示等功能,不过唯一的遗憾是不支持中文,这使人很失望,如果支持中文,肯定是首选.
此外还有editplus和ultraedit可供我们选择,这二者经过适当设置后即可支持语法加亮,自动填充,和shell执行等功能,使用起来很方便,在editplus中如何实现自
动编译链接请参见我以前写的一篇文章.由于具有良好的Unicode支持,所以推荐使用,唯一的缺点就是无法实现win32Api的参数提示和自动填充功能.
由于功能完善的asm集成编程环境还没有出现,所以我们只能根据各种编辑器的优缺点和自己的偏好加以选择.不要抱怨!谁叫你选择的是ASM???
我用的是editplus 2.01C.感觉不错,推荐使用.
(二)编译器的选择(compiler)
现在网上的编译器种类很多,比较著名的有MASM,TASM,NASM,FASM,SPASM,A86,GAS,GoASM等,究竟选择什么作为我们的编译器似乎是每个初学者几
乎必问的问题.其实各个编译器互有优缺点,究竟选择什么,取决于你的实际需要.下面介绍一下主要的几种:
1.MASM和TASM
Masm是微软的ASM编译器,TASM是编译器巨人Borland公司的产品,5.0版本现在已经可以在其主页免费下载,我最初选择masm的原因很简单,是因为大部分
教科书都使用他作为蓝本,在dos时代的机房里面配备的也基本都是MASM.就dos而言,但低版本的masm并不是最佳的,因为borland公司推出了TASM,Tasm完
全兼容masm,并且由于编译速度快等优点获得了编程者的厚爱.但在国内使用较多的还是MASM.WIn32下TASM5.0有点力不从心了,这是由于tasm的头文件和
库不完整,而Borland也放弃了Tasm的继续开发.相反MASM则不断更新,更由于hutch为Masm作了一个masm32V?.zip的开发包,里面有完整的头文件和库文件,
所以win32下使用masm的人数日渐增多,成为主流.
2.NASM
NASM(netwide assembler),是一个正在开发中的项目,其目的是支持多种平台的文件格式,比如elf,a.out等unix和linux所使用的文件格式,如果你要在上述平
台下写asm,那么最好的选择是nasm,不过nasm当然也可以用于写win32ASM,麻烦的问题和tasm是同样的,那就是缺少头文件和库文件的支持,不过如果你厌倦
了MASM的烦琐奇怪有时又容易产生歧义的伪操作符的话,试试Nasm的语法,相信会有所收获.NASM生成obj的链接可以使用Alink.
3.FASM
fasm(flat assembler)是一个纯粹用汇编语言写成,并采用自展技术的正在开发的的编译器,fasm最大的优点在于不需要额外的链接步骤而直接生成可执行文
件,没有什么烦琐的伪命令,所以写出来的代码看起来十分干净.另外用fasm写16bit exe或com可能是非常好的选择,简单而高效.此外在PE格式的
import,export,resource等的处理上也都别具一格.不过目前尚不完善...
4.其他
其他诸如spasm,a86等由于我没有用过,所以不妄加评论了.
所以如果你在写win32asm的话,MASM32仍然是最好的选择.其他:For Coding Pleasure...
5.资源编译器
WIn32涉及到资源,你可以用与rc(resourcecompiler)任何兼容的资源编译器,比如用微软的VC自带的资源编译器,或者Borland resource Workshop.大部
分开发包自带资源编译器.
(三)链接器(linker)
基本上述的种类的编译器,都附带有相应的链接器和资源编辑器.只有Nasm是个例外,与nasm配合使
用的链接器有很多,最普遍应用的是alink,目前版本是1.6.
Part II:MASM32宏(Macro)的使用
如果你已经使用了MASM,那么让我们来看一下masm是什么,masm是MACRO assembler的简称,你喜欢用Macro吗?用MAcro可以缩短源代码,简化程序的
编写,便于理解,而masm的宏还是比较强大的,虽然有些地方不够贴心和简洁,下面结合我的头文件简单介绍一下几个常用的宏...
1)m2m 在masm32包里面
mov 指令不允许mov mem1,mem,mem*代表地址,你每次都
mov eax,mem
mov mem1,eax
不会吧,实际上你可以定义一个m2m宏,为了不改变寄存器内容,使用的是堆栈
m2m MACRO M1, M2
push M2
pop M1
ENDM
以后每次在mem到mem的移动就很方便了....
2)return 在C里面经常遇到return语句,在win32asm里面也经常遇到需要返回值的地方,让我门来定义一个return语句:
return MACRO arg:=<0>
mov eax,arg
ret
ENDM
这样就可以方便地使用return TRUE或者return FALSE了...
不过这还不是最终的答案我们还可以根据返回的参数进一步优化一下,这是我的优化版本.
return MACRO arg:=<0>
IFIDNI <&arg>,<0>
sub eax,eax
ELSEIFIDNI <&arg>,<1>
xor eax,eax
inc eax
ELSE
mov eax,arg
ENDIF
ret
ENDM
3)CTEXT 有时候你需要在asm里面定义一些字符串可能只用到一次,这时你就可以用CTEXT宏
CTEXT MACRO y:VARARG ;This is a good macro
LOCAL sym
CONST segment
IFIDNI ,<>
sym db 0
ELSE
sym db y,0
ENDIF
CONST ends
EXITM
ENDM
这样你就可以用inoke LoadLibrary,CTEXT("KERNEL32.dll")方便地使用了,尤其是代码比较长的时候不用翻来翻去,定义数据,然后再offset ****了...呵呵.
其他的下面的宏功能基本是一致的.
dsText MACRO name,Text:VARARG
.data
name db Text,0
.code
ENDM
下面这个宏用于在代码段里面定义数据,在链接的时候注意假如/SECTION:.text,EWR否则读写这类数据会产生非法操作.
szText MACRO name,Text:VARARG
jmp @f
name db Text,0
@@:
ENDM
你可以根据需要选用.
4)$invoke宏,用于inline Coding,在C里面你肯定看到过GetModuleFileName(GetModuleHandle(0),&buf);的语句在asm里面依然可以实现...那就是用下
面的$invoke宏:
$invoke Macro fun:REQ,args:VARARG
IFNB
invoke fun,&args
ELSE
invoke fun
ENDIF
EXITM
ENDM
不过要注意寄存器争用的情况,比如第3个参数用到eax,那么第一、二个参数就不能用这个内嵌宏.
5)RGB和$RGB
在GDI编程里面经常遇到用RGB值作为参数的情况,为了简化这以过程可以定义下面的宏
RGB MACRO red,green,blue
xor eax,eax
mov ah,blue
shl eax,8
mov ah,green
mov al,red
ENDM
然后在RGB宏后面用eax作为RGB值参数,这诗歌好想法,不过还不够好,因为占用了多余的代码行,实际上我们需要的仅仅是一格RGB值,于是有了下面的宏:
$RGB MACRO red:REQ, green:REQ, blue:REQ ;This is better...
EXITM %(red + 256 * (green + (256 * blue)))
ENDM
6)pushm和popm
这时为了简化寄存器的入栈和出栈而设计的,很简单:
pushm Macro args:VARARG
IFNB
FOR arg,
push arg
ENDM
ELSE
.ERR
ENDIF
EndM
popm Macro args:VARARG
IFNB
FOR arg,
pop arg
ENDM
ELSE
.ERR
ENDIF
EndM
7)数据定义rb,rw,rd,rq....
你喜欢这种方式吗?比如buf db 260 dup(?)是不是很烦琐用下面的宏就可以rb buf,260是不是更简单呢?这时我喜欢的....
rb Macro label:REQ,count
IFNB
label db &count dup(?)
ELSE
label db ?
ENDIF
EndM
类似的是rw,rd,rq...等具体参见我的mac.h文件.
8)revargs将参数反向排列(special thx to lyb)
在stdcall调用模式里面使用的是反向压入参数的调用约定,MASM的宏并没有提供反向参数的机制,我们得字节写一个宏.
revargs MACRO args:VARARG
LOCAL target
target TEXTEQU <>
count=0
FOR arg,
target CATSTR ,,target
count=count+1
ENDM
IF count GT 0
target SUBSTR target,1,@SizeStr(%target)-1
EXITM target
ENDIF
ENDM
用于需要反向排列参数的地方.
9)iWin32,iWin32i,避免jmp tabel,有两种方法避免生成jmp tabel(thx to EliCZ's macro)
一种我以前写的一篇文章里面介绍过,就是利用声明
_imp__&Win32Api&A proto :DWORD,....
然后
call dword ptr [_imp__&Win32Api&A]
格式另一种是ELiCZ的方法,就是声明
externdef _imp__&Win32Api&A@NUM:DWORD,其中NUM是参数个数
然后
call _imp__&Win32Api&A来调用.
由于用EliCZ的方法写的macro较长,下面根据我的方法写一个宏:
iWin32 Macro Win32API:REQ,args:VARARG
LOCAL sz1
sz1 TEXTEQU <>
% FOR pxx,
IFNB
push pxx
sz1 CATSTR sz1,<,:DWORD>
ENDIF
ENDM
sz1 SUBSTR sz1,2,@SizeStr(%sz1)-1
sz1 CATSTR <_imp__&Win32API>,< PROTO >,sz1
sz1
call DWORD PTR [_imp__&Win32API]
ENDM
下面iWin32i可以根据情况生成对ansi或者UNICODE的调用,前提是定义UNICODE=0或者TRUE...
iWin32i Macro Win32API:REQ,args:VARARG
IF UNICODE
iWin32 Win32API&W,
ELSE
iWin32 Win32API&A,
ENDIF
ENDM
注意这个宏并没有参数检查,如果你不熟悉APi建议不要使用,你可以用masm包自带的工具转换头文件来避免产生jmp tabel...如果你喜欢宏,那么就用我这种方
式.
宏不同于封装,我们可以用他来简化我们的工作而又可以清楚知道将产生什么代码,为什么不用呢?
the way of Hume,2002
humewen@