IncFile() and IncArray() macros [Updated 22-1-2009]

Post your FreeBASIC source, examples, tips and tricks here. Please don’t post code without including an explanation.
voodooattack
Posts: 605
Joined: Feb 18, 2006 13:30
Location: Alexandria / Egypt
Contact:

Post by voodooattack »

Updated, check first post..
voodooattack
Posts: 605
Joined: Feb 18, 2006 13:30
Location: Alexandria / Egypt
Contact:

Post by voodooattack »

Another update, now IncFile() works inside functions once more, by adding some initialization code to the function body, at module level it will not generate a single ASM instruction (except for the jump when debugging).

Currently working on a way to make IncArray() work inside functions too.

P.S: IncFile() will not work inside SCOPE blocks at module level, since EXTERN is not allowed at that point.
darkblacklife
Posts: 149
Joined: Jun 18, 2008 22:10

Re: IncFile() and IncArray() macros [Updated 8-3-2008]

Post by darkblacklife »

voodooattack wrote:IncFile() and IncArray() are macros that allow you, as the names suggest, to embed files into your executable at compile-time.
The embedded files can then be accessed just like regular memory pointers (or as arrays, with IncArray).

incfile.bi

Code: Select all

#IFNDEF INCFILE_BI
#DEFINE INCFILE_BI

#MACRO IncFileEx(label, file, sectionName, attr)
#if __FUNCTION__ <> "__FB_MAINPROC__"
    
    dim label as const ubyte ptr = any
    dim label##_len as uinteger = any
    
    #if __FB_DEBUG__
        asm jmp .LT_END_OF_FILE_##label##_DEBUG_JMP
    #else
        ' Switch to/Create the specified section
        #if attr = ""
            asm .section sectionName
        #else
            asm .section sectionName, attr
        #endif
    #endif
    
    ' Assign a label to the beginning of the file
    asm .LT_START_OF_FILE_##label#:
    asm __##label##__start = .
    ' Include the file
    asm .incbin ##file
    ' Mark the end of the the file
    asm __##label##__len = . - __##label##__start
    asm .LT_END_OF_FILE_##label:
    ' Pad it with a NULL Integer (harmless, yet useful for text files)
    asm .LONG 0
#if __FB_DEBUG__
    asm .LT_END_OF_FILE_##label##_DEBUG_JMP:
#else
    ' Switch back to the .text (code) section               
    asm .section .text
    asm .balign 16
#endif
    asm .LT_SKIP_FILE_##label:
    asm lea eax, .LT_START_OF_FILE_##label#
    asm mov dword ptr [label], eax
    asm mov dword ptr [label##_len], __##label##__len
#else

    extern "c"
    extern label As ubyte ptr
    extern label##_len as uinteger
    end extern
   
    #if __FB_DEBUG__
        asm jmp .LT_END_OF_FILE_##label##_DEBUG_JMP
    #else
        ' Switch to/create the specified section
        #if attr = ""
            asm .section sectionName
        #else
            asm .section sectionName, attr
        #endif
    #endif
    
    ' Assign a label to the beginning of the file
    asm .LT_START_OF_FILE_##label#:
    asm __##label##__start = .
    ' Include the file
    asm .incbin ##file
    ' Mark the end of the the file
    asm __##label##__len = . - __##label##__start
    asm .LT_END_OF_FILE_##label:
    ' Pad it with a NULL Integer (harmless, yet useful for text files)
    asm .LONG 0
    asm label:
    asm .int .LT_START_OF_FILE_##label#
    asm label##_len:
    asm .int __##label##__len
#if __FB_DEBUG__
    asm .LT_END_OF_FILE_##label##_DEBUG_JMP:
#else
    ' Switch back to the .text (code) section               
    asm .section .text
    asm .balign 16
#endif
    asm .LT_SKIP_FILE_##label:
#endif        
#endmacro

#macro IncFile(label, file) 
    IncFileEx(label, file, .data, "")        'Use the .data (storage) section (SHARED)
#endmacro

#endif
Usage:

IncFile(label, filename)
creates a variable named "label" that points to the file contents, stores the file in the .data section, the length of the contents is stored in another variable called "label_len".

IncFileEx(label, filename, section_name, attr)
same as the above, but lets you specify a name for the section to store data in and the attributes of the section (can be 0 for default).

I hope some one finds it useful :)
Nice, I was looking for something like this... but could you explain me how this work... "the theory"? what's happen here? which is the main idea?... where can I find any documentation about this?

I'd like understand why it works

thanks :)
darkblacklife
Posts: 149
Joined: Jun 18, 2008 22:10

Post by darkblacklife »

