Use the #asm and #endasm directives to delimit blocks of assembly. An assembly block can be located both inside and outside of functions. One important note about this syntax is that all # directives must be the first non white space characters on a line. Therefore #asm and #endasm directives can't be placed in the middle of statements.
C variables are directly accessible from within the assembly block. The C compiler replaces the name of the variable with its address. Whether the variable address is used as an immediate value, or as a direct or indirect address depends on the assembly instruction.
Figure 6-1. Embedded blocks of assembly asm1.c
char arr[10]; /* declare an array of 10 characters */
void main(void)
{
int i;
i=sizeof(arr); /* assign the size of arr[] to i */
/* while(i--) arr[i]=0; */
#asm
LD B,#arr ;load B to point to arr
LD X,#i ;load X to point to i
clear_arr
LD S,#i>>8 ;select i RAM bank
LD A,[X] ;load i
DECA ;decrement
X A,[X] ;store A to i
IFEQ A,#0 ;done if i is zero
JP done_clear
LD S,#arr>>8 ;select arr RAM bank
CLRA
X A,[B+] ;clear arr[]
JP clear_arr ;clear next byte
done_clear
#endasm
}
If you need to return a value from a function written in assembly, you need to follow the same protocol that COP8C code does. If one byte is returned, it is passed though the A register. If two bytes are returned, they are passed using A and X. The most significant byte is placed in X and the least significant byte is placed in A.
Figure 6-2. Setting the function return value from assembly asm1.c
long inc_long(long l) /* increment a long variable */
{
/* return(++l); */
#asm
LD S,#l>>8 ; load high address of l into S
LD B,#l ; load low address of l into B
RC ; clear carry
LD A,[B] ; load low byte of l
ADC A,#1 ; add 1
X A,[B+] ; store result
LD A,[B-] ; load high byte of l
ADC A,#0 ; add carry to high byte
X A,X ; return high byte in X
LD A,[B] ; return low byte in A
#endasm
}
Including assembly into C is convenient when you have some assembly language code you wish to use unchanged.