flat assembler
Message board for the users of flat assembler.

Index > Macroinstructions > [fasmg] Is there a way to do a lookup table using CALM?

Author
Thread Post new topic Reply to topic
Jessé



Joined: 03 May 2025
Posts: 128
Location: Brazil
Jessé 03 Jul 2026, 22:19
Hello,

I finally - after postponed more than I should - decided to make my way under the realm of CALM with fasm(g|2), and I'm already having a lot of fun with it, because, in the end, it is quite easy to adopt if one was already familiar with fasmg macroinstructions, imo.

And, I decided to test an idea, which I suppose should work just like 'addwf PCL, F' under PIC assembly (which is one of the greatest feature in that assembly language): jump elsewhere using a lookup table, using an implicit (variable) index at that jump table.

My tested idea - which didn't work - is the following:

Code:
calminstruction jumptable target*
    start:
        match index|number, target, |
        jyes next
        err "only numbers are supported"
        exit
    next:
        compute index, index
        check index <= 9
        jyes next2
        err "jump value out of range"
        exit
    next2:
        jump target_#index  ; It didn't work...
        err "failed!"
        exit
    target_0:
        display "Jump at 0"
        jump done
    target_1:
        display "Jump at 1"
        jump done
    target_2:
        display "Jump at 2"
        jump done
    target_3:
        display "Jump at 3"
        jump done
    target_4:
        display "Jump at 4"
        jump done
    target_5:
        display "Jump at 5"
        jump done
    target_6:
        display "Jump at 6"
        jump done
    target_7:
        display "Jump at 7"
        jump done
    target_8:
        display "Jump at 8"
        jump done
    target_9:
        display "Jump at 9"
        jump done
    done:
        display 10
end calminstruction

jumptable 8

    


And then, I went to see if it would work miraculously (because I already expected it was unlikely I’d get something like that right on the first try):

Quote:

artix-avlt:[jesse6]:~/ASM64/fasm2_latest/tests/CALM$ fasmg.x64 jmptest.asm
flat assembler version g.l7xm
jmptest.asm [14]:
jump target_#index ; It didn't work...
Error: the target of the jump is not defined in this instruction.
[ble: exit 2]


So, I ask you:
Has anyone managed to get something working in a similar or identical way to this?
I believe the idea is somewhat clear from the example, but just in case: it is for that 'jump label_#index' instruction to jump based on a specified index value passed to the calminstruction.

Warning: it must be considered a newbie question, because, it has 4 days since I finally started with CALM. I apologize in anticipation for any mistake, and want to hear it from you, anyways, to help me improving...

Thanks,

_________________
jesse6
Post 03 Jul 2026, 22:19
View user's profile Send private message Visit poster's website Reply with quote
bitRAKE



