一編關於MASM宏macro的用法

來源:互聯網
上載者:User

MASM宏使用總結
導語
MASM(Macro Assembler)是由微軟公司提供的彙編工具,雖然有些年頭了,但是仍然存在於 vc.net這樣比較新的工具中。有很多彙編教科書以這個為對象,講述了如何用彙編去設計一個程式,作為電腦科學系學生的基礎課。但是,講述的內容大體上還是停留在5.1版,而且停留在DOS的時代。雖然提到了win32下的彙編,但是並沒有放在首要的位置。另一個被忽視的是作為MASM最大特色的宏,怎麼去看待彙編工具中提供的強大的宏,以及怎麼和在什麼場合下使用宏,語焉不詳。本文是作者在大量使用MASM宏,搭建了一個彙編環境下的OOP系統後,作的一個總結。 

 

善用宏,能夠減少重複編碼,以及構建強大的功能,是重用代碼,美化代碼的一個有力的工具。宏在進階語言中是一個應該被極力避免的東西,在低級語言中確未必如此。 

宏就是預先處理 
宏就是在代碼被彙編成為obj檔案之前進行的預先處理。由於發生在彙編期(Assembly-Time, 和進階語言中的編譯期是一個意思),所以不會給執行期帶來負擔,可以用作代碼產生工具,設定和C++中的模板一樣,用作meta-programming的工具。在MASM中宏可以分為兩種:1、 Text Macro 2、Procedure (Function) Macro。第一種宏就是和#define pi 31415926這樣的簡單的文本替換的宏,第二種就是那種帶參數,可以有局部變數,可以傳回值這樣的可以看作函數或者過程的宏。下面就先從Text Macro入手,看看如何使用簡單的宏。 

簡單的文本宏
你可以給一個字元序列指定一個符號名,然後在原始碼的其餘部分用這個名字來代替這個字元序列。這個指定了名字的文本就是文本宏。說白了就是文本替換。用TEXTEQU來定義一個這樣的宏。 

  name TEXTEQU <text>
  name TEXTEQU textvar
  name TEXTEQU %numvar

我這裡給出的使用說明和MASM Programmer Guide中給出的不大一樣,但是這個更能說明問題。我在這兒只解釋第一個用法,後面的用法將在講了“彙編期變數”之後再講。舉一些使用的例子。

  pi TEXTEQU <3.1416>
  DWPTR TEXTEQU <WORD PTR>
  arg1 TEXTEQU <[bp+4]>

然後在代碼中就可以用pi這些名字來代替3.1416這些。<>表示他們是字串,如果把不加<> 則會把你給出的字串當作一個彙編期的文本變數來進行求值,而這樣的話會出錯的。

彙編期的變數與常量
這個幾個東西其實都有自己的名字,其實按照用法來說就是彙編期的常量與變數的意思。比如Text Macro(對,就是前面的文本宏)用作彙編期的文本常量,Name Assignment用作彙編期的數值變數。

定義彙編期常量 

什麼是彙編期常量呢?其實也就是常量的意思,因為無論在編譯期還是執行期它都是靜態,一旦定義之後其值不能改變。回憶在C中,你用#define來定義常量。但是#define可以改變一個宏所等於的值,也就是說常量與否需要你的維護(編譯器會給出一個警告)。在MASM 中有一個關鍵字專門用來定義常量,嘗試改變常量的值會得到一個錯誤提示。

  name EQU expression
  name EQU <text>

第一個是用作定義個“數值”常量,第二個是用作定義“文本”常量。以後要特別區分開文本和數值。

定義彙編期文本變數

彙編期文本變數是對“text macro”的另外一個看法。其實它們是同一個事情。當你定義了一個文本宏之後,你可以把那個宏名看作彙編期文本變數的名字,被宏名替換的常值內容作為變數的文本值。

那麼前面說過的第二種用法:name TEXTEQU textvar就很好理解了。就是讓把一個文本變數賦給另一個文本變數。比如:

  talent TEXTEQU <genius>
  taowen TEXTEQU talent

第一行定義了一個名字為talent的彙編期文本變數,第二行把talent的值賦給了名為 taowen的變數。從結果上看這個和:

  talent TEXTEQU <genius>
  taowen TEXTEQU <talent>

