flat assembler
Message board for the users of flat assembler.

flat assembler > Macroinstructions > Some basic macros for fasm g

Author
Thread Post new topic Reply to topic
Tomasz Grysztar
Assembly Artist


Joined: 16 Jun 2003
Posts: 6871
Location: Kraków, Poland
While the macros of fasm g should be capable of doing everything that the macros of fasm 1 did, it does not help much when many of the widely-used macros do not have fasm g versions (and this is especially true for Win32/Win64 headers that fasm has, even as incomplete as they have always been). I have converted some of the macros to fasm g syntax when I was adapting it for self-hosting, but these are just some very basic ones, as fasm g sources do not use anything fancy.

One of the reasons why I'm reluctant to simply rewrite/adapt all the macros that came with fasm 1 is that their syntax is often a relic of very early fasm versions that had much simpler macro features, and they remained mostly unchanged to keep the compatibility. When designing similar macros with today's features I would most likely choose a different syntax for many of them, especially ones that build import/export tables and resources in PE.

On the other hand, it would still be nice to have a set of includes providing as much compatibility as possible. All the x86/x64 samples that come in fasm g package, including the Windows examples, are adapted from fasm 1 with no changes - but they do not use any macros that fasm 1 headers for Windows provide. I do not plan to provide more macros in the basic fasm g package (as it focuses on providing basic examples, not complete frameworks), but perhaps another Windows-focused package could be created one day (not necessarily by me?).

Let me show you some of the macros that I created that provide a bit more compatibility but are not included in the basic package.

First, the "struct" macro. The one that I used in fasm g self-hosting headers is a very simple implementation that does not allow to provide initial values in form of parameters to structure. I later made a little modification that adds such feature, but the values have to be labeled instead of providing them in a strict order:
Code:
macro struct? name macro ends?! end namespace iterate definition, args@struct match name:value, definition store value at .name else match name==value, definition store value at .name else match value, definition err 'unnamed values not supported' end match end iterate end struc virtual at 0 name name sizeof.name = $ end virtual purge ends? end macro struc name args@struct& label . : sizeof.name namespace . end macro ; usage example: struct RECT left dd ? top dd ? right dd ? bottom dd ? ends area RECT left:0,top:0,right:320,bottom:200
I think this syntax is actually better, while the implementation remains very simple. Making the parameters work in the same way as in the case of fasm's standard macro would require much more complex implementation (probably utilizing "eval"). At the same time, the fasmg variant is much more flexible, since it allows to put any constructions inside the structure, not only ones from the predefined set. You can use "align" or "virtual" inside such structure with not problem. You can even put some CPU instructions there if you needed it for some reason.

The aforementioned flexibility also means that the features like "union" can be implemented as completely independent macros. This simple implementation of "union" I made can be used on its own, but also works correctly when used inside structure defined with the previous macro:
Code:
macro union? union?. = $ macro endu?! purge ?,endu? end macro macro ? line& if $-union = 0 line else virtual at union line local size size = $-union end virtual if size > $-union rb size-($-union) end if end if end macro end macro ; usage example: union x dd ? left dd ? endu


Last edited by Tomasz Grysztar on 15 Jul 2016, 09:52; edited 1 time in total
Post 13 Jul 2016, 16:59
View user's profile Send private message Visit poster's website Reply with quote
Tomasz Grysztar
Assembly Artist


Joined: 16 Jun 2003
Posts: 6871
Location: Kraków, Poland
Another simple macro that I use for the compatibility with fasm 1 is the one that allows to use anonymous labels. This feature is not implemented into fasm g - the reason is that it was so often requested to provide various enhancements of this feature, that I decided it is better to keep it in form of macro that can then be customized to one's liking. And the basic implementation is really very plain:
Code:
macro @@ tail match label, @f? label tail @b? equ @f? end match local anonymous @f? equ anonymous end macro define @f? @@
Post 13 Jul 2016, 17:05
View user's profile Send private message Visit poster's website Reply with quote
shoorick



Joined: 25 Feb 2005
Posts: 1603
Location: Ukraine
he-he Smile exactly after I have rewritten all my 8080 projects to exclude all "@@" labels they are returning Smile

i made this fake subroutine to test some conditions:
Code:
macro @@ tail match label, @f? label tail @b? equ @f? end match local anonymous @f? equ anonymous end macro define @f? global: jmp .ttt .back: lxi h,100 @@: dad h jnc @B ret .ttt: jmp .back
and got an error:
Code:
Error: symbol '@B' is undefined or out of scope.


