Z-80 Code Generation with SDCC
I started looking at how sdcc compiles C for the Z-80. In order to write assembly for the processor, I need to knwo the calling convention. After all, I need to write putchar() and getchar() to use the GPIO/SPI CPLD to talk to the MAX3110E.
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.
1 int plus(int a, int b, int c)
2 {
3 int sum;
4 sum = a + b + c;
5 return sum;
6 }
7
8
9 int plusplus(int d, int e, int f)
10 {
11 int sum = 0;
12 int i;
13 for (i = 0; i < 5; i++)
14 {
15 sum += plus(d, e, f);
16 }
17 return sum;
18 }
Here is the assembly. The <– notes are mine.
1 ;--------------------------------------------------------
2 ; File Created by SDCC : free open source ANSI-C Compiler
3 ; Version 3.5.2 #9283 (MINGW64)
4 ; This file was generated Mon Aug 17 20:12:13 2015
5 ;--------------------------------------------------------
6 .module argtest
7 .optsdcc -mz80
8
9 ;--------------------------------------------------------
10 ; Public variables in this module
11 ;--------------------------------------------------------
12 .globl _plusplus
13 .globl _plus
14 ;--------------------------------------------------------
15 ; special function registers
16 ;--------------------------------------------------------
17 ;--------------------------------------------------------
18 ; ram data
19 ;--------------------------------------------------------
20 .area _DATA
21 ;--------------------------------------------------------
22 ; ram data
23 ;--------------------------------------------------------
24 .area _INITIALIZED
25 ;--------------------------------------------------------
26 ; absolute external ram data
27 ;--------------------------------------------------------
28 .area _DABS (ABS)
29 ;--------------------------------------------------------
30 ; global & static initialisations
31 ;--------------------------------------------------------
32 .area _HOME
33 .area _GSINIT
34 .area _GSFINAL
35 .area _GSINIT
36 ;--------------------------------------------------------
37 ; Home
38 ;--------------------------------------------------------
39 .area _HOME
40 .area _HOME
41 ;--------------------------------------------------------
42 ; code
43 ;--------------------------------------------------------
44 .area _CODE
45 ;argtest.c:1: int plus(int a, int b, int c)
46 ; ---------------------------------
47 ; Function plus
48 ; ---------------------------------
49 _plus::
50 ;argtest.c:4: sum = a + b + c;
51 ld hl,#4 <-- No IX. Why?
52 add hl,sp
53 ld iy,#2
54 add iy,sp
55 ld a,0 (iy)
56 add a, (hl)
57 ld d,a
58 ld a,1 (iy)
59 inc hl
60 adc a, (hl)
61 ld e,a
62 ld a,d
63 ld hl,#6
64 add hl,sp
65 add a, (hl)
66 ld d,a
67 ld a,e
68 inc hl
69 adc a, (hl)
70 ld h,a
71 ld l, d
72 ;argtest.c:5: return sum;
73 ret
74 ;argtest.c:9: int plusplus(int d, int e, int f)
75 ; ---------------------------------
76 ; Function plusplus
77 ; ---------------------------------
78 _plusplus::
79 call ___sdcc_enter_ix ; <-- sets up ix -- see below
80 ;argtest.c:11: int sum = 0;
81 ;argtest.c:13: for (i = 0; i < 5; i++)
82 ld hl,#0x0000
83 ld e,l
84 ld d,h
85 00102$:
86 ;argtest.c:15: sum += plus(d, e, f);
87 push hl ; <-- caller save HL for for _plus (sum)
88 push de ; <-- caller save DE for for _plus (i)
89 ld c,8 (ix)
90 ld b,9 (ix)
91 push bc
92 ld c,6 (ix)
93 ld b,7 (ix)
94 push bc
95 ld c,4 (ix)
96 ld b,5 (ix)
97 push bc
98 call _plus
99 pop af <-- throw away passed parameter d
100 pop af <-- throw away passed parameter e
101 pop af <-- throw away passed parameter f
102 ld c,l <-- return value low
103 ld b,h <-- return value high
104 pop de <-- restore caller saved registers
105 pop hl <-- restore caller saved registers
106 add hl,bc
107 ;argtest.c:13: for (i = 0; i < 5; i++)
108 inc de
109 ld a,e
110 sub a, #0x05
111 ld a,d
112 rla
113 ccf
114 rra
115 sbc a, #0x80
116 jr C,00102$
117 ;argtest.c:17: return sum;
118 pop ix
119 ret ; <-- HL already has sum
120 .area _CODE
121 .area _INITIALIZER
122 .area _CABS (ABS)
This is part of the library:
1 ;--------------------------------------------------------------------------
2 ; crtenter.s
3 ;
4 ; Copyright (C) 2015, Alan Cox, Philipp Klaus Krause
5 ;
6 ; This library is free software; you can redistribute it and/or modify it
7 ; under the terms of the GNU General Public License as published by the
8 ; Free Software Foundation; either version 2, or (at your option) any
9 ; later version.
10 ;
11 ; This library is distributed in the hope that it will be useful,
12 ; but WITHOUT ANY WARRANTY; without even the implied warranty of
13 ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 ; GNU General Public License for more details.
15 ;
16 ; You should have received a copy of the GNU General Public License
17 ; along with this library; see the file COPYING. If not, write to the
18 ; Free Software Foundation, 51 Franklin Street, Fifth Floor, Boston,
19 ; MA 02110-1301, USA.
20 ;
21 ; As a special exception, if you link this library with other files,
22 ; some of which are compiled with SDCC, to produce an executable,
23 ; this library does not by itself cause the resulting executable to
24 ; be covered by the GNU General Public License. This exception does
25 ; not however invalidate any other reasons why the executable file
26 ; might be covered by the GNU General Public License.
27 ;--------------------------------------------------------------------------
28
29 .area _CODE
30
31 .globl ___sdcc_enter_ix
32
33 ; Factor out some start of function code to reduce code size
34
35 ___sdcc_enter_ix:
36 pop hl ; return address
37 push ix ; save frame pointer
38 ld ix, #0
39 add ix, sp ; set ix to the stack frame
40 jp (hl) ; and return