flat assembler
Message board for the users of flat assembler.

Index > Macroinstructions > [fasmg] My multi level anonymous label macros

Author
Thread Post new topic Reply to topic
Jessé



Joined: 03 May 2025
Posts: 26
Jessé 04 May 2025, 02:28
I'm trying stuff with fasmg assembler, and successfully ported an idea I'm using with another assembler for years.
I'll let it as an attachment, also including a sample code that tests all levels and the 2 macros included.
And have a question: does anyone have a better way to do the '@@' one? For now, only way I've found is by using 'repeat' to pass the counter into the name of the anonymous label.

P.S.: the import64.inc and elfsym.inc are already provided by fasmg package. Make sure to include cet*.inc files at /include/cpu/ext too for endbr64 instruction to work if you are going to test this.

Sample program:
Code:
; ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
; ::: @@ and @@@ anonymous label tester.
;     This is a practical application of multi level jumps provided by these
; two macros, that work independently each other.
;      I recommend using edb-debugger for better visualization.
;
; By: Jessé Gonçalves
; ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

include '../include/format/format.inc'

format ELF64 executable ELFOSABI_LINUX
entry Start

include '../include/import64.inc'
include '../include/hla.inc'

; :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

interpreter '/lib64/ld-linux-x86-64.so.2'

needed 'libc.so.6'

import exit, fputs, stdout

; :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

_code   Start:                  endbr64
                                mov     rax, [stdout]
                                mov     rbx, [rax]

                                jmp     @f
                        @@      jmp     .odd
                        @@@     jmp     @@f

                        @@      jmp     @f15
                        @@      jmp     @f13
                        @@      jmp     @f11
                        @@      jmp     @f9
                        @@      jmp     @f7
                        @@      jmp     @f5
                        @@      jmp     @f3
                        @@      jmp     @f1
                .odd:           jmp     @b1
                        @@      jmp     @b3
                        @@      jmp     @b5
                        @@      jmp     @b7
                        @@      jmp     @b9
                        @@      jmp     @b11
                        @@      jmp     @b13
                        @@      jmp     @b15
                        @@      jmp     .even

                        @@      jmp     @f16
                        @@      jmp     @f14
                        @@      jmp     @f12
                        @@      jmp     @f10
                        @@      jmp     @f8
                        @@      jmp     @f6
                        @@      jmp     @f4
                        @@      jmp     @f2
                .even:          jmp     @f
                        @@      jmp     @b2
                        @@      jmp     @b4
                        @@      jmp     @b6
                        @@      jmp     @b8
                        @@      jmp     @b10
                        @@      jmp     @b12
                        @@      jmp     @b14
                        @@      jmp     @b16
                        @@      jmp     @@b

                        @@      jmp     @f
                        @@@     jmp     @b
                        @@      lea     rdi, [msgSuccess]
                                mov     rsi, rbx
                                call    [fputs]
                                xor     edi, edi
                                jmp     [exit]

; :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

_rdata  msgSuccess      db 'If you see this message, the jumps were'
                        db ' successfull and found the exit point.', 10, 0

    

Regards, Cool


Description: Macros collection
Download
Filename: hla.inc
Filesize: 7.34 KB
Downloaded: 34 Time(s)


_________________
jesse6
Post 04 May 2025, 02:28
View user's profile Send private message Reply with quote
bitRAKE



Joined: 21 Jul 2003
Posts: 4227
Location: vpcmpistri
bitRAKE 04 May 2025, 05:05
Generally, the macros presented with fasm2 show more advanced features. For example, the @@ macro is using a generator pattern - showing how intuitive it is to nest macros in fasmg/2.

Macros can even be recursive.
Macros are a general programming language at assemble-time.

What if you wanted a generalized @@ macro that support any depth of backward or forward references? This can be accomplished with a mapping of the local reference point to a global anonymous scope.

First, we select an easier to implement syntax:
Code:
        jmp @F.4        ; 0
@@:     jmp @F.2        ; 2
@@:     jmp @F.3        ; 4
@@:     jmp @B.2        ; 3
@@:     jmp @B.4        ; 1
@@:                     ; 5    
Each invocation of the @@ macro creates a label in the global @@ scope. Each use of @B/@F needs to translate to the global scope. One way to fake this is by redefining every @B.? and @F.? at each @@ -- oh, how that grows with use!

Another technique would be inline macros, but that changes the syntax again:
Code:
include 'macro\inline.inc'

inlinemacro @F(number)
        ; convert to global @@ context
end inlinemacro
inlinemacro @B(number)
        ; convert to global @@ context
end inlinemacro

        jmp @F(4)       ; 0