+++++++++++++++++
my fault! i should add this also:
Code:
define @f? @@ ; <<<!!!!!

i supposed it was a kind of example Smile
Post 14 Jul 2016, 09:25
View user's profile Send private message Visit poster's website Reply with quote
Tomasz Grysztar
Assembly Artist


Joined: 16 Jun 2003
Posts: 6871
Location: Kraków, Poland
shoorick wrote:
he-he Smile exactly after I have rewritten all my 8080 projects to exclude all "@@" labels they are returning Smile
This proves that I should have shared such macros in a more organized way (actually, this thread is my first effort at this). I previously included the "@@" macros in my demonstration of assembling TetrOS with fasmg and this was something very easy to miss.
Post 14 Jul 2016, 13:57
View user's profile Send private message Visit poster's website Reply with quote
shoorick



Joined: 25 Feb 2005
Posts: 1603
Location: Ukraine
I have collected some known to me macros into the single zip Cool


Description:
Download
Filename: include.zip
Filesize: 5.98 KB
Downloaded: 211 Time(s)


_________________
UNICODE forever!
Post 06 Sep 2016, 14:05
View user's profile Send private message Visit poster's website Reply with quote
Tomasz Grysztar
Assembly Artist


Joined: 16 Jun 2003
Posts: 6871
Location: Kraków, Poland
I have started working on conversions of other macros from fasm's standard Windows headers, and here comes the conversion of PROC32.INC:
Code:
macro stdcall? proc*,args& match any, args iterate arg, args indx 1+%%-% pushd arg end iterate end match call proc end macro macro ccall? proc*,args& local size size = 0 match any, args iterate arg, args indx 1+%%-% pushd arg size = size + 4 end iterate end match call proc if size add esp,size end if end macro macro invoke? proc*,args& stdcall [proc],args end macro macro cinvoke? proc*,args& ccall [proc],args end macro prologue@proc equ prologuedef macro prologuedef procname,flag,parmbytes,localbytes,reglist local loc loc = (localbytes+3) and (not 3) parmbase@proc equ ebp+8 localbase@proc equ ebp-loc if parmbytes | localbytes push ebp mov ebp,esp if localbytes sub esp,loc end if end if match any, reglist iterate reg, reglist push reg end iterate end match end macro epilogue@proc equ epiloguedef macro epiloguedef procname,flag,parmbytes,localbytes,reglist match any, reglist iterate reg, reglist indx %%-%+1 pop reg end iterate end match if parmbytes | localbytes leave end if if flag and 10000b retn else retn parmbytes end if end macro close@proc equ macro proc? statement& local _local,params,flag,regs,parmbytes,localbytes,current,tmp,initlocal macro endp?! localbytes = current purge ret?,locals?,endl?,LOCAL match close:reglist, close@proc,<regs> close name,flag,parmbytes,localbytes,reglist end match end namespace end if end match purge endp? end macro match name declaration, statement if used name name: namespace name match =stdcall? args, declaration define params args flag = 11b else match =stdcall?, declaration define params flag = 11b else match =c? args, declaration define params args flag = 10001b else match =c?, declaration define params flag = 10001b else define params declaration flag = 0 end match define regs match =uses? list, params define params list while 1 match =, tail, params define params tail break else match reg tail, params define params tail if % = 1 regs equ reg else regs equ regs,reg end if else break end match end while end match virtual at parmbase@proc match args, params iterate arg, args match argname:type, arg label #argname:type rb type else #arg dd ? end match end iterate end match parmbytes := $-(parmbase@proc) name#% = parmbytes/4 end virtual match prologue:reglist, prologue@proc:<regs> prologue name,flags,parmbytes,localbytes,reglist end match macro ret? operand match any, operand retn operand else match epilogue:reglist, epilogue@proc:<regs> epilogue name,flag,parmbytes,localbytes,reglist end match end match end macro current = 0 macro initlocal local area,pointer,length,value area:: pointer = localbase@proc+current length = $@ - (localbase@proc) - current current = $ - (localbase@proc) end virtual while length > 0 if length < 2 load value:byte from area:pointer mov byte [pointer],value pointer = pointer + 1 length = length - 1 else if length < 4 load value:word from area:pointer mov word [pointer],value pointer = pointer + 2 length = length - 2 else load value:dword from area:pointer mov dword [pointer],value pointer = pointer + 4 length = length - 4 end if end while virtual at localbase@proc+current end macro macro locals? virtual at localbase@proc+current macro ? line& line if $ > $@ initlocal end if end macro end macro macro endl? purge ? initlocal end virtual end macro macro LOCAL args& locals iterate arg, args match varname[count]:type, arg varname dbx type:count dup ? else match varname:type, arg varname dbx type, ? else match varname[count], arg varname rd count else arg dd ? end match end iterate endl end macro end macro
This is mostly compatible with fasm's original (it even includes the prologue@proc/epilogue@proc variables under the same names), while being much cleaner and easier to modify.

