Critical Naked Interrupts and Unpromoted Characters -- In SDCC
I decided to compile a test file just to see exactly what __critical, __naked, and __interrupt do. Here are the results.
__critical causes the prolog to insert:
1 ld a,i
2 di
3 push af
which saves the interrupt register to into the accumulator and then pushes it onto the stack.
The epilog is:
1 pop af
2 ret po
3 ei
4 ret
which is curious because what’s that return on parity odd? Well, if the interrupts were already disabled when calling di, they shouldn’t be reenabled. It would be possible to use a reference count to keep track of nested __critical functions, but this works as well. When the ld a,i is executed, PO gets the current enabled/disabled state. If the P flag is PO, then the interrupts were disabled coming and won’t be enabled.
Is this better than a reference count? If it’s used lightly, cetainly. It’s a total of 7 bytes with 2 bytes overhead per nested call. A reference count would be 6 bytes, with how many every bytes are needed to store the count and use the count.
__naked causes the prolog and epilog to not generate. The code is generated according to the calling convention. But no registers are saved or restored, no return is generated, and function level
__critical doesn’t take effect because that inserts the critical code into the prolog and epilogs.
__interrupt causes function to save and restore AF, BC, DE, HL, and IY. The return is reti.
__naked __interrupt is equivalent to __naked since __interrupt only affects the prolog and epilog.
__critical __interrupt is used to emit the retn instead of reti for returning from a non-maskable interrupt.
Other testing also shows that the __critical { … } works as expected, putting the same di, ei, jp PO into the code itself.
Finally, a note on integer promotion of function arguments. SDCC doesn’t follow the C standard of promoting chars to ints. And thank goodness! Because although an int is 16 bits, the Z-80 works best with 8 bit data, so coercing it to 16 bits when it can be treated as 8 bit data would be counter productive.
For reference, the test.c file is:
1 #include <stdio.h>
2
3 int main(char* argv[], int argc)
4 {
5 argc; argv;
6 printf("Hello, World!");
7 return 0;
8 }
9
10 int test0(int a, int b)
11 {
12 return a+b;
13 }
14 int test0c(int a, int b) __critical
15 {
16 return a+b;
17 }
18 int test0n(int a, int b) __naked
19 {
20 return a+b;
21 }
22 int test0cn(int a, int b) __critical __naked
23 {
24 return a+b;
25 }
26
27 void test0i() __interrupt
28 {
29 __asm__("nop");
30 }
31 void test0in() __interrupt __naked
32 {
33 __asm__("nop");
34 }
35 void test0ic() __interrupt __critical
36 {
37 __asm__("nop");
38 }
And the test.asm file is:
1 ;--------------------------------------------------------
2 ; File Created by SDCC : free open source ANSI-C Compiler
3 ; Version 3.5.2 #9283 (MINGW64)
4 ; This file was generated Wed Sep 02 15:19:10 2015
5 ;--------------------------------------------------------
6 .module test
7 .optsdcc -mz80
8
9 ;--------------------------------------------------------
10 ; Public variables in this module
11 ;--------------------------------------------------------
12 .globl _test0ic
13 .globl _test0in
14 .globl _test0i
15 .globl _test0cn
16 .globl _test0n
17 .globl _test0c
18 .globl _test0
19 .globl _main
20 .globl _printf
21 ;--------------------------------------------------------
22 ; special function registers
23 ;--------------------------------------------------------
24 ;--------------------------------------------------------
25 ; ram data
26 ;--------------------------------------------------------
27 .area _DATA
28 ;--------------------------------------------------------
29 ; ram data
30 ;--------------------------------------------------------
31 .area _INITIALIZED
32 ;--------------------------------------------------------
33 ; absolute external ram data
34 ;--------------------------------------------------------
35 .area _DABS (ABS)
36 ;--------------------------------------------------------
37 ; global & static initialisations
38 ;--------------------------------------------------------
39 .area _HOME
40 .area _GSINIT
41 .area _GSFINAL
42 .area _GSINIT
43 ;--------------------------------------------------------
44 ; Home
45 ;--------------------------------------------------------
46 .area _HOME
47 .area _HOME
48 ;--------------------------------------------------------
49 ; code
50 ;--------------------------------------------------------
51 .area _CODE
52 ;test.c:3: int main(char* argv[], int argc)
53 ; ---------------------------------
54 ; Function main
55 ; ---------------------------------
56 _main::
57 ;test.c:6: printf("Hello, World!");
58 ld hl,#___str_0
59 push hl
60 call _printf
61 pop af
62 ;test.c:7: return 0;
63 ld hl,#0x0000
64 ret
65 ___str_0:
66 .ascii "Hello, World!"
67 .db 0x00
68 ;test.c:10: int test0(int a, int b)
69 ; ---------------------------------
70 ; Function test0
71 ; ---------------------------------
72 _test0::
73 ;test.c:12: return a+b;
74 ld hl,#4
75 add hl,sp
76 ld iy,#2
77 add iy,sp
78 ld a,0 (iy)
79 add a, (hl)
80 ld d,a
81 ld a,1 (iy)
82 inc hl
83 adc a, (hl)
84 ld e,a
85 ld l, d
86 ld h, e
87 ret
88 ;test.c:14: int test0c(int a, int b) __critical
89 ; ---------------------------------
90 ; Function test0c
91 ; ---------------------------------
92 _test0c::
93 ld a,i
94 di
95 push af
96 ;test.c:16: return a+b;
97 ld hl,#4
98 add hl,sp
99 ld iy,#2
100 add iy,sp
101 ld a,0 (iy)
102 add a, (hl)
103 ld d,a
104 ld a,1 (iy)
105 inc hl
106 adc a, (hl)
107 ld e,a
108 ld l, d
109 ld h, e
110 pop af
111 ret PO
112 ei
113 ret
114 ;test.c:18: int test0n(int a, int b) __naked
115 ; ---------------------------------
116 ; Function test0n
117 ; ---------------------------------
118 _test0n::
119 ;test.c:20: return a+b;
120 ld hl,#4
121 add hl,sp
122 ld iy,#2
123 add iy,sp
124 ld a,0 (iy)
125 add a, (hl)
126 ld d,a
127 ld a,1 (iy)
128 inc hl
129 adc a, (hl)
130 ld e,a
131 ld l, d
132 ld h, e
133 ;test.c:22: int test0cn(int a, int b) __critical __naked
134 ; ---------------------------------
135 ; Function test0cn
136 ; ---------------------------------
137 _test0cn::
138 ;test.c:24: return a+b;
139 ld hl,#4
140 add hl,sp
141 ld iy,#2
142 add iy,sp
143 ld a,0 (iy)
144 add a, (hl)
145 ld d,a
146 ld a,1 (iy)
147 inc hl
148 adc a, (hl)
149 ld e,a
150 ld l, d
151 ld h, e
152 ;test.c:27: void test0i() __interrupt
153 ; ---------------------------------
154 ; Function test0i
155 ; ---------------------------------
156 _test0i::
157 push af
158 push bc
159 push de
160 push hl
161 push iy
162 ;test.c:29: __asm__("nop");
163 nop
164 pop iy
165 pop hl
166 pop de
167 pop bc
168 pop af
169 reti
170 ;test.c:31: void test0in() __interrupt __naked
171 ; ---------------------------------
172 ; Function test0in
173 ; ---------------------------------
174 _test0in::
175 ;test.c:33: __asm__("nop");
176 nop
177 ;test.c:35: void test0ic() __interrupt __critical
178 ; ---------------------------------
179 ; Function test0ic
180 ; ---------------------------------
181 _test0ic::
182 push af
183 push bc
184 push de
185 push hl
186 push iy
187 ;test.c:37: __asm__("nop");
188 nop
189 pop iy
190 pop hl
191 pop de
192 pop bc
193 pop af
194 retn
195 .area _CODE
196 .area _INITIALIZER
197 .area _CABS (ABS)