I'm trying to undertand how it works... I'm reading asm stuff on www... But I'm having problems to find documentation on google because google search omits terms like "##" or "_#" or "__##"... Ok, on freebasic "##" creates a new token by concatenating the texts at both sides of it.... but on asm i don't know what it does ("asm .incbin ##file" or "asm .LT_START_OF_FILE_##label#" with a single "#" at ends...)

And I see
__##label##__start = .
__##label##__len = . - __##label##__start
but this variables are not declared... only "label" and "label##_len" are declared... why don't you need declare these?

EDIT: I've cleaned a stupid question :P

Other questions about:

#if __FB_DEBUG__
asm jmp .LT_END_OF_FILE_##label##_DEBUG_JMP
#else
' Switch to/Create the specified section
#if attr = ""
asm .section sectionName
#else
asm .section sectionName, attr
#endif
#endif

and about...

#if __FB_DEBUG__
asm .LT_END_OF_FILE_##label##_DEBUG_JMP:
#else
' Switch back to the .text (code) section
asm .section .text
asm .balign 16
#endif

When debug option is set, section "sectionName" is no created... then... where is file loaded? (which section?)

When debug option is set, doesn't go to .text section... why?...was it on .text section already????

is filed loades on .text section when debug option is set????

Please, Could you give me some explanationa about these code lines?

thanks :)

Something else: I can't find documentation for "__FB_MAINPROC__" (a value of __FUNCTION__ ???, what values could __FUNCTION__ take????)
Last edited by darkblacklife on Sep 30, 2008 16:13, edited 2 times in total.
counting_pine
Site Admin
Posts: 6323
Joined: Jul 05, 2005 17:32
Location: Manchester, Lancs

Post by counting_pine »

darkblacklife wrote:Something else: I can't find documentation for "__FB_MAINPROC__" (a value of __FUNCTION__ ???, what values could __FUNCTION__ take????)
__FB_MAINPROC__ is the dummy name for __FUNCTION__ when it's used at module-level in the main module.
I've just updated __FUNCTION__'s page to show this:
http://www.freebasic.net/wiki/KeyPgDdfunction
darkblacklife
Posts: 149
Joined: Jun 18, 2008 22:10

Post by darkblacklife »

counting_pine wrote:
darkblacklife wrote:Something else: I can't find documentation for "__FB_MAINPROC__" (a value of __FUNCTION__ ???, what values could __FUNCTION__ take????)
__FB_MAINPROC__ is the dummy name for __FUNCTION__ when it's used at module-level in the main module.
I've just updated __FUNCTION__'s page to show this:
http://www.freebasic.net/wiki/KeyPgDdfunction
ok, thanks :)
voodooattack
Posts: 605
Joined: Feb 18, 2006 13:30
Location: Alexandria / Egypt
Contact:

Post by voodooattack »

darkblacklife wrote:I'm trying to undertand how it works... I'm reading asm stuff on www... But I'm having problems to find documentation on google because google search omits terms like "##" or "_#" or "__##"... Ok, on freebasic "##" creates a new token by concatenating the texts at both sides of it....
Yep, this is done to make unique symbol names in the gAS context, if you try to use the same identifiers twice in a macro, you'll get a duplicate definition, to avoid this, we simply derive a new identifier from the original macro argument to avoid conflicts with previous (or future) instances of the macro.
darkblacklife wrote:but on asm i don't know what it does ("asm .incbin ##file" or "asm .LT_START_OF_FILE_##label#" with a single "#" at ends...)
.incbin is a gAS statement that includes the target file into the assembler output, the hash characters are parsed at the FreeBASIC preprocessor level, the final output would be:

Code: Select all

asm .LT_STARTOF_FILE_myFile
if you take off the last hash character, it would be:

Code: Select all

asm .LT_STARTOF_FILE_label

so the last hash makes sure FBC treats "label" as a #define, and not a literal value.
darkblacklife wrote:And I see
__##label##__start = .
__##label##__len = . - __##label##__start
but this variables are not declared... only "label" and "label##_len" are declared... why don't you need declare these?
I explained this in the other thread, I'll provide the link in case someone reads this in the future: http://www.freebasic.net/forum/viewtopi ... 094#108094
darkblacklife wrote: Other questions about:

#if __FB_DEBUG__
asm jmp .LT_END_OF_FILE_##label##_DEBUG_JMP
#else
' Switch to/Create the specified section
#if attr = ""
asm .section sectionName
#else
asm .section sectionName, attr
#endif
#endif

and about...

#if __FB_DEBUG__
asm .LT_END_OF_FILE_##label##_DEBUG_JMP:
#else
' Switch back to the .text (code) section
asm .section .text
asm .balign 16
#endif

When debug option is set, section "sectionName" is no created... then... where is file loaded? (which section?)

