• Welcome to Dark Bit Factory & Gravity.
 

Making tiny FB executable programs

Started by Jim, July 09, 2007

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

Jim

I was looking at nino's latest tiny executables, and trying to see how to link them without linking in freebasic's startup code and default libraries.  These are really large, so it's important to be able to get rid of them for tiny exes.  The problem is that FB's linker and compiler hard code a few things and that means the startup code always gets linked.

So here's my solution to the problem.

First, write your program as usual, obviously NOT using an built-in FB functions like print, graphics, etc.
Here's my test program (tiny.bas)

#include "windows.bi"

declare function WinMainCRTStartup alias "WinMainCRTStartup"

function WinMainCRTStartup() as integer
messagebox(0,"Very Small FB EXE Sample","Hello",MB_OK)
return 0
end function


compile this using

fbc -c tiny.bas

The -c tells the FB compiler just to make a .o file and not an .exe.  .o files are the building blocks used by linkers to make executable files.

Next, we need to stub the functions that FB has hardcoded.
I used assembler for this (stub.asm)

.386p
.model flat
.code
public _fb_RtInit@0
_fb_RtInit@0:
end

I assembled it with MASM, but it can be built with lots of other assemblers.
Command line
ml /c /coff stub.asm
This produces stub.obj.

Finally, you need to link these all together.  FB is no use for this, you need a proper linker.  I've used crinkler, which has the added advantage of compressing while it links.  You can download it from www.crinkler.net

The command line I used to do the link is

crinkler /SUBSYSTEM:WINDOWS /OUT:tiny.exe /LIBPATH:"C:\program files\microsoft platform sdk for windows server 2003 r2\lib" kernel32.lib user32.lib tiny.o stub.obj




You will need the libs from Microsoft Platform SDK to perform this step.  You will need to change LIBPATH to point to wherever you have them installed.  You can change the name of the output file by changing OUT.

And that produces an exe which is just 605 bytes in size!

Included in the archive are:
All the source from this article, stub.obj for those who don't have MASM, and the final exe.
Crinkler is here www.crinkler.net

Have fun!

Jim
Challenge Trophies Won:

ninogenio

cool k+ jim!

lots of new stuff there for me to have fun with cheers.
Challenge Trophies Won:

Stonemonkey


Jim

#3
Here's taj's pixel shader 3.0 demo in FB.  Using the above technique it comes out at 1081 bytes.  Can anyone get it under the magic 1Kb?

Nino, this will definitely help you port trinity :)

' Chris Thornborrow (auld)... lalala credit would be nice ... lalala
' Example OGL + shaders in 1k
' Requires crinkler

' VS2005 modifications by benny!weltenkonstrukteur.de

' Freebasic modifications by Jim 9/7/2007

option explicit

#include "windows.bi"
#include "GL/gl.bi"
#include "GL/glu.bi"
#include "GL/glext.bi"

declare sub WinMainCRTStartup alias "WinMainCRTStartup"

' Draws the labyrinth using a pair of shaders...
' Vertex shader does NOT transform incoming vertex but stores that in p instead for later use
' The fragment shader is a complicated. It essentially raycasts into a constantly changing
' equation, similar in concept to chladni :-). Its carfully tuned so we dont crash into it as we move
' and so we get a feeling of depth. The for loop could be smaller when written as a do .. while but then
' it wouldnt work on NVidia cards of 7xxx series or below.
' Fragment shader has 2 lines for setup, 1 line for raycasting and function testing, 1 line for colour.

dim shared labvsh as zstring * 68 => "varying vec4 p;void main(){p=ftransform();gl_Position=gl_Vertex;}"
dim shared labfsh as zstring * 260 => "varying vec4 p;void main(){vec3 V=vec3(0,3.5*sin(p.z),p.z);vec3 D=vec3(p.x,p.y,0.5)*0.08;for(int i=800;i>0;i--,V+=D)if(length(fract(abs(sin(V))))>1.5+0.5*sin(V.z)*sin(p.z*0.4))i=0;gl_FragColor=vec4(1,0.8,0.8,0)*dot(vec3(0.4),fract(V))-length(V-p.xyz)*0.1;}"

type GenFP as sub () ptr

dim shared glFP(6) as GenFP
dim shared glnames(6) as zstring * 16 => {"glCreateShader", "glShaderSource", "glCompileShader", "glCreateProgram", "glAttachShader", "glLinkProgram", "glUseProgram"}

dim shared pfd as PIXELFORMATDESCRIPTOR
dim shared dmScreenSettings as DEVMODE

' declare everything global to save bytes
dim shared q as zstring ptr
dim shared i as integer
dim shared as GLuint v,f,p
dim shared hDC as HDC

dim shared m(15) as GLfloat ={1,-0.01f,0,0,0.01f,1,0,0,0,0,1,0,0,0,0.04f,1}