Some notes on the differences between this implementation and fasm's original
  • The entire body of procedure resides in its own namespace. This means that the labels no longer need to start with dot to be local to procedure (and you can still refer to them with "procedure.label" syntax when you are outside). The fasm did not have proper namespaces and it was the only reason why "proc" there implemented local data labels as symbolic variables that could be unedfined at the end of procedure, while other labels still had to be prepended with dot. With fasmg providing true namespaces I thought it would be better to simply make use of them.
  • The "locals"/"endl" blocks in version for fasmg are implemented much more cleanly, and you can put any directives there, including REPEAT loops, etc. They generate data-initializing instructions just like fasm's version did.
  • The "LOCAL" macro is implemented only in the upper-case variant. Otherwise to avoid conflict with fasmg's "local" directive used by macros it would be necessary to parse all lines inside "proc" block with "macro ?", and it is better to avoid line interceptors unless absolutely necessary.
  • The prologue/epilogue macro receive comma-separated list of registers, so that it can be iterated with IRP.
Post 16 Oct 2016, 16:52
View user's profile Send private message Visit poster's website Reply with quote
rCX



Joined: 29 Jul 2007
Posts: 166
Location: Maryland, USA
Tomasz Grysztar wrote:
Another simple macro that I use for the compatibility with fasm 1 is the one that allows to use anonymous labels. This feature is not implemented into fasm g - the reason is that it was so often requested to provide various enhancements of this feature, that I decided it is better to keep it in form of macro that can then be customized to one's liking. And the basic implementation is really very plain:
Code:
macro @@ tail match label, @f? label tail @b? equ @f? end match local anonymous @f? equ anonymous end macro define @f? @@

Really excited to learn about fasm g Very Happy
Please be patient with my dumb questions as I haven't coded in fasm in 6 years.

So I'm trying to understand how the @@ macro works. Basically you define a macro named "@@" with parameter "tail". From the documentation I understand that everything between "match" and "end match" is only assembled if "label" is "@f" or "@F". I know that in fasm g "label" is a keyword used to define a label, but here you are matching it with @f. So is "label" a variable here? Also why do you have to define "@f?" before "@@"?
Post 15 Jan 2017, 05:29
View user's profile Send private message Reply with quote
Tomasz Grysztar
Assembly Artist


Joined: 16 Jun 2003
Posts: 6871
Location: Kraków, Poland
rCX wrote:
So I'm trying to understand how the @@ macro works. Basically you define a macro named "@@" with parameter "tail". From the documentation I understand that everything between "match" and "end match" is only assembled if "label" is "@f" or "@F". I know that in fasm g "label" is a keyword used to define a label, but here you are matching it with @f. So is "label" a variable here? Also why do you have to define "@f?" before "@@"?
The "match label, @f?" defines a new parameter called "label" with a value that gather the entire matched text. Because (just like in fasm 1) symbolic variables are replaced with their values before the matching is started, the value of "@f?" variable become copied into "label" parameter, unless it is empty - in such case the match fails and the entire block is skipped.

So when "@f?" is defined initially with an empty value, that block is skipped the first time. But then "@f?" is re-defined with a new value and it become a symbolic link to a local "anonymous" label. Then the next "match label, @f?" assigns "anonymous" as a value to "label" parameter and executes the block. At this point we have two parameters - "tail" with the value like ":" (or any other construction that followed "@@" in the line that called the macro) and "label" with value "anonymous". The line "label tail" becomes (during the preprocessing) "anonymous :", and this is then interpreted as a definition of "anonymous" label.

