Asm Command

Purpose

Invokes the inline assembler.

Syntax

. | Asm mnemonic destination, source

Description

The inline assembler lets you embed assembly-language instructions directly in your GFA programs without extra assembly and link steps. The inline assembler is built into the compiler - you don’t need a separate assembler such as the Microsoft Macro Assembler (MASM).

Because the inline assembler doesn’t require separate assembly and link steps, it is more convenient than a separate assembler. Inline assembly code can use any GFABASIC32 variable or functionname that is in scope, so it is easy to integrate it with your program’s code. And because the assembly code can be mixed with other statements, it can do tasks that are cumbersome or impossible in GFABASIC alone.

The dot is a shortcut for the Asm keyword and invokes the inline assembler and can appear wherever a GFA-BASIC 32 statement is legal. It cannot appear by itself. It must be followed by an assembly instruction.

The assembler commands use the INTEL parameter sequence, for example:

. mov dest, source

The following code consists of simple Asm block. (The code is a custom function prolog sequence.)

Asm push ebp

Asm mov  ebp, esp

Asm sub  esp, __LOCAL_SIZE

Alternatively, you can use a 'dot - space' in front of each assembly instruction:

. push ebp

. mov  ebp, esp

. sub  esp, __LOCAL_SIZE

You can also put assembly instructions on the same line using the statement separator:

. nop : . inc eax

Asm nop : Asm inc eax

Assembler labels

A label inside an assembler block differs from the rules above. A label always starts with dot directly followed by its name, and directly followed by a semicolon (:). However the semicolon is not used in the jump or call instruction.

. cmp eax, 0

. je .next

. jmp .exit

.next:

. cmp eax, 65

Like an ordinary GFA-BASIC 32 label, an assembler label has scope throughout the function in which it is defined. Both assembly instructions and GoTo/GoSub statements can jump to labels inside or outside the assembler instructions. GoTo and GoSub refer to the assembler labels without the preceding dot.

To jump to an ordinary label using an assembler instruction, the label is preceded with a dot when used as an argument in the instruction.

Dim i As Int

test:' GFA-BASIC 32 label

Print "Hallo"  : i --

. mov eax, [i]

. test eax, eax

. je .test; note the dot in front of the label

To jump to an assembler label using GoTo or GoSub leave out the starting dot.

GoTo 00Assem' note the missing dot

// assembler code

.00Assem: Print "Hallo"

Because assembler label names start with a dot, GFA-BASIC 32 allows the use keywords for label names, this in contrast to C/C++ inline assembler. This feature allow you to choose meaningful label names like .next, .try, .exit, .end, that would otherwise impossible.

Using variables

To move the contents of a variable to a register use mov reg, [varname]. For instance

Dim i As Int, j%

. mov eax, [i]

. mov [j%], eax      ; $AutoPost has no meaning

$AutoPost settings are not obliged in assembler instructions. This can be a point for confusion; in assembler i As Int is different from i%.

The variable name is a place holder for the address of the variable; the instruction mov eax, [i] is the same operation as DPeek(*i).

The compiler directly inserts the address of the variable for the second argument [i]

. mov eax, [$00EEDD11]

For local variables, the address of the variable is compiled as an offset from ebx.

. movzx eax, [localvar]

. movzx eax, wpt [ebx + 124] ; if it is the first local variable

Using the form mov reg,[var] to get access to the value of the variable is only true for simple types like integers and floating-point data types. The address of the variable is the place where the data is kept. To access variables this way the following must be valid: *var = ArrPtr(var) = V: var

The fixed string and UDT data types are accessible through *, as well. For example a fixed string can be indexed as follows

Debug.Show

f()

 

Sub f()

Dim sFixed As String * 26

.   xor ecx, ecx

.   mov al, 65

.l:

.    mov sFixed[ ecx], al

.    inc ecx

.    inc eax

.    cmp ecx, 25

.    jle .l

Trace sFixed

More complex type variables like strings and arrays are managed through their descriptor (*str <> V: str). The variable name is placeholder for a reference to their descriptor and not to the bytes where the actual data reside. For these types the starting address of the data bytes must be stored in a temporarily long integer (variable or register) and accordingly used.

