flat assembler
Message board for the users of flat assembler.

Index > Macroinstructions > Some basic macros for fasm g

Author
Thread Post new topic Reply to topic
Tomasz Grysztar



Joined: 16 Jun 2003
Posts: 8351
Location: Kraków, Poland
Tomasz Grysztar 13 Jul 2016, 16:59
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 08 Apr 2019, 15:14; edited 2 times in total
Post 13 Jul 2016, 16:59
View user's profile Send private message Visit poster's website Reply with quote
Tomasz Grysztar



Joined: 16 Jun 2003
Posts: 8351
Location: Kraków, Poland
Tomasz Grysztar 13 Jul 2016, 17:05
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: 1614
Location: Ukraine
shoorick 14 Jul 2016, 09:25
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



Joined: 16 Jun 2003
Posts: 8351
Location: Kraków, Poland
Tomasz Grysztar 14 Jul 2016, 13:57
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: 1614
Location: Ukraine
shoorick 06 Sep 2016, 14:05
I have collected some known to me macros into the single zip Cool


Description:
Download
Filename: include.zip
Filesize: 5.98 KB
Downloaded: 1210 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



Joined: 16 Jun 2003
Posts: 8351
Location: Kraków, Poland
Tomasz Grysztar 16 Oct 2016, 16:52
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: 172
Location: Maryland, USA
rCX 15 Jan 2017, 05:29
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



Joined: 16 Jun 2003
Posts: 8351
Location: Kraków, Poland
Tomasz Grysztar 15 Jan 2017, 10:14
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: 172
Location: Maryland, USA
rCX 17 Jan 2017, 03:09
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: 501
Location: Belarus
zhak 18 Jan 2017, 20:17
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



Joined: 16 Jun 2003
Posts: 8351
Location: Kraków, Poland
Tomasz Grysztar 18 Jan 2017, 21:09
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



Joined: 16 Jun 2003
Posts: 8351
Location: Kraków, Poland
Tomasz Grysztar 20 Feb 2017, 15:35
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



Joined: 16 Jun 2003
Posts: 8351
Location: Kraków, Poland
Tomasz Grysztar 31 Mar 2018, 20:12
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 experimental still, but it saves me many headaches in some situations.

Also, another tweak allows to unroll IRPS only conditionally, which has many advantages:
Code:
macro irps_as_irpv? 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 irps?! arguments&
        macro irpv!
                purge irpv
        end macro
        irpv
        outscope irps_as_irpv arguments
end macro

macro end?.irps?! 
        end irpv 
end macro    
Post 31 Mar 2018, 20:12
View user's profile Send private message Visit poster's website Reply with quote
shoorick



Joined: 25 Feb 2005
Posts: 1614
Location: Ukraine
shoorick 11 Nov 2018, 07:59
well, I put my hands on hobby coding after another long delay and found @@ does not work for me again...
the latest fasmg version says while including this macro:
Code:
flat assembler  version g.ibh5n
Hello, world!
hello.asm [6]:
        @@:
macro @@ [6] macro equ [4]:
        . = value
Processed: . = anonymous
Error: symbol 'anonymous' is undefined or out of scope.    

-- it works in clean source with only "db", but does not work with 8051.inc:
Code:
include "include/@@.inc"
include "8051.inc"
display "Hello, world!"

db 0,0,0,0
@@:
db 0,0,0,0
db @F
db @B
db 0,0,0,0
@@:
db 0,0,0,0    
Post 11 Nov 2018, 07:59
View user's profile Send private message Visit poster's website Reply with quote
Tomasz Grysztar



Joined: 16 Jun 2003
Posts: 8351
Location: Kraków, Poland
Tomasz Grysztar 11 Nov 2018, 08:53
The problem here is that 8051.inc alters the meaning of EQU, and this can break any macros that use it. In this case it should be enough to replace EQU with DEFINE in @@ macro, but in general - to make 8051.inc operate with various standard macros - this re-definition of EQU should perhaps be done differently.
Post 11 Nov 2018, 08:53
View user's profile Send private message Visit poster's website Reply with quote
shoorick



Joined: 25 Feb 2005
Posts: 1614
Location: Ukraine
shoorick 11 Nov 2018, 09:35
I also have supposing it is around "TT equ P1.3" situation, but was not sure Smile
If it will work with DEFINE it should be enough, but I failed to do it by myself Rolling Eyes

I wrote:
Code:
macro @@ tail
        match label, @f?
                label tail
                define @b? @f?
        end match
        local anonymous
        define @f? anonymous
end macro

define @f?
@@    
, but this source:
Code:
include "include/@@.inc"
include "8051.inc"
display "Hello, world!"

        mov  R1,#10
@@:
        xch  A,B
        djnz R1,@B
        jmp  $    
gives
Code:
flat assembler  version g.ibh5n
Hello, world!
hello.asm [8]:
        djnz R1,@B
macro DJNZ [11]:
        offset = -($+2)+addr
Processed: offset = -($+2)+@B
Error: symbol 'anonymous' is undefined or out of scope.    

_________________
UNICODE forever!
Post 11 Nov 2018, 09:35
View user's profile Send private message Visit poster's website Reply with quote
Tomasz Grysztar



Joined: 16 Jun 2003
Posts: 8351
Location: Kraków, Poland
Tomasz Grysztar 11 Nov 2018, 13:52
DEFINE does not expand symbolic values like EQU does, taking this into account the modified version should be:
Code:
macro @@ tail
        match label, @f?
                label tail
                define @b? label
        end match
        local anonymous
        define @f? anonymous
end macro

define @f?
@@    
Post 11 Nov 2018, 13:52
View user's profile Send private message Visit poster's website Reply with quote
shoorick



Joined: 25 Feb 2005
Posts: 1614
Location: Ukraine
shoorick 11 Nov 2018, 15:34
thanks! now it works! i love these labels!
Post 11 Nov 2018, 15:34
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 © 1999-2024, Tomasz Grysztar. Also on GitHub, YouTube.

Website powered by rwasa.