Invokes the inline assembler.
. | Asm mnemonic destination, source
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
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.
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 "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 "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 "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.
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
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).
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]
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.
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.)
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]
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.
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 |
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 |
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
{Created by Sjouke Hamstra; Last updated: 20/10/2014 by James Gaite}