FreshLib directory: macros
This directory contains several libraries that provides common convenience functions to be used with big assembly projects. All libraries can be included in the project at once from the file "allmacros.inc".
There is no overhead at including all those libraries because there is no code to be generated, just macros. There is a little delay in compile time but thanks to fasm's speed, it is barely noticeable.
Lets examine each one of these libraries.
In general this library provides ways of definition and invoking of the procedures with argument passing through the stack. It supports STDCALL and CCALL calling conventions.
macro proc name, [arg] macro begin macro endp macro return macro cret macro locals macro endl
These macros define a procedure, creates a stack frame for the local variables and defines symbolic names for the arguments. The macro "proc" defines the global label "name" as a name for the procedure. All arguments and local variables are defined as a local labels with regard to the name of the procedure. That is why all arguments and local variables must have names beginning with dot.
Between the line with "proc" and "begin", any number of local variables can be defined. The macro "begin" marks the begining of the procedural code.
The macro "endp" marks the end of the procedural code.
The return from procedure instruction is provided by macros "return" or "cret" depending on the calling convention we want to use: "return" clears the arguments from the stack and "cret" do not.
Inside the procedure, a secondary stack frame can be allocated with the pair "locals" and "endl". All data definitions, enclosed between these macros will define a secondary stack frame that is a continuation of the stack frame defined between "proc" and "begin".
Any number of "locals" and "endl" pairs can be used, but all of these secondary stack frames will overlap between each other. This feature is specially intended to provide savings of stack space and at the same time, to provide talking variable names for the different parts of more complex procedures.
For example (in Win32) if we have complex window procedure that have to process multiple messages: One of the message handlers may need one variable .rect. Another message handler may need two variables called .point1 and .point2. But the procedure as a whole is never going to need all those variables at the same time, because it process only one message at a time. On the other hand it may need the variable .ctrldata for every message processed. The optimal solution is to define the variables in the following way:
proc CtrlWinProc,.hwnd,.wmsg,.wparam,.lparam .ctrldata dd ? begin invoke GetWindowLong, [.hwnd], GWL_USERDATA mov [.ctrldata], eax cmp [.wmsg], WM_MESSAGE1 je .message1 cmp [.wmsg], WM_MESSAGE2 je .message2 return .message1: locals .rect RECT endl return .message2: locals .point1 POINT .point2 POINT endl return endp
In the above example, .ctrldata is defined on [EBP-4]; .rect is defined on [EBP-20]; .point1 is also defined on [EBP-20] and .point2 is defined on [EBP-12].
As you can see, .rect occupies the same memory as .point1 and .point2, but .ctrldata is never overlapped and exists independently.
As a general rule, you have to use the definitions between "proc" and "begin" for local variables that are used in every call of the procedure and separate locals/endl definitions for variables needed for the particular branches inside the procedure.
This approach will always provide the optimal size for the locals stack frame.
macro initialize macro finalize
The macros "initialize" and "finalize" defines one special type of procedures that during compilation are registered in a two separate lists - one for "initialize" and one for "finalize" procedures.
After that using the macros "InitializeAll" and "FinalizeAll", all these procedures can be call at once. "initialize" procedures are call in the order of their definition and "finalize" procedures in reverse order.
These macros provides standard and consistent way to process the initialization and the finalization of the libraries and modules of the application.
Procedures defined with "initialize" and "finalize" must have no any arguments.
FreshLib uses this mechanism and the user is free to use it also.
macro stdcall proc, [arg] macro ccall proc, [arg] macro invoke proc, [arg] macro cinvoke proc, [arg]
This macros call the procedures with STDCALL and CCALL calling convention.
"stdcall" macro pushes the arguments to the stack in reverse order and then call the procedure with label "proc". As long as the macro "stdcall" does not provide any stack cleanup (this duty is assigned to the procedure) the arguments can be pushed in free manner using, for example, separate push instructions for part of the arguments and arguments in the stdcall line for the remaining arguments. This can be very convenient in some cases. For example see the following source:
stdcall CreateSomeObject push eax stdcall DoSomething stdcall DeleteSomeObject
Here, the procedure DoSomething changes the value of eax, so the handle is saved in the stack. The procedure DeleteSomeObject needs one argument - a handle of the object. But as long as the proper value is already in the stack, it is mindless to pop the value and then to push it again. So the source calls DeleteSomeObject without any arguments. The procedure knows the proper number of arguments (one in this example) and clean the stack properly.
The standard (and wrong) approach is to pop the argument from the stack and then to pass it to the procedure explicitly is the stdcall statement:
stdcall CreateSomeObject push eax ; save the handle. stdcall DoSomething pop eax ; ??? Why ??? stdcall DeleteSomeObject, eax
This source will generate the meaningless instructions sequence:
pop eax push eax
"invoke" macro is the same as "stdcall" with the only difference - it calls the procedure indirectly (call [proc] instead of call proc).
This mechanism is used to call the functions imported from external dynamic linked libraries.
"ccall" macro calls a procedure with CCALL convention. This means that the procedure returns with simple "retn", without cleaning the parameters from the stack. Then "ccall" macro provides instructions that remove the arguments from the stack.
Because ccall have to know the exact count of passed arguments, all arguments have to be passed explicitly as a values in the ccall statement.
Tricks as described above will not work properly and leads to stack not properly cleaned after the call.
"cinvoke" is the same as ccall, but using indirect call. The reason is the same as with "invoke" macro.
About the calling conventions: While all Win32 dynamic linked libraries uses STDCALL convention, most (if not all) of Linux libraries uses CCALL convention.
All code libraries of Fresh use STDCALL calling convention and it is platform independient.
This library defines several macros intended to deal with data definitions.
Usually all data definitions have to be placed in special section of the executable file. This is not very convenient, because the code that process this data and the data definitions must reside in separate places of the source code, and in most cases even in different files.
The idea of globals.inc macro library is to allow the data definition to be described anywhere in the source code, but these definitions to be defined at once, at the place the programmer wants - usually in the data section of the program.
macro uglobal macro iglobal macro endg macro IncludeAllGlobals
"uglobal" begins block for undefined data definition. The block ends with "endg" macro. Between "uglobal" and "endg" macro any count of data definitions can be inserted.
Note that because uglobal block defines undefined data, it is only the labels and the size of data that have meaning inside this block. Any data defined with data definition directive will not be included in the binary file.
The undefined data will be defined later at the place where "IncludeAllGlobals" macro resides.
The undefined data is always placed at the end of all data definitions, so it will never increase the size of the executable file.
"iglobal" macro, again combined with "endg" defines initialized data. The data defined in the block will be created at "IncludeAllGlobals" statement.
This block increases the size of the executable file, because it contains sensible data, that have to be included in the file.
Actually, neither uglobal, nor iglobal blocks defines any data immediately.
Instead, the data definitions are stored in a list. The real definition occurs later, when IncludeAllGlobals macro is invoked.
For this reason, IncludeAllGlobals must be placed in the source after all used global data blocks.
struc text [val]
The macro "text" is actually a structure. It needs to be preceded by some label name.
This macro defines a zero terminated string constant, and also a local label for this string in the "sizeof" global label.
The "text" macro, similar to iglobal and uglobal simply stores string data for defer definition.
This definition, as for all global data macros, occur in IncludeAllGlobals invocation.
Why to define separate macro for the strings and not to use the normal iglobal block? The thing is that the macro "text" was planned to check the strings and to not define any string more than once. In the case of repetitive strings, this macro should return the pointer to the already defined string constant.
In that case, it would be very convenient and harmless to use string constants in the function calling macros - stdcall, ccall etc.
Unfortunately, regardless of the power of fasm macro language, this functionality cannot be implemented. Or, more precisely, it can be implemented, but the implementation is too slow for any real project use.
This ineffective implementation is still leaved inside the file _globals.inc - commented block that defines macro with name "InitStringList". If someone have ideas about fixing this problem, please send it to me!
This library contains only one simple macro:
macro struct name ends
This macro is aimed to provide easy creation of data structures. The "struc" directive in FASM is known to not create actual labels, but only the template for the label definitions. So, we need to create an instance of the data structure in order to have addresses and offsets of its fields.
But very often we don't have static data structure of the given type, but data structure, pointed by some of the registers. In this case in order to use offsets to the fields of the data structure, we need to define at least one virtual instance of the structure at address 0. Then we can use the values of the fields as an offsets in the instructions - for example: mov eax, [esi+RECT.right].
So, this is exactly what "struct" macro does. Besides it defines the "struc" structure with the given fields, it creates a single virtual instance of this structure, in order to be used later for register addressing. In all remaining functionality it behaves exactly as the struc directive.
The syntax of struct macro is the following:
struct StructureName .field1 dd ? .field2 RECT .fieldN: ends
The definition begins with "struct" followed by the structure name. The definition ends with "ends" directive. Between both, any local label definition becomes a member of the structure.
This library contains macros that enhance the functionality of standard FASM "display" directive.
macro disp [arg]
The macro "disp" displays the strings given in the arguments, just as "display" FASM directive does
disp <number, radix>
The macro here is used to display numbers in any radix.
macro DispSize Text, Sz
"DispSize" macro is very specialized macro, that displays the text and number in the following form:
Size of [Text] is: Sz bytes
The size number is automatically scaled to bytes or kbytes, depending on the value of Sz.
This macro allows easy display and control of the sizes of particular areas of the program - data structures, subroutines etc.
How Fresh implements "display" directive
There are some specifics in Fresh, concerning message displaying. The "display" directive in Fresh works in a different way than original FASM directive. It outputs text in Fresh message window. Each message can have one of six icons, or it can have no icon at all. And because message window is implemented as a TreeView control, you can organize your messages into groups (directories). Implementation is a bit "tricky" - when you display a character whose code is less than 16, it is interpreted in a special way. Characters from 1 to 6 set an icon of current message. It sounds complicated, but it is quite simple. Try:
display 2, "some message"
It will display "some message" with an error icon. Another codes are used for controlling directory structure. Try to type following lines and see what would happen:
display 3, "message at root", 0x09 display 3, "child message1", 0x0a display 3, "child message2", 0x0d display 3, "again at root", 0x0a
Of course you don't have to put each message in separate display directive, you can, with the same result write:
display 3, "at root",$09,3,"child1",$0a,3,"child2", $0d,3,"again at root",$0a
Here is the complete list of all special characters and their meanings:
|$01||set current icon to "warning"|
|$02||set current icon to "error"|
|$03||set current icon to "info"|
|$04||set current icon to "find"|
|$05||set current icon to "none"|
|$06||set current icon to "debug"|
|$08||end current row and set one level back.|
|$09||end current row and set it as new directory.|
|$0a||end current row and keep current level|
|$0d||end current row and set current level to root level|