The following example illustrates how to use variables by calling API functions with assembler.

' By John Findlay

Type RECT

Left   As Long

Top    As Long

Right  As Long

Bottom As Long

End Type

Global rc As RECT, hUser32 As Handle, i As Int

Global lpGetClientRect  As Int, lpGetWindowRect As Int

' Find the addresses of the two functions

hUser32 = LoadLibrary("user32.dll")

lpGetClientRect = GetProcAddress(hUser32, "GetClientRect")

lpGetWindowRect = GetProcAddress(hUser32, "GetWindowRect")

OpenW 1

Print "Example of calling the GetClientRect() and GetWindowRect() API's with assembler."

Print

Print "Client Coords"

Print MyGetClientRect(Win_1.hWnd, *rc), "Return value from asm call"

Print rc.Left,   "Left"

Print rc.Top,    "Top"

Print rc.Right"Right"

Print rc.Bottom, "Bottom"

Print

Print "Window Coords"

Print MyGetWindowRect(Win_1.hWnd, rc), "Return value from asm call"

Print rc.Left,   "Left"

Print rc.Top,    "Top"

Print rc.Right"Right"

Print rc.Bottom, "Bottom"

Print

Print "Press a key to exit."

~FreeLibrary(hUser32)

KeyGet i

CloseW 1

 

Function  MyGetClientRect(hWnd As Int, lpRect As Int) As Int Naked

. push [lpRect] : . push [hWnd]

. call [lpGetClientRect]

. mov [MyGetClientRect], eax  ' Return

EndFunc

 

Function  MyGetWindowRect(hWnd As Int, ByRef lpRect As RECT) As Int

. push [lpRect] : . push [hWnd]

. call [lpGetWindowRect]

. mov [MyGetWindowRect], eax  ' Return

EndFunc

Note The mov instructions in both functions are redundant. Return values from functions (API or GFA-BASIC 32) are always placed in eax. Returning a value through a temporary variable with the same name as the function name is a VB quirk, which simply results in a move back to eax, which then holds the return value of the function.

Assembler data

To define constant values the following assembler statements are available

. db const - byte constants and Strings (values -128 to +255)

. dw const - 2 byte integer (-32768 to + 65535)

. dd const - 4 byte integer (possible to store label address)

. dl const - 8 byte (large) integer

. dq const - 8 byte (double) floating-point (. dq 12.34 or . dq PI/180*23.5)

. ds const - 4 byte (single) floating point (. ds 12.34 or . ds 12.34!)

In contrast with other assemblers . dd 1.0 is not the same as . ds 1.0!

Examples:

.text:

. db "This is a Text", 0

. dd "This is a Text", 0

Shortcuts

Out of efficiency reasons, there are shortcuts for byte ptr, word ptr, dword ptr, qword ptr, tbyte ptr, and fword ptr. The shortcuts are respectively, bpt, wpt, dpt, qpt, tpt, and fpt. The following instructions are equivalent.

. mov bpt [i], 1

. mov byte ptr [i], 1

The disassembler uses the shortcuts by default (cannot be changed).

Jumping and calling

The jump statements (jcc, jmp, loopx, jcxz, etc) only accept a relative offset or a label:

. jc $+nn; addresss relative to $ ( = eip )

. jmp $+ 2

. jc .label; a label (use .)

As in MASM programs, the dollar symbol ($) serves as the current location counter. It is a label for the instruction currently being assembled.

With call and jmp other addressing modes are possible as well:

. call ecx

. jmp .tab[eax*4]

Calling GFA-BASIC 32 functions

A special assembler command - scall - is required to call a GFA-BASIC 32 function by its name. For instance, to call the GFA-BASIC 32-internal function MessageBeep(0) the following is used:

. push 0

. scall MessageBeep

Internally, scall is implemented as call dword ptr [ ] , where the name is known to the compiler only.

Floating-point extensions

GFA-BASIC 32 extents the normal INTEL x86 floating-point assembler instructions that works with a constant. For instance, there is no command like fadd 0.125, instead (external) assembler requires the following construction:

'data

Kon0_125: . dd 0.125

'text

. fadd [Kon0_125]

The GFA-BASIC 32 inline assembler allows simple additions like this:

. fadd 0.125

The management of the memory for the constants is done by the assembler.

The floating-point instructions fld, fadd, fsub, etc. support both Double (default) and Single arguments. To force single floating-point operations the argument must be converted to a single value explicitly like

. fadd 12.4!

. fadd CSng(PI)

The floating-point extensions apply to fadd, fsub, fmul, fdiv, fsubr, fdivr, fcom, fcomp, fldcw, fld, fild, fbld, fiadd, fisub, fisubr, fimul, fidiv, fidivr, ficom, ficomp.

(The integer statement bound (bound eax, [addr] or bound eax, lo, hi) also accepts a constant.)

Math with labels

The difference between two labels can be obtained indirectly only:

. mov ecx, .label2

. sub ecx, .label1

Since math with label addresses is forbidden, the following is not allowed:

. mov ecx, .label2 - .label1; not possible

. mov al,[.label][3]

Rather than:

.tmp: . dd 1234

. mov al,[.tmp][3]; not allowed

you should use:

.tmp:  . db GetByte0(1234)

.1tmp: . db GetByte1(1234)

.2tmp: . db GetByte2(1234)

.3tmp: . db GetByte3(1234)

. mov al,[.3tmp]

or better:

.tmp: . dd 1234

. lea eax,[.tmp]

. mov al, 3[eax]

This restriction applies to labels only and not to variables:

Dim iTmp As Int

. mov al, 3[iTmp]

Assembler Opcodes

To identify commands that require a 80486-processor the first character is uppercase (this is automatically set by the editor). For instance .Xadd and .Cmpxchg require processors with at least a 80486 processor and are visually identified by their uppercase.

. Xadd [i], eax

. Cmpxchg [eax], ecx

To identify Pentium statements the first two characters are converted to uppercase. For instance, the GFA-BASIC 32 function _Rdtsc requires a Pentium and should it used in assembler it is visually differentiated.

. RDtsc

. Cmpxchg qpt [i]

Finally, MMX commands like PADD are entirely uppercase.

Assembler statements

aaa aad *10 aam *10 aas Adc add
align † N and arpl bound Bsf bsr
Bswap bt btc btr Bts call
cbw cdq clc cld Cli clts
cmc cmp cmps cmpsb°° Cmpsd°° cmpsw°°
Cmpxchg CMpxchg8b CPuid cwd Cwde daa
das db † N dd † N dec Div dl † N
dq † N ds † N dw † N enter f2xm1 fabs
fadd faddp fbld fbstp Fchs fclex
fcom fcomp fcompp fcos fdecstp fdisi
fdiv fdivp fdivr fdivrp Feni ffree
fiadd ficom ficomp fidiv Fidivr fild
fimul fincstp finit fist Fistp fisub
fisubr fld fld1 fldcw Fldenv fldl2e
fldl2t fldlg2 fldln2 fldpi Fldz fmul
fmulp fnclex fninit fnop fnsave fnstcw
fnstenv fnstsw fpatan fprem Fprem1 fptan
frndint frstor fsave fscale fsetpm fsin
fsincos fsqrt fst fstcw Fstenv fstp
fstsw fsub fsubp fsubr Fsubrp ftst
fucom fucomp fucompp fwait Fxam fxch
fxtract fyl2x fyl2xp1 hlt Idiv imul
in inc ins insb Insd insw
int into Invd Invlpg Iret ja
jae jb jbe jc jcxz¹ jcxzd³²
je jecxz³² jg jge Jl jle
jmp jna jnae jnb Jnbe jnc
jne jng jnge jnl Jnle jno
jnp jns jnz jo Jp jpe
jpo js jz lahf Lar lds
lea leave les lfs Lgdt lgs
lidt lldt lmsw lock Lods lodsb°°
lodsd°° lodsw°° loop³² loopd³² loopde³² loopdne³²
loopdnz³² loopdz³² loope³² looped³² loopew¹ loopne³²
loopned³² loopnew¹ loopnz³² loopnzd³² loopnzw¹ loopw¹
loopwe¹ loopwne¹ loopwnz¹ loopwz¹ loopz ³² loopzd³²
loopzw¹ lsl lss ltr Mov movb°°
movl°° movs movsb°° movsd°° movsw°° movsx
movsxb°° movsxw°° movw°° movzx movzxb°° movzxw°°
mul neg nop not Or out
outs outsb °° outsd °° outsw °° Pop popa¹
popad³² popf¹ popfd³² popw¹ Push pusha¹
pushad³² pushf¹ pushfd³² pushw¹ Rcl rcr
RDmsr RDtsc rep repe Repne ret
retf retn rol ror RSm sahf
sal sar sbb scall Scas scasb °°
scasd°° scasw°° seta setae Setb setbe
setc sete setg setge Setl setle
setna setnae setnb setnbe Setnc setne
setng setnge setnl setnle Steno setnp
setns setnz seto setp Setpe setpo
sets setz sgdt shl Shld shr
shrd sidt sldt smsw Stc std
sti stos stosb°° stosd°° Stosw°° str
sub test verr verw Wait Wb_invd
WRmsr Xadd xchg xlat Xlatb xor

