flat assembler
Message board for the users of flat assembler.

Index > Main > FASMLIB tutorial part 1

Author
Thread Post new topic Reply to topic
vid
Verbosity in development


Joined: 05 Sep 2003
Posts: 7105
Location: Slovakia
vid 23 Mar 2006, 18:52
hi, this is my quick introduction to FASMLIB.

It's all x86 32bit ring3 code in flat mode (CS = DS = ES = SS). It is just a set of routines and macros, which you can call. For now it has only win32 version, more OSes should be added soon (you can participate!)

First let's see most basic win32 example without FASMLIB. It does nothing just exits:
Code:
include "%FASMINC%/win32axp.inc"

.code

start:
        invoke  ExitProcess, 0

.data

.end start   
    
Now let's convert it to be using FASMLIB. FASMLIB is just set of modules, from which you can include those which you use.

First basic module is called fasmlib. It contains library's common declarations and macros, which are used in other modules.

There are also few more modules which must be always included. It's because they are very often used, and would be included in every bigger project anyway. This way you can always use them and don't have to care if they are included. These modules are
process - working with processes (you always need at least to exit process)
mem - memory routines (allocation, etc.)
str - string library (every project has some string manipulation)

Now how to include module? Each module is separate .inc file, in directory fasmlib. I suggest you copy this directory into your project directory, so you don't need to mess with some environment variables. Then you just to need include all files (file fasmlib.inc must be first, other can be in any order). So with includes it loks like this:
Code:
include "%FASMINC%/win32axp.inc"

SYSTEM equ win32
include "fasmlib/fasmlib.inc"
include "fasmlib/process.inc"
include "fasmlib/mem.inc"
include "fasmlib/str.inc"

.code

start:
        invoke  ExitProcess, 0

.data

.end start   
    
note the additional SYSTEM equ win32 line, this tells FASMLIB for which operating system is it compiling. In future there will be versions for more operating systems, and changing project's OS will require just changing this one line.

Now to make your code easier portable (one of main goals of fasmlib), you should use fasmlib procedures instead of system calls, if they are provided. One (only here) such procedure is procedure exit from module process. It takes one argument, which is exit code.

Now how to call fasmlib procedure. All arguments are dwords. all fasmlib procedures are STDCALL (arguments pushed right to left, calee removes them from stack). You can call them directly with call instruction. So the call looks like this:

Code:
include "%FASMINC%/win32axp.inc"

SYSTEM equ win32
include "fasmlib/fasmlib.inc"
include "fasmlib/process.inc"
include "fasmlib/mem.inc"
include "fasmlib/str.inc"

.code

start:
        push 0
        call exit

.data

.end start   
    
Some of modules also need to be initialized before they can be used. From required modules, mem and str need to. You initialize module with <module name>.init procedure. When you finish using module, you need to uninitialize it. This is done with <module name>.uninit.
Both init and uninit doesn't take any arguments. mem module should be initialized first.

Code:
include "%FASMINC%/win32axp.inc"

SYSTEM equ win32
include "fasmlib/fasmlib.inc"
include "fasmlib/process.inc"
include "fasmlib/mem.inc"
include "fasmlib/str.inc"

.code

start:
        ;initialize modules
        call mem.init
        call str.init

        ; ... do the work ...

        ;uninitialize modules
        call str.uninit
        call mem.uninit
        
        ;exit process
        push 0
        call exit

.data

.end start
    
This is almost good win32 fasmlib program template. But it lacks one important thing: error checking. Error checking is so problematic in most lowlevel languages, that i had taken "radical" step, which proved to be great solution. Error is always (!!! ALWAYS !!!) returned in CF, and must be ALWAYS checked (well, except exit procedure Very Happy )

So after each call to fasmlib procedure, first thing you will do will be "jc" instruction. Then, you must handle the error somehow, here is the most simple way:

Code:
include "%FASMINC%/win32axp.inc"