@@:     jmp @F(2)       ; 2
@@:     jmp @F(3)       ; 4
@@:     jmp @B(2)       ; 3
@@:     jmp @B(4)       ; 1
@@:                     ; 5    
There are no doubt other methods - this is what just came to mind.

... in practical use fasm2's advanced @@ seems sufficient and its complexity is constant.

It's nice to see the experimentation - look at the macros in fasm2 and perhaps you will be inspired.

_________________
¯\(°_o)/¯ “languages are not safe - uses can be” Bjarne Stroustrup
Post 04 May 2025, 05:05
View user's profile Send private message Visit poster's website Reply with quote
Jessé



Joined: 03 May 2025
Posts: 26
Jessé 04 May 2025, 09:19
Thanks for the help! I almost forgot that fasm2 is a separate thing from fasmg (although not by much). I'll check the repositories and see what I can find. Since it seems the preprocessor is the same for both.
I'm "new" to fasmg, so, I have a lot to figure out about its preprocessor.
Post 04 May 2025, 09:19
View user's profile Send private message Reply with quote
Tomasz Grysztar



Joined: 16 Jun 2003
Posts: 8394
Location: Kraków, Poland
Tomasz Grysztar 04 May 2025, 10:06
bitRAKE wrote:
Another technique would be inline macros, but that changes the syntax again
Interesting thing about this approach is that it would allow indexes as computed expressions:
Code:
include 'macro/inline.inc'

@@.current = 0

macro @@ tail&
        local anonymous
        anonymous tail
        repeat 1, i: @@.current + 1
                define @@.i anonymous
                @@.current = i
        end repeat
end macro

inlinemacro @B(n)
        repeat 1, i: @@.current - (n)
                . equ @@.i
        end repeat
end inlinemacro

inlinemacro @F(n)
        repeat 1, i: @@.current + 1 + n
                . equ @@.i
        end repeat
end inlinemacro    
Code:
@@:
        jmp     @B(0)
        jmp     @F(3-3)
        jmp     @F(1)

@@:
        jmp     @B(7/7)
        assert  @B(0) = @F(-1)

@@:
        assert  @B(2) = @F(-3)
    
Code:
; A little hack to allow simplified syntax:

inlinemacro.@b equ inlinemacro.@B(0)
inlinemacro.@f equ inlinemacro.@F(0)

; (this relies on case-sensitivity)

        assert @b = @B(0)    
Post 04 May 2025, 10:06
View user's profile Send private message Visit poster's website Reply with quote
Jessé



Joined: 03 May 2025
Posts: 26
Jessé 04 May 2025, 18:24
Great, Tomasz, this is what I'm looking for: something simple, which for sure will run smoothly on the compiler.
I'm on fasmg on Linux, I think I will need to import the inlinemacro to do it from fasm2 includes, right?
Well, I will need it anyway, because my next approach will be my attempt to rise a macro I have on another assembler with allows me to call functions just like C code, including passing strings as parameters directly.

Regards,
Post 04 May 2025, 18:24
View user's profile Send private message Reply with quote
Jessé



Joined: 03 May 2025
Posts: 26
Jessé 04 May 2025, 19:58
Unfortunalety, it didn't work.
I try it with the simple code:

Code:

include '../include/format/format.inc'

format ELF64 executable ELFOSABI_LINUX
entry Start

include '../include/import64.inc'
include 'macro_@@.inc'
; include '../include/hla.inc'

segment readable executable
                        @@:     jmp     @F(4)
        Start:                  endbr64
                                jmp     @F(1)
                        @@:     nop
                                jmp     @F(2)
                        @@:     jmp     @B(2)
                        @@:     jmp     @B(1)
                        @@:     jmp     @B(0)
                                ud2
    


And get the following error:

Quote:
╰─ ❯ fasmg testAA.fasm testAA
flat assembler version g.kp60
testAA.fasm [17]:
@@: jmp @B(2)
? [37] macro @@ [2] jmp? [2] x86.parse_jump_operand@dest [21] x86.parse_operand@dest [32] (CALM)
Error: symbol '@@.0' is undefined or out of scope.
[exit=2]

Code for macro is exactly what Tomasz has posted above,
I manually copy xcalm.inc and inline.inc from fasm2 repository into my Linux fasmg instalation, adjust include paths, and it ended here.
I don't know if I misinterpret proper usage of this, too.
Also, I didn't try to figure out (for now) what's wrong, because I need to be more familiar with fasmg macro and also CALM way of doing things.

Regards,

_________________
jesse6
Post 04 May 2025, 19:58
View user's profile Send private message Reply with quote
Tomasz Grysztar



Joined: 16 Jun 2003
Posts: 8394
Location: Kraków, Poland
Tomasz Grysztar 04 May 2025, 21:45
I used a different numbering scheme, your @b1 is equivalent to @B(0) in my example, @b2 is @B(1), etc.