Joined: 21 Jul 2003
Posts: 4535
Location: vpcmpistri
bitRAKE 03 Jul 2026, 23:30
Code:
repeat 10,i:0
        calminstruction jumptable.i
                display "Jump at " bappend `i bappend 10
        end calminstruction
end repeat

calminstruction jumptable target*

        ; this catches negative numbers, too
        match index|number, target, |
        jno nan

        check index > 9
        jyes out

        ; "dynamic" labels must be baked at assemble-time
        arrange target,=jumptable.index
        assemble target
        exit

nan:    err "only numbers are supported"
        exit
out:    err "jump value out of range"

end calminstruction

jumptable 7
jumptable 0
jumptable 17    

_________________
¯\(°_o)/¯ AI may [not] have aided with the above reply.
Post 03 Jul 2026, 23:30
View user's profile Send private message Visit poster's website Reply with quote
Jessé



Joined: 03 May 2025
Posts: 128
Location: Brazil
Jessé 05 Jul 2026, 12:52
Interesting (and creatve) approach.
It kind of resolves the quest, but at the cost of exiting current CALM statement.
Anyways, I just find a way to use it on what I first came up when I had this usage idea.

Thanks, bitRAKE!

P.S.: tested and working fine!
Post 05 Jul 2026, 12:52
View user's profile Send private message Visit poster's website Reply with quote
bitRAKE



Joined: 21 Jul 2003
Posts: 4535
Location: vpcmpistri
bitRAKE 05 Jul 2026, 15:56
The other technique that might work is the CALM builder technique - super meta though. Look at the start of x86-2.inc for an example.
Post 05 Jul 2026, 15:56
View user's profile Send private message Visit poster's website Reply with quote
Jessé



Joined: 03 May 2025
Posts: 128
Location: Brazil
Jessé 07 Jul 2026, 00:03
bitRAKE wrote:
The other technique that might work is the CALM builder technique - super meta though. Look at the start of x86-2.inc for an example.


Great suggestion!
I stare at this header very often, because it is a very rich resource of things that work with fasmg.
But, so far, most of them are quite misterious to me yet.
Tomasz has a very unique way of implementing his ideas, which is quite afar from my way of doing things.
And that's the reason that keeps me interested, and retrying to decipher those enigmatic headers bundled with fasm2 and fasmg. It's a (somehow) new way of implementing stuff (to me), and one that works nicely.

...

By the way, this is the main reason I pushed this thread here.
Thats my converted internal function:

Code:
        define __q_reg

        calminstruction get_q_register register*
                local RegID, info, class
                compute info, register metadata 1
                compute class, info metadata 1
                check class relativeto x86.reg
                jyes next
                error:
                        err "register not found or unknown/unsupported type"
                        exit
                next:
                        compute RegID, 0 scaleof info
                next0:
                        check RegID = 0
                        jno next1
                        compute __q_reg, rax
                        jump done
                next1:
                        check RegID = 1
                        jno next2
                        compute __q_reg, rcx
                        jump done
                next2:
                        check RegID = 2
                        jno next3
                        compute __q_reg, rdx
                        jump done
                next3:
                        check RegID = 3
                        jno next4
                        compute __q_reg, rbx
                        jump done
                next4:
                        check RegID = 4
                        jno next5
                        compute __q_reg, rsp
                        jump done
                next5:
                        check RegID = 5
                        jno next6
                        compute __q_reg, rbp
                        jump done
                next6:
                        check RegID = 6
                        jno next7
                        compute __q_reg, rsi
                        jump done
                next7:
                        check RegID = 7
                        jno next8
                        compute __q_reg, rdi
                        jump done
                next8:
                        check RegID = 8
                        jno next9
                        compute __q_reg, r8
                        jump done
                next9:
                        check RegID = 9
                        jno next10
                        compute __q_reg, r9
                        jump done
                next10:
                        check RegID = 10
                        jno next11
                        compute __q_reg, r10
                        jump done
                next11:
                        check RegID = 11
                        jno next12
                        compute __q_reg, r11
                        jump done
                next12:
                        check RegID = 12
                        jno next13
                        compute __q_reg, r12
                        jump done
                next13:
                        check RegID = 13
                        jno next14
                        compute __q_reg, r13
                        jump done
                next14:
                        check RegID = 14
                        jno next15
                        compute __q_reg, r14
                        jump done
                next15:
                        check RegID = 15
                        jno error
                        compute __q_reg, r15
                        jump done
                done:
        end calminstruction
    


At least judging by the way it looks like, it seems clear why a "jump lookup" might be useful, avoiding all those checks, and going straight to the point, pointed by the already known (scaleof) number (to jump).
I think I can put your suggestion to work here, although I've not tried yet.

The original previous macro version is:

Code:
        macro get_q_register register*
                local RegID, info, class
                info = register metadata 1
                class = info metadata 1
                if class relativeto x86.reg
                        RegID = 0 scaleof info
                        if RegID = 0
                                __q_reg = rax
                        else if RegID = 1
                                __q_reg = rcx
                        else if RegID = 2
                                __q_reg = rdx
                        else if RegID = 3
                                __q_reg = rbx
                        else if RegID = 4
                                __q_reg = rsp
                        else if RegID = 5
                                __q_reg = rbp
                        else if RegID = 6
                                __q_reg = rsi
                        else if RegID = 7
                                __q_reg = rdi
                        else if RegID = 8
                                __q_reg = r8
                        else if RegID = 9
                                __q_reg = r9
                        else if RegID = 10
                                __q_reg = r10
                        else if RegID = 11
                                __q_reg = r11
                        else if RegID = 12
                                __q_reg = r12
                        else if RegID = 13
                                __q_reg = r13
                        else if RegID = 14
                                __q_reg = r14
                        else if RegID = 15
                                __q_reg = r15
                        else
                                err "Error: register not found or unknown/unsupported type.", 10
                        end if
                else
                        err "Error: register not found or unknown/unsupported type.", 10
                end if
        end macro
    


I also noted that compute, as well as transform, have no support to synthesize labels based on variables, unlike arrange does. But I must be wrong about this, because arrange may not do it either, but its result resolve those symbols after.

I thought on an idea of achieving it using 'eval', but, 'eval' is not a valid statement under CALM, as I had figured out.

Also important: I understand (I guess) that, as 'Compiled' assembly-like macro, it means that it leaves to be a text thing on fasmg at the time of its definition/processing, so, I really don't know how viable (or impactful in terms of performance) would be to implement such things. Maybe the cost-efficiency of it, or the added complexity may not pay itself off, I suppose. Anyways, I'm looking forward to make good use of it sooner. Very Happy

_________________
jesse6
Post 07 Jul 2026, 00:03
View user's profile Send private message Visit poster's website Reply with quote
Jessé



Joined: 03 May 2025
Posts: 128
Location: Brazil
Jessé 07 Jul 2026, 01:07
Working (first attempt) approach, with embedded diagnosis:

Code:
        define __q_reg

        iterate <ID,reg,name>, 0,rax,'rax', 1,rcx,'rcx', 2,rdx,'rdx', 3,rbx,'rbx', 4,rsp,'rsp', 5,rbp,'rbp', 6,rsi,'rsi', 7,rdi,'rdi'
        calminstruction get_reg_id_#ID
            compute __q_reg, reg
            display "Result: " bappend name bappend 10
        end calminstruction
        end iterate

        repeat 8, ID:8
        calminstruction get_reg_id_#ID
            local name
            arrange reg, =r#ID
            arrange name, reg
            stringify name
            compute __q_reg, reg
            display "Result: " bappend name bappend 10
        end calminstruction
        end repeat

        calminstruction get_q_register register*
                local RegID, info, class, select
                compute info, register metadata 1
                compute class, info metadata 1
                check class relativeto x86.reg
                jyes next
                error:
                        err "register not found or unknown/unsupported type"
                        exit
                next:
                        compute RegID, 0 scaleof info
                        arrange select, =get_reg_id_#RegID
                        assemble select
        done:
        end calminstruction

get_q_register cl
get_q_register r12w
get_q_register sp
    


At production code:

Code:
        define __q_reg

        iterate <ID,reg>, 0,rax, 1,rcx, 2,rdx, 3,rbx, 4,rsp, 5,rbp, 6,rsi, 7,rdi
        calminstruction get_reg_id_#ID
            compute __q_reg, reg
        end calminstruction
        end iterate

        repeat 8, ID:8
        calminstruction get_reg_id_#ID
            arrange reg, =r#ID
            compute __q_reg, reg
        end calminstruction
        end repeat

        calminstruction get_q_register register*
                local RegID, info, class, select
                compute info, register metadata 1
                compute class, info metadata 1
                check class relativeto x86.reg
                jyes next
                error:
                        err "register not found or unknown/unsupported type"
                        exit
                next:
                        compute RegID, 0 scaleof info
                        arrange select, =get_reg_id_#RegID
                        assemble select
        end calminstruction
    


Seems working fine, so far...
Post 07 Jul 2026, 01:07
View user's profile Send private message Visit poster's website Reply with quote
Tomasz Grysztar



Joined: 16 Jun 2003
Posts: 8543
Location: Kraków, Poland
Tomasz Grysztar 07 Jul 2026, 08:04
Jessé wrote:
I also noted that compute, as well as transform, have no support to synthesize labels based on variables, unlike arrange does. But I must be wrong about this, because arrange may not do it either, but its result resolve those symbols after.
ARRANGE glues pieces of tokenized text together and stores this text in a symbolic variable. You can arrange pieces in such way that it forms new identifiers, but ARRANGE does not require the text you produce to have any meaning, it is up to you to use the value of this variable for chosen purpose later.

For example, TRANSFORM would take the text contained in this variable and look for identifiers of other symbolic variables to replace each of them with its content (and each has a value containing tokenized source text, since this is what "symbolic variable" is in fasm) and put this final unrolled text into the same variable. Here your newly constructed identifier (like one with appended index) would get recognized.

As for COMPUTE, normally it does not deal with tokenized text at all, it computes a pre-compiled expression and stores the numeric (not symbolic) result in a variable. Only if the pre-compiled expression uses a variable that happens to have a symbolic value, COMPUTE parses this text as a new sub-expression and the evaluates it. This is obviously slower, as it requires additional parsing, while the main expression for the COMPUTE has been already parsed at the time when the instruction was compiled (that is: during the definition of CALM instruction).

However this means that if you construct an indexed identifier and put it into a variable that is accessed by pre-compiled expression of COMPUTE, it is going to be parsed and evaluated as a sub-expression, and thus it can also access a symbol with newly synthesised name.

And, of course, the target variable of ARRANGE, TRANSFORM, COMPUTE is always just a pre-compiled reference to a symbol, so it is static, already decided at the time when the instruction is defined.

The target of a CALL is also a static reference to a symbol, in this case an instruction-class symbol. You can still alter the value of such symbol, by redefining a macro, or with MVMACRO/PURGE. However in case of JUMP/JYES/JNO the target is doubly static, because it is compiled into an internal reference to a specific position inside CALM bytecode. Once the instruction is compiled, this target cannot be changed in any way.

Hope that clarifies a little.
Post 07 Jul 2026, 08:04
View user's profile Send private message Visit poster's website Reply with quote
Jessé



Joined: 03 May 2025
Posts: 128
Location: Brazil
Jessé 08 Jul 2026, 00:44
Yes, it helped a lot, good explanation on those internals. I appreciate it.

Quote:

ARRANGE glues pieces of tokenized text together and stores this text in a symbolic variable. You can arrange pieces in such way that it forms new identifiers, but ARRANGE does not require the text you produce to have any meaning, it is up to you to use the value of this variable for chosen purpose later.

I must say that this is a great behavior (for this directive), it feels quite natural to me to deal with, leading to almost no mistakes when I use 'arrange' on something. But maybe this is due to the fact that I'm already very used to 'match' characteristics as well, because they're related to each other (one joins, and the other splits).
Post 08 Jul 2026, 00:44
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-2026, Tomasz Grysztar. Also on GitHub, YouTube.

Website powered by rwasa.