FASMLIB GUIDE


0. What is FASMLIB
==================
  It is 32bit flat-mode library for FASM. It confer OS-independence and set of
  routines.

  Rules:
  - 386 compatible processor
  - code is 32bit, ring3 by default
  - flat mode (es, ds, cs, ss are same)
  - multi-OS
  - modular
  - await as few user-defined things as possible (don't use invoke, system
    constants etc., see fasmlib/win32/mem.inc for an example)
  - keep FASM's rule to be able to compile code for any enviroment (OS) under
    any enviroment
  - filenames should be 8.3
  - all data pointers should be above 10000h

1. File header:
===============
  
  At very beginning of file:  
	;FASM STANDARD LIBRARY
	;[OS_name] brief description of module
  "OS_name" part is present only in OS-dependant modules. It is name of OS, like  	
	;FASM STANDARD LIBRARY
	;Win32 memory routines
  in common modules is only description of module
	;FASM STANDARD LIBRARY
	;Basic definition macros used by library

  Then comes quick description of interface of module (what "user" of module
  needs to know)
	;contains:
	; <type> <name> <arguments>
  <type> can be "proc ", "cproc", "macro", "struc", "const". (note all of them
  5 chars for indentation. <name> is name under which is thing defined. after
  <name> comes enough spaces to have all <arguments> on same indent. Like:
	;contains:
	; const STR_MINSTRLEN   minimum string length (default = 16)
	; struc strconst        string
	; proc  str.init
	; proc  str.uninit
	; proc  str.copy        hdest, source
	; cproc printf          format, [args]

  Things should be listed in order they appear in file, so reader can find one
  easier.

  "const" is user-defineable constant. it can be defined by user (prior to
  including module). It can have default value if not defined by user, use
  "ifndef" macros from fasmlib.inc to define it:
	ifndef STR_MINCOUNT
	  STR_MINCOUNT = 10
	end if

2. Global data definitons
=========================

  If you need to define some global data, it should have name prefix same as
  library. Define it with "udata" or "idata" macros:
	;parameters of dynamic strings list.
	udata
	  str.table:
	   .count    dd 0	    ; max count of the strings in table.
	   .LastHandle dd 0	    ; last allocated handle.
	   .ptr      dd 0	    ; pointer to the table data
	endg


3. Procedure declaration
========================

  Then comes definition of procedures. Here is "template" i use:
	;=========================================
	;<name>
	;desc: <description of what procedure does>
	;args: <arg1> - <description of arg1>
	;      <arg2> - <description of arg2>
	;ret: CF set on error[, otherwise
	;     eax = <someting>]
	;[note: <notes>
	;       <notes>]    
	proc <name> .<arg1>,.<arg2>
	  push <all regs it could modify except EAX>
	
	  <code>
	
	.rnc:
	  clc
	  jmp .r
        .error:
	  stc
	.r:
	  pop <all registers it pushed>
	  ret
	endp

  all <args> should be defined with "." to prevent name conflict. in
  description you can omit "."
  
  Anywhere in code (if you didn't push anything) you can use "jmp .rnc" to
  return without error and "jmp .error" to return error.

  Even if procedure doesn't return anything, it should always return error
  status in CF.
	;ret: CF set on error

  If it returns some value it should use eax. If procedure returns error (CF=1)
  then other returns values are invalid:
	;ret: CF set on error, otherwise
	;     eax = return value
  Only exception is returning boolean, where ZF should be used.

  Every procedre returns error status in CF, modifies EAX and flags, and
  preserves all other general purpose registers (EBX, ECX, EDX, ESI, EDI, ESP,
  EBP).

  If procedure returns error status in CF, then no other registers have predictable value
  (including flags).

  You should add notes about implementation or warnings for user in the "note"
  section:
	;note: some one
	;      other note

  Here is example of fully - equiped procedure. Not very real, but should
  demonstrate almost everything. Not tested, sorry.
	;=========================================
	;str.cmp
	;desc: copies bock of memory
	;args: str1 - pointer or handle to first string to compare
	;      str2 - pointer or handle to second string to compare
	;ret: CF set on error, otherwise
	;     ZF = 1 if strings are equal, otherwise
	;     eax = index of first character where strings differ
	;note: case sensitive
	proc str.cmp .str1,.str2
	  push ecx esi edi         ;<-- save all regs we use except eax
	  pushf                    ;<-- useful when we are using ZF to return boolean
	
	  ;get pointers if arguments are handles <-- comment your code enough for reader
	  ;to understand what block of code does without looking at it. That should be
	  ;needed only when he needs to know HOW it is done. (in theory... ;) )
	  libcall str.ptr,[.str1]
	  jc .error                ;<-- never forget to check return value!!!
	  mov esi,eax
	  libcall str.ptr,[.str2]
	  jc .error
	  mov edi,eax

	  ;compare (stupid algorithm...)
	  xor ecx,ecx
	.byte:                     ;<-- all labels must be local
	  mov al,[esi+ecx]
	  cmp al,[edi+ecx]
	  jne .different	  
	  inc ecx
	  or al,al                 ;test end of string	  
	  jnz .byte
	  
	  ;strings are same
	  or [esp],(1 shl 6)       ;set zero flag in stack
	  jmp .rnc

	  ;strings are different (ecx = index where they are different)
	.different:
	  and [esp],not (1 shl 6)  ;clear zero flag in stack
	  mov eax,ecx		   ;return value in eax

	.rnc:
	  and [esp],not 1          ;clear carry flag in stack
	  jmp .r
        .error:
	  or [esp],1               ;set carry flag in stack
	.r:
	  popf
	  pop edi esi ecx
	  ret
	endp

4. File ending
==============
  At the end file author(s) of file should be noted:
	;by vid
	;29.2.2005: modified by 50/\/\30|\|3_3|z - bugfix in str.cmp

  And other licensing blah-blah if you like
	;(C) MacroHard co-piration
	;distributed under EULA 1235-17.45234#1231/1432.6

5. Special Rules
================

  5.0 General Rules
  -----------------
    Every procedure must return error status in CF
  
    Every procedure of every module should at first check if module is
    initialized, and retrn error if it isn't.
	;check if module is initialized
	cmp [thismodule.initialized],1
	jne .error

    When calling any fasmlib procedure ALWAYS (!!!) check return value for
    error:
	call mem.size,[memblock]
	jc .error ;  <---- !!!!
    exception to this is module.uninit, this can be called in case of error, so
    you should try to free as much used things as possible, not terminate after
    first unsucessfull free.

    Don't forget to clear (or set) DF flag whenever you use rep. If you return
    with DF modified mention it in ret:
	; ret: DF=0, CF set on error, otherwise

  5.1 Special usage of string library
  -----------------------------------
    Place "h" before string argument name if it has to be (!) handle, and
    mention it in argument description.

    If your procedure takes some string paramaters (eg. pointers to ascii
    terminated strings), always call "str.ptr" for that argument. It works even
    if library is not initialized, then it just returns argument in eax. This
    way you will allow user of your procedure to use string handles as
    arguments, without having him to call str.ptr himself. 
	call str.ptr,[.hdest]
	jc .error
	mov [.hdest],eax

    If you are returning string then preferably create it using strlib, and add
    note to description that it's on user to deallocate it when it's not
    needed. This way you don't have limit size of string or care with buffer
    yourself. And it's standardized.

    (TODO) If you are altering some string argument (changing it's size),
    accept only handles:
	test [.hstring],$FFFF0000 ;all handles are below 00010000h
	jnz .error