Still, there is another problem I overlooked: when the instruction is put on the same line as anonymous label, as in your code, it is not going to assemble correctly, because inline macros are computed before the @@ macro is executed to define the label, so any references in the instruction are going to use previous index. Standard inline macro package is not good enough for this, it would probably be better to rewrite it as a preprocessor dedicated to handling such anonymous labels.
Post 04 May 2025, 21:45
View user's profile Send private message Visit poster's website Reply with quote
bitRAKE



Joined: 21 Jul 2003
Posts: 4227
Location: vpcmpistri
bitRAKE 04 May 2025, 22:33
Code:
include 'macro/inline.inc'

@@.current = 0

macro @@ tail&
        local anonymous
        anonymous tail
        repeat 1, i: @@.current + 1
                define @@.i anonymous
                @@.current = i
        end repeat
end macro

inlinemacro @B(n)
        repeat 1, i: @@.current - (n)
                . equ @@.i
        end repeat
end inlinemacro

inlinemacro @F(n)
        repeat 1, i: @@.current + 1 + n
                . equ @@.i
        end repeat
end inlinemacro

calminstruction ?! &text&
        match @@: text,text
        jno go
        asm @@:
go:     assemble text
end calminstruction

; A little hack to allow simplified syntax:

inlinemacro.@b equ inlinemacro.@B(0)
inlinemacro.@f equ inlinemacro.@F(0)    
... this hack works by splitting the anon label prior to inline.
(I think it might be broken by the include dynamics of inline macro though.)

_________________
¯\(°_o)/¯ “languages are not safe - uses can be” Bjarne Stroustrup
Post 04 May 2025, 22:33
View user's profile Send private message Visit poster's website Reply with quote
Jessé



Joined: 03 May 2025
Posts: 26
Jessé 05 May 2025, 01:54
Yes, Tomasz, because I use them as placeholders only: so that they don't leave a hole in the middle of the code. Just a matter of personal taste, of course.

I tested the version suggested by bitRAKE and got a very strange error, affecting another part of the macro sessions this time within the standard macros provided:

Quote:
─ ❯ fasmg testAA.fasm testAA
flat assembler version g.kp60
Error: could not generate code within the allowed number of passes.
testAA.fasm [4] macro format?.ELF64? [18] macro format?.include [1] ../include/format/elfexe.inc [145]:
e_phnum dw NUMBER_OF_SEGMENTS
Processed: e_phnum dw NUMBER_OF_SEGMENTS
Error: symbol 'ELF.NUMBER_OF_SEGMENTS' is undefined or out of scope.
[exit=2]


And I verify this by trying to compile without including the macro, and also by adapting the scenario to the macro I already made.
And in the latter case, the compilation succeeded.

Tomorrow I will try break your samples with the manual, and try solving them.
I like this approach, and I want to make it work.

Thanks bitRAKE and Tomasz once again for your help.
If I find something, I will come back here. And also come back if I can't find a solution... Smile

Regards,

_________________
jesse6
Post 05 May 2025, 01:54
View user's profile Send private message Reply with quote
Tomasz Grysztar



Joined: 16 Jun 2003
Posts: 8394
Location: Kraków, Poland
Tomasz Grysztar 05 May 2025, 09:09
bitRAKE wrote:
(I think it might be broken by the include dynamics of inline macro though.)
This is something that I planned to address with my modular preprocessor. I made a prototype, but forgot to share it, so I'm doing it now.
Post 05 May 2025, 09:09
View user's profile Send private message Visit poster's website Reply with quote
bitRAKE



Joined: 21 Jul 2003
Posts: 4227
Location: vpcmpistri
bitRAKE 05 May 2025, 21:34
Working in namespaces is another option:
(Using the modular preprocessor.)
Code:
macro @@ tail&
        if ~ definite @@.current
                @@.current = 0
        end if
        repeat 1, i: @@.current + 1
                ..@@.i tail
                @@.current = i
        end repeat
end macro

inlinemacro @B(n)
        repeat 1, i: @@.current - (n)
                . equ ..@@.i
        end repeat
end inlinemacro

inlinemacro @F(n)
        repeat 1, i: @@.current + 1 + n
                . equ ..@@.i
        end repeat
end inlinemacro

; A little hack to allow simplified syntax:

inlinemacro.@b equ inlinemacro.@B(0)
inlinemacro.@f equ inlinemacro.@F(0)

calminstruction Preprocessor?! &text&
        match @@: text,text
        jno go
        asm @@:
