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 { str1: db 'Hello world!',0 }
.data { var1 dd ? }
.init { mov [var1],-1 }
.stuff {
nop
rep movsb
ret
}
.string { str2: db '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
|
|