In fasm LOCAL created a new unique name for each such label, with fasmg this works a bit differently - the name is always the same, but it carries the context in which it was defined (that's why I used the term "symbolic link", you may find more information on this in one of the advanced tutorials that I wrote [relatively] recently).
Post 15 Jan 2017, 10:14
View user's profile Send private message Visit poster's website Reply with quote
rCX



Joined: 29 Jul 2007
Posts: 166
Location: Maryland, USA
Thanks for the explanation. I think I understand it now.
Post 17 Jan 2017, 03:09
View user's profile Send private message Reply with quote
zhak



Joined: 12 Apr 2005
Posts: 489
Location: Belarus
Hmmm struct macro is pretty simple, indeed. But what if struct contains another struct and that one contains another? initializing them as name:value would be a pain in the ass. Also, this macro doesn't allow passing sequences of values like in:
Code:
struct MY a rb 4 ends my MY <1,2,3,4>
Post 18 Jan 2017, 20:17
View user's profile Send private message Reply with quote
Tomasz Grysztar
Assembly Artist


Joined: 16 Jun 2003
Posts: 6871
Location: Kraków, Poland
zhak wrote:
But what if struct contains another struct and that one contains another? initializing them as name:value would be a pain in the ass.
It does not need to be as clunky as you suggest:
Code:
struct POINT x dd ? y dd ? ends struct RECTN topleft POINT bottomright POINT ends area RECTN topleft.x:0,topleft.y:0, bottomright.x:320,bottomright.y:200
The point is, when only initializing select few of the fields is important to you, this variant has strong advantages. And even when you need to initialize most or all of them, having them labeled may make things easier to interpret visually when reading the code.

zhak wrote:
Also, this macro doesn't allow passing sequences of values (...)
Yes, and I mentioned it above, noting that allowing the sequential definition would require much more complex implementation. In the thread about basic Win32 headers for fasmg I mentioned that I plan to write such variant, but I haven't got it ready yet.
Post 18 Jan 2017, 21:09
View user's profile Send private message Visit poster's website Reply with quote
Tomasz Grysztar
Assembly Artist


Joined: 16 Jun 2003
Posts: 6871
Location: Kraków, Poland
The IRPS directive of fasm 1 has no equivalent in fasmg - because there were a few different variants of it that could be implemented and I have not decided which one of them to choose (similarly to how I did not implement "@@" internally because there are many different variants of it that are better implemented as macros that can be chosen according to preference). But it is possible to make a macro implementing a variant very similar to the original:
Code:
macro irps?! parameter*, text& local block,buffer macro irps?.process purge irps?.process define buffer text while 1 match car cdr, buffer define buffer cdr block car else match any, buffer block any end match break end match end while end macro esc macro block parameter& end macro macro end?.irps?! esc end macro irps?.process end macro
Code:
irps a, sum=x+y display `a,13,10 end irps
EDIT: I have also made a version that gives correct % and %% values like IRP, but it is much more costly performance-wise:
Code:
macro with? parameter*, value& local block macro end?.with?! esc end macro purge end?.with? block value end macro esc macro block parameter& end macro macro irps?! parameter*, text& local buffer,symbol define buffer text while 1 match car cdr, buffer define symbol car define buffer cdr else match any, buffer define symbol any end match break end match end while irpv sym, symbol with parameter, sym end macro macro end?.irps?! end with end irpv end macro
Code:
irps a, sum=x+y display `a,' (',`%,'/',`%%,')',13,10 end irps
Post 20 Feb 2017, 15:35
View user's profile Send private message Visit poster's website Reply with quote
Tomasz Grysztar
Assembly Artist


Joined: 16 Jun 2003
Posts: 6871
Location: Kraków, Poland
With the introduction of new OUTSCOPE directive into fasmg the implementation of IRPS becomes much more natural:
Code:
macro irps?! parameter*, text& local buffer,symbol define buffer text while 1 match car cdr, buffer define symbol car define buffer cdr else match any, buffer define symbol any end match break end match end while outscope irpv parameter, symbol end macro macro end?.irps?! end irpv end macro
I consider the feature still experimental, but it saves me many headaches in some situations.
Post 31 Mar 2018, 20:12
View user's profile Send private message Visit poster's website Reply with quote
Display posts from previous:
Post new topic Reply to topic

Jump to:  


< Last Thread | Next Thread >

Forum Rules:
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum
You cannot attach files in this forum
You can download files in this forum


Copyright © 2004-2018, Tomasz Grysztar.

Powered by rwasa.