When debug option is set, doesn't go to .text section... why?...was it on .text section already????

is filed loades on .text section when debug option is set????

Please, Could you give me some explanationa about these code lines?

thanks :)
Yes, due to problems with how FB emits debug data, it's not possible to include the files in .data/other sections when compiling in debug mode, the compiler emits debug data per line. (switching between .stabs and .text once per line), once this interferes with how the macro works, you'll get assembler errors and assembly would fail.

The only trick I could come with was to include the target file INSIDE code blocks (.text), and jump over it instead, a bit hack-ish, but it works, and the transition is mostly transparent, and as far as I can tell, does not interfere with the debugger at all, I don't think it's not that important in a debug build anyways ;)
darkblacklife wrote: Something else: I can't find documentation for "__FB_MAINPROC__" (a value of __FUNCTION__ ???, what values could __FUNCTION__ take????)
I think counting_pine pretty much explained what it does, now I'll explain why I used it in the macro..

This macro is context-sensitive, meaning, it functions differently when used at module-level, than inside the body of a function.

When you instantiate it at module-level, it detects it using that __FB_MAINPROC__ trick, and uses EXTERN to access your data (the proper way), everything is static and is resolved at link-time and no assembly code is generated at all (except for the jump instruction in debug builds).

But when you instantiate it inside a function's body, and because we're not allowed to use EXTERN inside functions, things are a bit different, the macro defines two variables and fills them with the values instead, and from there, they follow scoping rules properly like all other variables.
darkblacklife
Posts: 149
Joined: Jun 18, 2008 22:10

Post by darkblacklife »

voodooattack wrote:
darkblacklife wrote:but on asm i don't know what it does ("asm .incbin ##file" or "asm .LT_START_OF_FILE_##label#" with a single "#" at ends...)
.incbin is a gAS statement that includes the target file into the assembler output, the hash characters are parsed at the FreeBASIC preprocessor level, the final output would be:

Code: Select all

asm .LT_STARTOF_FILE_myFile
if you take off the last hash character, it would be:

Code: Select all

asm .LT_STARTOF_FILE_label
so the last hash makes sure FBC treats "label" as a #define, and not a literal value.
Ok, but I tried deleting ## on "asm .incbin ##file" and single # on "asm .LT_START_OF_FILE_##label#" and compiling with -r option.... I get same .asm code ... so ... could these be not needed?
voodooattack wrote:
darkblacklife wrote:And I see
__##label##__start = .
__##label##__len = . - __##label##__start
but this variables are not declared... only "label" and "label##_len" are declared... why don't you need declare these?
I explained this in the other thread, I'll provide the link in case someone reads this in the future: http://www.freebasic.net/forum/viewtopi ... 094#108094
Yes I think you did ... I re-asked a doubt about it in that same thread ...
myself wrote:don't you need to declare "__source__start" or "__source__len"???
is it due to in the very final output these are not present???
Thanks for all the time that you spent to aswer me ... :)

... And I have other questions :D

I assume you write:

Code: Select all

lea eax, .LT_START_OF_FILE_##label#
mov dword Ptr [label], eax
because it is more efficient that:

Code: Select all

mov dword Ptr [label], OFFSET .LT_START_OF_FILE_##label#
or this:

Code: Select all

mov dword Ptr [label], OFFSET __##label##__start
right???

but you write:

Code: Select all