是一樣的,但是第二種做法是先因為文本宏替換的作用把talent變成了genius。實際的效果是這樣的:

  taowen TEXTEQU <genius>

顯示文本變數的內容

在C中經常用printf,在運行期顯示一些變數的內容來進行調試。而在MASM中則用echo來在彙編期顯示文本變數的內容。

  china TEXTEQU <great country>
  %echo china

這樣會在彙編時的命令列中出現great country。如果你把%號去掉,則顯示的是china。你應該可以推測出%是幹什麼的了,就是對一個變數進行求值。

定義彙編期數值變數

常量有兩種那麼變數也應該有兩種。這裡就介紹彙編期數值變數的用法。

  name = expression

expression是一個數值運算式,比如:

  val = 3+4

此時val就是一個數值變數,其值為7。你也可以這麼寫:

  valexp TEXTEQU <3+4>
  val = valexp

看上去好像式把一個文本變數賦給了數值變數,進行了類型轉換(呵呵,效果是一樣的)。其實實際上是把3+4寫到了valexp處,因為文本宏進行了文本替換。

把數值變數賦給文本變數

前面我們看到了如何“把文本變數賦給數值變數”,那麼反過來呢?

  val = 3+4
  valexp TEXTEQU val

結果是提示錯誤:STest.asm(15) : error A2051: text item required。彙編器說需要文本項,那麼我們加上<>就好了。

  val = 3+4
  valexp TEXTEQU <val>

用%echo valexp檢查一下你就會發現,並不是如你所願的顯示的是7,而是val。這個是因為<>使得彙編器認為val是一個字串,由於數值變數不是文本替換的宏,並不會把val替換為7,所以當然顯示的是val。正確的做法是:

  val = 3+4
  valexp TEXTEQU %val

%號和前面的用法一樣,是用作求值。回憶一開始介紹的文本宏的用法中的第三條就是: name TEXTEQU %numvar。這個用法就是讓一個數值變數的值賦給文本變數,經常用作顯示一個數值變數的值。調試的時候這麼寫。

  pi = 3.1415926
  temp TEXTEQU %pi
  %echo temp

這個是一個很重要的調試技巧。

宏過程和宏函數
前面從簡單的文本宏引出彙編期的常量與變數。如果僅僅是用在宏外的代碼中,一個文本宏作一些簡單的替換就足夠了。它們更多的是用在複雜的宏中,這些宏可以看作過程和函數。同彙編期的變數一樣,它們是用在彙編期的。

下面將不再把text macro視為宏,而把它視為文本變數。宏直接指宏過程或者宏函數。宏過程是不帶傳回值的宏,而宏函數是帶傳回值的宏。它們都可以帶參數,也都可以有局部變數。其實可以統一的成為宏函數,或者彙編期函數

用如下的格式建立一個簡單的宏

  name MACRO
 statements
  ENDM

statements中可以進行判斷或者迴圈,可以說是非常的全功能。但是彙編期的函數和執行期的函數是很不一樣的,一個是發生在彙編期的預先處理,另一個是把執行期的執行位置改變,執行一段代碼之後返回。

  clear_eax_m MACRO
   xor eax, eax
  ENDM
  clear_eax_p Proc
 xor eax, eax
 ret
  clear_eax_p Endp

關於這個兩者的區別,我假定你已經理解了,如果不理解可以參考任何一本彙編教科書,上面有完整的彙編代碼說明為什麼不同。

給宏傳遞參數
參數對於函數的重要性不言而喻,對於宏的參數如下定義。

  name MACRO parameterlist
  statements
  ENDM

簡單情況下,對於parameterlist就是參數名字用,號格開,比如:

  clear_reg MACRO reg
 xor reg, reg
  ENDM

調用的時候,用這種格式:

  clear_reg eax

對於宏過程,這個是唯一的調用格式。

參數的傳遞和執行期的函數的參數傳遞也是很不一樣的。參數是被直接替換的。你可以作這麼一個實驗:

  TestMacro MACRO param
 echo param
 %echo param
  ENDM
  TextVar TEXTEQU <Hello>
  TestMacro TextVar