MMX statements

EMMS MOVD MOVQ PACKSSDW PACKSSWB PACKUSWB
PADDB PADDD PADDSB PADDSW PADDUSB PADDUSW
PADDW PAND PANDN PCMPCGD PCMPEQB PCMPEQD
PCMPEQD PCMPEQW PCMPGTB PCMPGTW PMADDWD PMULHW
PMULLW POR PSLLD PSLLQ PSLLW PSRAD
PSRAW PSRLD PSRLQ PSRLW PSUBB PSUBD
PSUBSB PSUBSW PSUBUSB PSUBUSW PSUBW PUNPCKHBW
PUNPCKHDQ PUNPCKHWD PUNPCKLBW PUNPCKLDQ PUNPCKULWD PXOR

Pentium specific assembler and disassembler statements

For Pentium Pro/II/III... an additional set of move statements is added. The presence of these statements is indicated by bit #15 in _CPUIDD.

cMOVo cMOVno CMOVb cMOVc CMOVnae cMOVnb CMOVnc cMOVdae
cMOVz cMOVe CMOVnz cMOVne CMOVbe cMOVna CMOVnbe cMOVa
cMOVs cMOVns CMOVp cMOVpe CMOVnp cMOVpo cMOVl cMOVnge
cMOVnl cMOVge CMOVle cMOVng cMOVnle cMOVg    

These move statements move bytes when a condition is met (o, no, b, etc.), like jcc or setcc. As destination only one of the eight possible general registers is allowed (esp included). Also allowed are the 16 bit registers (and addresses). The source operand cannot be a constant.

Explanation

¹ This is the 16 bit statement, loopw.

With pushw/popw: pushw ds is a 16 bit push of the ds register, push ds a 32 bit push. Instructions using segment registers, not allowed in flat mode, are handled as pseudo-32 bit registers by the processor. Therefore, a far call in 32 bit mode requires 8 bytes for a return address (4 bytes offset, 2 byte cs and 2 byte dummy to pad to 32 bit).

³² This is 32 bit instruction: loop, loopd

°° With instructions taking multiple data types (like movs) the size of data type can be specified by using a postfix character (b, w, or d). Saves a bit of typing:

. movsd

. movs dword ptr es:[edi], dword ptr [esi]

Other shortcuts for mov and movsx/movzx:

. MOVD 8[ebp], 12

. mov dword ptr 8[ebp], 12

. movzxb eax,[eax]

. movzx eax, byte ptr [eax]

† Pseudo instructions using constants as parameter:

align 2 - Alignment at word border ( or a nop)

align 4 - Alignment on DWORD border (or some nops or other instructions that don't modify registers: mov ecx, ecx or lea edx, 0[edx])

align 8 - Alignment on 8 byte border (useful together with dq)

align 16 - Alignment on 16 byte border

See Also

. Assembler Instruction

{Created by Sjouke Hamstra; Last updated: 20/10/2014 by James Gaite}