The calling convention is that arguments are pushed onto the stack from right to left. Return values are returned in L for 8 bit, HL for 16 bit, and DEHL for 32 bit return values.
The other part that's important to know is who saves registers. The default is that the caller saves them. The problem is when the caller calls a small function that doens't use registers. In this case, it would be nice to let the the callee save the registers. One way of doing this is using the --callee-saves function-list or --callee-saves-all. Also the __naked option can be used on the function definition. Finally, #pragma callee_saves can be used. Except, I didn't notice any of that working. SO right now, I'm concluding that it is not supported on the Z-80 port. So, I'll just assume it's always caller saved and the functions can just use any register (except IX).
I'm not sure why some functions use IX and some don't. But it probably doesn't really matter too much. I just have to know how to use IX if I want and how not to if I don't.
Here is some sample C code to see how the calling convention works.
int plus(int a, int b, int c)
{
int sum;
sum = a + b + c;
return sum;
}
int plusplus(int d, int e, int f)
{
int sum = 0;
int i;
for (i = 0; i < 5; i++)
{
sum += plus(d, e, f);
}
return sum;
}
Here is the assembly. The <-- notes are mine.
;--------------------------------------------------------
; File Created by SDCC : free open source ANSI-C Compiler
; Version 3.5.2 #9283 (MINGW64)
; This file was generated Mon Aug 17 20:12:13 2015
;--------------------------------------------------------
.module argtest
.optsdcc -mz80
;--------------------------------------------------------
; Public variables in this module
;--------------------------------------------------------
.globl _plusplus
.globl _plus
;--------------------------------------------------------
; special function registers
;--------------------------------------------------------
;--------------------------------------------------------
; ram data
;--------------------------------------------------------
.area _DATA
;--------------------------------------------------------
; ram data
;--------------------------------------------------------
.area _INITIALIZED
;--------------------------------------------------------
; absolute external ram data
;--------------------------------------------------------
.area _DABS (ABS)
;--------------------------------------------------------
; global & static initialisations
;--------------------------------------------------------
.area _HOME
.area _GSINIT
.area _GSFINAL
.area _GSINIT
;--------------------------------------------------------
; Home
;--------------------------------------------------------
.area _HOME
.area _HOME
;--------------------------------------------------------
; code
;--------------------------------------------------------
.area _CODE
;argtest.c:1: int plus(int a, int b, int c)
; ---------------------------------
; Function plus
; ---------------------------------
_plus::
;argtest.c:4: sum = a + b + c;
ld hl,#4 <-- No IX. Why?
add hl,sp
ld iy,#2
add iy,sp
ld a,0 (iy)
add a, (hl)
ld d,a
ld a,1 (iy)
inc hl
adc a, (hl)
ld e,a
ld a,d
ld hl,#6
add hl,sp
add a, (hl)
ld d,a
ld a,e
inc hl
adc a, (hl)
ld h,a
ld l, d
;argtest.c:5: return sum;
ret
;argtest.c:9: int plusplus(int d, int e, int f)
; ---------------------------------
; Function plusplus
; ---------------------------------
_plusplus::
call ___sdcc_enter_ix <-- sets up ix -- see below
;argtest.c:11: int sum = 0;
;argtest.c:13: for (i = 0; i < 5; i++)
ld hl,#0x0000
ld e,l
ld d,h
00102$:
;argtest.c:15: sum += plus(d, e, f);
push hl <-- caller save HL for for _plus (sum)
push de <-- caller save DE for for _plus (i)
ld c,8 (ix)
ld b,9 (ix)
push bc
ld c,6 (ix)
ld b,7 (ix)
push bc
ld c,4 (ix)
ld b,5 (ix)
push bc
call _plus
pop af <-- throw away passed parameter d
pop af <-- throw away passed parameter e
pop af <-- throw away passed parameter f
ld c,l <-- return value low
ld b,h <-- return value high
pop de <-- restore caller saved registers
pop hl <-- restore caller saved registers
add hl,bc
;argtest.c:13: for (i = 0; i < 5; i++)
inc de
ld a,e
sub a, #0x05
ld a,d
rla
ccf
rra
sbc a, #0x80
jr C,00102$
;argtest.c:17: return sum;
pop ix
ret <-- HL already has sum
.area _CODE
.area _INITIALIZER
.area _CABS (ABS)
This is part of the library:
;--------------------------------------------------------------------------
; crtenter.s
;
; Copyright (C) 2015, Alan Cox, Philipp Klaus Krause
;
; This library is free software; you can redistribute it and/or modify it
; under the terms of the GNU General Public License as published by the
; Free Software Foundation; either version 2, or (at your option) any
; later version.
;
; This library is distributed in the hope that it will be useful,
; but WITHOUT ANY WARRANTY; without even the implied warranty of
; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
; GNU General Public License for more details.
;
; You should have received a copy of the GNU General Public License
; along with this library; see the file COPYING. If not, write to the
; Free Software Foundation, 51 Franklin Street, Fifth Floor, Boston,
; MA 02110-1301, USA.
;
; As a special exception, if you link this library with other files,
; some of which are compiled with SDCC, to produce an executable,
; this library does not by itself cause the resulting executable to
; be covered by the GNU General Public License. This exception does
; not however invalidate any other reasons why the executable file
; might be covered by the GNU General Public License.
;--------------------------------------------------------------------------
.area _CODE
.globl ___sdcc_enter_ix
; Factor out some start of function code to reduce code size
___sdcc_enter_ix:
pop hl ; return address
push ix ; save frame pointer
ld ix, #0
add ix, sp ; set ix to the stack frame
jp (hl) ; and return
No comments:
Post a Comment