go:     call Preprocessor?, text
end calminstruction    
It supports isolation ...
Code:
                        @@:     jmp     @F(4)
        Start:                  endbr64
                                jmp     @F(1)
                        @@:     nop
                                jmp     @F(2)
                        @@:     jmp     @B(2)
                        @@:     jmp     @B(1)
                        @@:     jmp     @B(0)
                                ud2

namespace any
;                               jmp     @B(0) ; would be an error
                        @@:     jmp     @F(4)
        Start:                  endbr64
                                jmp     @F(1)
                        @@:     nop
                                jmp     @F(2)
                        @@:     jmp     @B(2)
                        @@:     jmp     @B(1)
                        @@:     jmp     @B(0)
                                ud2
end namespace    
Post 05 May 2025, 21:34
View user's profile Send private message Visit poster's website Reply with quote
Jessé



Joined: 03 May 2025
Posts: 26
Jessé 06 May 2025, 00:50
I tested again, still does not work, but this time I think is my bad, due to my inexperiece with fasmg advanced features. I try, but, for now, can't completely understand what modular preprocessor means, and how to properly set it up.
And, since I already have a model that works, even though it is quite "rustic", I will keep using it for now and focus on the other part that I have to build (the fastcall System V ABI C style thing), which will require a lot more work, I suppose.

I will possibly open a new topic for it, since I believe I will need a lot of help.

Feel free to post suggestions, anyway. I'll keep getting back here and test what I find.

And, as soon as I make some progress, learn more, or if I find an effective solution, I will come back here and post it.
Post 06 May 2025, 00:50
View user's profile Send private message Reply with quote
Jessé



Joined: 03 May 2025
Posts: 26
Jessé 06 May 2025, 03:40
In the meantime, while I'm testing things related to the match directive and reading the manual, I've created an updated version of my initial concept:

Code:
_desired_levels_ = 16   ; WARNING: number of possible jumps (more means more time processing every @@)
__lbl_count_ = _desired_levels_ ; be careful when tweaking this, no one needs more than 10!
repeat 1, i: __lbl_count_
  define @f? ..@@_UL_#i
  x = i
  y = x + _desired_levels_
  z = 1
  while x < y
    repeat 1, j: z, k: x
      define @f#j? ..@@_UL_#k
    end repeat
    z = z + 1
    x = x + 1
  end while
end repeat
purge x, y, z
macro @@ line&
  local x, y, z, cnt
  cnt = __lbl_count_ - (_desired_levels_ - 1)
  x = 1
  y = _desired_levels_
  z = _desired_levels_
  while z >= x
    repeat 1, h: y, i: cnt
      define @b#h? ..@@_UL_#i
      if h = 1
        ; display '@b', '->', `i, ' '
        define @b? ..@@_UL_#i
      end if
      ; display '@b', `h, '->', `i, ' '
    end repeat
    cnt = cnt + 1
    y = y - 1
    x = x + 1
  end while
  ; display 10
  x = 1
  z = _desired_levels_
  while x <= z
    repeat 1, h: x, i: cnt
      if h = 1
        define @f? ..@@_UL_#i
        ; display '@f', '->', `i, ' '
      end if
      define @f#h? ..@@_UL_#i
      ; display '@f', `h, '->', `i, ' '
    end repeat
    cnt = cnt + 1
    x = x + 1
  end while
  purge x, y, z
  ; display 10
  repeat 1, i:__lbl_count_
    ..@@_UL_#i#: line
  end repeat
  __lbl_count_ = __lbl_count_ + 1
end macro
    


This works the same as the first idea, but has a more compressed execution. Tested and validated with the same code.
The improvement relies on '_desired_levels_' being the adjustment of how much @f's and @b's you want.
Less is more performance.

_________________
jesse6
Post 06 May 2025, 03:40
View user's profile Send private message Reply with quote
bitRAKE



Joined: 21 Jul 2003
Posts: 4227
Location: vpcmpistri
bitRAKE 06 May 2025, 05:48
Here is the modular preprocessing idea from the other thread. I've split the parts to better demonstrate the idea. I'm not sure where errors could be coming from, so I've also included four test cases. I even updated to the latest fasmg to insure we are on the same page (though, I'm using the x86-2.inc processor model and no output format).


Description:
Download
Filename: modular.zip
Filesize: 3.07 KB
Downloaded: 31 Time(s)


_________________
¯\(°_o)/¯ “languages are not safe - uses can be” Bjarne Stroustrup
Post 06 May 2025, 05:48
View user's profile Send private message Visit poster's website Reply with quote
Jessé



Joined: 03 May 2025
Posts: 26
Jessé 06 May 2025, 12:39
Nice, I'll check it sooner.
Thanks for help...
Post 06 May 2025, 12:39
View user's profile Send private message 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-2025, Tomasz Grysztar. Also on GitHub, YouTube.

Website powered by rwasa.