When writing code to be compiled with SDCC targetting Z80, assembler code fragments can be inserted in the C functions by enclosing them between the __asm and __endasm; tags:
void DoNotDisturb()
{
  __asm
  di
  __endasm;
  DoSomething();
  __asm
  ei
  halt
  __endasm;
}
SDCC 3.2 introduced the __asm__ keyword, that allows to specify the assembler code inside a string:
void DoNotDisturb()
{
  __asm__ ("di");
  DoSomething();
  __asm__ ("ei\nhalt");
}
Adding __naked to the function definition will cause the compiler to not generate the ret statement at the end of the function, you will usually use it when the entire body of the function is written in assembler:
void TerminateProgram() __naked
{
  __asm
  ld c,#0
  jp #5
  __endasm;
}
Assembler code must preserve the value of IX, all other registers can be used freely.
The return value of a C function is passed to the caller as follows:
- Functions that return 
char: in theLregister - Functions that return 
intor a pointer: in theHLregisters - Functions that return 
long: in theDEHLregisters 
char GetMagicNumber() __naked
{
  __asm
  ld l,#34
  ret
  __endasm;
}
int GetMagicYear() __naked
{
  __asm
  ld hl,#1987
  ret
  __endasm;
}
long GetReallyStrongPassword() __naked
{
  __asm
  ld de,#0x1234
  ld hl,#0x5678
  ret
  __endasm;
}
Parameters for the function are pushed to the stack before the function is called. They are pushed from right to left, so the leftmost parameter is the first one found when going up in the stack:
char SumTwoChars(char x, char y) __naked
{
    __asm
    ld iy,#2
    add iy,sp ;Bypass the return address of the function 
    ld l,(iy)   ;x
    ld a,1(iy)  ;y
    add l
    ld l,a      ;return value
    ret
    __endasm;
}
int SumCharAndInt(char x, int y) __naked
{
    __asm
    ld iy,#2
    add iy,sp
    ld e,(iy)   ;x
    ld d,#0
    ld l,1(iy)  ;y (low)
    ld h,2(iy)  ;y (high)
    add hl,de   ;return value
    ret
    __endasm;
}
long SumCharIntAndLong(char x, int y, long z) __naked
{
    __asm
    ld iy,#2
    add iy,sp
    ld c,(iy)   ;x
    ld b,#0
    ld l,1(iy)  ;y (low)
    ld h,2(iy)  ;y (high)
    add hl,bc   ;x+y
    ld a,l
    add 3(iy)   ;z (lower)
    ld l,a
    ld a,h
    adc 4(iy)
    ld h,a
    ld a,#0
    adc 5(iy)
    ld e,a
    ld a,#0
    adc 6(iy)   ;z (higher)
    ld d,a
    ret     ;return value = DEHL
    __endasm;
}
It is possible to call other C functions from assembler code. Just push the parameters that the function expects, call the function assembler name (it's the C name prepended with _), pop the parameters to restore the stack state, and act on the return value as appropriate:
int SumTwo(int x, int y)
{
    return x+y;
}
int SumThree(int x, int y, int z) __naked
{
    __asm
    ld iy,#2
    add iy,sp
    ld l,2(iy)
    ld h,3(iy)
    push hl     ;y for SumTwo
    ld l,(iy)
    ld h,1(iy)
    push hl     ;x for SumTwo
    call _SumTwo  ;Return value in HL
    pop af      ;x
    pop af      ;y
    ld iy,#2
    add iy,sp
    ld e,4(iy)
    ld d,5(iy)  ;z
    add hl,de
    ret
    __endasm;
}
It is possible to define pure assembler functions intended to be called exclusively from assembler code. In this case the standard parameter passing rules can be overriden:
//DO NOT call this function from C code!
int SumHLDE() __naked
{
    __asm
    add hl,de
    ret
    __endasm;
}
int SumThree(int x, int y, int z) __naked
{
    __asm
    ld iy,#2
    add iy,sp
    ld l,2(iy)
    ld h,3(iy)
    ld e,(iy)
    ld d,1(iy)
    call _SumHLDE
    ld e,4(iy)
    ld d,5(iy)  ;z
    add hl,de
    ret
    __endasm;
}
The Z80 assembler supplied with SDCC uses a pretty much standard syntax for the assembler source code except for the following:
- Decimal numeric constants must be preceded with 
# - Hexadecimal numeric constants must be preceded with 
#0x - The syntax for offsets when using index registers is 
n(ix), where in other assemblers it's usually(ix+n)