輸出的結果是TextVar和Hello。到底是怎麼回事就不用我多說了。你甚至可以進一步測試:

  TestMacro MACRO param
 param TEXTEQU <How are you>
  ENDM
  TextVar TEXTEQU <Hello>
  TestMacro TextVar
  %echo TextVar

輸出的結果是How are you。可見所謂的參數不過就是替換。參數名會被引數名給替換(引數就是調用時候傳遞過去的那些參數)。由於MASM中宏這個系統中,所有的這些變數名的符號都是在一個共同的空間之中(呵呵,怎麼聽起來像數學術語?),都是全域的東西。 

對於參數可以進行一些限定修飾,比如讓你調用的時候一定要傳遞這個參數:

  Clear_reg MACRO reg:REQ
 xor reg, reg
  ENDM

或者指定一個預設值:

  Clear_reg MACRO reg:=<eax>
 xor reg, reg
  ENDM

或者讓參數個數成為一個變數。

  Clear_reg MACRO regs:VARARG
 FOR reg, <regs>
  xor reg, reg
 ENDM
  ENDM

不過要注意的是VARARG修飾的參數必須是參數中的最後一個。

讓宏返回一個值
宏過程和宏函數的區別在於是否有傳回值。當然這裡的傳回值和執行期的函數的傳回值也是很不一樣的。執行期的函數是通過eax來傳遞傳回值的。而這裡,也不過是直接替換而已。傳回值的文法是這樣的:

EXITM textitem

一個宏函數可以有多個EXITM,就像C中的函數可以有多個return一樣。不過必須傳回值一致。看一個簡單的例子

  Who MACRO
 EXITM <taowen>
  ENDM
  %echo Who()

結果是顯示taowen。如果把()去掉,則顯示的是who。可見對於宏函數的調用一定要加上 ()。而調用宏過程則不能加()。看一個有趣的例子:

  Who MACRO temp
 %echo temp
  ENDM
  Who()

顯示的結果是()。說明()被當作傳遞給宏過程的參數了。

可以比較隨意的使用傳回值,可以把傳回值這麼用。

  Who MACRO
 EXITM <taowen>
  ENDM
  Who() TEXTEQU <genius>

這樣就定義了彙編期文本變數,值為genius。可見宏函數可以用在任何文本變數可以出現的地方,很多地方可以把進階語言中函數中那些類推過來。

局部變數
宏中可以有局部變數,它看起來像局部的,實際上不過是一些名稱上的小技巧。

對於局部變數有兩點事實:1、在函數外無法訪問,2、在對函數的不同次的調用中其值應該不受前次調用的影響。

  TestMacro MACRO
  LOCAL LocalVar
 %echo LocalVar
 LocalVar TEXTEQU <Hello>
  ENDM

如果對於函數的調用每次之間會互相影響,那麼這麼調用:

  TestMacro
  TestMacro

第一次會產生一個未定義變數的錯誤,而第二次就會輸出Hello。事實上,由於LocalVar 是LOCAL的。所以兩次都是未定義錯誤。這個就體現了局部變數的多次調用的獨立性。

下面我們來揭穿局部變數的底牌。不用我多敘述,直接看看這個你就明白了:

  TestMacro MACRO
  LOCAL LocalVar
 echo LocalVar
  ENDM
  TestMacro
  TestMacro

輸出的結果是:??0000與??0001。這個就是局部變數的實際名字。局部變數就是通過怪怪的名字讓外部無法訪問(你不知道它是什麼名字),然後在每次展開同一個宏的時候用不同的名字替換局部變數的名字,使得多次調用之間不會互相影響。

其實你可以實驗一下這個:

  ??0000 TEXTEQU <Hello>
  TestMacro MACRO
  LOCAL LocalVar
 %echo LocalVar
  ENDM
  TestMacro

顯示的結果是Hello,這樣就在一個宏過程(函數)的外部存取了局部變數。

文本操作
MASM內建了兩套文本操作功能,一個是宏函數,另一個是Directive。功能是一樣的,但是提供了表達上的靈活性。

name CATSTR [[textitem1 [[, textitem2]] ...]]
name INSTR [[position,]] textitem1, textitem2
name SIZESTR textitem
name SUBSTR textitem, position [[, length]]

這一套是Directive,作用分別是:串連文本,尋找子文本,獲得文本長度,取子文本。 

@CatStr( string1 [[, string2...]] )
@InStr( [[position]], string1, string2 )
@SizeStr( string )
@SubStr( string, position [[, length]] )

這一套是宏函數。用一個例子顯示兩套其實是一樣的:

taowen TEXTEQU @CatStr(<He is >, <genius>)
%echo taowen

taowen CATSTR <He is >, <genius>
%echo taowen

可以看到兩個例子輸出的都是He is genius。有兩點需要注意:1、@CatStr這樣的宏函數可以作為左值,被用來賦值。2、@CatStr這樣的宏函數對於參數並不自動求值,當行為和你想的不一樣的時候,加上%。

對於具體的使用不是很難,實驗一下就可以知道了。一點就是第一個字元的索引是1,而不是C中的0。

%與求值
%可能是最難使用的文法了。一般就是行為和你想的不一樣的時候,加上%實驗一下。%與 <>與!等,構成了一團糟。

一般情況下,你不用%號。用%可以把數值變數轉變為文本變數。可以用%號強製取出文本的值。比如:

  Index = 0
  NameT CATSTR <Person>, %Index
  %NameT TEXTEQU <taowen>
  %echo Person0

先對Index用%就是把數值變成文本,第二個%就是把NameT變成Person0。這裡也示範了一個產生變數名的很重要的技巧,只要把Index進行一些遞增,就能夠構建一個變數的數組了。 

<>可以一定程度上放置被求值,不過大部分情況下由於文本宏的替換不受影響,所以仍然取到的是替換後的值。!用來取消符號原有的意思。比如:

  Symbol CATSTR <Go >, <!,>, <Go>
  %echo Symbol

輸出的結果是Go, Go。如果不加!則,號會導致錯誤。有趣的是如果你在第二個Go後面加上 !本來應該是Go, Go!。結果確實一個缺少右角括弧的錯誤,原來是!把>的原有意思變化了,不再表示結束了。如果你這樣:

  Symbol CATSTR <Go >, <!,>, <Go!>>
  %echo Symbol

得出的就是Go, Go>。看出來是怎麼回事了吧。要產生!,就這麼寫:

  Symbol CATSTR <Go >, <!,>, <Go!!>
  %echo Symbol

關於什麼時候用<>什麼時候不用,我的看法是最好能用<>就用<>。具體為什麼,是因為能夠加大適用範圍。

在宏中進行迴圈
迴圈有四種:WHILE,REPEAT,FOR,FORC。文法如下:

  WHILE expression
 statements
  ENDM
  REPEAT expression
 statements
  ENDM
  FOR parameter [[:REQ | :=default]] , <argument [[, argument]]...>
 statements
  ENDM
  FORC parameter, <string>
 statements
  ENDM

WHILE與REPEAT從用法到效果是一樣的,至少我認為是一樣的。expression要求值為一個數值,可以用EQ(等於),LT(小於)這些判斷Operator來比較數值。

  I = 0
  WHILE I LT 10
 Temp TEXTEQU %I
 %echo Temp
 I = I + 1
  ENDM

輸出的結果就是0一直到9。

FOR與FORC是專門用途的迴圈,一個是用於取得一個參數列表中的各個參數,另一個是逐個取出一個字串中的每個字元。各舉兩個例子就可以明白:

  TestMacro MACRO params:VARARG
 FOR param, <params>
  %echo param
 ENDM
  ENDM
  TestMacro arg1, arg2, arg3

顯示的結果是arg1然後是arg2然後是arg3。

  FORC char, <Hello>
 %echo char
  ENDM

分別顯示的是H和e和l和l和o。

在宏中進行判斷
判斷很簡單了,不過有很多種IF,IFDEF,IFIDN,IFE,IFNDEF,IFDIF。使用上沒有什麼值得注意的都比較簡單。分別是判斷數值,判斷符號是否定義,判斷兩個文本是否一致。

關於宏還有定義在宏中的宏,OPATTR, SIZEOF, LENGTHOF, 等等許多比較進階的東西。不過我相信有前面講述的基礎,這些東西的使用不過是查查手冊的事情。

ps:

轉載一編關於MASM宏的用法。 By kiki(於2004-6-11發表)

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.