SYSTEM equ win32
include "fasmlib/fasmlib.inc"
include "fasmlib/process.inc"
include "fasmlib/mem.inc"
include "fasmlib/str.inc"

.code

start:
        ;initialize modules
        call mem.init
        jc error
        call str.init
        jc error

        ; ... do the work ...

        ;uninitialize modules
        call str.uninit
        jc error
        call mem.uninit
        jc error
        
        ;exit process
        push 0
        call exit

error:
        ;try to uninitialize what we can, without further error checking
        call str.uninit
        call mem.uninit

        push 0
        call exit

.data

.end start    
If error happens, we just try to uninitalize as much as we can, but we don't check for error anymore, no way to handle it anyway. Really, don't forget to check EVERY call for error

Last thing we must do is include two more lines in data section, i will explain these later:

Code:
include "%FASMINC%/win32axp.inc"

SYSTEM equ win32
include "fasmlib/fasmlib.inc"
include "fasmlib/process.inc"
include "fasmlib/mem.inc"
include "fasmlib/str.inc"

.code

start:
        ;initialize modules
        call mem.init
        jc error
        call str.init
        jc error

        ; ... do the work ...

        ;uninitialize modules
        call str.uninit
        jc error
        call mem.uninit
        jc error
        
        ;exit process
        push dword 0
        call exit

error:
        ;try to uninitialize what we can, without further error checking
        call str.uninit
        call mem.uninit

        ;exit with code -1
        push dword -1
        call exit

.data
        IncludeIData
        IncludeUData

.end start
    
And this is complete proper fasmlib template. I know it looks somewhat slobby, but you will find out reason for all this later, when you get deeper into it. Next time it will be some kind of hello world.

I am looking forward for all your feedback, please post all opinions, corrections, ideas etc. here.
Post 23 Mar 2006, 18:52
View user's profile Send private message Visit poster's website AIM Address MSN Messenger ICQ Number Reply with quote
pelaillo
Missing in inaction


Joined: 19 Jun 2003
Posts: 878
Location: Colombia
pelaillo 24 Mar 2006, 13:33
Vid, you have awesome didactic capabilities. It's all very clear.

A suggestion for the error handling:
Code:
start: 
        ;initialize modules 
        call mem.init 
        jc mem_error 
        call str.init 
        jc str_error 

        ; ... do the work ... 

        ;uninitialize modules 
        call str.uninit 
        jc str_error 
        call mem.uninit 
        jc mem_error 
         
        ;exit process 
        push dword 0 
        call exit 

mem_error: 
        call mem.last_error
        ;last error in eax to do further handling
        jmp error
str_error:
        call str.last_error
        ;last error in eax to do further handling
        jmp error
error:
        ;try to uninitialize what we can 
        call str.uninit 
        call mem.uninit 

        ;exit with code -1 
        push dword -1 
        call exit 
    
Post 24 Mar 2006, 13:33
View user's profile Send private message Yahoo Messenger Reply with quote
vid
Verbosity in development


Joined: 05 Sep 2003
Posts: 7105
Location: Slovakia
vid 28 Mar 2006, 01:37
pellialo: error handling is more advanced than described here.... i'll cover it later. and it will be even more advanced in future (ability to store call stack with argument values, on error). Of course this will be optional.
Post 28 Mar 2006, 01:37
View user's profile Send private message Visit poster's website AIM Address MSN Messenger ICQ Number Reply with quote
Reverend



Joined: 24 Aug 2004
Posts: 408
Location: Poland
Reverend 28 Mar 2006, 09:35
pelaillo: Why create whole new proc xxx.last_error? I guees that every proc should alert error by setting CF and also copy error code to eax immidietaly. So it would be always obvious that when CF is set, eax contains info about error (and if implemented in whole library - it would be easy to accustom for particulaer programmer)
Post 28 Mar 2006, 09:35
View user's profile Send private message Visit poster's website Reply with quote
RedGhost



