flat assembler > Macroinstructions > Building lists efficiently in fasm

Author
Thread Post new topic Reply to topic
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 8142
Location: Moonbase Alpha
Building lists efficiently in fasm
Building lists efficiently in fasm

The most obvious way to build a list in fasm is to use the equ directive to collect a set of names. Like this:

Code:
.list1_list equ                         ;initialise the list of names

macro .list1 [a] {
        common
        local z
        .list1_list equ .list1_list,z   ;build the list incrementally
        macro z a                       ;open the macro
}

macro list1_list {
        match =,the_list,.list1_list \{
                irp instr,the_list \\{
                        instr           ;replay each line
                \\}
        \}
}

So let's test this with the following command line: "fasm -m 10000 ListBuilder1.asm" (i.e. we limit the memory to 10MB)

Code:
rept 1430 x {
        .list1 \{ db `x,0 \}
}
list1_list


fasm -m 10000 ListBuilder1.asm wrote:

flat assembler version 1.69.24 (10000 kilobytes memory)
1 passes, 6038 bytes.

So far so good. But if we increase the number of repetitions by one more to 1431:

fasm -m 10000 ListBuilder1.asm wrote:
flat assembler version 1.69.24 (10000 kilobytes memory)
error: out of memory.

Not a very good result. Just 6038 bytes of code and using 10MB of memory.

But we can do better. Instead of using equ to build a list of names, instead we can use the rept arithmetic to count the names. thus:

Code:
c equ 0                                 ;initialise the counter

macro .list2 [a] {
        common
        rept 1 x:c+1 \{
                c equ x                 ;update the counter
                macro m\#x a            ;open the macro
        \}
}
macro list2_list {
        rept c x \{
                m\#x                    ;replay each line
        \}
}

So let's test this with the following command line: "fasm -m 10000 ListBuilder2.asm" (i.e. we limit the memory to 10MB)

Code:
rept 13767 x {
        .list2 \{ db `x,0 \}
}
list2_list


fasm -m 10000 ListBuilder2.asm wrote:
flat assembler version 1.69.24 (10000 kilobytes memory)
1 passes, 71496 bytes.

Now we can get 13767 lines using the same 10MB of memory. But there is a catch here, we need to careful about namespace clashes. The macro deliberately uses single character names to save memory, and these names might also be something you are using within your application. So we have just one more thing to add to make the macros play nicely with other label names, we put it into a wrapper macro. This also allows us to easily define more building macros with a single command:

Code:
macro list_builder name {
        local m,c                       ;avoid name clashing
        c equ 0                         ;initialise the counter
        macro .#name [a] \{
                \common
                rept 1 x:c+1 \\{
                        c equ x         ;update the counter
                        macro m\\#x a   ;open the macro
                \\}
        \}
        macro name#_list \{
                rept c x \\{
                        m\\#x           ;replay each line
                \\}
        \}
}
list_builder list2

This costs us a little bit of memory but we still get 13480 lines with the same memory usage:

Code:
rept 13480 x {
        .list2 \{ db `x,0 \}
}
list2_list


fasm -m 10000 ListBuilder3.asm wrote:
flat assembler version 1.69.24 (10000 kilobytes memory)
1 passes, 69774 bytes.

And now we can build multiple different lists:

Code:
macro list_builder name {
        local m,c                       ;avoid name clashing
        c equ 0                         ;initialise the counter
        macro .#name [a] \{
                \common
                rept 1 x:c+1 \\{
                        c equ x         ;update the counter
                        macro m\\#x a   ;open the macro
                \\}
        \}
        macro name#_list \{
                rept c x \\{
                        m\\#x           ;replay each line
                \\}
        \}
}

;define some lists

list_builder string
list_builder data
list_builder init
list_builder stuff

;put some stuff into the lists

.string { str1db 'Hello world!',0 }
.data   { var1 dd ? }
.init   { mov [var1],-1 }
.stuff  {
        nop
        rep movsb
        ret
}

.string { str2db 'Bye bye world',0 }
.data   { var2 dd ? }
.init   { mov [var2],-2 }

;place the defined data into the assembly stream

init_list
stuff_list
data_list
string_list



Last edited by revolution on 04 Oct 2010, 23:58; edited 1 time in total
Post 04 Oct 2010, 06:34
View user's profile Send private message Visit poster's website Reply with quote
Tomasz Grysztar
Assembly Artist


Joined: 16 Jun 2003
Posts: 4694
Location: Kraków, Poland
You can decrease the memory usage further by reducing the number of empty lines generated:

Code:
macro list_builder name {
        local m,c
        c equ 0
        macro .#name [a] \{ 
                \common 
                rept 1 x:c+1 \\{ c equ x
                                 macro m\\#x a \\}
        \} 
        macro name#_list \{ 
                rept c x \\{ m\\#x \\}
        \} 

list_builder list2 

This way it goes up 15311.

And some other interesting version would be to use the numbered lists:

Code:
c equ 0

macro .listx id,[a] {
        common 
        rept 1 x:c+1,y:id*1000000+c+1 \{ c equ x
                                         macro m\#y a \}

macro listx_list id {
        rept c y:id*1000000+1 \{ m\#y \}
}



PS. Yes, I see that those empty lines in preprocessor is something that perhaps deserves optimizing. You would then not get those lines in the preprocessed source in .fas file, but would that really be a problem?
Post 04 Oct 2010, 07:58
View user's profile Send private message Visit poster's website Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 8142
Location: Moonbase Alpha
Extreme unreadableness and maximum number of lines:

Code:
macro b n{local m,c
c equ 0
macro .#n[a]\{\common rept 1 x:c+1\\{c equ x
macro m\\#x a\\}\}macro n#_\{rept c x\\{m\\#x\\}\}}
b l
rept 19350 x{.l\{db`x,0\}}l_


fasm -m10000 ListBuilderObfuscated.asm wrote:
flat assembler version 1.69.24 (10000 kilobytes memory)
1 passes, 104994 bytes.

And, yeah, I vote to take the blank lines out of the fas file.
Post 04 Oct 2010, 08:55
View user's profile Send private message Visit poster's website Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 8142
Location: Moonbase Alpha
Latest fasm version 1.69.25

WHATSNEW.TXT wrote:
version 1.69.25 (Oct 11, 2010)

[-] Macroinstructions no longer breed the lines that are empty in their
definitions.
...

Post 16 Oct 2010, 03:22
View user's profile Send private message Visit poster's website Reply with quote
vid
Verbosity in development


Joined: 05 Sep 2003
Posts: 6790
Location: Slovakia
So, what's the top score so far? Smile
Post 16 Oct 2010, 08:55
View user's profile Send private message Visit poster's website AIM Address MSN Messenger ICQ Number Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 8142
Location: Moonbase Alpha
Just for vid:

Code:
                                1.69.24 1.69.25
ListBuilder1.asm                1430    1450
ListBuilder2.asm                13767   19693
ListBuilderMacro.asm            13480   19111
ListBuilderTomasz.asm           15311   19996
ListBuilderObfuscated.asm       19350   20205

Post 16 Oct 2010, 12:41
View user's profile Send private message Visit poster's website Reply with quote