common.inc
;--------------------------------------------------------------------
; common.inc
; 宏中不要使用@@, 因为它在展开后,会与程序中定义的@@冲突
; 引用文本宏的值时,注意不要为 文本宏名 加<>, 否则会出错
; 引用参数的值作为文本值使用时,注意一定要为 形参名 加<>,否则会出错。因为 形参名 只是占位符,
; 在宏展开时会被替换为 实参的值。
; 宏的调试:定义一个局部文本宏debugtext, 赋给它调试用的信息,再配合% echo debugtext来在汇编时输出
; 或是直接用echo输出一些调试信息
ifndef COMMON_INC
COMMON_INC equ 1
;TRUE = 1
;FALSE = 0 ; 以上两个在windows.inc中有定义
CR = 0dh
LF = 0ah
TAB = 9 ; 占8个空格的显示空间
StdOut textequ <_StdOut> ; 与masm32库一致
StdErr textequ <_StdErr>
StdIn textequ <_StdIn>
; 用来查看宏的传参情况
printargs macro vars:vararg
local debugtext
debugtext textequ <>
for param,<vars>
debugtext textequ <"¶m">
% echo debugtext
endm
endm
;----------------------------------------------------
; 以下3个宏是用来定义字符串指针表。如果能把指针表及字符串都定义在常量区是最完美的,但
; 如此会出现这个问题:strtable_cont宏展开时会在指针表中间定义字符串,破坏掉表的连续性
; 在常量段定义字符串并在数据段中引用其偏移地址
TXT macro text:vararg ; 参数可为多个,格式同一般的在数据段的字符或字符串定义
local lblText
.const
lblText db text, 0
.data
exitm <offset lblText> ; 调用宏的文本被替换为此
endm
; 用来在数据区中调用,本宏展开后会在数据区中定义字符串指针表,并在常量区定义对应的字符串
; strtable tblSregsNames, TXT("cs"), TXT("ds"), TXT("ss"), TXT("es"), TXT("fs"), TXT("gs"), \
; TXT("ldtr"), TXT("tr")
; strtable tblClasses, offset tblSystemClass, offset tblDataSegClass ; 二级表
strtable macro tablename:req, strs:vararg
align 4
tablename dd strs
endm
; 因为宏调用语句有行长度的限制(用\续行的话,还是算同一行), 注意紧接着strtable后面用这个宏
; continue
strtable_cont macro strs:vararg
dd strs
endm
;----------------------------------------------------
; e.g.: jmptable name, lbl1, lbl2, lbl3, lbl4
; 引用标号时可以不用加offset
jmptable macro name:req, args:vararg
.data
align 4
name dd args
.code
endm
; 把一个数值(如数值型宏)转成文本值,方便输出调试信息
; e.g.: cnt = 1
; % echo num2text(cnt)
num2text macro num
exitm %(num)
endm
; 返回参数的个数
getargc macro args:vararg
local cnt
cnt = 0
for param, <args>
cnt = cnt + 1
endm
exitm <cnt>
endm
; 取参数序列args中的第idx个,idx从1开始
getarg macro idx:req, args:vararg
local cnt, retarg
cnt = 0
retarg textequ <>
for param, <args>
cnt = cnt + 1
if cnt eq idx
;;% echo num2text(cnt) ;;测试num2text宏
;;exitm <param> ;;只是退出for宏而已
retarg textequ <param>
exitm ;;退出当前宏
endif
endm
exitm retarg ;; 没有取到的话,返回空
endm
; pushs与pops成对,方便保存与后续恢复多个变量,帮助消除push与pop的配对问题
; e.g.: pushs eax, edx, ecx, ebx, f
; pops eax, edx, ecx, ebx, f
pushs macro vars:vararg
for param, <vars> ;;vars若为空,则进不了这个for宏
push param
endm
endm
; 与pushs成对
; 写本宏时尝试了用echo来输出调试信息
pops macro vars:vararg
local cnt;;, debugtext
cnt = getargc(vars)
while cnt
;;debugtext textequ <<getarg(cnt,vars)>>
;;% echo debugtext
pop getarg(cnt, vars)
cnt = cnt - 1
endm
endm
; bHigh与bLow得为常数,也即立即数
MAKEWORD macro bHigh, bLow
exitm <( ((bHigh) shl 8) or (bLow) )> ; 用实参替代掉<>中的形参,然后用替换后的<>中的文本替换掉调用宏的指令
endm
makeword equ MAKEWORD
; wHigh与wLow得为常数,也即立即数,与Win32 API文档中的MAKELPARAM宏类似,只是参数顺序相反
MAKEDWORD macro wHigh, wLow
exitm <( ((wHigh) shl 16) or (wLow) )> ; 用实参替代掉<>中的形参,然后用替换后的<>中的文本替换掉调用宏的指令
endm
makedword equ MAKEDWORD
; 三个参数得是常数
RGB macro red, green, blue ; R在最低字节
exitm <( ((blue) shl 16) or ((green) shl 8) or (red) )>
endm
rgb equ RGB
BLACK = RGB(0,0,0) ;windows.inc中有对应的如Black这样的仅首字母大写形式的宏
BLUE = RGB(0,0,255)
GREEN = RGB(0,255,0)
CYAN = RGB(0,255,255) ;青色、蓝绿
RED = RGB(255,0,0)
MAGENTA = RGB(255,0,255) ;洋红、红紫
YELLOW = RGB(255,255,0)
WHITE = RGB(255,255,255)
GRAY = RGB(8,8,8)
; 在常量段定义字符串并在代码段中引用其偏移地址
CTEXT macro text:vararg ; 参数可为多个,格式同一般的在数据段的字符或字符串定义
local lblText
.const
lblText db text, 0
.code
exitm <offset lblText> ; 调用宏的文本被替换为此
endm
ctext equ CTEXT
CTXT equ CTEXT ; 与masm32一致
ctxt equ CTXT
; 定义函数的静态局部变量
; 定义结构体变量,赋值时要用双层<>, 如static hitpointL, POINT, <<0, 0>>。<>是用来包含特殊字符,如空格和<>
static macro varName, varType, varValue:vararg ;值域可为多个,否则如'1','2'就只能取前一个
.data
align 4 ;
varName varType varValue
.code ; 切换回代码段,否则会报错
endm
;---------------------------- c风格 ----------------------------------
float equ <real4> ; 不能为float = <real4>
double equ <real8> ; = 后面只能是数值表达式;equ后面可以是数值表达式,也可以是文本
; retVal只能是常数值,为空,或是不计较大小写的eax
return macro retVal
;;local debugtext
;;debugtext textequ <"&retVal">
;;% echo debugtext
comment /*
return FALSE
return TRUE ; 1
return INVALID_HANDLE_VALUE ;-1
return edx
return eax
return EAX
return 6+7
% echo debugtext分别输出
"FALSE"
"TRUE"
"INVALID_HANDLE_VALUE"
"edx"
"eax"
"EAX"
"6+7"
*/
;;retVal是形参,只是个占位符,在宏展开时会被实参的值替换,
;;所以一定要加<>, 不加的话,在retVal为空的时候,会出现问题
ifb <retVal>
ret
exitm
endif
ifidni <retVal>,<eax> ;;不区分大小写
ret
exitm
endif
if retVal eq 0 ;;用eq的话,如果retVal是如edx等其他的文本值,展开时会出错
xor eax, eax
elseif retVal eq 1
xor eax, eax
inc eax
elseif retVal eq -1
xor eax, eax
dec eax
else
mov eax, retVal
endif
ret
endm
exit macro exitCode:=<0> ; 默认值为0,用<>包起来,因为默认值表示为文本值
invoke ExitProcess, exitCode
endm
fclose macro hFile:req ;req表示参数是必需的
invoke CloseHandle, hFile
endm
malloc macro cbSize
invoke GlobalAlloc, GMEM_FIXED or GMEM_ZEROINIT, cbSize
exitm <eax>
endm
free macro pMem
invoke GlobalFree, pMem
endm
;--------------------------------------------------------------------
;---------------------------- DOS命令风格 -----------------------------
chdir macro pathName
local char, lblPathName
char substr <pathName>,1,1
ifidn char,<">
.const
lblPathName db pathName, 0
.code
invoke SetCurrentDirectory, offset lblPathName
else
ifidn char, <'>
.const
lblPathName db pathName, 0
.code
invoke SetCurrentDirectory, offset lblPathName
else
;; pathName为路径字符串的地址
invoke SetCurrentDirectory, pathName
endif
endif
endm
cd equ chdir
;---------------------------------------------------------------------
;---------------------------------------------------------------------
; fld/fild的操作数不支持常数, 本宏模拟fld/fild加载常数。注意fpVal/intVal得为常数
; e.g.: fld8 1234.56789
; fild4 1234
fld4 macro fpVal
local name
.data
align 4
name real4 fpVal
.code
fld name
endm
fld8 macro fpVal
local name
.data
align 4
name real8 fpVal
.code
fld name
endm
fld10 macro fpVal
local name
.data
align 4
name real10 fpVal
.code
fld name
endm
; 16位整数
fild2 macro intVal
local name
.data
align 2
name dw intVal
.code
fild name
endm
fild4 macro intVal
local name
.data
align 4
name dd intVal
.code
fild name
endm
fild8 macro intVal
local name
.data
align 4
name dq intVal
.code
fild name
endm
;---------------------------------------------------------------------
; "aaa.bbb.ccc.ddd"
MAKEIP macro aaa, bbb, ccc, ddd
exitm <( ((aaa) shl 24) or ((bbb) shl 16) or ((ccc) shl 8) or (ddd) )>
endm
makeip equ MAKEIP
; e.g.: mov hInstance, FUNC(GetModuleHandle,NULL)
FUNC macro args:vararg
invoke args
exitm <eax>
endm
func equ FUNC
rv equ FUNC ; 与masm32一致: return value
; title默认为NULL, 显示为“错误”或"error"
msgbox macro owner:=<NULL>, text:req, title:=<NULL>, style:=<MB_OK>
invoke MessageBox, owner, text, title, style
endm
GetLastErrMsg macro
ifndef @@errmsg@@
.data?
@@errmsg@@ db 1024 dup (?)
endif
.code
invoke GetLastError
invoke FormatMessage, FORMAT_MESSAGE_FROM_SYSTEM, NULL, \
eax, 0, offset @@errmsg@@, sizeof @@errmsg@@, NULL
exitm <offset @@errmsg@@>
endm
m2m macro m1, m2
push m2
pop m1
endm
; 用于控制台
write macro text:vararg
invoke StdOut, CTXT(text)
endm
print equ write
; 在代码节区插入字符串
inlineText macro textName, text:vararg
local lblNext
;; 宏中不要使用@@, 因为它在展开后,会与程序中定义的@@冲突
jmp lblNext ;;@f
textName db text, 0
lblNext: ;;@@:
endm
inlinetext equ inlineText
;-------------------- oop之c++风格 ----------------------------------
class macro className
className struct
VirtFuncTAble dd 32 dup (?)
szClassName db "className", 0
className ends
endm
new macro className
invoke GetProcessHeap
invoke HeapAlloc, eax, 0, sizeof className
mov [eax], offset className&VirtFuncTable
exitm <eax>
endm
delete macro pObj
mov eax, pObj
mov eax, [eax] ; 得到类的虚函数表的地址
call [eax] ; 调用虚函数表的第一个函数,即销毁自身
invoke GetProcessHeap
invoke HeapFree, eax, 0, pObj
endm
method macro
endm
;--------------------------------------------------------------------
; 定义纯英文的Unicode字符串
; 其实用dw定义,更方便,生成的汇编文本也越少
wstr macro text:vararg
local lblText, textline, strvalue, ch, idx, cnt ;; 局部标号与局部宏名的定义
;; 若上面的宏名不用local定义,在宏外可以访问到
;;得再这样定义一次,否则在无名宏中不可见
textline textequ <lblText db >
strvalue textequ <>
ch textequ <>
idx = 0
cnt sizestr textline ;; 若宏生成的汇编代码文本行太长,汇编器不会接受
for arg, <text>
ch substr <arg>, 1, 1
ifidn ch,<">
;; 两头以"包住字符串,字符串中不会有", 因为masm不允许
idx = 2
while 1
ch substr <arg>, idx, 1
ifidn ch, <">
exitm
endif
ifb strvalue
strvalue catstr <">, ch, <",0>
cnt = cnt + 5
else
strvalue catstr strvalue, <,">, ch, <",0>
cnt = cnt + 6
endif
if cnt ge 80 ;; 换一行输出
textline catstr textline, strvalue
.const
textline
textline textequ <db >
cnt = 3
strvalue textequ <>
endif
idx = idx + 1
endm
else
ifidn ch,<'>
;; 两头以'包住字符串,字符串中不会有', 因为masm不允许
idx = 2
while 1
ch substr <arg>, idx, 1
ifidn ch, <'>
exitm
endif
ifb strvalue
strvalue catstr <'>, ch, <',0>
cnt = cnt + 5
else
strvalue catstr strvalue, <,'>, ch, <',0>
cnt = cnt + 6
endif
if cnt ge 80 ;; 换一行输出
textline catstr textline, strvalue
.const
textline
textline textequ <db >
cnt = 3
strvalue textequ <>
endif
idx = idx + 1
endm
else
;; 不知道arg的长度。为保险,输出后换一行
ifb strvalue
strvalue catstr <arg>, <,0>
else
strvalue catstr strvalue, <arg>, <,0>
endif
textline catstr textline, strvalue
.const
textline
textline textequ <db >
cnt = 3
strvalue textequ <>
endif
endif
endm
ifb strvalue
strvalue textequ <0,0>
else
strvalue catstr strvalue, <,0,0>
endif
textline catstr textline, strvalue
.const
textline
.code
exitm <offset lblText>
endm
; 在代码节区嵌入纯英文的Unicode字符串
; 其实用dw定义,更方便,生成的汇编文本也越少
inlineWText macro name:req, text:vararg
local lblNext, textline, strvalue, ch, idx, cnt ;; 局部标号与局部宏名的定义
;; 若上面的宏名不用local定义,在宏外可以访问到
;;得再这样定义一次,否则在无名宏中不可见
textline catstr <name>, < db >
strvalue textequ <>
ch textequ <>
idx = 0
cnt sizestr textline ;; 若宏生成的汇编代码文本行太长,汇编器不会接受
.code
jmp lblNext
for arg, <text>
ch substr <arg>, 1, 1
ifidn ch,<">
;; 两头以"包住字符串,字符串中不会有", 因为masm不允许
idx = 2
while 1
ch substr <arg>, idx, 1
ifidn ch, <">
exitm
endif
ifb strvalue
strvalue catstr <">, ch, <",0>
cnt = cnt + 5
else
strvalue catstr strvalue, <,">, ch, <",0>
cnt = cnt + 6
endif
if cnt ge 80 ;; 换一行输出
textline catstr textline, strvalue
.code
textline
textline textequ <db >
cnt = 3
strvalue textequ <>
endif
idx = idx + 1
endm
else
ifidn ch,<'>
;; 两头以'包住字符串,字符串中不会有', 因为masm不允许
idx = 2
while 1
ch substr <arg>, idx, 1
ifidn ch, <'>
exitm
endif
ifb strvalue
strvalue catstr <'>, ch, <',0>
cnt = cnt + 5
else
strvalue catstr strvalue, <,'>, ch, <',0>
cnt = cnt + 6
endif
if cnt ge 80 ;; 换一行输出
textline catstr textline, strvalue
.code
textline
textline textequ <db >
cnt = 3
strvalue textequ <>
endif
idx = idx + 1
endm
else
;; 不知道arg的长度。为保险,输出后换一行
ifb strvalue
strvalue catstr <arg>, <,0>
else
strvalue catstr strvalue, <arg>, <,0>
endif
textline catstr textline, strvalue
.code
textline
textline textequ <db >
cnt = 3
strvalue textequ <>
endif
endif
endm
ifb strvalue
strvalue textequ <0,0>
else
strvalue catstr strvalue, <,0,0>
endif
textline catstr textline, strvalue
.code
textline
lblNext:
endm
inlinewtext equ inlineWText
else
echo -----------------------------------------
echo WARNING Duplicate include file common.inc
echo -----------------------------------------
endif
;----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
; common.asm
.686p
.model stdcall, flat
option casemap: none
include common.inc
include file.inc
includelib file.lib ; 静态库
include console.inc
includelib console.lib ; 静态库
include windows.inc
include kernel32.inc
includelib kernel32.lib
include user32.inc
includelib user32.lib
include gdi32.inc
includelib gdi32.lib
include comctl32.inc
includelib comctl32.lib
include shell32.inc
includelib shell32.lib
include comdlg32.inc
includelib comdlg32.lib