Joined: 18 May 2005
Posts: 443
Location: BC, Canada
RedGhost 28 Mar 2006, 11:17
Reverend wrote:
pelaillo: Why create whole new proc xxx.last_error? I guees that every proc should alert error by setting CF and also copy error code to eax immidietaly. So it would be always obvious that when CF is set, eax contains info about error (and if implemented in whole library - it would be easy to accustom for particulaer programmer)


i agree this is the best approach (also much simpler and probably faster than all that redundant HLL error handling)

_________________
redghost.ca
Post 28 Mar 2006, 11:17
View user's profile Send private message AIM Address MSN Messenger Reply with quote
vid
Verbosity in development


Joined: 05 Sep 2003
Posts: 7105
Location: Slovakia
vid 28 Mar 2006, 12:34
error code in eax is interesting... but now it always copies error code to [err] variable, which i think is somewhat better, you can need eax in error handling and then you

also error descirption is always in [err.str].

I also found nice way to make unique error code (you can be sure that if you declare new error, it's code is not used). Trick is using label of error message as error code. So [err] is always address of simple error message:
Code:
ERR_FILE_NOT_FOUND db "File not found",0
...
mov [err], ERR_FILE_NOT_FOUND    


but sometimes you can get error message from system, in such cases error message can be something like ERR_FILE_NOT_FOUND and description is gotten from system, like "Invalid path", "Invalid drive" etc. Also in future there will be stack trace i mentioned included, and maybe some formatted messages: "MyProc - Invalid argument #4 - fileHandle (00004ACC)"

So [err] is used for your program to find out what didn't work (file not found) and [err.str] is for user (path doesn't exist).

Error handling will be better described in future. I have not even completely implemented this yet, there are still few issues i need to solve with this.
Post 28 Mar 2006, 12:34
View user's profile Send private message Visit poster's website AIM Address MSN Messenger ICQ Number Reply with quote
shism2



Joined: 14 Sep 2005
Posts: 248
shism2 29 Mar 2006, 02:40
Where is the fasmlib?
Post 29 Mar 2006, 02:40
View user's profile Send private message Reply with quote
RedGhost



Joined: 18 May 2005
Posts: 443
Location: BC, Canada
RedGhost 29 Mar 2006, 04:15
shism2 wrote:
Where is the fasmlib?


http://www.board.flatassembler.net/topic.php?t=4696

_________________
redghost.ca
Post 29 Mar 2006, 04:15
View user's profile Send private message AIM Address MSN Messenger Reply with quote
MazeGen



Joined: 06 Oct 2003
Posts: 977
Location: Czechoslovakia
MazeGen 29 Mar 2006, 13:24
vid wrote:

There are also few more modules which must be always included. It's because they are very often used, and would be included in every bigger project anyway. This way you can always use them and don't have to care if they are included. These modules are
process - working with processes (you always need at least to exit process)
mem - memory routines (allocation, etc.)
str - string library (every project has some string manipulation)


If these should be always included, why these are not included by default using:
Code:
include "fasmlib/fasmlib.inc"    

In other words, is it good idea to include them always by hand?

vid wrote:

Some of modules also need to be initialized before they can be used. From required modules, mem and str need to.

Again, couldn't these be initialized by default, if they are included?
Post 29 Mar 2006, 13:24
View user's profile Send private message Visit poster's website Reply with quote
vid
Verbosity in development


Joined: 05 Sep 2003
Posts: 7105
Location: Slovakia
vid 29 Mar 2006, 14:28
Quote:
In other words, is it good idea to include them always by hand?

For clearness. I want that user of library always knows what is hapenning. That's also why i first explain how things work without macros. I don't want to end up with too complex, things. Understatnding these lines is in my opinion easier than understanding and remembering sentence in documentation.

Quote:
Again, couldn't these be initialized by default, if they are included?

Same reason as before, plus FASMLIB doesn't execute any code itself, you must call it. There is no entry code, you again always know what is happening.

