Inline assembly

Top  Previous  Next

The compiler supports inserting assembly language source code directly into your Emergence BASIC source files. It is not the purpose of this text to instruct on how to program in assembly language, only on how to insert assembly language statements into your source code and accessing variables through assembly. Refer to the Assembler documentation accessible from the Help menu for more details on assembly syntax.

To begin an inline assembly block use the _asm keyword and end the block with the _endasm keyword.  Any text between the two statements will be ignored by the compiler and is copied verbatim to the resultant compiler .a output file at whatever point the _asm statement appears. The statements must be in lower case. Example
 

DEF myInt as INT
_asm
    mov eax, 1
    add eax, 20
    mov dword [$myInt], eax
_endasm
PRINT myInt

The editor does not understand assembly code and will sometimes colorize assembler keywords. This is normal and will not affect the operation of the compiler.

Referring to global variables

Global variables, defined outside of any subroutine, can be referenced by your inline assembly code by using the $ symbol followed by the variable name. The example above stores the number 21 in the myInt variable. The assembler itself is case sensitive and any reference to a global variable must match the case of the DEF/DIM statement.

All global variables are references to a memory location. The [ ] symbols is the same as a pointer dereferencing operation in Emergence BASIC.

Referring to local variables

Local variables are either defined in a subroutine or are one of the subroutines parameters. Unlike global variables you can't access a local variable by name but must compute its stack offset

When an EBASIC subroutine is entered the register EBP contains a pointer to the current stack frame. All variables are accessed by using offsets to the EBX register depending on their type. For a subroutines parameters the first argument will be located at EBP+8. For variables defined in the  subroutine the first variable is located at EBP-4. Consider this short example
 

SUB asmtest(b as INT),INT
DEF c as int
_asm
    lea esi,[ebp-4]       ;address of c
    mov eax,[ebp+8]       ;value of b
    mov [esi],eax         ;c = b
    add [esi],dword 2     ;c=c+2
_endasm
RETURN c
ENDSUB

The parameter 'b' is located at ebp+8 and the variable 'c' is located at ebp-4. If the parameter is a reference such as a string, array or pointer then only an address to the reference is contained on the stack. You must use LEA to obtain that address and dereference as you would with any other pointer
 

SUB asmtest2(b as INT BYREF),INT
DEF c as int
_asm
    lea esi,[ebp-4]       ;address of c
    lea eax,[ebp+8]       ;address of variable pushed as b
    mov eax, [eax]        ;dereference the address
    mov [esi],eax         ;c = b
    add [esi],dword 2     ;c=c+2
_endasm
RETURN c
ENDSUB

Labels in assembly code

You can use labels for branching in your assembly code provided they do not interfere with any EBASIC source code label or subroutine name.
 

_asm
    mov eax, dword [$test]
    cmp eax, 0
    jz out1
    inc eax
    mov dword [$test], eax
out1:
_endasm

 

Register usage and context

You are free to use any register in your inline assembly. If creating straight assembly language subroutines you must preserve the contents of EBX, EDI and ESI by pushing them to the stack and popping them before your subroutine returns. If your assembly language subroutine uses any local stack space you must set up a proper stack frame with the EBP register and restore it before the subroutine returns.