sub WinMainCRTStartup()
dmScreenSettings.dmSize=sizeof(dmScreenSettings)
dmScreenSettings.dmPelsWidth   = 640
dmScreenSettings.dmPelsHeight  = 480
'dmScreenSettings.dmBitsPerPel = 32
' its risky to remove the flag and bits but probably safe on compo machine :-)
dmScreenSettings.dmFields=DM_PELSWIDTH Or DM_PELSHEIGHT
ChangeDisplaySettings(@dmScreenSettings,CDS_FULLSCREEN)

' minimal windows setup code for opengl 
pfd.cColorBits = 32
pfd.cDepthBits = 32
pfd.dwFlags    = PFD_SUPPORT_OPENGL Or PFD_DOUBLEBUFFER
hDC = GetDC(CreateWindow("edit", 0, WS_POPUP Or WS_VISIBLE Or WS_MAXIMIZE, 0, 0, 0, 0, 0, 0, 0, 0))

SetPixelFormat(hDC, ChoosePixelFormat(hDC, @pfd), @pfd)
wglMakeCurrent(hDC, wglCreateContext(hDC))

' create the shaders inlined to save bytes
for i=0 to 6
glFP(i) = cast(GenFP, wglGetProcAddress(glnames(i)))
next

v = cast(PFNGLCREATESHADERPROC, glFP(0))(GL_VERTEX_SHADER)
f = cast(PFNGLCREATESHADERPROC, glFP(0))(GL_FRAGMENT_SHADER)
p = cast(PFNGLCREATEPROGRAMPROC, glFP(3))()

q = @labvsh
cast(PFNGLSHADERSOURCEPROC, glFP(1))(v, 1, cast(byte ptr ptr, @q), NULL)
cast(PFNGLCOMPILESHADERPROC, glFP(2))(v)

q = @labfsh
cast(PFNGLSHADERSOURCEPROC, glFP(1))(f, 1, cast(byte ptr ptr, @q), NULL)
cast(PFNGLCOMPILESHADERPROC, glFP(2))(f)

cast(PFNGLATTACHSHADERPROC, glFP(4))(p,v)
cast(PFNGLATTACHSHADERPROC, glFP(4))(p,f)

cast(PFNGLLINKPROGRAMPROC, glFP(5))(p)
cast(PFNGLUSEPROGRAMPROC, glFP(6))(p)

ShowCursor(FALSE)

'**********************
' NOW THE MAIN LOOP...
'**********************

' there is no depth test or clear screen...as we draw in order and cover
' the whole area of the screen.

while GetAsyncKeyState(VK_ESCAPE) = 0
' move forward and rotate slightly each frame
' glMultMatrix is smaller (20 bytes) than glTranslatef and glRotatef!!
glMultMatrixf(@m(0))
glRecti(-1,-1,1,1)
SwapBuffers(hDC)
wend
' necessary under vista it seems... :-( extra bytes required.
ExitProcess(0)
end sub


Jim
Challenge Trophies Won:

slippy

wow ... that's a fat tut ... k+ m8 ... and thanks for this :)

ninogenio

Cool cheers jim that helps a lot!
Challenge Trophies Won:

Shockwave

Also fails to compile here It is failing in glu.bi
Any clues?
Shockwave ^ Codigos
Challenge Trophies Won:

benny!

I am not using FB ... but this is a very interesting tutorial, Jim.

K++ !!!
[ mycroBLOG - POUET :: whatever keeps us longing - for another breath of air - is getting rare ]

Challenge Trophies Won:

p01


Jim

QuoteAlso fails to compile here It is failing in glu.bi
Any clues?
Different versions of FB.  Me and nino are both using 0.16b.  Has everyone else upgraded while we weren't looking? :P
I'll try to get a fix for 0.17b later.

Jim
Challenge Trophies Won:

Stonemonkey

I'm using 0.17b and it's working here.

Jim

I've just downloaded 0.17b and it works for me too (I just had to remove 'option explicit').  Which version of FB are you using Shockwave?

Jim
Challenge Trophies Won:

ninogenio

it could be 0.15 he is yousing as the problem he is getting is an incompatibility between glu.bi and windows.bi im sure check my trinity port thread for further details.
Challenge Trophies Won:

Jim

Any idea where I can download 0.15b - it's not available at freebasic.net any more!

Jim
Challenge Trophies Won:

ninogenio

not sure if it can be downloaded any more jim as i dont think many people use it anymore  :-\
Challenge Trophies Won:

Stonemonkey


Jim

Found it.  It's hidden on sourceforge :)
Jim
Challenge Trophies Won:

Jim

I'm really sorry Shockwave, there's no way 0.15b will ever compile this code.  It's easy to get round the 'max' problem, but then it fails on all the function pointer stuff :(
If anyone else wants a look, the 'max' fix is just

#define max
#include "windows.bi"
#undef max


Jim
Challenge Trophies Won:

ninogenio

bugger thats a real shame is there no way you could update sw?
Challenge Trophies Won:

Jim

You can install as many versions of fb into different directories/program groups as you like.  I've now got 0.15/0.16/0.17 all installed at the same time :)  You just have to change the default values the installer gives you.

Jim
Challenge Trophies Won: