Open mvac7 opened 1 year ago
Input: two 8-bit parameters Output: 8-bit value
char aFunction(char param1, char param2) __naked
{
param1; //A
param2; //L
__asm
ld B,A ;param1
ld A,L ;param2
LOOP01:
inc A
djnz LOOP01
ret ;return A
__endasm;
}
Input: 16-bit value Output: ---
void aFunction(uint param1) __naked
{
param1; // HL
__asm
ex DE,HL ;exchange DE <-->HL
ret
__endasm;
}
Input: two parameters: 8-bit and 16-bit Output: 8-bit value
char aFunction(char param1, uint param2) __naked
{
param1; // A
param2; // DE
__asm
ld B,A
ld A,(DE)
add A,B
ret ; return A
__endasm;
}
Input: two parameters: 16-bit and 16-bit Output: 16-bit value
uint aFunction(uint param1, uint param2) __naked
{
param1; // HL
param2; // DE
__asm
add HL,DE
ex DE,HL
ret ; return DE
__endasm;
}
Input: two parameters: 16-bit and 8-bit Output: --
void aFunction(uint param1, char param2)
{
param1; // HL
param2; // stack
__asm
push IX
ld IX,#0
add IX,SP
ld A,4(IX) ;takes the value of the second parameter from the stack
ld (HL),A
pop IX
__endasm;
}
Input: three 8-bit parameters Output: 8-bit value
char aFunction(char param1, char param2, char param3)
{
param1; // A
param2; // L
param3; // Stack
__asm
push IX
ld IX,#0
add IX,SP
ld B,4(IX) ;takes the value of the second parameter from the stack
loop$:
add A,L
djnz loop$
; output value in A
pop IX
__endasm;
}
The use of __naked
is not recommended in functions that receive parameters through the stack, since it does not add the code that positions the stack.
For a function that returns an 8-bit value:
char Function8(unsigned int var16, char var8, unsigned int var2_16)
{
var16; //HL
var8; //Stack
var2_16; //Stack
__asm
push IX
ld IX,#0
add IX,SP
ld E,5(IX)
ld D,6(IX)
ld A,4(IX)
ld (HL),A
inc HL
ld A,(DE)
pop IX
__endasm;
}
The compiler generates the following code:
; ---------------------------------
; Function Function8
; ---------------------------------
_Function8::
;src\Test.c:339: __endasm;
push IX
ld IX,#0
add IX,SP
ld E,5(IX)
ld D,6(IX)
ld A,4(IX)
ld (HL),A
inc HL
ld A,(DE)
pop IX
;src\Test.c:340: }
pop hl
pop bc
inc sp
jp (hl)
For a function that returns an 16-bit value:
unsigned int Function16(unsigned int var16, char var8)
{
var16 += var8;
return var16;
}
The compiler generates the following code:
; ---------------------------------
; Function Function16
; ---------------------------------
_Function16::
push ix
ld ix,#0
add ix,sp
;src\Test.c:327: var16 += var8;
ld c, 4 (ix)
ld b, #0x00
add hl, bc
ex de, hl
;src\Test.c:329: return var16;
;src\Test.c:330: }
pop ix
pop hl
inc sp
jp (hl)
If you use the IX register, you must save it at the beginning and retrieve it at the end of your function, since SDCC can use it to point out where the values of local variables are located.
References
z88dk
Calling conventions and Function decorators
MRC development forum entry:
SDCC 4.1.12, a Game changer for C programming?
by @aoineko-frMRC development forum entry:
Fusion-C and SDCC 4.2.0
SDCC Compiler User Guide (page 71)
4.3.3 Z80, Z180, Z80N and R800 calling conventions
The current default is the SDCC calling convention, version 1. Using the command-line option –sdcccall 0, the default can be changed to version 0. There are three other calling conventions supported, which can be specified using the keywords smallc, z88dk_fastcall and z88dk_callee. They are primarily intended for compatibility with libraries written for other compilers. For __z88dk_fastcall, there may be only one parameter of at most 32 bits, which is passed the same way as the return value of sdcccall(0). For z88dk_callee, the stack is not adjusted for stack parameters the parameters after the call (thus the callee has to do this instead). __z88dk_callee can be combined with smallc, sdcccall(0) or sdcccall(1).
4.3.3.1 Z80 SDCC calling convention, version 1
This calling convention can be chosen per function via __sdcccall(1). 8-bit return values are passed in a, 16-bit values in de, 24-bit values in lde, 32-bit values in hlde. Larger return values (as well as struct and union independent of their size) are passed in memory in a location specified by the caller through a hidden pointer argument.
For functions that have variable arguments: All parameters are passed on the stack. The stack is not adjusted for the parameters by the callee (thus the caller has to do this instead). For Functions that do not have variable arguments: the first parameter is passed in a if it has 8 bits. If it has 16 bits it is passed in hl. If it has 32 bits, it is passed in hlde. If the first parameter is in a, and the second has 8 bits, it is passed in l; if the first is passed in a or hl, and the second has 16 bits, it is passed in de; all other parameters are passed on the stack, right-to-left. Independent of their size, struct / union parameters and all following parameters are always passed on the stack.