Doing it yours way would save only very little coding time, these things are writen about once per project, and this approach can prevent big confusion and need to explore library internals.

One of my main goals is to make library CLEAR, so it's user always knows what he doing. That's why we code assembly, no?
Post 29 Mar 2006, 14:28
View user's profile Send private message Visit poster's website AIM Address MSN Messenger ICQ Number Reply with quote
Reverend



Joined: 24 Aug 2004
Posts: 408
Location: Poland
Reverend 30 Mar 2006, 21:57
I have nice idea about initalization. We will have one equate with list of modules. Every included module will add another string to the equate list. Then we will have this list defined somewhere as data. There would be only one initalization procedure which will just jump through the jump table. Check below the example:
Code:
; USER CODE
include 'mem.inc' ; list equ mem.init
                  ; count = 1
include 'str.inc' ; list equ mem.init, str.init
                  ; count = 2
...
        call    fasmlib.init    
Code:
; FASMLIB CODE
idata {
  fasmlib.jump_table dd list, 0
}
fasmlib.init:
        mov     eax, fasmlib.jump_table
        mov     ecx, count
  @@:
        call    dword [eax+ecx*4-4]
        loop    @B
        stc
        retn    
Post 30 Mar 2006, 21:57
View user's profile Send private message Visit poster's website Reply with quote
MazeGen



Joined: 06 Oct 2003
Posts: 977
Location: Czechoslovakia
MazeGen 31 Mar 2006, 07:27
vid:

If you use "proc" or "local" macros in your code, do you exactly know the offsets of your local variables and what instructions are generated? You don't and you even don't bother about it, because it simply works. Only rough knowledge is needed. No hand coding of stack frame and local offsets.

Same way, one don't want to bother with forced initialization at the entry and uninitialization at the exit.
Post 31 Mar 2006, 07:27
View user's profile Send private message Visit poster's website Reply with quote
vid
Verbosity in development


Joined: 05 Sep 2003
Posts: 7105
Location: Slovakia
vid 31 Mar 2006, 07:46
Reverend: you don't always want to initialize module at startup. your solution will be better in most cases, but will not work in all cases - it's not general. Imagine some modules which need to allocate lot of memory on initialization, and are needed only for some particular operation (like some packing or encryption algos that need to make big tables).
Post 31 Mar 2006, 07:46
View user's profile Send private message Visit poster's website AIM Address MSN Messenger ICQ Number Reply with quote
Reverend



Joined: 24 Aug 2004
Posts: 408
Location: Poland
Reverend 31 Mar 2006, 13:37
MazeGen: It will be like pushing ebx esi edi in the beginning of your code modifying them.
Code:
push esi edi ebx
...
; your code
...
pop ebx edi esi    
Code:
call xxx.init
...
; your code
...
call xxx.uninit    


vid: You're right. I thought only about programs which use all procs from the beginning to the very end, ie. initalization is at startup and uninitialization at the end. But it will not always be true. So my idea won't work then.
Post 31 Mar 2006, 13:37
View user's profile Send private message Visit poster's website Reply with quote
MazeGen



Joined: 06 Oct 2003
Posts: 977
Location: Czechoslovakia
MazeGen 31 Mar 2006, 14:24
Reverend wrote:
MazeGen: It will be like pushing ebx esi edi in the beginning of your code modifying them.
Code:
push esi edi ebx
...
; your code
...
pop ebx edi esi    
Code:
call xxx.init
...
; your code
...
call xxx.uninit    


Yes, the initialization would be done at the entry and the uninitialization would be done when you call "exit" function.
Anyway, vid already told me private that he don't want to go this way.

[edit] Eh, I got it now. You meant this un/initialization is as natural as it is in procedures. It is really about one's choice. [/edit]
Post 31 Mar 2006, 14:24
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-2025, Tomasz Grysztar. Also on GitHub, YouTube.

Website powered by rwasa.