what type of argument must be passed to the exitprocess procedure
- Download source code - iv.8 KB
Introduction
A macro is a symbolic name that y'all give to a serial of characters called a text macro or give to one or more than statements, chosen a macro procedure or function. As the assembler evaluates each line of your programs, it scans the source lawmaking for the proper noun of an early on defined macro, so substitutes the macro definitions for the macro name. A macro procedure is a named cake of associates language statements. Once defined, it can exist invoked (chosen) many times, fifty-fifty receiving dissimilar arguments passed. Then you can avoid repeatedly writing the same code in places.
In this commodity, nosotros'll talk about some usages that are not thoroughly discussed or not conspicuously documented. We'll bear witness and analyze examples in the Microsoft Visual Studio IDE. The topics will be related miscellaneously to the Repeat
directive, checking the parameter type and size in a macro procedure, and generating memory in repetitions with the current location counter $
.
All the materials presented here came from my teaching [ii] for years. Thus, to read this article, a general agreement of Intel x86-64 assembly language is assumed, and being familiar with Visual Studio 2010 or above is required. Preferred, having read a textbook typically like [3]; or the MASM Programmer's Guide [5] that was originated in 1992 by Microsoft merely still so valuable in today's MASM learning. If you lot are taking an Assembly Linguistic communication Programming class, this could be a supplemental reading or study reference.
Using Echo to the Output Window
According to MSDN, the Repeat
directive displays a message to the standard output device. In Visual Studio, you are able to use ECHO
to transport a string to the IDE's Output pane to evidence assembling/compiling messages, such as warning or error, similar to what MSBuild does.
A simplified snippet to examination Repeat
would exist like this:
.386 .model flat,stdcall ExitProcess proto,dwExitCode:DWORD mTestEcho MACRO ECHO ** Examination Echo with Zero ** ENDM .code main PROC mTestEcho invoke ExitProcess,0 main ENDP END master
This code was working fine in VS 2008. Unfortunately, since VS 2010, the Echo
directive does not event in writing to the output window in Visual Studio. As seen in [4], information technology doesn't, unless you configure it to generate verbose output to assembly your code. To do this, you should go:
Tools->Options->Projects and Solutions->Build and Run
And from the "MSBuild projection build output verbosity" dropdown box, cull the "Detailed" option (observe that default is "Minimal"):
To test the macro, you only need to compile an individual .ASM file, instead of building the whole projection. Only right click on your file and choose Compile:
To verify, you have to sentry the display in the VS Output pane. The Echo
directive is really working, nonetheless, the message you interested "** Test Echo with Nothing **" is buried somewhere in hundreds of lines that MSBuild generated. You must search tediously to find:
In learning MASM Assembly programming, this is definitely not a preferred mode to practice. What I suggested is to use text "Mistake:" or "Warning:" with ECHO
, while still leaving MSBuild default output setting "Minimal" unchanged.
1. Output as Error
Simply add together "Mistake:
" in the Echo
statement in the macro and proper name it as mTestEchoError
:
mTestEchoError MACRO Repeat Mistake: ** Test Echo with Error ** ENDM
Now let'southward call mTestEchoError
in main PROC
. Compiling the code, you can see the minimal output and so concise as below. Notice that because of Error here, reasonably the consequence said failed.
ii. Output as Warning
Only add "Warning:
" in the Echo
statement and proper name it as mTestEchoWarning
:
mShowEchoWarning MACRO Repeat Alarm: ** Exam Echo with Alarm ** ENDM
And so calling mTestEchoWarning
in chief PROC
and compiling it, y'all can see the minimal output much simpler equally beneath. Since but Warning designated, the compiling succeeded.
Equally yous are enlightened, this mode, the Echo
directive generates curtailed and clear letters without you searching for the outputs. The sample is in TestEcho.asm for download.
Checking Parameter Type and Size
When you laissez passer an argument to a macro procedure, the procedure receives it from the parameter although just a text substitution. Ordinarily, you will check some weather from the parameter to do something accordingly. Since this happens at assembling time, it means that the assembler would choose some instructions if a condition is satisfied, else provide other instructions for unsatisfied if needed. Definitely, you can check the constant statement values, either in cord or number. Notwithstanding another useful bank check perhaps, is based on the parameter type or size for registers and variables. For example, one macro procedure only accepts unsigned integers and featherbed signed ones, while the second macro may deal with 16 and 32-chip without for eight and 64-bit arguments.
ane. Argument as a Retentiveness Variable
Let'southward define three variables here:
.information swVal SWORD i wVal Discussion 2 sdVal SDWORD 3
When applying the TYPE
and <lawmaking>SIZEOF
operators to these variables, we merely have:
mov eax, TYPE swVal mov eax, SIZEOF swVal mov eax, Type wVal mov eax, SIZEOF wVal mov eax, Blazon sdVal mov eax, SIZEOF sdVal
Equally seen above, at that place is no numeric difference either between Blazon
and SIZEOF
, or between WORD
and SWORD
. The start four instructions all are moving the byte count 2
to EAX
. However, Blazon
can do more than than just returning byte counts. Let'southward try to check SWORD
blazon and size with the parameter par
:
mParameterTYPE MACRO par IF TYPE par EQ Blazon SWORD Repeat warning: ** Blazon par is Type SWORD ELSE ECHO warning: ** TYPE par is Non Blazon SWORD ENDIF ENDM mParameterSIZEOF MACRO par IF SIZEOF par EQ SIZEOF SWORD Repeat warning: ** SIZEOF par is SIZEOF SWORD ELSE ECHO alarm: ** SIZEOF par is Not SIZEOF SWORD ENDIF ENDM
And then calling two macros by passing the above defined variables
Repeat alert: --- Checking TYPE and SIZEOF for wVal --- mParameterTYPE wVal mParameterSIZEOF wVal ECHO warning: --- Checking TYPE and SIZEOF for swVal --- mParameterTYPE swVal mParameterSIZEOF swVal Echo warning: --- Checking TYPE and SIZEOF for sdVal --- mParameterTYPE sdVal mParameterSIZEOF sdVal
Run into the following results in Output:
Obviously, the TYPE
operator tin can be used to differentiate the signed or unsigned arguments passed, as SWORD
and Give-and-take
are different types. While SIZEOF
is simply a comparison of byte counts, equally SWORD
and WORD
are both two bytes. The last 2 checks means the type of SDWORD
is not SWORD
and the size of SDWORD
is 4 bytes not 2.
Furthermore, let'due south make directly checks, since two operators also can apply to data type names hither:
mCheckTYPE MACRO IF Blazon SWORD EQ TYPE Word ECHO warning: ** TYPE SWORD EQ TYPE Give-and-take ELSE ECHO alarm: ** TYPE SWORD Non EQ TYPE WORD ENDIF ENDM mCheckSIZEOF MACRO IF SIZEOF SWORD EQ SIZEOF Word Echo warning: ** SIZEOF SWORD EQ SIZEOF Discussion ELSE Echo warning: ** SIZEOF SWORD Non EQ SIZEOF Give-and-take ENDIF ENDM
The following result is intuitive and straightforward:
two. Argument as a Register
Since an argument can be a annals, let's call ii previous macros to check its TYPE
and SIZEOF
:
mParameterTYPE AL mParameterSIZEOF AL mParameterTYPE AX mParameterSIZEOF AX
We receive such messages:
As we see hither, for blazon check, neither AL
nor AX
(even xvi-bit) is signed WORD
. Really, you cannot employ SIZEOF
to a annals that causes assembling error A2009
. You lot can verify it direct:
mov ebx, SIZEOF al mov ebx, TYPE al
Only which type is for registers? The reply is all registers are unsigned by default. Simply make this:
mParameterTYPE2 MACRO par IF Blazon par EQ WORD Echo warning: ** Type par is WORD ELSE Echo warning: ** Type par is Non Discussion ENDIF ENDM
And phone call:
mParameterTYPE2 AL mParameterTYPE2 AX
Also notice that I direct utilise the information type name Discussion
here equivalent to using Blazon Discussion
.
3. An Case in Practice
Now let's take a look at a concrete example that requires moving an statement of a 8, 16, or 32-bit singed integer into EAX
. To create such a macro, we take to utilise either the instruction mov
or the sign-extension movsx
based on the parameter size. The following is i possible solution to compare the parameter's type with the required sizes. The %OUT
is the same equally ECHO
equally an culling.
mParToEAX MACRO intVal IF TYPE intVal LE SIZEOF Discussion movsx eax, intVal ELSEIF Type intVal EQ SIZEOF DWORD mov eax,intVal ELSE %OUT Mistake: *************************************************************** %OUT Fault: Argument intVal passed to mParToEAX must be viii, 16, or 32 bits. %OUT Mistake:**************************************************************** ENDIF ENDM
Test it with different sizes and types for variables and registers:
mParToEAX bVal mParToEAX swVal mParToEAX wVal mParToEAX sdVal mParToEAX qVal mParToEAX AH mParToEAX BX mParToEAX EDX mParToEAX RDX
Every bit expected, the Output shows the following messages to reject qVal
reasonably. Besides fine is an error reported for RDX
, as our 32-bit projection doesn't recognize a 64-fleck annals.
You lot can try the downloadable lawmaking in ParToEAX.asm. Furthermore, allow's generate its listing file to run across what instructions the assembler has created to substitute macro calls. As expected, bVal
, swVal
, wVal
, and sdVal
are good just without qVal
; while AH
, BX
, and EDX
good merely without RDX
:
00000000 .data 00000000 03 bVal BYTE 3 00000001 FFFC swVal SWORD -4 00000003 0005 wVal Give-and-take 5 00000005 FFFFFFFA sdVal SDWORD -6 00000009 qVal QWORD seven 0000000000000007 00000000 .code 00000000 main_pe PROC mParToEAX bVal 00000000 0F BE 05 one movsx eax, bVal 00000000 R mParToEAX swVal 00000007 0F BF 05 1 movsx eax, swVal 00000001 R mParToEAX wVal 0000000E 0F BF 05 one movsx eax, wVal 00000003 R mParToEAX sdVal 00000015 A1 00000005 R 1 mov eax,sdVal mParToEAX qVal mParToEAX AH 0000001A 0F Be C4 1 movsx eax, AH mParToEAX BX 0000001D 0F BF C3 i movsx eax, BX mParToEAX EDX 00000020 8B C2 1 mov eax,EDX mParToEAX RDX 1 IF TYPE RDX LE SIZEOF WORD AsmCode\ParToEAX.asm(45) : error A2006:undefined symbol : RDX mParToEAX(1): Macro Called From AsmCode\ParToEAX.asm(45): Main Line Lawmaking 1 ELSE AsmCode\ParToEAX.asm(45) : error A2006:undefined symbol : RDX mParToEAX(3): Macro Called From AsmCode\ParToEAX.asm(45): Master Line Code invoke ExitProcess,0 00000029 main_pe ENDP END
Generating Data in Repetitions
In this section, we'll talk almost using macros to generate a retentiveness block, an assortment of integers in the information segment, rather than calling macros in lawmaking. We'll prove three ways to create the same linked list: using an unchanged location counter $
, retrieving changed values from the counter $
, and calling a macro in data segment.
1. Using an Unchanged Locate Counter $
I simply borrowed the LinkedList
snippet from the textbook [3] to modify it with eight nodes as:
LinkedList ->11h ->12h ->13h ->14h ->15h ->16h ->17h ->18h ->00h
I added six extra DWORD
s of 01111111h
at the cease for padding, although unnecessary while easy to format in the Memory window to watch:
ListNode STRUCT NodeData DWORD ? NextPtr DWORD ? ListNode ENDS TotalNodeCount = eight .data Counter = 0 LinkedList LABEL PTR ListNode REPT TotalNodeCount Counter = Counter + 1 ListNode <Counter+10h, ($ + Counter * SIZEOF ListNode)> ENDM ListNode <0,0> DWORD 01111111h, 01111111h, 01111111h, 01111111h, 01111111h, 01111111h
The memory is created. The list header is the characterization LinkedList
, an alias that points to 0x00404010
:
Each node contains iv-byte DWORD
for NodeData
and another DWORD
for NextPtr
. As Intel IA-32 using piddling endian, the first integer in retentiveness 11 00 00 00
, is 00000011
in hexadecimal; and its next pointer xviii forty xl 00
, is 0x00404018
. So the 2 rows encompass all eight listing nodes. In the tertiary row, the first node with two zero DWORD
s acts as a tail (although a waste node). Immediately followed is padding of 6 01111111
.
Now let's encounter what happens to the current location counter $
. As mentioned in [iii]:
The expression ($
+ Counter
* SIZEOF ListNode
) tells the assembler to multiply the counter past the ListNode
size and add their production to the current location counter. The value is inserted into the NextPtr
field in the structure. [It'south interesting to notation that the location counter'due south value ($
) remains fixed at the first node of the listing.]
This is really true that the value of $
always remains 0x00404010
without changing in each iteration in the REPT
block. The NextPtr
address calculated past ($
+ Counter
* SIZEOF ListNode
) makes node by node to link together to generate LinkedList
eventually. However, you might enquire if we could become the actual electric current memory address to use in iteration? Yes. Hither information technology comes.
ii. Retrieving Changed Values From the Location Counter $
.data Counter = 0 LinkedList2 Label PTR ListNode REPT TotalNodeCount Counter = Counter + one ThisPointer = $ ListNode <Counter+20h, (ThisPointer + SIZEOF ListNode)> ENDM ListNode <0,0> DWORD 02222222h, 02222222h, 02222222h, 02222222h, 02222222h, 02222222h len = ($ - LinkedList)/Blazon DWORD
Hey, almost nothing changed simply to proper noun a new symbolic constant ThisPointer
= $
that just assigns the $
'south electric current memory address to ThisPointer
. Now nosotros can utilise ThisPointer
in the similar calculations to initialize the NextPtr
field of ListNode
object by a simpler expression (ThisPointer
+ SIZEOF ListNode
). This also makes node by node to link one another to generate LinkedList2
this time. You can cheque LinkedList2
's retention, 0x00404070
:
To differentiate the first LinkedList
, I let Counter+20h
to make it as:
LinkedList2 ->21h ->22h ->23h ->24h ->25h ->26h ->27h ->28h ->00h
By comparing ii retentivity blocks, both perform exactly the same functionality. Notice that at final, I purposely calculate the len
to meet how many DWORD
s generated until now.
len = ($ - LinkedList)/TYPE DWORD
Equally an interesting exercise, please think of the value of len
in your listen. In code, move len
to a register to verify.
3. Calling a Macro in Information Segment
By making the 3rd linked list, nosotros can understand that not but can yous call a macro in code, but you lot can also call ane in the data segment. For this purpose, I ascertain a macro named mListNode
with a parameter called starting time
, where a ListNode
object is simply initialized. To differentiate the previous two, I make Counter+30h
for NodeData
and assign NodePtr equally (starting time
+ Counter
* SIZEOF ListNode
).
.information mListNode MACRO start Counter = Counter + i ListNode <Counter+30h, (start + Counter * SIZEOF ListNode)> ENDM LinkedList3 = $ Counter = 0 REPT TotalNodeCount mListNode LinkedList3 ENDM ListNode <0,0> DWORD 03333333h, 03333333h, 03333333h, 03333333h, 03333333h, 03333333h
The third list looks like:
LinkedList3 ->31h ->32h ->33h ->34h->35h ->36h->37h ->38h->00h
Nosotros now accept the lesson from LinkedList2
by having LinkedList3
= $
get-go at the beginning. Find I simply use symbolic constant LinkedList3
as the third list header, instead of the Label
directive. Now I set up the REPT
repetition with only i macro call by passing the header accost LinkedList3
to mListNode
. That'south information technology! Run into retention at 0x004040D0
:
Imagine what if you lot pass $
every bit an argument to mListNode
, without LinkedList3
= $
?
4. Checking an Address and Traversing a Linked List
Finally, let us put all generations of 3 lists together and run LinkedList.asm (available for download). In the code segment, I first remember iii list headers' addresses as below:
mov edx,Start LinkedList mov ebx,OFFSET LinkedList2 mov esi,OFFSET LinkedList3 mov eax, len
Equally expected EDX
, 00404010
is for LinkedList
; EBX
, 00404070
for LinkedList2
; and ESI
, 004040D0
for LinkedList3
. The whole memory of three lists is neighboring each other as shown:
Notice because of LinkedList3
as a symbolic 1, nosotros don't even accept to employ the Kickoff
operator hither. Let'due south go out ESI
for the LinkedList3
and traverse this list to meet every NodeData
values with a loop like this:
NextNode: mov eax, (ListNode PTR [esi]).NextPtr cmp eax, 0 je quit mov eax, (ListNode PTR [esi]).NodeData mov esi, (ListNode PTR [esi]).NextPtr jmp NextNode quit:
Unfortunately, we oasis't involved whatsoever implementation of an output procedure to call here to show EAX
that NodeData
moved. But in your debugging, but setting a break indicate in that location to watch EAX
should exist enough to verify from 31h
, 32h
, …, to 38h
.
Summary
By scrutinizing the above examples, we exposed something that you may not know about the macro in MASM assembly programming. An assembly language programme can be executed with Intel or AMD specified instructions at runtime. While on the other side, MASM provides many directives, operators, and symbols to command and organize the instructions and retentiveness variables during the assembling fourth dimension, similar to preprocessing in other programming languages. In fact, with all the features, the MASM macro itself could be considered every bit a sub or mini programming language with iii control mechanisms of sequential, provisional, and repetition.
However, some usages of MASM macro accept non been discussed in detail. In the article, we beginning introduced a better fashion to output your error or alert text making it so piece of cake to trace macro behaviors. Then with if
-else
structures, we presented how to cheque the type and size for a macro's parameter that is a usual practice either for memory or register arguments. Finally, nosotros discussed the macro repetitions with 3 examples to generate the aforementioned linked list, as well equally a ameliorate understanding to apply the current address locator $
symbol. The downloadable zip file contains all samples in .asm files. The projection file MacroTest.vcxproj has been created in VS 2010, while it tin exist opened and upgraded in any recent VS version.
This article does non involve hot technologies like .NET or C#. Assembly Linguistic communication is comparatively traditional without much sensation. But please refer to TIOBE Programming Customs Alphabetize, the ranking of Assembly Linguistic communication is on the rise recently, which means unlike types of assembly languages play an important role in new device development. Academically, Assembly Linguistic communication Programming is considered equally a enervating form in Information science. Therefore, I hope this commodity could serve as trouble-solving examples for students and perchance, developers as well.
References
- Microsoft Macro Assembler Reference
- CSCI 241, Assembly Language Programming class site
- Kip Irvine, Associates Language for x86 Processors, 7th edition
- MASM Echo directive does not result in writing to the output window
- MASM 6.1 Documentation
History
- Feb 26, 2016 -- Original version posted
Source: https://www.codeproject.com/Articles/1080585/Something-You-May-Not-Know-About-the-Macro-in-MASM
0 Response to "what type of argument must be passed to the exitprocess procedure"
Post a Comment