flat assembler
Message board for the users of flat assembler.
 Home   FAQ   Search   Register 
 Profile   Log in to check your private messages   Log in 
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: 6314
Location: Kraków, Poland
Some basic macros for fasm g
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 definitionargs@struct
                                match name:valuedefinition
                                        store value at .name
                                else match name==valuedefinition
                                        store value at .name
                                else match valuedefinition
                                        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: 6314
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: 1563
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:
Errorsymbol '@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: 6314
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: 1563
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: 56 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: 6314
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 anyargs
                iterate argargs
                        indx 1+%%-%
                        pushd arg
                end iterate
        end match
        call proc
end macro

macro ccall? proc*,args&
        local size
        size = 0
        match anyargs
                iterate argargs
                        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+3and (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 anyreglist
                iterate regreglist
                        push reg
                end iterate
        end match
end macro

epilogue@proc equ epiloguedef

macro epiloguedef procname,flag,parmbytes,localbytes,reglist
        match anyreglist
                iterate regreglist
                        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:reglistclose@proc,<regs>
                                close name,flag,parmbytes,localbytes,reglist
                        end match

                end namespace
                end if
                end match

                purge endp?

        end macro

        match name declarationstatement
        if used name
        name:
        namespace name

                match =stdcall? argsdeclaration
                        define params args
                        flag = 11b
                else match =stdcall?declaration
                        define params
                        flag = 11b
                else match =c? argsdeclaration
                        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? listparams
                        define params list
                        while 1
                                match =, tailparams
                                        define params tail
                                        break
                                else match reg tailparams
                                        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 argsparams
                                iterate argargs
                                        match argname:typearg
                                                label #argname:type
                                                rb type
                                        else
                                                #arg dd ?
                                        end match
                                end iterate
                        end match
                        parmbytes := $-(parmbase@proc)
                        name#% = parmbytes/4
                end virtual

                match prologue:reglistprologue@proc:<regs>
                        prologue name,flags,parmbytes,localbytes,reglist
                end match

                macro ret? operand
                        match anyoperand
                                retn operand
                        else
                                match epilogue:reglistepilogue@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 argargs
                                        match varname[count]:typearg
                                                varname dbx type:count dup ?
                                        else match varname:typearg
                                                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: 167
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: 6314
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: 167
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: 469
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: 6314
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:0bottomright.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: 6314
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 still have not chosen any of them. 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 end?.irps?!
                esc end macro
                purge end?.irps?
                define buffer text
                while 1
                        match car cdrbuffer
                                define buffer cdr
                                block car
                        else
                                match anybuffer
                                        block any
                                end match
                                break
                        end match
                end while
        end macro

        esc macro block parameter&
end macro

macro end?.irps?!
        esc end macro
end macro


Code:
irps asum=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 cdrbuffer  
                        define symbol car  
                        define buffer cdr  
                else  
                        match anybuffer  
                                define symbol any  
                        end match  
                        break  
                end match  
        end while  

        irpv symsymbol 
        with parametersym
end macro

macro end?.irps?!
        end with
        end irpv
end macro


Code:
irps asum=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
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


Powered by phpBB © 2001-2005 phpBB Group.

Main index   Download   Documentation   Examples   Message board
Copyright © 2004-2016, Tomasz Grysztar.