flat assembler
Message board for the users of flat assembler.
Index
> Macroinstructions > [fasmg] My multi level anonymous label macros |
| Author |
|
|
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,
|
|||||||||||
|
|
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 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 ... 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)/¯ AI may [not] have aided with the above reply. |
|||
|
|
Tomasz Grysztar 04 May 2025, 10:06
bitRAKE wrote: Another technique would be inline macros, but that changes the syntax again 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) |
|||
|
|
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, |
|||
|
|
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 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, |
|||
|
|
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. |
|||
|
|
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) (I think it might be broken by the include dynamics of inline macro though.) _________________ ¯\(°_o)/¯ AI may [not] have aided with the above reply. |
|||
|
|
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 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... Regards, |
|||
|
|
Tomasz Grysztar 05 May 2025, 09:09
bitRAKE wrote: (I think it might be broken by the include dynamics of inline macro though.) |
|||
|
|
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 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 |
|||
|
|
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. |
|||
|
|
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. |
|||
|
|
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).
_________________ ¯\(°_o)/¯ AI may [not] have aided with the above reply. |
|||||||||||
|
|
Jessé 06 May 2025, 12:39
Nice, I'll check it sooner.
Thanks for help... |
|||
|
|
Jessé 22 Jul 2026, 06:52
Sorry to respawn to life one of first posts, but it's worth it:
Jessé wrote: 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: I've used this code since I've created it, but now, I was able to make a CALM version of it in one take (which made me quite happy... 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! ; NEW! CALM version! calminstruction init_@fx local i, j, fsym, fval compute i, 1 compute j, __lbl_count_ arrange fsym, =@f? arrange fval, ..=@@_UL_#j publish fsym, fval loop: arrange fsym, =@f#i#? arrange fval, ..=@@_UL_#j publish fsym, fval compute i, i + 1 compute j, j + 1 check i <= _desired_levels_ jyes loop end calminstruction calminstruction @@ line& local h, i, j, sym, val compute i, __lbl_count_ - (_desired_levels_ - 1) compute h, _desired_levels_ compute j, 1 loopb: arrange sym, =@b#h#? arrange val, ..=@@_UL_#i publish sym, val compute h, h - 1 compute i, i + 1 compute j, j + 1 check _desired_levels_ >= j jyes loopb arrange sym, =@b? arrange val, ..=@@_UL_#__lbl_count_ publish sym, val compute h, i arrange sym, =@f? arrange val, ..=@@_UL_#i publish sym, val compute j, 1 loopf: arrange sym, =@f#j#? arrange val, ..=@@_UL_#i publish sym, val compute i, i + 1 compute j, j + 1 check j <= _desired_levels_ jyes loopf arrange sym, ..=@@_UL_#__lbl_count_: line assemble sym compute __lbl_count_, h end calminstruction init_@fx purge init_@fx It works the same as the previous model, but it proved to be slightly faster. Last edited by Jessé on 27 Jul 2026, 06:52; edited 1 time in total |
|||
|
|
Jessé 24 Jul 2026, 00:46
I also have created a CALM version that behaves similar to the original fasm2 @n (extended) macro, but inherits some attributes from the one I did (and have shown you twice at this post):
Code: ; (maybe) required if you don't want to touch the original fasm2 headers repeat 10 i:0 purge @#i? restore @#i#f? restore @#i#b? end repeat ; end required _desired_levels_ = 16 ; from @1 to @16 (with its corresponding @nb and @nf) repeat _desired_levels_ ; don't underestimate this tiny code, because it gives you __lbl_count_#%#__ = 0 ; '_desired_levels_' quantity of independent @n (CALM processed) labels! calminstruction @#% line& ; and it's lighter than my version! local i, sym, val compute i, __lbl_count_#%#__ arrange sym, =@#%#=b? arrange val, ..=@#%#=_UL_#i publish sym, val compute i, i + 1 arrange sym, =@#%#=f? arrange val, ..=@#%#=_UL_#i publish sym, val compute __lbl_count_#%#__, i compute i, i - 1 arrange sym, ..=@#%#=_UL_#i: line assemble sym end calminstruction @#% restore @#%#b? end repeat With this version: Code: @2 jmp @3f ; #1 Jumps to the next '@3' label at #5 @1 jmp @1b ; #2 Jumps to this '@1' label (equal to: 'jmp $') at #2 @1 jmp @1f ; #3 Jumps to the next '@1' label at #4 @1 jmp @2b ; #4 Jumps to the previous @2 label at #1 @3 jmp @3b ; #5 Jump to this '@3' label (equal to: 'jmp $') at #5 ; ... and so on... As a placeholder-like (as I call it), it require not to have ':'; to make it with ':', just remove that ':' prior to the assemble statement. This is one of those attributes. Another one, is that this macro is projected to be used at the same line (definitions update before the code following the label) as well as multiline. It's behavior is identical in both situations. And another attribute is that its (not so) anonymous real label begin with two dots '..', which it is supposed to not mess up with label hierarchy (which, for the sake of curiosity, is the same approach I've used at my nasm version). I must say that I prefer this original fasm2-like approach, rather than my 18-year old (I guess) approach. Much lighter, and also you don't get lost too easily counting '@@' labels to know where is that jump's going... |
|||
|
< Last Thread | Next Thread > |
Forum Rules:
|
Copyright © 1999-2026, Tomasz Grysztar. Also on GitHub, YouTube.
Website powered by rwasa.