ÜÜÜ Ūßßß ßŪ ŪŪ ŪŪ Ü ŪŪÜÜ ÜßßßŪÜ ÜßßßßÜŽßÜŽßßÜßŪŻ ŪŪ ŪŻ ŽŪ ß ŽŪ Ū Ż ŪŪ ÜßßßßßŪŪ ßßßŪÜÜ ŽŪ Ū Ż ŪŪ Ū ŪŪ Ü ŽŪ ŽŪ Ū Ż ßßßßßß ßßßßß ßß ßßßßßß ß ß ß flat assembler version 1.32 Copyright (c) 1999-2002, Tomasz Grysztar. All rights reserved. Table of Contents 1 Introduction 1.1 What is fasm? 1.2 Hardware requirements 1.3 Software and OS requirements 1.4 Output formats 1.5 Output file ordering 1.6 Output code optimization 2 Using flat assembler 2.1 Assembler syntax 2.1.1 Instruction syntax 2.1.1.1 Data access 2.1.1.2 Jumps and calls 2.1.1.3 Labels and constants 2.1.1.4 Addresses 2.1.1.5 Special cases 2.1.1.6 FPU and MMX instructions 2.1.2 Number syntax 2.1.3 Logical expressions syntax 2.1.4 Defining constants 2.1.5 Defining data 2.2 Directives 2.2.1 Preprocessor directives 2.2.2 Code and format settings 2.2.3 Label and constant definitions 2.2.4 Other directives ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ 1 Introduction 1.1 What is fasm? The flat assembler is a fast, efficient 80x86 assembler that runs in 'flat real mode'. So, it must be run on at least an 80386 PC, but it can certainly assemble programs for any 80x86 PC. Unlike many other 80x86 assemblers, fasm only requires the source code to include the information it really needs. See the 'asm' files in the 'examples' subdirectory to see how simple it can be! The files in the 'source' and 'examples' subdirectories are good references for more complex examples. You may notice that fasm is case sensitive for all symbols (even instructions and directives). 1.2 Hardware requirements Aside from the above-mentioned need for at least an 80386 CPU, the hardware needs are small; at least one megabyte of extended memory is reccommended for best performance. 1.3 Software and OS requirements Other than an OS compatible with MSDOS 2.0+, fasm needs no other software. Note that fasm will not run if the CPU is in protected or V86 modes, since flat real mode cannot be entered in that case. But it will still run under Win32, because version for that system is attached to fasm executable. 1.4 Output formats There are three possible output formats; by default, the output of fasm is a non-relocatable binary image such as a 'com' or 'sys' program. Use of the directive 'format MZ' produces a relocatable and possibly multi-segment 'exe' program. No extra 'linking' step is needed! Use of the directive 'format PE' produces portable executable. 1.5 Output file ordering All output code is in the order in which it was entered into the source file. So, program code or at least an instruction such as 'jmp start' must be at the beginning of the source code unless the program is in format other than binary. In that case, see the 'entry' directive (below). 1.6 Output code optimization The flat assembler will do multiple (but fast) assembly passes to generate optimal output code. When size of jump or address in instruction is not specified, fasm will generate as small instruction code as possible. That's the only code optimization fasm will do. 2 Using flat assembler 2.1 Assembler syntax 2.1.1 Instruction syntax 2.1.1.1 Data access Moving data to or from a register involves two simple rules; 'mov eax,myvar' will place the offset (address) of 'myvar' in the eax register, while 'mov eax,[myvar]' will place the 32-bit value of the variable 'myvar' in eax. Note that fasm will exit with an error if 'myvar' has not been defined as a 32-bit value. When 'myvar' has not been defined as 32-bit value, but you need to assemble instruction 'mov eax,[myvar]', use size override: 'mov eax,dword [myvar]'. This is dangerous if 'myvar' has been defined as an 8-bit or a 16-bit variable; eax will contain whatever is in the four bytes beginning at the address of 'myvar'! Allowed size overrides are: byte (8-bit), word (16-bit), dword (32-bit), pword (48-bit), qword (64-bit), tword (80-bit), dqword (128-bit). 2.1.1.2 Jumps and calls Unconditional: jmp alpha ; simple jump jmp near byte beta ; short jump jmp near dword beta ; force dword size jmp 10h:50h ; far jump jmp pword 10h:50000h ; 32-bit far jump call far pword [1000h] ; 32-bit far call call far dword [delta] ; 16-bit far call call near dword [delta] ; 32-bit near call Conditional: je alpha ; will be optimized jge byte alpha ; force byte (error if out of range) jb dword alpha ; force dword (no optimization) 2.1.1.3 Labels and constants Examples of defining labels: alpha: ; simple label label beta ; as above label gamma byte ; low byte of delta dword delta dd 0 ; data label epsilon = alpha + 1 ; constant definition Local labels: sigma: .alpha: ; local label (1) jmp .alpha ; jump to (1) omega: .alpha: ; local label (2) jmp .alpha ; jump to (2) jmp sigma.alpha ; jump to (1) 2.1.1.4 Addresses The fasm is able to do some simple arithmetics on registers, so forms like '[ebx*5]' are allowed (it will be assembled as '[ebx+ebx*4]'). Defining size of address value is possible; '[word 4]' and '[dword 4]' will generate different output code: [0004] and [00000004], '[word bx]' will be [bx+0000], '[byte bx]' will be [bx+00], '[dword ebx+1]' will be [ebx+00000001], etc. 2.1.1.5 Special cases 'xlat' instruction accepts one argument: '[bx]'/'byte [bx]' to create 16-bit version, or '[ebx]'/'byte [ebx]' to create 32-bit version of opcode; in the similar way advanced settings of string instructions can be done, like 'movs byte [es:edi],[fs:esi]', to adjust address registers size and segment override ('movs' needs two arguments). 'xlatb', 'movsb', etc. accept no arguments. 2.1.1.6 FPU and MMX instructions Examples of FPU instructions: fld tword [si] ; load fp fadd st0,st3 ; add fp fimul dword [ebx] ; multiply integer fstp qword [edx] ; store fp and pop stack Examples of MMX instructions: paddb mm0,mm1 ; packed add bytes pand mm7,qword [esi] ; logical and addps xmm0,xmm1 ; packed single fp add cmpeqps xmm7,dqword [esi] ; packed single fp compare shufps xmm4,xmm5,1 ; shuffle single fp 2.1.2 Number syntax Decimal numbers: 15, 15d Binary: 1011b Hexadecimal: 0ABh, 0xAB Octal: 231o, 0231 Floating point: 3.14, 1.0E3 Special: $ - current address value, % - current repeat number (see 2.2.3) Operators: +,-,*,/,mod,not,and,or,xor,shl,shr Address conversion operator: rva (only in PE format) 2.1.3 Logical expressions syntax See 'macros' examples in Section 2.2.1 for usage examples. The logical operators are: ~ (not), | (or), & (and). Logical values can be defined using comparing operators: = (equal), < (less), > (greater), <= (less or equal), >= (greater or equal), <> (not equal) for numbers; 'eq' or 'in' for all other symbols. 2.1.4 Defining constants As opposed to 'equ', '=' works with values only, and they are calculated at define time; for example 'xyz = $' defines xyz symbol as an address of point where it was defined, 'xyz equ $' will just define equivalent to '$', which will be always address of point where it's used. 2.1.5 Defining data Data can be defined in two ways; if the data is to be initialised to a specific value, the forms are: gdtr db 16,0,0,0,0,0,0 ; sequence of bytes attrib db 0x1E ; single byte (hexadecimal) command: times 127 db 0 ; byte string str001 db 'test.bin',0 ; character string argv: times 10 dd 0 ; ten 32-bit double words picture file 'star.gif' ; whole file contents When loading data with 'file' instruction, after file name can follow position in file preceded by colon, then comma and count of bytes to load. Other forms available are: dw ; 16-bit word dp ; 48-bit pointer dq ; 64-bit quad word dt ; 80-bit floating point du ; 16-bit word strings If it is only desired to reserve space for data, the forms are: _proname rd 1 ; reserve one dword _inch rb 1 ; reserve one byte newstack rd 255 ; reserve 255 dwords Other forms available are: rw ; 16-bit word rp ; 48-bit pointer rq ; 64-bit quad word rt ; 80-bit floating point Note that if the 'times' directive is to be used to define multiple data items, any label used must end with a colon. The other way is to use 'label' directive before data definition, for example: label argv dword times 10 dd 0 To create union of some variables, you can use label directive with 'at
' phrase, or virtual directive, for example: GDT_limit dw 15 GDT_address dd ? ; now define virtual variable for lgdt and sgdt instructions label GDTR pword at GDT_limit ; and now the other method LDTR dp ? virtual at LDTR LDT_limit dw ? LDT_address dd ? end virtual 2.2 Directives 2.2.1 Preprocessor directives The all preprocessor directives in the source code are processed before the main assembly process, and so they are not affected by the 'if' or 'repeat' directives, which are processed at assembly time (see 2.2.4). include - includes source file in source code before assembly macro - defines macroinstructions, arguments are macro name and names of macro arguments separated with commas, then '{' (start of macro) character, macro instructions, and '}' (end of macro) character. Here's an example of macroinstruction for data alignment: macro align value { rb (value-1) - ($ + value-1) mod value } Note that in PE format you should replace '$' with 'rva $' to allow this calculations. Macroinstructions may have empty arguments; to check if argument is empty use something like 'if eq <>'. If a macro is defined that uses an instruction with the same name inside the macro definition, the previous meaning of this name is used; useful redefinition of macros can be done in that way, for example: macro mov arg1,arg2 { if arg1 in & arg2 in push arg2 pop arg1 else mov arg1,arg2 ; here original mov instruction will be used end if } macro mov arg1,arg2,arg3 { if eq <> mov arg1,arg2 ; here previous macro will be used else mov arg1,arg2 mov arg2,arg3 end if } mov ax,bx ; just 'mov ax,bx' mov ds,es ; 'push es' and 'pop ds' mov es,ds,dx ; 'push ds', 'pop es' and 'mov ds,dx' But note that using these macros in big programs will dramatically slow down assembly (up to 3 times!) and increase memory requirements. If you type arguments in brackets, macroinstruction will allow multiple groups of arguments, and will repeat all instructions for each arguments group separately. For example: macro stoschar [char] { mov al,char stosb } stoschar 1,2,3,4 ; store four bytes at es:di There are some special directives available only inside macro definitions. The 'local' directive defines local symbols, which will have unique names in every macro call; arguments are local symbol names, separated with commas. The 'forward', 'reverse' and 'common' directives divide macroinstruction into parts, each one processed after the previous one processing is finished. The part defined using 'forward' directive will be processed for each group of multiple arguments, from first to last; the same as default macroinstruction behaviour. Local symbol defined in one of the parts will be available in all next parts, when processing the same group of arguments. For example, to create table of addresses to strings, and then strings data, use this macro: macro strtbl [string] { forward local label dd label forward label db string,0 } The 'reverse' directive will cause groups of arguments to be processed from last to first; and the 'common' directive will define part, which will be processed only once. Here's example how to define macro for calling procedures with arguments on stack in using the above directives: macro stdcall proc,[arg] { reverse push arg common call proc } Note that instructions can follow the directive defining part of macroinstruction. Also, in 'common' part name of the macro argument will be replaced with all the argument values, from first to last, separated with commas. So we can define next macro for indirectly invoking procedures this way: macro invoke proc,[arg] { common stdcall [proc],arg } struc - this is the variant of macro directive used to create data structures. When struc macro is used in program, it must be preceded by a label. Preprocessor will insert this label at the beginning of every symbol starting with a single dot. For example: struc pixel x,y,color { .x dw x .y dw y .color db color } mypix pixel 10,10,4 ; so we have defined mypix.x, mypix.y and mypix.color variables virtual at 0 pixel pixel ?,?,? end virtual ; and now also pixel.x, pixel.y, pixel.color offsets To handle cases, when one or more of macro params are empty (for example just: 'mypix pixel'), you can write something like '.x dw x+0', or use 'if eq <>'. purge - argument is one or more macro names, separated with commas; this directive will remove last definition of specified macro; for example when you've redefined some macro, you can get previous definition back using purge. If the macro has not been defined, you won't get any error. When you need to totally undefine some non-preprocessor symbol (defined with equ, or labels), use this clever macro: macro undefine symbol { local undefined symbol equ undefined } For example after 'undefine add', you can't use 'add' instruction, but definition and usage of 'add' label is possible. Also, if you use '_add equ add', and then 'undefine add', you can define 'add' label and have access to 'add' instruction using '_add' symbol. equ - defines symbolic constant. This directive should be preceded by the symbol name, and followed by the symbol value. 2.2.2 Code and format settings use16 - set code type to 16-bit (this is default code setting). use32 - set code type to 32-bit (this is default code setting for PE format). org - argument must be an address value; set from what address this code will start in memory. format - set output file format, can be set binary, MZ or PE; binary is the default format, MZ is an EXE file format, PE is a portable executable file format. When specified format is PE, more format settings can follow; use 'console', 'GUI' or 'native' words to set target subsystem type (subsystem version can follow); 'i386', 'i486' or 'i586' to specify target machine type; 'DLL' to create dynamic link library. Use 'on ' phrase to specify custom stub program. segment - define segment at current position (only in MZ format), code will be aligned to paragraph. In segment definition, after segment name, 'use16' or 'use32' is allowed. section - define section at current position (only in PE format), code will be aligned to page (4096 bytes). First argument is a string containing section name, optional next arguments are section type (code, data, udata, export, import, resource), and section attributes (readable, writeable, executable, shareable, discardable). entry - define program entry point (only in MZ and PE formats). In MZ format argument must be a segment:offset address (like in far jump). stack - define program stack size (only in MZ and PE formats), when no stack is defined, default size of 1000h bytes is used. In MZ format the stack location can be defined in segment:offset form. In PE format second argument is allowed to define stack commit (first argument defines stack reserve). heap - define 'heap' size in paragraphs (only in MZ and PE formats). In MZ format argument is a 16-bit value, defining maximum size of additional heap in paragraphs (note that this is heap in addition to stack and undefined data; use 'heap 0' to always allocate only memory program really needs); default value of 'heap' is FFFFh. In PE format argument is a 32-bit value, defining heap reserve; second argument is allowed to define heap commit. data - begin PE format specific data definition. Argument is a number of PE data directory, or a predefined data type (export, import, resource). Data definition should be ended with 'end data' directive. 2.2.3 Label and constant definitions label - define label, needed argument is a name of label, optional arguments are size of labeled data and 'at
' phrase. load - define constant equal to binary value loaded from file; arguments are constant name, optionally constant size (when no size is specified, constant will be one byte), then 'from ' phrase, after file name position in file preceded by a colon can follow. 2.2.4 Other directives times - repeat instruction n times; arguments are number of repeats and instruction to repeat (optionally character ':' can be used to separate number and instructions), also % value (current repeat number) can be used in instruction, so 'times 5 db %' will create 01,02,03,04,05 bytes; recurrency is also allowed, so 'times 3 times % db %' will create 01,01,02,01,02,03 bytes. repeat - larger version of times directive, repeat has one argument - number of repeats, instructions to repeat are expected in next lines, ended with 'end repeat' directive. virtual - creates virtual code or data at specified address; this data won't be included in output file, but labels defined there can be useful in other parts of program; argument to virtual is 'at
' phrase, virtual instructions are expected in next lines, ended with 'end virtual' directive. If directive has no arguments, it uses the current address, the same as 'virtual at $'. if - used to assemble some part of codes, when specified conditions are met; argument is a logical expression, in next lines instructions to assemble when this expression value is true, then 'end if' directive, or 'else', or 'else if' and next logical expression. end - used to end structures defined with some other directives. display - display text at the assembly time; arguments are strings or byte values to be displayed. ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ Thanks to: Leonid Petroff - for really good work with bugs reporting and many helpful suggestions Bartek Uliasz - without him the flat assembler project would never be started and all others who helped me (there were many of them)!