asm mov dword Ptr [label##_len], __##label##__len
is not this more efficent:

Code: Select all

asm mov eax, __##label##__len
asm mov dword Ptr [label##_len], eax
?????

and something else...

I tried this:

Code: Select all

mov dword Ptr [label], __##label##__start
without "OFFSET" operator and I had this error message:
Error: too many memory references for 'mov'

I was thinking that on flat memory model offset address is THE address ... why do I have this error message???

Thank you
voodooattack
Posts: 605
Joined: Feb 18, 2006 13:30
Location: Alexandria / Egypt
Contact:

Post by voodooattack »

As for the preprocessor stuff, I wrote this for FB v0.18 or something around that if I remember correctly, and it was (and probably still) full of quirks..

The correct answer is that those were mostly work-arounds for issues that got fixed later, or stuff that I forgot to change/remove after revising this macro over 4 times already.

About OFFSET, I guess you're right, I guess I forgot about it because I never use incfile inside a function, I just provided that functionality in case someone needs it. I always do it at module-level.. thanks for pointing that out, I'll update the macro when I have time to test it. :)
and something else...

I tried this:

Code: Select all

mov dword Ptr [label], __##label##__start
without "OFFSET" operator and I had this error message:
Error: too many memory references for 'mov'

I was thinking that on flat memory model offset address is THE address ... why do I have this error message???

Thank you
That error message generally means that you need to move the value to an intermediate register first.. I'm no expert but as far as I know, if you omit OFFSET, the assembler treats both operands as dynamic values, in which case you'd need an extra register, OFFSET tells it to use a static value instead.
darkblacklife
Posts: 149
Joined: Jun 18, 2008 22:10

Post by darkblacklife »

voodooattack wrote:
and something else...

I tried this:

Code: Select all

mov dword Ptr [label], __##label##__start
without "OFFSET" operator and I had this error message:
Error: too many memory references for 'mov'

I was thinking that on flat memory model offset address is THE address ... why do I have this error message???

Thank you
That error message generally means that you need to move the value to an intermediate register first.. I'm no expert but as far as I know, if you omit OFFSET, the assembler treats both operands as dynamic values, in which case you'd need an extra register, OFFSET tells it to use a static value instead.
Yes but...
asm mov dword Ptr [label##_len], __##label##__len
it doesn't need OFFSET operator... why?
sorex
Posts: 15
Joined: Jul 11, 2006 12:04

Post by sorex »

Hello,

Yesterday I created a thread about adding images to my EXE file and got redirected to this thread.

Which include source do I actually need to embed a BMP image and how do I access it? I guess BLOAD won't work at this level?

I see a lot of sources in this thread, long and small ones but it's not clear which one should be used (to me atleast ;o) )
voodooattack
Posts: 605
Joined: Feb 18, 2006 13:30
Location: Alexandria / Egypt
Contact:

Post by voodooattack »

darkblacklife wrote:Yes but...
asm mov dword Ptr [label##_len], __##label##__len
it doesn't need OFFSET operator... why?
The second value resolves to a constant value, same as if you had put a literal number in there..

asm mov dword ptr [myvar], 5
sorex wrote:Hello,

Yesterday I created a thread about adding images to my EXE file and got redirected to this thread.

Which include source do I actually need to embed a BMP image and how do I access it? I guess BLOAD won't work at this level?

I see a lot of sources in this thread, long and small ones but it's not clear which one should be used (to me atleast ;o) )
I wouldn't recommend using bitmaps for embedding images, I'd recommend use PNGs instead..

You can use yetifoot's png library here: http://www.freebasic.net/viewtopic.php?t=8024&start=0

and then use this macro:

Code: Select all

#macro IncPNG(label, file)
    incfile (p##label, file)
#if __FUNCTION__ = "__FB_MAINPROC__"
    dim shared as FB.IMAGE ptr label = any    
#else
    dim as FB.IMAGE ptr label = any
#endif
    label = PNG_Load_Mem(cast(any ptr, p##label), p##label##_len, PNG_TARGET_FBNEW)
#endmacro
here's an example:

Code: Select all

#include "incfile.bi"
#include "fbpng.bi"
#include "fbgfx.bi"

incpng(test, "test.png")

screenres 640, 480, 32

put (0,0), test

sleep
[ Macro updated to use OFFSET instead of LEA and I fixed some new glitches I found, check first post. ]
darkblacklife
Posts: 149
Joined: Jun 18, 2008 22:10

Post by darkblacklife »

voodooattack wrote:
darkblacklife wrote:Yes but...
asm mov dword Ptr [label##_len], __##label##__len
it doesn't need OFFSET operator... why?
The second value resolves to a constant value, same as if you had put a literal number in there..
But "__##label##__start" and "__##label##__len" are similar.... why is "__##label##__len" like a constant? and why is "__##label##__start" like a dynamic value?
voodooattack wrote: [ Macro updated to use OFFSET instead of LEA and I fixed some new glitches I found, check first post. ]
and do you say to use OFFSET and mov is better (more efficient) than lea?
sorex
Posts: 15
Joined: Jul 11, 2006 12:04

Post by sorex »

Thanks Voodoo,

It seems to include my PNG but at execution it's nagging about a missing zlib.dll.

Can I include/attach that to my EXE aswell or will I still end up with several files instead of just 1 EXE file?

Edit: ok, grabbed zlib1.dll from that mp3 player in the projects thread and now it works.

odd thing is that my 8bpp (256 colors) PNG doesn't get shown with mode 13, need to change it to 320x200 32 before it shows up.
toneboy
Posts: 95
Joined: May 04, 2008 4:51
Location: Australia
Contact:

Post by toneboy »

please correct me if I'm doing something wrong. (which i am)

I compiled the voodooattack's code at the start, named it incfile.bi

then I run voodooattack's example, but then for some reason FBIDE goes and opens up incfile.bi and the only text in that is 'MZ' which it says "expected '=' "
It also wont compile

I'm supposed to see a read out of the demo program arnt I? (if it were working)

Thanks
Post Reply